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/None.h"
14 #include "llvm/ADT/Optional.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/Path.h"
23 // Tries to strip \p Prefix from beginning of \p Path. Returns true on success.
24 // If \p Prefix doesn't match, leaves \p Path untouched and returns false.
25 bool pathConsumeFront(PathRef
&Path
, PathRef Prefix
) {
26 if (!pathStartsWith(Prefix
, Path
))
28 Path
= Path
.drop_front(Prefix
.size());
33 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
>
34 buildTestFS(llvm::StringMap
<std::string
> const &Files
,
35 llvm::StringMap
<time_t> const &Timestamps
) {
36 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> MemFS(
37 new llvm::vfs::InMemoryFileSystem
);
38 MemFS
->setCurrentWorkingDirectory(testRoot());
39 for (auto &FileAndContents
: Files
) {
40 llvm::StringRef File
= FileAndContents
.first();
42 File
, Timestamps
.lookup(File
),
43 llvm::MemoryBuffer::getMemBufferCopy(FileAndContents
.second
, File
));
48 MockCompilationDatabase::MockCompilationDatabase(llvm::StringRef Directory
,
49 llvm::StringRef RelPathPrefix
)
50 : ExtraClangFlags({"-ffreestanding"}), Directory(Directory
),
51 RelPathPrefix(RelPathPrefix
) {
52 // -ffreestanding avoids implicit stdc-predef.h.
55 llvm::Optional
<ProjectInfo
>
56 MockCompilationDatabase::getProjectInfo(PathRef File
) const {
57 return ProjectInfo
{std::string(Directory
)};
60 llvm::Optional
<tooling::CompileCommand
>
61 MockCompilationDatabase::getCompileCommand(PathRef File
) const {
62 if (ExtraClangFlags
.empty())
65 auto FileName
= llvm::sys::path::filename(File
);
67 // Build the compile command.
68 auto CommandLine
= ExtraClangFlags
;
69 CommandLine
.insert(CommandLine
.begin(), "clang");
70 if (RelPathPrefix
.empty()) {
71 // Use the absolute path in the compile command.
72 CommandLine
.push_back(std::string(File
));
74 // Build a relative path using RelPathPrefix.
75 llvm::SmallString
<32> RelativeFilePath(RelPathPrefix
);
76 llvm::sys::path::append(RelativeFilePath
, FileName
);
77 CommandLine
.push_back(std::string(RelativeFilePath
.str()));
80 return {tooling::CompileCommand(Directory
!= llvm::StringRef()
82 : llvm::sys::path::parent_path(File
),
83 FileName
, std::move(CommandLine
), "")};
86 const char *testRoot() {
88 return "C:\\clangd-test";
90 return "/clangd-test";
94 std::string
testPath(PathRef File
, llvm::sys::path::Style Style
) {
95 assert(llvm::sys::path::is_relative(File
) && "FileName should be relative");
97 llvm::SmallString
<32> NativeFile
= File
;
98 llvm::sys::path::native(NativeFile
, Style
);
99 llvm::SmallString
<32> Path
;
100 llvm::sys::path::append(Path
, Style
, testRoot(), NativeFile
);
101 return std::string(Path
.str());
104 /// unittest: is a scheme that refers to files relative to testRoot().
105 /// URI body is a path relative to testRoot() e.g. unittest:///x.h for
106 /// /clangd-test/x.h.
107 class TestScheme
: public URIScheme
{
109 static const char *Scheme
;
111 llvm::Expected
<std::string
>
112 getAbsolutePath(llvm::StringRef
/*Authority*/, llvm::StringRef Body
,
113 llvm::StringRef HintPath
) const override
{
114 if (!HintPath
.empty() && !pathStartsWith(testRoot(), HintPath
))
115 return error("Hint path is not empty and doesn't start with {0}: {1}",
116 testRoot(), HintPath
);
117 if (!Body
.consume_front("/"))
118 return error("Body of an unittest: URI must start with '/'");
119 llvm::SmallString
<16> Path(Body
.begin(), Body
.end());
120 llvm::sys::path::native(Path
);
121 return testPath(Path
);
125 uriFromAbsolutePath(llvm::StringRef AbsolutePath
) const override
{
126 if (!pathConsumeFront(AbsolutePath
, testRoot()))
127 return error("{0} does not start with {1}", AbsolutePath
, testRoot());
129 return URI(Scheme
, /*Authority=*/"",
130 llvm::sys::path::convert_to_slash(AbsolutePath
));
134 const char *TestScheme::Scheme
= "unittest";
136 static URISchemeRegistry::Add
<TestScheme
> X(TestScheme::Scheme
, "Test schema");
138 volatile int UnittestSchemeAnchorSource
= 0;
140 } // namespace clangd