1 //===--- TweakTesting.h - Test helpers for refactoring actions ---*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H
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"
25 // Fixture base for testing tweaks. Intended to be subclassed for each tweak.
28 // TWEAK_TEST(ExpandDeducedType);
30 // TEST_F(ExpandDeducedTypeTest, ShortensTypes) {
32 // namespace foo { template<typename> class X{}; }
33 // using namespace foo;
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>();");
41 class TweakTest
: public ::testing::Test
{
45 // Inputs are wrapped in file boilerplate before attempting to apply a tweak.
46 // Context describes the type of boilerplate.
48 // Code snippet is placed directly into the source file. e.g. a declaration.
50 // Snippet will appear within a function body. e.g. a statement.
52 // Snippet is an expression.
56 // Mapping from file name to contents.
57 llvm::StringMap
<std::string
> ExtraFiles
;
60 TweakTest(const char *TweakID
) : TweakID(TweakID
) {}
62 // Contents of a header file to be implicitly included.
63 // This typically contains declarations that will be used for a set of related
67 llvm::StringRef FileName
= "TestTU.cpp";
69 // Extra flags passed to the compilation in apply().
70 std::vector
<std::string
> ExtraArgs
;
72 // Context in which snippets of code should be placed to run tweaks.
73 CodeContext Context
= File
;
75 // Index to be passed into Tweak::Selection.
76 std::unique_ptr
<const SymbolIndex
> Index
= nullptr;
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)
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;
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
);
103 MATCHER_P2(FileWithContents
, FileName
, Contents
, "") {
104 return arg
.first() == FileName
&& arg
.second
== Contents
;
107 #define TWEAK_TEST(TweakID) \
108 class TweakID##Test : public ::clang::clangd::TweakTest { \
110 TweakID##Test() : TweakTest(#TweakID) {} \
113 #define EXPECT_AVAILABLE_(MarkedCode, Available) \
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); \
123 #define EXPECT_AVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, true)
124 #define EXPECT_UNAVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, false)
126 } // namespace clangd