xref: /llvm-project/clang/unittests/libclang/TestUtils.h (revision 86da763ab6ed19c58349d3f6f62d4bb52becab2c)
1 //===- unittests/libclang/TestUtils.h -------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_CLANG_TEST_TESTUTILS_H
10 #define LLVM_CLANG_TEST_TESTUTILS_H
11 
12 #include "clang-c/Index.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/Support/FileSystem.h"
15 #include "llvm/Support/Path.h"
16 
17 #include "gtest/gtest.h"
18 #include <fstream>
19 #include <functional>
20 #include <memory>
21 #include <string>
22 #include <vector>
23 
24 class LibclangParseTest : public ::testing::Test {
25   typedef std::unique_ptr<std::string> fixed_addr_string;
26   std::map<fixed_addr_string, fixed_addr_string> UnsavedFileContents;
27 public:
28   // std::greater<> to remove files before their parent dirs in TearDown().
29   std::set<std::string, std::greater<>> FilesAndDirsToRemove;
30   std::string TestDir;
31   bool RemoveTestDirRecursivelyDuringTeardown = false;
32   CXIndex Index;
33   CXTranslationUnit ClangTU;
34   unsigned TUFlags;
35   std::vector<CXUnsavedFile> UnsavedFiles;
36 
SetUp()37   void SetUp() override {
38     llvm::SmallString<256> Dir;
39     ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("libclang-test", Dir));
40     TestDir = std::string(Dir.str());
41     TUFlags = CXTranslationUnit_DetailedPreprocessingRecord |
42       clang_defaultEditingTranslationUnitOptions();
43     CreateIndex();
44     ClangTU = nullptr;
45   }
TearDown()46   void TearDown() override {
47     clang_disposeTranslationUnit(ClangTU);
48     clang_disposeIndex(Index);
49 
50     namespace fs = llvm::sys::fs;
51     for (const std::string &Path : FilesAndDirsToRemove)
52       EXPECT_FALSE(fs::remove(Path, /*IgnoreNonExisting=*/false));
53     if (RemoveTestDirRecursivelyDuringTeardown)
54       EXPECT_FALSE(fs::remove_directories(TestDir, /*IgnoreErrors=*/false));
55     else
56       EXPECT_FALSE(fs::remove(TestDir, /*IgnoreNonExisting=*/false));
57   }
WriteFile(std::string & Filename,const std::string & Contents)58   void WriteFile(std::string &Filename, const std::string &Contents) {
59     if (!llvm::sys::path::is_absolute(Filename)) {
60       llvm::SmallString<256> Path(TestDir);
61       namespace path = llvm::sys::path;
62       for (auto FileI = path::begin(Filename), FileEnd = path::end(Filename);
63            FileI != FileEnd; ++FileI) {
64         ASSERT_NE(*FileI, ".");
65         path::append(Path, *FileI);
66         FilesAndDirsToRemove.emplace(Path.str());
67       }
68       Filename = std::string(Path.str());
69     }
70     llvm::sys::fs::create_directories(llvm::sys::path::parent_path(Filename));
71     std::ofstream OS(Filename);
72     OS << Contents;
73     assert(OS.good());
74   }
MapUnsavedFile(std::string Filename,const std::string & Contents)75   void MapUnsavedFile(std::string Filename, const std::string &Contents) {
76     if (!llvm::sys::path::is_absolute(Filename)) {
77       llvm::SmallString<256> Path(TestDir);
78       llvm::sys::path::append(Path, Filename);
79       Filename = std::string(Path.str());
80     }
81     auto it = UnsavedFileContents.insert(std::make_pair(
82         fixed_addr_string(new std::string(Filename)),
83         fixed_addr_string(new std::string(Contents))));
84     UnsavedFiles.push_back({
85         it.first->first->c_str(),   // filename
86         it.first->second->c_str(),  // contents
87         it.first->second->size()    // length
88     });
89   }
90   template <typename F>
Traverse(const CXCursor & cursor,const F & TraversalFunctor)91   void Traverse(const CXCursor &cursor, const F &TraversalFunctor) {
92     std::reference_wrapper<const F> FunctorRef = std::cref(TraversalFunctor);
93     clang_visitChildren(cursor,
94                         &TraverseStateless<std::reference_wrapper<const F>>,
95                         &FunctorRef);
96   }
97 
Traverse(const F & TraversalFunctor)98   template <typename F> void Traverse(const F &TraversalFunctor) {
99     Traverse(clang_getTranslationUnitCursor(ClangTU), TraversalFunctor);
100   }
101 
fromCXString(CXString cx_string)102   static std::string fromCXString(CXString cx_string) {
103     std::string string{clang_getCString(cx_string)};
104     clang_disposeString(cx_string);
105     return string;
106   };
107 
108 protected:
CreateIndex()109   virtual void CreateIndex() { Index = clang_createIndex(0, 0); }
110 
111 private:
112   template<typename TState>
TraverseStateless(CXCursor cx,CXCursor parent,CXClientData data)113   static CXChildVisitResult TraverseStateless(CXCursor cx, CXCursor parent,
114       CXClientData data) {
115     TState *State = static_cast<TState*>(data);
116     return State->get()(cx, parent);
117   }
118 };
119 
120 #endif // LLVM_CLANG_TEST_TESTUTILS_H
121