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());
CodeCompletionBuilder Builder
DiagnosticConsumer DiagConsumer
SmallString< 256 > TestDir
llvm::StringRef Directory
std::unique_ptr< CompilerInvocation > CI
llvm::raw_string_ostream OS
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.
llvm::StringRef PathRef
A typedef to represent a ref to file path.