//===- unittests/libclang/TestUtils.h -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TEST_TESTUTILS_H #define LLVM_CLANG_TEST_TESTUTILS_H #include "clang-c/Index.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "gtest/gtest.h" #include #include #include #include #include class LibclangParseTest : public ::testing::Test { typedef std::unique_ptr fixed_addr_string; std::map UnsavedFileContents; public: // std::greater<> to remove files before their parent dirs in TearDown(). std::set> FilesAndDirsToRemove; std::string TestDir; bool RemoveTestDirRecursivelyDuringTeardown = false; CXIndex Index; CXTranslationUnit ClangTU; unsigned TUFlags; std::vector UnsavedFiles; void SetUp() override { llvm::SmallString<256> Dir; ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("libclang-test", Dir)); TestDir = std::string(Dir.str()); TUFlags = CXTranslationUnit_DetailedPreprocessingRecord | clang_defaultEditingTranslationUnitOptions(); CreateIndex(); ClangTU = nullptr; } void TearDown() override { clang_disposeTranslationUnit(ClangTU); clang_disposeIndex(Index); namespace fs = llvm::sys::fs; for (const std::string &Path : FilesAndDirsToRemove) EXPECT_FALSE(fs::remove(Path, /*IgnoreNonExisting=*/false)); if (RemoveTestDirRecursivelyDuringTeardown) EXPECT_FALSE(fs::remove_directories(TestDir, /*IgnoreErrors=*/false)); else EXPECT_FALSE(fs::remove(TestDir, /*IgnoreNonExisting=*/false)); } void WriteFile(std::string &Filename, const std::string &Contents) { if (!llvm::sys::path::is_absolute(Filename)) { llvm::SmallString<256> Path(TestDir); namespace path = llvm::sys::path; for (auto FileI = path::begin(Filename), FileEnd = path::end(Filename); FileI != FileEnd; ++FileI) { ASSERT_NE(*FileI, "."); path::append(Path, *FileI); FilesAndDirsToRemove.emplace(Path.str()); } Filename = std::string(Path.str()); } llvm::sys::fs::create_directories(llvm::sys::path::parent_path(Filename)); std::ofstream OS(Filename); OS << Contents; assert(OS.good()); } void MapUnsavedFile(std::string Filename, const std::string &Contents) { if (!llvm::sys::path::is_absolute(Filename)) { llvm::SmallString<256> Path(TestDir); llvm::sys::path::append(Path, Filename); Filename = std::string(Path.str()); } auto it = UnsavedFileContents.insert(std::make_pair( fixed_addr_string(new std::string(Filename)), fixed_addr_string(new std::string(Contents)))); UnsavedFiles.push_back({ it.first->first->c_str(), // filename it.first->second->c_str(), // contents it.first->second->size() // length }); } template void Traverse(const CXCursor &cursor, const F &TraversalFunctor) { std::reference_wrapper FunctorRef = std::cref(TraversalFunctor); clang_visitChildren(cursor, &TraverseStateless>, &FunctorRef); } template void Traverse(const F &TraversalFunctor) { Traverse(clang_getTranslationUnitCursor(ClangTU), TraversalFunctor); } static std::string fromCXString(CXString cx_string) { std::string string{clang_getCString(cx_string)}; clang_disposeString(cx_string); return string; }; protected: virtual void CreateIndex() { Index = clang_createIndex(0, 0); } private: template static CXChildVisitResult TraverseStateless(CXCursor cx, CXCursor parent, CXClientData data) { TState *State = static_cast(data); return State->get()(cx, parent); } }; #endif // LLVM_CLANG_TEST_TESTUTILS_H