1//===--- TweakTesting.h - Test helpers for refactoring actions ---*- C++-*-===//
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_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H
10#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H
11
12#include "ParsedAST.h"
13#include "index/Index.h"
14#include "llvm/ADT/StringMap.h"
15#include "llvm/ADT/StringRef.h"
16#include "llvm/Testing/Annotations/Annotations.h"
17#include "gmock/gmock.h"
18#include "gtest/gtest.h"
19#include <memory>
20#include <string>
21
22namespace clang {
23namespace clangd {
24
25// Fixture base for testing tweaks. Intended to be subclassed for each tweak.
26//
27// Usage:
28// TWEAK_TEST(ExpandDeducedType);
29//
30// TEST_F(ExpandDeducedTypeTest, ShortensTypes) {
31// Header = R"cpp(
32// namespace foo { template<typename> class X{}; }
33// using namespace foo;
34// )cpp";
35// Context = Function;
36// EXPECT_THAT(apply("[[auto]] X = foo<int>();"),
37// "foo<int> X = foo<int();");
38// EXPECT_AVAILABLE("^a^u^t^o^ X = foo<int>();");
39// EXPECT_UNAVAILABLE("auto ^X^ = ^foo<int>();");
40// }
41class TweakTest : public ::testing::Test {
42 const char *TweakID;
43
44public:
45 // Inputs are wrapped in file boilerplate before attempting to apply a tweak.
46 // Context describes the type of boilerplate.
47 enum CodeContext {
48 // Code snippet is placed directly into the source file. e.g. a declaration.
49 File,
50 // Snippet will appear within a function body. e.g. a statement.
51 Function,
52 // Snippet is an expression.
53 Expression,
54 };
55
56 // Mapping from file name to contents.
57 llvm::StringMap<std::string> ExtraFiles;
58
59protected:
60 TweakTest(const char *TweakID) : TweakID(TweakID) {}
61
62 // Contents of a header file to be implicitly included.
63 // This typically contains declarations that will be used for a set of related
64 // testcases.
65 std::string Header;
66
67 llvm::StringRef FileName = "TestTU.cpp";
68
69 // Extra flags passed to the compilation in apply().
70 std::vector<std::string> ExtraArgs;
71
72 // Context in which snippets of code should be placed to run tweaks.
73 CodeContext Context = File;
74
75 // Index to be passed into Tweak::Selection.
76 std::unique_ptr<const SymbolIndex> Index = nullptr;
77
78 // Apply the current tweak to the range (or point) in MarkedCode.
79 // MarkedCode will be wrapped according to the Context.
80 // - if the tweak produces edits, returns the edited code (without markings)
81 // for the main file.
82 // Populates \p EditedFiles if there were changes to other files whenever
83 // it is non-null. It is a mapping from absolute path of the edited file to
84 // its new contents. Passing a nullptr to \p EditedFiles when there are
85 // changes, will result in a failure.
86 // The context added to MarkedCode will be stripped away before returning,
87 // unless the tweak edited it.
88 // - if the tweak produces a message, returns "message:\n<message>"
89 // - if prepare() returns false, returns "unavailable"
90 // - if apply() returns an error, returns "fail: <message>"
91 std::string apply(llvm::StringRef MarkedCode,
92 llvm::StringMap<std::string> *EditedFiles = nullptr) const;
93
94 // Helpers for EXPECT_AVAILABLE/EXPECT_UNAVAILABLE macros.
95 using WrappedAST = std::pair<ParsedAST, /*WrappingOffset*/ unsigned>;
96 WrappedAST build(llvm::StringRef) const;
97 bool isAvailable(WrappedAST &, llvm::Annotations::Range) const;
98 // Return code re-decorated with a single point/range.
99 static std::string decorate(llvm::StringRef, unsigned);
100 static std::string decorate(llvm::StringRef, llvm::Annotations::Range);
101};
102
103MATCHER_P2(FileWithContents, FileName, Contents, "") {
104 return arg.first() == FileName && arg.second == Contents;
105}
106
107#define TWEAK_TEST(TweakID) \
108 class TweakID##Test : public ::clang::clangd::TweakTest { \
109 protected: \
110 TweakID##Test() : TweakTest(#TweakID) {} \
111 }
112
113#define EXPECT_AVAILABLE_(MarkedCode, Available) \
114 do { \
115 llvm::Annotations A{llvm::StringRef(MarkedCode)}; \
116 auto AST = build(A.code()); \
117 assert(!A.points().empty() || !A.ranges().empty()); \
118 for (const auto &P : A.points()) \
119 EXPECT_EQ(Available, isAvailable(AST, {P, P})) << decorate(A.code(), P); \
120 for (const auto &R : A.ranges()) \
121 EXPECT_EQ(Available, isAvailable(AST, R)) << decorate(A.code(), R); \
122 } while (0)
123#define EXPECT_AVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, true)
124#define EXPECT_UNAVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, false)
125
126} // namespace clangd
127} // namespace clang
128
129#endif
130

source code of clang-tools-extra/clangd/unittests/tweaks/TweakTesting.h