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
15namespace clang {
16namespace clangd {
17
18// Force the unittest URI scheme to be linked,
19static int LLVM_ATTRIBUTE_UNUSED UnittestSchemeAnchorDest =
20 UnittestSchemeAnchorSource;
21
22namespace {
23
24using ::testing::AllOf;
25
26MATCHER_P(scheme, S, "") { return arg.scheme() == S; }
27MATCHER_P(authority, A, "") { return arg.authority() == A; }
28MATCHER_P(body, B, "") { return arg.body() == B; }
29
30std::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
38URI 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
45TEST(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
52TEST(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
63std::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
70TEST(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
79TEST(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
89TEST(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
95TEST(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
125TEST(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
134TEST(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
149TEST(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
166std::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
174TEST(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(AbsPath: testPath(File: "x"), HintPath: FilePath);
185 EXPECT_FALSE(Resolve);
186 llvm::consumeError(Err: Resolve.takeError());
187}
188
189TEST(URITest, Platform) {
190 auto Path = testPath(File: "x");
191 auto U = URI::create(AbsolutePath: Path, Scheme: "file");
192 EXPECT_TRUE(static_cast<bool>(U));
193 EXPECT_THAT(resolveOrDie(*U), Path);
194}
195
196TEST(URITest, ResolveFailed) {
197 auto FailedResolve = [](StringRef Uri) {
198 auto Path = URI::resolve(U: parseOrDie(Uri));
199 if (!Path) {
200 consumeError(Err: 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

source code of clang-tools-extra/clangd/unittests/URITests.cpp