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
|