xref: /llvm-project/clang-tools-extra/clangd/unittests/URITests.cpp (revision 8edfc2f814fe107bf31e3470478aa9bcacdf5aed)
1 //===-- URITests.cpp  ---------------------------------*- C++ -*-----------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Matchers.h"
10 #include "TestFS.h"
11 #include "URI.h"
12 #include "gmock/gmock.h"
13 #include "gtest/gtest.h"
14 
15 namespace clang {
16 namespace clangd {
17 
18 // Force the unittest URI scheme to be linked,
19 static int LLVM_ATTRIBUTE_UNUSED UnittestSchemeAnchorDest =
20     UnittestSchemeAnchorSource;
21 
22 namespace {
23 
24 using ::testing::AllOf;
25 
26 MATCHER_P(scheme, S, "") { return arg.scheme() == S; }
27 MATCHER_P(authority, A, "") { return arg.authority() == A; }
28 MATCHER_P(body, B, "") { return arg.body() == B; }
29 
createOrDie(llvm::StringRef AbsolutePath,llvm::StringRef Scheme="file")30 std::string createOrDie(llvm::StringRef AbsolutePath,
31                         llvm::StringRef Scheme = "file") {
32   auto Uri = URI::create(AbsolutePath, Scheme);
33   if (!Uri)
34     llvm_unreachable(toString(Uri.takeError()).c_str());
35   return Uri->toString();
36 }
37 
parseOrDie(llvm::StringRef Uri)38 URI parseOrDie(llvm::StringRef Uri) {
39   auto U = URI::parse(Uri);
40   if (!U)
41     llvm_unreachable(toString(U.takeError()).c_str());
42   return *U;
43 }
44 
TEST(PercentEncodingTest,Encode)45 TEST(PercentEncodingTest, Encode) {
46   EXPECT_EQ(URI("x", /*authority=*/"", "a/b/c").toString(), "x:a/b/c");
47   EXPECT_EQ(URI("x", /*authority=*/"", "a!b;c~").toString(), "x:a%21b%3Bc~");
48   EXPECT_EQ(URI("x", /*authority=*/"", "a123b").toString(), "x:a123b");
49   EXPECT_EQ(URI("x", /*authority=*/"", "a:b;c").toString(), "x:a:b%3Bc");
50 }
51 
TEST(PercentEncodingTest,Decode)52 TEST(PercentEncodingTest, Decode) {
53   EXPECT_EQ(parseOrDie("x:a/b/c").body(), "a/b/c");
54 
55   EXPECT_EQ(parseOrDie("s%2b://%3a/%3").scheme(), "s+");
56   EXPECT_EQ(parseOrDie("s%2b://%3a/%3").authority(), ":");
57   EXPECT_EQ(parseOrDie("s%2b://%3a/%3").body(), "/%3");
58 
59   EXPECT_EQ(parseOrDie("x:a%21b%3ac~").body(), "a!b:c~");
60   EXPECT_EQ(parseOrDie("x:a:b%3bc").body(), "a:b;c");
61 }
62 
resolveOrDie(const URI & U,llvm::StringRef HintPath="")63 std::string resolveOrDie(const URI &U, llvm::StringRef HintPath = "") {
64   auto Path = URI::resolve(U, HintPath);
65   if (!Path)
66     llvm_unreachable(toString(Path.takeError()).c_str());
67   return *Path;
68 }
69 
TEST(URITest,Create)70 TEST(URITest, Create) {
71 #ifdef _WIN32
72   EXPECT_THAT(createOrDie("c:\\x\\y\\z"), "file:///c:/x/y/z");
73 #else
74   EXPECT_THAT(createOrDie("/x/y/z"), "file:///x/y/z");
75   EXPECT_THAT(createOrDie("/(x)/y/\\ z"), "file:///%28x%29/y/%5C%20z");
76 #endif
77 }
78 
TEST(URITest,CreateUNC)79 TEST(URITest, CreateUNC) {
80 #ifdef _WIN32
81   EXPECT_THAT(createOrDie("\\\\test.org\\x\\y\\z"), "file://test.org/x/y/z");
82   EXPECT_THAT(createOrDie("\\\\10.0.0.1\\x\\y\\z"), "file://10.0.0.1/x/y/z");
83 #else
84   EXPECT_THAT(createOrDie("//test.org/x/y/z"), "file://test.org/x/y/z");
85   EXPECT_THAT(createOrDie("//10.0.0.1/x/y/z"), "file://10.0.0.1/x/y/z");
86 #endif
87 }
88 
TEST(URITest,FailedCreate)89 TEST(URITest, FailedCreate) {
90   EXPECT_ERROR(URI::create("/x/y/z", "no"));
91   // Path has to be absolute.
92   EXPECT_ERROR(URI::create("x/y/z", "file"));
93 }
94 
TEST(URITest,Parse)95 TEST(URITest, Parse) {
96   EXPECT_THAT(parseOrDie("file://auth/x/y/z"),
97               AllOf(scheme("file"), authority("auth"), body("/x/y/z")));
98 
99   EXPECT_THAT(parseOrDie("file://au%3dth/%28x%29/y/%5c%20z"),
100               AllOf(scheme("file"), authority("au=th"), body("/(x)/y/\\ z")));
101 
102   EXPECT_THAT(parseOrDie("file:///%28x%29/y/%5c%20z"),
103               AllOf(scheme("file"), authority(""), body("/(x)/y/\\ z")));
104   EXPECT_THAT(parseOrDie("file:///x/y/z"),
105               AllOf(scheme("file"), authority(""), body("/x/y/z")));
106   EXPECT_THAT(parseOrDie("file:"),
107               AllOf(scheme("file"), authority(""), body("")));
108   EXPECT_THAT(parseOrDie("file:///x/y/z%2"),
109               AllOf(scheme("file"), authority(""), body("/x/y/z%2")));
110   EXPECT_THAT(parseOrDie("http://llvm.org"),
111               AllOf(scheme("http"), authority("llvm.org"), body("")));
112   EXPECT_THAT(parseOrDie("http://llvm.org/"),
113               AllOf(scheme("http"), authority("llvm.org"), body("/")));
114   EXPECT_THAT(parseOrDie("http://llvm.org/D"),
115               AllOf(scheme("http"), authority("llvm.org"), body("/D")));
116   EXPECT_THAT(parseOrDie("http:/"),
117               AllOf(scheme("http"), authority(""), body("/")));
118   EXPECT_THAT(parseOrDie("urn:isbn:0451450523"),
119               AllOf(scheme("urn"), authority(""), body("isbn:0451450523")));
120   EXPECT_THAT(
121       parseOrDie("file:///c:/windows/system32/"),
122       AllOf(scheme("file"), authority(""), body("/c:/windows/system32/")));
123 }
124 
TEST(URITest,ParseFailed)125 TEST(URITest, ParseFailed) {
126   // Expect ':' in URI.
127   EXPECT_ERROR(URI::parse("file//x/y/z"));
128   // Empty.
129   EXPECT_ERROR(URI::parse(""));
130   EXPECT_ERROR(URI::parse(":/a/b/c"));
131   EXPECT_ERROR(URI::parse("\"/a/b/c\" IWYU pragma: abc"));
132 }
133 
TEST(URITest,Resolve)134 TEST(URITest, Resolve) {
135 #ifdef _WIN32
136   EXPECT_THAT(resolveOrDie(parseOrDie("file:///c%3a/x/y/z")), "c:\\x\\y\\z");
137   EXPECT_THAT(resolveOrDie(parseOrDie("file:///c:/x/y/z")), "c:\\x\\y\\z");
138 #else
139   EXPECT_EQ(resolveOrDie(parseOrDie("file:/a/b/c")), "/a/b/c");
140   EXPECT_EQ(resolveOrDie(parseOrDie("file://auth/a/b/c")), "//auth/a/b/c");
141   EXPECT_THAT(resolveOrDie(parseOrDie("file://au%3dth/%28x%29/y/%20z")),
142               "//au=th/(x)/y/ z");
143   EXPECT_THAT(resolveOrDie(parseOrDie("file:///c:/x/y/z")), "c:/x/y/z");
144 #endif
145   EXPECT_EQ(resolveOrDie(parseOrDie("unittest:///a"), testPath("x")),
146             testPath("a"));
147 }
148 
TEST(URITest,ResolveUNC)149 TEST(URITest, ResolveUNC) {
150 #ifdef _WIN32
151   EXPECT_THAT(resolveOrDie(parseOrDie("file://example.com/x/y/z")),
152               "\\\\example.com\\x\\y\\z");
153   EXPECT_THAT(resolveOrDie(parseOrDie("file://127.0.0.1/x/y/z")),
154               "\\\\127.0.0.1\\x\\y\\z");
155   // Ensure non-traditional file URI still resolves to correct UNC path.
156   EXPECT_THAT(resolveOrDie(parseOrDie("file:////127.0.0.1/x/y/z")),
157               "\\\\127.0.0.1\\x\\y\\z");
158 #else
159   EXPECT_THAT(resolveOrDie(parseOrDie("file://example.com/x/y/z")),
160               "//example.com/x/y/z");
161   EXPECT_THAT(resolveOrDie(parseOrDie("file://127.0.0.1/x/y/z")),
162               "//127.0.0.1/x/y/z");
163 #endif
164 }
165 
resolvePathOrDie(llvm::StringRef AbsPath,llvm::StringRef HintPath="")166 std::string resolvePathOrDie(llvm::StringRef AbsPath,
167                              llvm::StringRef HintPath = "") {
168   auto Path = URI::resolvePath(AbsPath, HintPath);
169   if (!Path)
170     llvm_unreachable(toString(Path.takeError()).c_str());
171   return *Path;
172 }
173 
TEST(URITest,ResolvePath)174 TEST(URITest, ResolvePath) {
175   StringRef FilePath =
176 #ifdef _WIN32
177       "c:\\x\\y\\z";
178 #else
179       "/a/b/c";
180 #endif
181   EXPECT_EQ(resolvePathOrDie(FilePath), FilePath);
182   EXPECT_EQ(resolvePathOrDie(testPath("x"), testPath("hint")), testPath("x"));
183   // HintPath is not in testRoot(); resolution fails.
184   auto Resolve = URI::resolvePath(testPath("x"), FilePath);
185   EXPECT_FALSE(Resolve);
186   llvm::consumeError(Resolve.takeError());
187 }
188 
TEST(URITest,Platform)189 TEST(URITest, Platform) {
190   auto Path = testPath("x");
191   auto U = URI::create(Path, "file");
192   EXPECT_TRUE(static_cast<bool>(U));
193   EXPECT_THAT(resolveOrDie(*U), Path);
194 }
195 
TEST(URITest,ResolveFailed)196 TEST(URITest, ResolveFailed) {
197   auto FailedResolve = [](StringRef Uri) {
198     auto Path = URI::resolve(parseOrDie(Uri));
199     if (!Path) {
200       consumeError(Path.takeError());
201       return true;
202     }
203     return false;
204   };
205 
206   // Invalid scheme.
207   EXPECT_TRUE(FailedResolve("no:/a/b/c"));
208   // File path needs to be absolute.
209   EXPECT_TRUE(FailedResolve("file:a/b/c"));
210 }
211 
212 } // namespace
213 } // namespace clangd
214 } // namespace clang
215