aboutsummaryrefslogtreecommitdiff
path: root/clang/unittests/libclang/TestUtils.h
blob: 013aad17358211fe0f36731356ea8e3284316aa8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//===- 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 <fstream>
#include <functional>
#include <memory>
#include <string>
#include <vector>

class LibclangParseTest : public ::testing::Test {
  typedef std::unique_ptr<std::string> fixed_addr_string;
  std::map<fixed_addr_string, fixed_addr_string> UnsavedFileContents;
public:
  // std::greater<> to remove files before their parent dirs in TearDown().
  std::set<std::string, std::greater<>> FilesAndDirsToRemove;
  std::string TestDir;
  bool RemoveTestDirRecursivelyDuringTeardown = false;
  CXIndex Index;
  CXTranslationUnit ClangTU;
  unsigned TUFlags;
  std::vector<CXUnsavedFile> 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 <typename F>
  void Traverse(const CXCursor &cursor, const F &TraversalFunctor) {
    std::reference_wrapper<const F> FunctorRef = std::cref(TraversalFunctor);
    clang_visitChildren(cursor,
                        &TraverseStateless<std::reference_wrapper<const F>>,
                        &FunctorRef);
  }

  template <typename F> 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<typename TState>
  static CXChildVisitResult TraverseStateless(CXCursor cx, CXCursor parent,
      CXClientData data) {
    TState *State = static_cast<TState*>(data);
    return State->get()(cx, parent);
  }
};

#endif // LLVM_CLANG_TEST_TESTUTILS_H