21#include "llvm/Support/FileSystem.h"
22#include "llvm/Support/raw_ostream.h"
23#include "gmock/gmock.h"
24#include "gtest/gtest.h"
29class MockDirectoryCompilationDatabase :
public MockCompilationDatabase {
31 MockDirectoryCompilationDatabase(StringRef TestDir,
const ThreadsafeFS &TFS)
32 : MockCompilationDatabase(
TestDir),
33 MockedCDBPtr(std::make_shared<MockClangCompilationDatabase>(*this)),
35 this->ExtraClangFlags.push_back(
"-std=c++20");
36 this->ExtraClangFlags.push_back(
"-c");
39 void addFile(llvm::StringRef
Path, llvm::StringRef Contents);
41 std::unique_ptr<ProjectModules> getProjectModules(
PathRef)
const override {
46 class MockClangCompilationDatabase :
public tooling::CompilationDatabase {
48 MockClangCompilationDatabase(MockDirectoryCompilationDatabase &MCDB)
51 std::vector<tooling::CompileCommand>
52 getCompileCommands(StringRef FilePath)
const override {
53 std::optional<tooling::CompileCommand> Cmd =
54 MCDB.getCompileCommand(FilePath);
59 std::vector<std::string> getAllFiles()
const override {
return Files; }
61 void AddFile(StringRef
File) { Files.push_back(
File.str()); }
64 MockDirectoryCompilationDatabase &MCDB;
65 std::vector<std::string> Files;
68 std::shared_ptr<MockClangCompilationDatabase> MockedCDBPtr;
69 const ThreadsafeFS &TFS;
73void MockDirectoryCompilationDatabase::addFile(llvm::StringRef
Path,
74 llvm::StringRef Contents) {
75 ASSERT_FALSE(llvm::sys::path::is_absolute(
Path));
78 llvm::sys::path::append(AbsPath,
Path);
81 llvm::sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
84 llvm::raw_fd_ostream
OS(AbsPath, EC);
88 MockedCDBPtr->AddFile(
Path);
91class PrerequisiteModulesTests :
public ::testing::Test {
93 void SetUp()
override {
94 ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory(
"modules-test", TestDir));
97 void TearDown()
override {
98 ASSERT_FALSE(llvm::sys::fs::remove_directories(TestDir));
104 std::string getFullPath(llvm::StringRef
Path) {
105 SmallString<128> Result(TestDir);
106 llvm::sys::path::append(Result,
Path);
107 EXPECT_TRUE(llvm::sys::fs::exists(Result.str()));
108 return Result.str().str();
111 ParseInputs getInputs(llvm::StringRef
FileName,
112 const GlobalCompilationDatabase &CDB) {
113 std::string FullPathName = getFullPath(
FileName);
116 std::optional<tooling::CompileCommand> Cmd =
117 CDB.getCompileCommand(FullPathName);
119 Inputs.CompileCommand = std::move(*Cmd);
122 if (
auto Contents = FS.view(TestDir)->getBufferForFile(FullPathName))
123 Inputs.Contents = Contents->get()->getBuffer().str();
136TEST_F(PrerequisiteModulesTests, NonModularTest) {
137 MockDirectoryCompilationDatabase CDB(
TestDir, FS);
139 CDB.addFile(
"foo.h", R
"cpp(
143 CDB.addFile("NonModular.cpp", R
"cpp(
153 auto NonModularInfo =
154 Builder.buildPrerequisiteModulesFor(getFullPath(
"NonModular.cpp"), FS);
155 EXPECT_TRUE(NonModularInfo);
157 HeaderSearchOptions HSOpts;
158 NonModularInfo->adjustHeaderSearchOptions(HSOpts);
159 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
163 EXPECT_TRUE(NonModularInfo->canReuse(*Invocation, FS.
view(
TestDir)));
166TEST_F(PrerequisiteModulesTests, ModuleWithoutDepTest) {
167 MockDirectoryCompilationDatabase CDB(TestDir, FS);
169 CDB.addFile(
"foo.h", R
"cpp(
173 CDB.addFile("M.cppm", R
"cpp(
181 auto MInfo =
Builder.buildPrerequisiteModulesFor(getFullPath(
"M.cppm"), FS);
185 HeaderSearchOptions HSOpts;
186 MInfo->adjustHeaderSearchOptions(HSOpts);
187 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
191 EXPECT_TRUE(MInfo->canReuse(*Invocation, FS.
view(TestDir)));
194TEST_F(PrerequisiteModulesTests, ModuleWithDepTest) {
195 MockDirectoryCompilationDatabase CDB(TestDir, FS);
197 CDB.addFile(
"foo.h", R
"cpp(
201 CDB.addFile("M.cppm", R
"cpp(
207 CDB.addFile("N.cppm", R
"cpp(
213 CDB.addFile("N-part.cppm", R
"cpp(
214// Different module name with filename intentionally.
220 auto NInfo =
Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
223 ParseInputs NInput = getInputs(
"N.cppm", CDB);
224 std::unique_ptr<CompilerInvocation> Invocation =
227 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.
view(TestDir)));
233 HeaderSearchOptions HSOpts;
234 NInfo->adjustHeaderSearchOptions(HSOpts);
236 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"M"));
237 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"N:Part"));
244 HeaderSearchOptions HSOpts;
245 HSOpts.PrebuiltModuleFiles[
"M"] =
"incorrect_path";
246 HSOpts.PrebuiltModuleFiles[
"N:Part"] =
"incorrect_path";
247 NInfo->adjustHeaderSearchOptions(HSOpts);
249 EXPECT_TRUE(StringRef(HSOpts.PrebuiltModuleFiles[
"M"]).ends_with(
".pcm"));
251 StringRef(HSOpts.PrebuiltModuleFiles[
"N:Part"]).ends_with(
".pcm"));
255TEST_F(PrerequisiteModulesTests, ReusabilityTest) {
256 MockDirectoryCompilationDatabase CDB(TestDir, FS);
258 CDB.addFile(
"foo.h", R
"cpp(
262 CDB.addFile("M.cppm", R
"cpp(
268 CDB.addFile("N.cppm", R
"cpp(
274 CDB.addFile("N-part.cppm", R
"cpp(
275// Different module name with filename intentionally.
281 auto NInfo =
Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
285 ParseInputs NInput = getInputs(
"N.cppm", CDB);
286 std::unique_ptr<CompilerInvocation> Invocation =
288 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.
view(TestDir)));
292 CDB.addFile(
"L.cppm", R
"cpp(
298 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
300 CDB.addFile("bar.h", R
"cpp(
302inline void bar(int) {}
304 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
309 CDB.addFile(
"M.cppm", R
"cpp(
315 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
317 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
318 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.
view(TestDir)));
320 CDB.addFile(
"foo.h", R
"cpp(
322inline void foo(int) {}
324 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
326 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
327 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.
view(TestDir)));
330 CDB.addFile(
"N-part.cppm", R
"cpp(
332// Intentioned to make it uncompilable.
333export int NPart = 4LIdjwldijaw
335 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
336 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
338 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.
view(TestDir)));
340 CDB.addFile(
"N-part.cppm", R
"cpp(
342export int NPart = 43;
345 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
346 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
348 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.
view(TestDir)));
352 CDB.addFile(
"N-part.cppm", R
"cpp(
354export int NPart = 43;
356 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
358 CDB.addFile("N.cppm", R
"cpp(
366 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.
view(TestDir)));
370TEST_F(PrerequisiteModulesTests, ParsedASTTest) {
371 MockDirectoryCompilationDatabase CDB(TestDir, FS);
373 CDB.addFile(
"A.cppm", R
"cpp(
378 CDB.addFile("Use.cpp", R
"cpp(
384 ParseInputs Use = getInputs("Use.cpp", CDB);
387 std::unique_ptr<CompilerInvocation>
CI =
395 EXPECT_TRUE(
Preamble->RequiredModules);
402 EXPECT_TRUE(D.isFromASTFile());
406TEST_F(PrerequisiteModulesTests, CodeCompleteTest) {
407 MockDirectoryCompilationDatabase CDB(TestDir, FS);
409 CDB.addFile(
"A.cppm", R
"cpp(
414 llvm::StringLiteral UserContents = R"cpp(
421 CDB.addFile("Use.cpp", UserContents);
422 Annotations Test(UserContents);
426 ParseInputs Use = getInputs(
"Use.cpp", CDB);
429 std::unique_ptr<CompilerInvocation>
CI =
437 EXPECT_TRUE(
Preamble->RequiredModules);
439 auto Result =
codeComplete(getFullPath(
"Use.cpp"), Test.point(),
441 EXPECT_FALSE(Result.Completions.empty());
442 EXPECT_EQ(Result.Completions[0].Name,
"printA");
445TEST_F(PrerequisiteModulesTests, SignatureHelpTest) {
446 MockDirectoryCompilationDatabase CDB(TestDir, FS);
448 CDB.addFile(
"A.cppm", R
"cpp(
450export void printA(int a);
453 llvm::StringLiteral UserContents = R"cpp(
460 CDB.addFile("Use.cpp", UserContents);
461 Annotations Test(UserContents);
465 ParseInputs Use = getInputs(
"Use.cpp", CDB);
468 std::unique_ptr<CompilerInvocation>
CI =
476 EXPECT_TRUE(
Preamble->RequiredModules);
478 auto Result =
signatureHelp(getFullPath(
"Use.cpp"), Test.point(),
480 EXPECT_FALSE(Result.signatures.empty());
481 EXPECT_EQ(Result.signatures[0].label,
"printA(int a) -> void");
482 EXPECT_EQ(Result.signatures[0].parameters[0].labelString,
"int a");
485TEST_F(PrerequisiteModulesTests, ReusablePrerequisiteModulesTest) {
486 MockDirectoryCompilationDatabase CDB(TestDir, FS);
488 CDB.addFile(
"M.cppm", R
"cpp(
492 CDB.addFile("A.cppm", R
"cpp(
495export int A = 43 + M;
497 CDB.addFile("B.cppm", R
"cpp(
500export int B = 44 + M;
505 auto AInfo =
Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
507 auto BInfo =
Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
509 HeaderSearchOptions HSOptsA(TestDir);
510 HeaderSearchOptions HSOptsB(TestDir);
511 AInfo->adjustHeaderSearchOptions(HSOptsA);
512 BInfo->adjustHeaderSearchOptions(HSOptsB);
514 EXPECT_FALSE(HSOptsA.PrebuiltModuleFiles.empty());
515 EXPECT_FALSE(HSOptsB.PrebuiltModuleFiles.empty());
518 EXPECT_EQ(HSOptsA.PrebuiltModuleFiles, HSOptsB.PrebuiltModuleFiles);
521 CDB.addFile(
"M.cppm", R
"cpp(
523export constexpr int M = 43;
526 ParseInputs AUse = getInputs("A.cppm", CDB);
527 AUse.ModulesManager = &
Builder;
528 std::unique_ptr<CompilerInvocation> AInvocation =
530 EXPECT_FALSE(AInfo->canReuse(*AInvocation, FS.
view(TestDir)));
532 ParseInputs BUse = getInputs(
"B.cppm", CDB);
533 AUse.ModulesManager = &
Builder;
534 std::unique_ptr<CompilerInvocation> BInvocation =
536 EXPECT_FALSE(BInfo->canReuse(*BInvocation, FS.
view(TestDir)));
539 Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
541 Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
542 EXPECT_TRUE(NewAInfo);
543 EXPECT_TRUE(NewBInfo);
544 HeaderSearchOptions NewHSOptsA(TestDir);
545 HeaderSearchOptions NewHSOptsB(TestDir);
546 NewAInfo->adjustHeaderSearchOptions(NewHSOptsA);
547 NewBInfo->adjustHeaderSearchOptions(NewHSOptsB);
549 EXPECT_FALSE(NewHSOptsA.PrebuiltModuleFiles.empty());
550 EXPECT_FALSE(NewHSOptsB.PrebuiltModuleFiles.empty());
552 EXPECT_EQ(NewHSOptsA.PrebuiltModuleFiles, NewHSOptsB.PrebuiltModuleFiles);
554 EXPECT_NE(NewHSOptsA.PrebuiltModuleFiles, HSOptsA.PrebuiltModuleFiles);
CodeCompletionBuilder Builder
DiagnosticConsumer DiagConsumer
SmallString< 256 > TestDir
llvm::StringRef Directory
std::unique_ptr< CompilerInvocation > CI
static std::optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > view(std::nullopt_t CWD) const
Obtain a vfs::FileSystem with an arbitrary initial working directory.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
std::unique_ptr< ProjectModules > scanningProjectModules(std::shared_ptr< const clang::tooling::CompilationDatabase > CDB, const ThreadsafeFS &TFS)
Providing modules information for the project by scanning every file.
std::string Path
A typedef to represent a file path.
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats)
Build a preamble for the new inputs unless an old one can be reused.
CodeCompleteResult codeComplete(PathRef FileName, Position Pos, const PreambleData *Preamble, const ParseInputs &ParseInput, CodeCompleteOptions Opts, SpeculativeFuzzyFind *SpecFuzzyFind)
Gets code completions at a specified Pos in FileName.
llvm::StringRef PathRef
A typedef to represent a ref to file path.
SignatureHelp signatureHelp(PathRef FileName, Position Pos, const PreambleData &Preamble, const ParseInputs &ParseInput, MarkupKind DocumentationFormat)
Get signature help at a specified Pos in FileName.