1 //===-- PathMappingTests.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 "PathMapping.h"
10 #include "llvm/Support/JSON.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
18 using ::testing::ElementsAre
;
19 MATCHER_P2(Mapping
, ClientPath
, ServerPath
, "") {
20 return arg
.ClientPath
== ClientPath
&& arg
.ServerPath
== ServerPath
;
23 bool failedParse(llvm::StringRef RawMappings
) {
24 llvm::Expected
<PathMappings
> Mappings
= parsePathMappings(RawMappings
);
26 consumeError(Mappings
.takeError());
32 TEST(ParsePathMappingTests
, WindowsPath
) {
33 // Relative path to C drive
34 EXPECT_TRUE(failedParse(R
"(C:a=/root)"));
35 EXPECT_TRUE(failedParse(R
"(\C:a=/root)"));
36 // Relative path to current drive.
37 EXPECT_TRUE(failedParse(R
"(\a=/root)"));
39 llvm::Expected
<PathMappings
> ParsedMappings
=
40 parsePathMappings(R
"(C:\a=/root)");
41 ASSERT_TRUE(bool(ParsedMappings
));
42 EXPECT_THAT(*ParsedMappings
, ElementsAre(Mapping("/C:/a", "/root")));
44 ParsedMappings
= parsePathMappings(R
"(\\Server\C$=/root)");
45 ASSERT_TRUE(bool(ParsedMappings
));
46 EXPECT_THAT(*ParsedMappings
, ElementsAre(Mapping("//Server/C$", "/root")));
49 TEST(ParsePathMappingTests
, UnixPath
) {
51 EXPECT_TRUE(failedParse("a/b=/root"));
53 llvm::Expected
<PathMappings
> ParsedMappings
= parsePathMappings("/A/b=/root");
54 ASSERT_TRUE(bool(ParsedMappings
));
55 EXPECT_THAT(*ParsedMappings
, ElementsAre(Mapping("/A/b", "/root")));
56 // Absolute unix path w/ backslash
57 ParsedMappings
= parsePathMappings(R
"(/a/b\\ar=/root)");
58 ASSERT_TRUE(bool(ParsedMappings
));
59 EXPECT_THAT(*ParsedMappings
, ElementsAre(Mapping(R
"(/a/b\\ar)", "/root")));
62 TEST(ParsePathMappingTests
, ImproperFormat
) {
64 EXPECT_TRUE(failedParse("/home/myuser1="));
65 // mappings need to be absolute
66 EXPECT_TRUE(failedParse("home/project=/workarea/project"));
67 // duplicate delimiter
68 EXPECT_TRUE(failedParse("/home==/workarea"));
70 EXPECT_TRUE(failedParse("/home"));
72 EXPECT_TRUE(failedParse("/home,/workarea"));
75 TEST(ParsePathMappingTests
, ParsesMultiple
) {
76 std::string RawPathMappings
=
77 "/home/project=/workarea/project,/home/project/.includes=/opt/include";
78 auto Parsed
= parsePathMappings(RawPathMappings
);
79 ASSERT_TRUE(bool(Parsed
));
81 ElementsAre(Mapping("/home/project", "/workarea/project"),
82 Mapping("/home/project/.includes", "/opt/include")));
85 bool mapsProperly(llvm::StringRef Orig
, llvm::StringRef Expected
,
86 llvm::StringRef RawMappings
, PathMapping::Direction Dir
) {
87 llvm::Expected
<PathMappings
> Mappings
= parsePathMappings(RawMappings
);
90 std::optional
<std::string
> MappedPath
= doPathMapping(Orig
, Dir
, *Mappings
);
91 std::string Actual
= MappedPath
? *MappedPath
: Orig
.str();
92 EXPECT_STREQ(Expected
.str().c_str(), Actual
.c_str());
93 return Expected
== Actual
;
96 TEST(DoPathMappingTests
, PreservesOriginal
) {
97 // Preserves original path when no mapping
98 EXPECT_TRUE(mapsProperly("file:///home", "file:///home", "",
99 PathMapping::Direction::ClientToServer
));
102 TEST(DoPathMappingTests
, UsesFirstMatch
) {
103 EXPECT_TRUE(mapsProperly("file:///home/foo.cpp", "file:///workarea1/foo.cpp",
104 "/home=/workarea1,/home=/workarea2",
105 PathMapping::Direction::ClientToServer
));
108 TEST(DoPathMappingTests
, IgnoresSubstrings
) {
109 // Doesn't map substrings that aren't a proper path prefix
110 EXPECT_TRUE(mapsProperly("file://home/foo-bar.cpp", "file://home/foo-bar.cpp",
111 "/home/foo=/home/bar",
112 PathMapping::Direction::ClientToServer
));
115 TEST(DoPathMappingTests
, MapsOutgoingPaths
) {
116 // When IsIncoming is false (i.e.a response), map the other way
117 EXPECT_TRUE(mapsProperly("file:///workarea/foo.cpp", "file:///home/foo.cpp",
119 PathMapping::Direction::ServerToClient
));
122 TEST(DoPathMappingTests
, OnlyMapFileUris
) {
123 EXPECT_TRUE(mapsProperly("test:///home/foo.cpp", "test:///home/foo.cpp",
125 PathMapping::Direction::ClientToServer
));
128 TEST(DoPathMappingTests
, RespectsCaseSensitivity
) {
129 EXPECT_TRUE(mapsProperly("file:///HOME/foo.cpp", "file:///HOME/foo.cpp",
131 PathMapping::Direction::ClientToServer
));
134 TEST(DoPathMappingTests
, MapsWindowsPaths
) {
135 // Maps windows properly
136 EXPECT_TRUE(mapsProperly("file:///C:/home/foo.cpp",
137 "file:///C:/workarea/foo.cpp", R
"(C:\home=C:\workarea)",
138 PathMapping::Direction::ClientToServer
));
141 TEST(DoPathMappingTests
, MapsWindowsUnixInterop
) {
142 // Path mappings with a windows-style client path and unix-style server path
143 EXPECT_TRUE(mapsProperly(
144 "file:///C:/home/foo.cpp", "file:///workarea/foo.cpp",
145 R
"(C:\home=/workarea)", PathMapping::Direction::ClientToServer
));
148 TEST(ApplyPathMappingTests
, PreservesOriginalParams
) {
149 auto Params
= llvm::json::parse(R
"({
150 "textDocument
": {"uri
": "file
:///home/foo.cpp"},
151 "position": {"line": 0, "character": 0}
153 ASSERT_TRUE(bool(Params));
154 llvm::json::Value ExpectedParams = *Params;
155 PathMappings Mappings;
156 applyPathMappings(*Params, PathMapping::Direction::ClientToServer, Mappings);
157 EXPECT_EQ(*Params, ExpectedParams);
160 TEST(ApplyPathMappingTests, MapsAllMatchingPaths) {
161 // Handles nested objects and array values
162 auto Params = llvm::json::parse(R"({
163 "rootUri": {"uri": "file:///home/foo.cpp"},
164 "workspaceFolders": ["file:///home/src", "file:///tmp"]
166 auto ExpectedParams = llvm::json::parse(R"({
167 "rootUri": {"uri": "file:///workarea/foo.cpp"},
168 "workspaceFolders": ["file:///workarea/src", "file:///tmp"]
170 auto Mappings = parsePathMappings("/home
=/workarea
");
171 ASSERT_TRUE(bool(Params) && bool(ExpectedParams) && bool(Mappings));
172 applyPathMappings(*Params, PathMapping::Direction::ClientToServer, *Mappings);
173 EXPECT_EQ(*Params, *ExpectedParams);
176 TEST(ApplyPathMappingTests, MapsOutbound) {
177 auto Params = llvm::json::parse(R"({
180 {"uri": "file:///opt/include/foo.h"},
181 {"uri": "file:///workarea/src/foo.cpp"}]
183 auto ExpectedParams = llvm::json::parse(R"({
186 {"uri": "file:///home/.includes/foo.h"},
187 {"uri": "file:///home/src/foo.cpp"}]
190 parsePathMappings("/home
=/workarea
,/home
/.includes
=/opt
/include
");
191 ASSERT_TRUE(bool(Params) && bool(ExpectedParams) && bool(Mappings));
192 applyPathMappings(*Params, PathMapping::Direction::ServerToClient, *Mappings);
193 EXPECT_EQ(*Params, *ExpectedParams);
196 TEST(ApplyPathMappingTests, MapsKeys) {
197 auto Params = llvm::json::parse(R"({
199 "file:///home/foo.cpp": {"newText": "..."},
200 "file:///home/src/bar.cpp": {"newText": "..."}
203 auto ExpectedParams = llvm::json::parse(R"({
205 "file:///workarea/foo.cpp": {"newText": "..."},
206 "file:///workarea/src/bar.cpp": {"newText": "..."}
209 auto Mappings = parsePathMappings("/home
=/workarea
");
210 ASSERT_TRUE(bool(Params) && bool(ExpectedParams) && bool(Mappings));
211 applyPathMappings(*Params, PathMapping::Direction::ClientToServer, *Mappings);
212 EXPECT_EQ(*Params, *ExpectedParams);
216 } // namespace clangd