File: TweakTesting.h

package info (click to toggle)
llvm-toolchain-19 1%3A19.1.7-3~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 1,998,492 kB
  • sloc: cpp: 6,951,680; ansic: 1,486,157; asm: 913,598; python: 232,024; f90: 80,126; objc: 75,281; lisp: 37,276; pascal: 16,990; sh: 10,009; ml: 5,058; perl: 4,724; awk: 3,523; makefile: 3,167; javascript: 2,504; xml: 892; fortran: 664; cs: 573
file content (129 lines) | stat: -rw-r--r-- 5,224 bytes parent folder | download | duplicates (12)
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
121
122
123
124
125
126
127
128
129
//===--- TweakTesting.h - Test helpers for refactoring actions ---*- C++-*-===//
//
// 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_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H

#include "ParsedAST.h"
#include "index/Index.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Testing/Annotations/Annotations.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>
#include <string>

namespace clang {
namespace clangd {

// Fixture base for testing tweaks. Intended to be subclassed for each tweak.
//
// Usage:
// TWEAK_TEST(ExpandDeducedType);
//
// TEST_F(ExpandDeducedTypeTest, ShortensTypes) {
//   Header = R"cpp(
//     namespace foo { template<typename> class X{}; }
//     using namespace foo;
//   )cpp";
//   Context = Function;
//   EXPECT_THAT(apply("[[auto]] X = foo<int>();"),
//               "foo<int> X = foo<int();");
//   EXPECT_AVAILABLE("^a^u^t^o^ X = foo<int>();");
//   EXPECT_UNAVAILABLE("auto ^X^ = ^foo<int>();");
// }
class TweakTest : public ::testing::Test {
  const char *TweakID;

public:
  // Inputs are wrapped in file boilerplate before attempting to apply a tweak.
  // Context describes the type of boilerplate.
  enum CodeContext {
    // Code snippet is placed directly into the source file. e.g. a declaration.
    File,
    // Snippet will appear within a function body. e.g. a statement.
    Function,
    // Snippet is an expression.
    Expression,
  };

  // Mapping from file name to contents.
  llvm::StringMap<std::string> ExtraFiles;

protected:
  TweakTest(const char *TweakID) : TweakID(TweakID) {}

  // Contents of a header file to be implicitly included.
  // This typically contains declarations that will be used for a set of related
  // testcases.
  std::string Header;

  llvm::StringRef FileName = "TestTU.cpp";

  // Extra flags passed to the compilation in apply().
  std::vector<std::string> ExtraArgs;

  // Context in which snippets of code should be placed to run tweaks.
  CodeContext Context = File;

  // Index to be passed into Tweak::Selection.
  std::unique_ptr<const SymbolIndex> Index = nullptr;

  // Apply the current tweak to the range (or point) in MarkedCode.
  // MarkedCode will be wrapped according to the Context.
  //  - if the tweak produces edits, returns the edited code (without markings)
  //    for the main file.
  //    Populates \p EditedFiles if there were changes to other files whenever
  //    it is non-null. It is a mapping from absolute path of the edited file to
  //    its new contents. Passing a nullptr to \p EditedFiles when there are
  //    changes, will result in a failure.
  //    The context added to MarkedCode will be stripped away before returning,
  //    unless the tweak edited it.
  //  - if the tweak produces a message, returns "message:\n<message>"
  //  - if prepare() returns false, returns "unavailable"
  //  - if apply() returns an error, returns "fail: <message>"
  std::string apply(llvm::StringRef MarkedCode,
                    llvm::StringMap<std::string> *EditedFiles = nullptr) const;

  // Helpers for EXPECT_AVAILABLE/EXPECT_UNAVAILABLE macros.
  using WrappedAST = std::pair<ParsedAST, /*WrappingOffset*/ unsigned>;
  WrappedAST build(llvm::StringRef) const;
  bool isAvailable(WrappedAST &, llvm::Annotations::Range) const;
  // Return code re-decorated with a single point/range.
  static std::string decorate(llvm::StringRef, unsigned);
  static std::string decorate(llvm::StringRef, llvm::Annotations::Range);
};

MATCHER_P2(FileWithContents, FileName, Contents, "") {
  return arg.first() == FileName && arg.second == Contents;
}

#define TWEAK_TEST(TweakID)                                                    \
  class TweakID##Test : public ::clang::clangd::TweakTest {                    \
  protected:                                                                   \
    TweakID##Test() : TweakTest(#TweakID) {}                                   \
  }

#define EXPECT_AVAILABLE_(MarkedCode, Available)                               \
  do {                                                                         \
    llvm::Annotations A{llvm::StringRef(MarkedCode)};                          \
    auto AST = build(A.code());                                                \
    assert(!A.points().empty() || !A.ranges().empty());                        \
    for (const auto &P : A.points())                                           \
      EXPECT_EQ(Available, isAvailable(AST, {P, P})) << decorate(A.code(), P); \
    for (const auto &R : A.ranges())                                           \
      EXPECT_EQ(Available, isAvailable(AST, R)) << decorate(A.code(), R);      \
  } while (0)
#define EXPECT_AVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, true)
#define EXPECT_UNAVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, false)

} // namespace clangd
} // namespace clang

#endif