15934a791SAdam Czachorowski //===--- TweakTesting.h - Test helpers for refactoring actions ---*- C++-*-===// 25934a791SAdam Czachorowski // 35934a791SAdam Czachorowski // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45934a791SAdam Czachorowski // See https://llvm.org/LICENSE.txt for license information. 55934a791SAdam Czachorowski // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65934a791SAdam Czachorowski // 75934a791SAdam Czachorowski //===----------------------------------------------------------------------===// 85934a791SAdam Czachorowski 95bd643d3SChristian Kühnel #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H 105bd643d3SChristian Kühnel #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H 115934a791SAdam Czachorowski 12a316a981SSam McCall #include "ParsedAST.h" 135934a791SAdam Czachorowski #include "index/Index.h" 145934a791SAdam Czachorowski #include "llvm/ADT/StringMap.h" 155934a791SAdam Czachorowski #include "llvm/ADT/StringRef.h" 163432f4bfSJordan Rupprecht #include "llvm/Testing/Annotations/Annotations.h" 175934a791SAdam Czachorowski #include "gmock/gmock.h" 185934a791SAdam Czachorowski #include "gtest/gtest.h" 195934a791SAdam Czachorowski #include <memory> 205934a791SAdam Czachorowski #include <string> 215934a791SAdam Czachorowski 225934a791SAdam Czachorowski namespace clang { 235934a791SAdam Czachorowski namespace clangd { 245934a791SAdam Czachorowski 255934a791SAdam Czachorowski // Fixture base for testing tweaks. Intended to be subclassed for each tweak. 265934a791SAdam Czachorowski // 275934a791SAdam Czachorowski // Usage: 28*1feb7af0Sv1nh1shungry // TWEAK_TEST(ExpandDeducedType); 295934a791SAdam Czachorowski // 30*1feb7af0Sv1nh1shungry // TEST_F(ExpandDeducedTypeTest, ShortensTypes) { 315934a791SAdam Czachorowski // Header = R"cpp( 325934a791SAdam Czachorowski // namespace foo { template<typename> class X{}; } 335934a791SAdam Czachorowski // using namespace foo; 345934a791SAdam Czachorowski // )cpp"; 355934a791SAdam Czachorowski // Context = Function; 365934a791SAdam Czachorowski // EXPECT_THAT(apply("[[auto]] X = foo<int>();"), 375934a791SAdam Czachorowski // "foo<int> X = foo<int();"); 385934a791SAdam Czachorowski // EXPECT_AVAILABLE("^a^u^t^o^ X = foo<int>();"); 395934a791SAdam Czachorowski // EXPECT_UNAVAILABLE("auto ^X^ = ^foo<int>();"); 405934a791SAdam Czachorowski // } 415934a791SAdam Czachorowski class TweakTest : public ::testing::Test { 425934a791SAdam Czachorowski const char *TweakID; 435934a791SAdam Czachorowski 445934a791SAdam Czachorowski public: 455934a791SAdam Czachorowski // Inputs are wrapped in file boilerplate before attempting to apply a tweak. 465934a791SAdam Czachorowski // Context describes the type of boilerplate. 475934a791SAdam Czachorowski enum CodeContext { 485934a791SAdam Czachorowski // Code snippet is placed directly into the source file. e.g. a declaration. 495934a791SAdam Czachorowski File, 505934a791SAdam Czachorowski // Snippet will appear within a function body. e.g. a statement. 515934a791SAdam Czachorowski Function, 525934a791SAdam Czachorowski // Snippet is an expression. 535934a791SAdam Czachorowski Expression, 545934a791SAdam Czachorowski }; 555934a791SAdam Czachorowski 565934a791SAdam Czachorowski // Mapping from file name to contents. 575934a791SAdam Czachorowski llvm::StringMap<std::string> ExtraFiles; 585934a791SAdam Czachorowski 595934a791SAdam Czachorowski protected: TweakTest(const char * TweakID)605934a791SAdam Czachorowski TweakTest(const char *TweakID) : TweakID(TweakID) {} 615934a791SAdam Czachorowski 625934a791SAdam Czachorowski // Contents of a header file to be implicitly included. 635934a791SAdam Czachorowski // This typically contains declarations that will be used for a set of related 645934a791SAdam Czachorowski // testcases. 655934a791SAdam Czachorowski std::string Header; 665934a791SAdam Czachorowski 675934a791SAdam Czachorowski llvm::StringRef FileName = "TestTU.cpp"; 685934a791SAdam Czachorowski 695934a791SAdam Czachorowski // Extra flags passed to the compilation in apply(). 705934a791SAdam Czachorowski std::vector<std::string> ExtraArgs; 715934a791SAdam Czachorowski 725934a791SAdam Czachorowski // Context in which snippets of code should be placed to run tweaks. 735934a791SAdam Czachorowski CodeContext Context = File; 745934a791SAdam Czachorowski 755934a791SAdam Czachorowski // Index to be passed into Tweak::Selection. 765934a791SAdam Czachorowski std::unique_ptr<const SymbolIndex> Index = nullptr; 775934a791SAdam Czachorowski 785934a791SAdam Czachorowski // Apply the current tweak to the range (or point) in MarkedCode. 795934a791SAdam Czachorowski // MarkedCode will be wrapped according to the Context. 805934a791SAdam Czachorowski // - if the tweak produces edits, returns the edited code (without markings) 815934a791SAdam Czachorowski // for the main file. 825934a791SAdam Czachorowski // Populates \p EditedFiles if there were changes to other files whenever 835934a791SAdam Czachorowski // it is non-null. It is a mapping from absolute path of the edited file to 845934a791SAdam Czachorowski // its new contents. Passing a nullptr to \p EditedFiles when there are 855934a791SAdam Czachorowski // changes, will result in a failure. 865934a791SAdam Czachorowski // The context added to MarkedCode will be stripped away before returning, 875934a791SAdam Czachorowski // unless the tweak edited it. 885934a791SAdam Czachorowski // - if the tweak produces a message, returns "message:\n<message>" 895934a791SAdam Czachorowski // - if prepare() returns false, returns "unavailable" 905934a791SAdam Czachorowski // - if apply() returns an error, returns "fail: <message>" 915934a791SAdam Czachorowski std::string apply(llvm::StringRef MarkedCode, 925934a791SAdam Czachorowski llvm::StringMap<std::string> *EditedFiles = nullptr) const; 935934a791SAdam Czachorowski 94a316a981SSam McCall // Helpers for EXPECT_AVAILABLE/EXPECT_UNAVAILABLE macros. 95a316a981SSam McCall using WrappedAST = std::pair<ParsedAST, /*WrappingOffset*/ unsigned>; 96a316a981SSam McCall WrappedAST build(llvm::StringRef) const; 97a316a981SSam McCall bool isAvailable(WrappedAST &, llvm::Annotations::Range) const; 98a316a981SSam McCall // Return code re-decorated with a single point/range. 99a316a981SSam McCall static std::string decorate(llvm::StringRef, unsigned); 100a316a981SSam McCall static std::string decorate(llvm::StringRef, llvm::Annotations::Range); 1015934a791SAdam Czachorowski }; 1025934a791SAdam Czachorowski 1035934a791SAdam Czachorowski MATCHER_P2(FileWithContents, FileName, Contents, "") { 1045934a791SAdam Czachorowski return arg.first() == FileName && arg.second == Contents; 1055934a791SAdam Czachorowski } 1065934a791SAdam Czachorowski 1075934a791SAdam Czachorowski #define TWEAK_TEST(TweakID) \ 1085934a791SAdam Czachorowski class TweakID##Test : public ::clang::clangd::TweakTest { \ 1095934a791SAdam Czachorowski protected: \ 1105934a791SAdam Czachorowski TweakID##Test() : TweakTest(#TweakID) {} \ 1115934a791SAdam Czachorowski } 1125934a791SAdam Czachorowski 113a316a981SSam McCall #define EXPECT_AVAILABLE_(MarkedCode, Available) \ 1145934a791SAdam Czachorowski do { \ 115a316a981SSam McCall llvm::Annotations A{llvm::StringRef(MarkedCode)}; \ 116a316a981SSam McCall auto AST = build(A.code()); \ 117a316a981SSam McCall assert(!A.points().empty() || !A.ranges().empty()); \ 118a316a981SSam McCall for (const auto &P : A.points()) \ 119a316a981SSam McCall EXPECT_EQ(Available, isAvailable(AST, {P, P})) << decorate(A.code(), P); \ 120a316a981SSam McCall for (const auto &R : A.ranges()) \ 121a316a981SSam McCall EXPECT_EQ(Available, isAvailable(AST, R)) << decorate(A.code(), R); \ 1225934a791SAdam Czachorowski } while (0) 123a316a981SSam McCall #define EXPECT_AVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, true) 124a316a981SSam McCall #define EXPECT_UNAVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, false) 1255934a791SAdam Czachorowski 1265934a791SAdam Czachorowski } // namespace clangd 1275934a791SAdam Czachorowski } // namespace clang 1285934a791SAdam Czachorowski 1295934a791SAdam Czachorowski #endif 130