Skip to content

Commit be32cf7

Browse files
HighCommander4clingfei
authored andcommitted
[clangd][Tweaks] Improve test infra for testing tweaks with an index (llvm#160391)
1 parent 403e3da commit be32cf7

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ namespace clang {
1515
namespace clangd {
1616
namespace {
1717

18+
using ::testing::UnorderedElementsAre;
19+
1820
TWEAK_TEST(DefineOutline);
1921

2022
TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
@@ -747,6 +749,43 @@ TEST_F(DefineOutlineTest, FailsMacroSpecifier) {
747749
}
748750
}
749751

752+
TWEAK_WORKSPACE_TEST(DefineOutline);
753+
754+
// Test that DefineOutline's use of getCorrespondingHeaderOrSource()
755+
// to find the source file corresponding to a header file in which the
756+
// tweak is invoked is working as intended.
757+
TEST_F(DefineOutlineWorkspaceTest, FindsCorrespondingSource) {
758+
llvm::Annotations HeaderBefore(R"cpp(
759+
class A {
760+
void bar();
761+
void f^oo(){}
762+
};
763+
)cpp");
764+
std::string SourceBefore(R"cpp(
765+
#include "a.hpp"
766+
void A::bar(){}
767+
)cpp");
768+
std::string HeaderAfter = R"cpp(
769+
class A {
770+
void bar();
771+
void foo();
772+
};
773+
)cpp";
774+
std::string SourceAfter = R"cpp(
775+
#include "a.hpp"
776+
void A::bar(){}
777+
void A::foo(){}
778+
)cpp";
779+
Workspace.addSource("a.hpp", HeaderBefore.code());
780+
Workspace.addMainFile("a.cpp", SourceBefore);
781+
auto Result = apply("a.hpp", {HeaderBefore.point(), HeaderBefore.point()});
782+
EXPECT_THAT(Result,
783+
AllOf(withStatus("success"),
784+
editedFiles(UnorderedElementsAre(
785+
FileWithContents(testPath("a.hpp"), HeaderAfter),
786+
FileWithContents(testPath("a.cpp"), SourceAfter)))));
787+
}
788+
750789
} // namespace
751790
} // namespace clangd
752791
} // namespace clang

clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,37 @@ std::string TweakTest::decorate(llvm::StringRef Code,
162162
.str();
163163
}
164164

165+
// TODO: Reuse more code between TweakTest::apply() and
166+
// TweakWorkspaceTest::apply().
167+
TweakResult
168+
TweakWorkspaceTest::apply(StringRef InvocationFile,
169+
llvm::Annotations::Range InvocationRange) {
170+
auto AST = Workspace.openFile(InvocationFile);
171+
if (!AST) {
172+
ADD_FAILURE() << "No file '" << InvocationFile << "' in workspace";
173+
return TweakResult{"failed to setup"};
174+
}
175+
176+
auto Index = Workspace.index();
177+
auto Result = applyTweak(
178+
*AST, InvocationRange, TweakID, Index.get(),
179+
&AST->getSourceManager().getFileManager().getVirtualFileSystem());
180+
if (!Result)
181+
return TweakResult{"unavailable"};
182+
if (!*Result)
183+
return TweakResult{"fail: " + llvm::toString(Result->takeError())};
184+
const auto &Effect = **Result;
185+
if ((*Result)->ShowMessage)
186+
return TweakResult{"message:\n" + *Effect.ShowMessage};
187+
188+
TweakResult Retval{"success"};
189+
for (auto &It : Effect.ApplyEdits) {
190+
auto NewText = It.second.apply();
191+
if (!NewText)
192+
return TweakResult{"bad edits: " + llvm::toString(NewText.takeError())};
193+
Retval.EditedFiles.insert_or_assign(It.first(), *NewText);
194+
}
195+
return Retval;
196+
}
165197
} // namespace clangd
166198
} // namespace clang

clang-tools-extra/clangd/unittests/tweaks/TweakTesting.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H
1111

1212
#include "ParsedAST.h"
13+
#include "TestWorkspace.h"
1314
#include "index/Index.h"
1415
#include "llvm/ADT/StringMap.h"
1516
#include "llvm/ADT/StringRef.h"
@@ -123,6 +124,73 @@ MATCHER_P2(FileWithContents, FileName, Contents, "") {
123124
#define EXPECT_AVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, true)
124125
#define EXPECT_UNAVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, false)
125126

127+
// A helper class to represent the return value of TweakWorkspaceTest::apply().
128+
struct TweakResult {
129+
// A string representation the status of the operation.
130+
// For failure cases, this is the same as the return value of
131+
// TweakTest::apply() (see the comment above that for details).
132+
// For success cases, this is "success".
133+
std::string Status;
134+
// The contents of all files changed by the tweak, including
135+
// the file in which it was invoked. Keys are absolute paths.
136+
llvm::StringMap<std::string> EditedFiles = {};
137+
};
138+
139+
// GTest matchers to allow more easily writing assertions about the
140+
// expected value of a TweakResult.
141+
MATCHER_P(withStatus, S, "") { return arg.Status == S; }
142+
template <class EditedFilesMatcher>
143+
::testing::Matcher<TweakResult> editedFiles(EditedFilesMatcher M) {
144+
return ::testing::Field(&TweakResult::EditedFiles, M);
145+
}
146+
147+
// Used for formatting TweakResult objects in assertion failure messages,
148+
// so it's easier to understand what didn't match.
149+
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
150+
const TweakResult &Result) {
151+
Stream << "{ status: " << Result.Status << ", editedFiles: [";
152+
for (const auto &F : Result.EditedFiles) {
153+
Stream << F.first() << ":\n";
154+
Stream << F.second;
155+
}
156+
return Stream << "] }";
157+
}
158+
159+
// A version of TweakTest that makes it easier to create test cases that
160+
// involve multiple files which are indexed.
161+
// Usage:
162+
// - Call `Workspace.addMainFile(filename, contents)` to add
163+
// source files which are indexer entry points (e.g. would show
164+
// up in `compile_commands.json`).
165+
// - Call `Workspace.addSource(filename, contents)` to add other
166+
// source files (e.g. header files).
167+
// - Call `apply(filename, range)` to invoke the tweak on the
168+
// indicated file with the given range selected. Can be called
169+
// multiple times for the same set of added files.
170+
// The implementation takes care of building an index reflecting
171+
// all added source files, and making it available to the tweak.
172+
// Unlike TweakTest, this does not have a notion of a `CodeContext`
173+
// (i.e. the contents of all added files are interpreted as being
174+
// in a File context).
175+
class TweakWorkspaceTest : public ::testing::Test {
176+
const char *TweakID;
177+
178+
public:
179+
TweakWorkspaceTest(const char *TweakID) : TweakID(TweakID) {}
180+
181+
TweakResult apply(StringRef InvocationFile,
182+
llvm::Annotations::Range InvocationRange);
183+
184+
protected:
185+
TestWorkspace Workspace;
186+
};
187+
188+
#define TWEAK_WORKSPACE_TEST(TweakID) \
189+
class TweakID##WorkspaceTest : public ::clang::clangd::TweakWorkspaceTest { \
190+
protected: \
191+
TweakID##WorkspaceTest() : TweakWorkspaceTest(#TweakID) {} \
192+
}
193+
126194
} // namespace clangd
127195
} // namespace clang
128196

0 commit comments

Comments
 (0)