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 | |
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 | } |
46 | void TearDown() override { |
47 | clang_disposeTranslationUnit(ClangTU); |
48 | clang_disposeIndex(index: 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 | } |
58 | void WriteFile(std::string &Filename, const std::string &Contents) { |
59 | if (!llvm::sys::path::is_absolute(path: Filename)) { |
60 | llvm::SmallString<256> Path(TestDir); |
61 | namespace path = llvm::sys::path; |
62 | for (auto FileI = path::begin(path: Filename), FileEnd = path::end(path: Filename); |
63 | FileI != FileEnd; ++FileI) { |
64 | ASSERT_NE(*FileI, "." ); |
65 | path::append(path&: Path, a: *FileI); |
66 | FilesAndDirsToRemove.emplace(args: Path.str()); |
67 | } |
68 | Filename = std::string(Path.str()); |
69 | } |
70 | llvm::sys::fs::create_directories(path: llvm::sys::path::parent_path(path: Filename)); |
71 | std::ofstream OS(Filename); |
72 | OS << Contents; |
73 | assert(OS.good()); |
74 | } |
75 | void MapUnsavedFile(std::string Filename, const std::string &Contents) { |
76 | if (!llvm::sys::path::is_absolute(path: Filename)) { |
77 | llvm::SmallString<256> Path(TestDir); |
78 | llvm::sys::path::append(path&: Path, a: Filename); |
79 | Filename = std::string(Path.str()); |
80 | } |
81 | auto it = UnsavedFileContents.insert(x: std::make_pair( |
82 | x: fixed_addr_string(new std::string(Filename)), |
83 | y: fixed_addr_string(new std::string(Contents)))); |
84 | UnsavedFiles.push_back(x: { |
85 | .Filename: it.first->first->c_str(), // filename |
86 | .Contents: it.first->second->c_str(), // contents |
87 | .Length: it.first->second->size() // length |
88 | }); |
89 | } |
90 | template <typename F> |
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 | |
98 | template <typename F> void Traverse(const F &TraversalFunctor) { |
99 | Traverse(clang_getTranslationUnitCursor(ClangTU), TraversalFunctor); |
100 | } |
101 | |
102 | static std::string fromCXString(CXString cx_string) { |
103 | std::string string{clang_getCString(string: cx_string)}; |
104 | clang_disposeString(string: cx_string); |
105 | return string; |
106 | }; |
107 | |
108 | protected: |
109 | virtual void CreateIndex() { Index = clang_createIndex(excludeDeclarationsFromPCH: 0, displayDiagnostics: 0); } |
110 | |
111 | private: |
112 | template<typename TState> |
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 | |