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