1 //===-- TestFS.cpp ----------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "GlobalCompilationDatabase.h"
11 #include "support/Logger.h"
12 #include "support/Path.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/Support/Path.h"
22 // Tries to strip \p Prefix from beginning of \p Path. Returns true on success.
23 // If \p Prefix doesn't match, leaves \p Path untouched and returns false.
24 bool pathConsumeFront(PathRef
&Path
, PathRef Prefix
) {
25 if (!pathStartsWith(Prefix
, Path
))
27 Path
= Path
.drop_front(Prefix
.size());
32 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
>
33 buildTestFS(llvm::StringMap
<std::string
> const &Files
,
34 llvm::StringMap
<time_t> const &Timestamps
) {
35 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> MemFS(
36 new llvm::vfs::InMemoryFileSystem
);
37 MemFS
->setCurrentWorkingDirectory(testRoot());
38 for (auto &FileAndContents
: Files
) {
39 llvm::StringRef File
= FileAndContents
.first();
41 File
, Timestamps
.lookup(File
),
42 llvm::MemoryBuffer::getMemBufferCopy(FileAndContents
.second
, File
));
47 MockCompilationDatabase::MockCompilationDatabase(llvm::StringRef Directory
,
48 llvm::StringRef RelPathPrefix
)
49 : ExtraClangFlags({"-ffreestanding"}), Directory(Directory
),
50 RelPathPrefix(RelPathPrefix
) {
51 // -ffreestanding avoids implicit stdc-predef.h.
54 std::optional
<ProjectInfo
>
55 MockCompilationDatabase::getProjectInfo(PathRef File
) const {
56 return ProjectInfo
{std::string(Directory
)};
59 std::optional
<tooling::CompileCommand
>
60 MockCompilationDatabase::getCompileCommand(PathRef File
) const {
61 if (ExtraClangFlags
.empty())
64 auto FileName
= llvm::sys::path::filename(File
);
66 // Build the compile command.
67 auto CommandLine
= ExtraClangFlags
;
68 CommandLine
.insert(CommandLine
.begin(), "clang");
69 if (RelPathPrefix
.empty()) {
70 // Use the absolute path in the compile command.
71 CommandLine
.push_back(std::string(File
));
73 // Build a relative path using RelPathPrefix.
74 llvm::SmallString
<32> RelativeFilePath(RelPathPrefix
);
75 llvm::sys::path::append(RelativeFilePath
, FileName
);
76 CommandLine
.push_back(std::string(RelativeFilePath
.str()));
79 return {tooling::CompileCommand(Directory
!= llvm::StringRef()
81 : llvm::sys::path::parent_path(File
),
82 FileName
, std::move(CommandLine
), "")};
85 const char *testRoot() {
87 return "C:\\clangd-test";
89 return "/clangd-test";
93 std::string
testPath(PathRef File
, llvm::sys::path::Style Style
) {
94 assert(llvm::sys::path::is_relative(File
) && "FileName should be relative");
96 llvm::SmallString
<32> NativeFile
= File
;
97 llvm::sys::path::native(NativeFile
, Style
);
98 llvm::SmallString
<32> Path
;
99 llvm::sys::path::append(Path
, Style
, testRoot(), NativeFile
);
100 return std::string(Path
.str());
103 /// unittest: is a scheme that refers to files relative to testRoot().
104 /// URI body is a path relative to testRoot() e.g. unittest:///x.h for
105 /// /clangd-test/x.h.
106 class TestScheme
: public URIScheme
{
108 static const char *Scheme
;
110 llvm::Expected
<std::string
>
111 getAbsolutePath(llvm::StringRef
/*Authority*/, llvm::StringRef Body
,
112 llvm::StringRef HintPath
) const override
{
113 if (!HintPath
.empty() && !pathStartsWith(testRoot(), HintPath
))
114 return error("Hint path is not empty and doesn't start with {0}: {1}",
115 testRoot(), HintPath
);
116 if (!Body
.consume_front("/"))
117 return error("Body of an unittest: URI must start with '/'");
118 llvm::SmallString
<16> Path(Body
.begin(), Body
.end());
119 llvm::sys::path::native(Path
);
120 return testPath(Path
);
124 uriFromAbsolutePath(llvm::StringRef AbsolutePath
) const override
{
125 if (!pathConsumeFront(AbsolutePath
, testRoot()))
126 return error("{0} does not start with {1}", AbsolutePath
, testRoot());
128 return URI(Scheme
, /*Authority=*/"",
129 llvm::sys::path::convert_to_slash(AbsolutePath
));
133 const char *TestScheme::Scheme
= "unittest";
135 static URISchemeRegistry::Add
<TestScheme
> X(TestScheme::Scheme
, "Test schema");
137 volatile int UnittestSchemeAnchorSource
= 0;
139 } // namespace clangd