blob: 6ce152caa44dd6aa1d7a157cb39884ac1152be81 [file] [log] [blame]
Avi Drissman505076bc2022-10-06 21:15:301// Copyright 2020 The Chromium Authors
Ben Kelly0e5c63e82020-11-12 21:24:082// Use of this source code is governed by an MIT-style license that can be
3// found in the LICENSE file or at https://opensource.org/licenses/MIT.
4
5#include "third_party/liburlpattern/parse.h"
Helmut Januschkaed56d612024-07-12 21:11:096
7#include <string_view>
8
Takashi Nakayamae011ac42025-05-02 00:20:019#include "base/types/expected.h"
Ben Kelly0e5c63e82020-11-12 21:24:0810#include "testing/gtest/include/gtest/gtest.h"
Takashi Nakayamae011ac42025-05-02 00:20:0111#include "third_party/abseil-cpp/absl/status/status.h"
12#include "third_party/abseil-cpp/absl/status/statusor.h"
Ben Kellyd18c0e822021-03-05 22:42:1713#include "third_party/abseil-cpp/absl/strings/str_format.h"
Ben Kelly0e5c63e82020-11-12 21:24:0814#include "third_party/liburlpattern/pattern.h"
15
Ben Kelly02c1d172021-03-16 15:33:2516namespace {
Ben Kelly0e5c63e82020-11-12 21:24:0817
Takashi Nakayamae011ac42025-05-02 00:20:0118base::expected<std::string, absl::Status> PassThrough(std::string_view input) {
Ben Kellyd18c0e822021-03-05 22:42:1719 return std::string(input);
20}
21
Ben Kelly02c1d172021-03-16 15:33:2522} // namespace
23
24namespace liburlpattern {
25
Takashi Nakayamae011ac42025-05-02 00:20:0126base::expected<std::string, absl::Status> ToUpper(std::string_view input) {
Ben Kellyd18c0e822021-03-05 22:42:1727 std::string output;
28 std::transform(input.begin(), input.end(), std::back_inserter(output),
29 [](unsigned char c) { return std::toupper(c); });
30 return output;
31}
32
Helmut Januschkaed56d612024-07-12 21:11:0933void RunParseTest(std::string_view pattern,
Ben Kellyd18c0e822021-03-05 22:42:1734 absl::StatusOr<std::vector<Part>> expected,
35 EncodeCallback callback = PassThrough) {
36 auto result = Parse(pattern, std::move(callback));
Takashi Nakayamae011ac42025-05-02 00:20:0137 ASSERT_EQ(result.has_value(), expected.ok())
38 << "parse status '" << result.error() << "' for: " << pattern;
Ben Kelly0e5c63e82020-11-12 21:24:0839 if (!expected.ok()) {
Takashi Nakayamae011ac42025-05-02 00:20:0140 ASSERT_EQ(result.error().code(), expected.status().code())
Ben Kelly0e5c63e82020-11-12 21:24:0841 << "parse status code for: " << pattern;
Takashi Nakayamae011ac42025-05-02 00:20:0142 EXPECT_NE(result.error().message().find(expected.status().message()),
Ben Kelly0e5c63e82020-11-12 21:24:0843 std::string::npos)
Takashi Nakayamae011ac42025-05-02 00:20:0144 << "parse message '" << result.error().message()
Ben Kelly0e5c63e82020-11-12 21:24:0845 << "' does not contain '" << expected.status().message()
46 << "' for: " << pattern;
47 return;
48 }
49 const auto& expected_part_list = expected.value();
50 const auto& part_list = result.value().PartList();
51 EXPECT_EQ(part_list.size(), expected_part_list.size())
52 << "parser should produce expected number of parts for: " << pattern;
53 for (size_t i = 0; i < part_list.size() && i < expected_part_list.size();
54 ++i) {
55 EXPECT_EQ(part_list[i], expected_part_list[i])
56 << "token at index " << i << " wrong for: " << pattern;
57 }
58}
59
60TEST(ParseTest, EmptyPattern) {
61 RunParseTest("", std::vector<Part>());
62}
63
Ben Kellyd18c0e822021-03-05 22:42:1764TEST(ParseTest, EncoderCallback) {
65 std::vector<Part> expected_parts = {
66 Part(PartType::kFixed, "/FOO/BAR", Modifier::kNone),
67 };
68 RunParseTest("/foo/bar", expected_parts, ToUpper);
Ben Kelly0e5c63e82020-11-12 21:24:0869}
70
71TEST(ParseTest, Fixed) {
72 std::vector<Part> expected_parts = {
73 Part(PartType::kFixed, "/foo", Modifier::kNone),
74 };
75 RunParseTest("/foo", expected_parts);
76}
77
78TEST(ParseTest, FixedInGroup) {
79 std::vector<Part> expected_parts = {
80 Part(PartType::kFixed, "/foo", Modifier::kNone),
81 };
82 RunParseTest("{/foo}", expected_parts);
83}
84
Ben Kellyccecd872021-07-27 18:37:1785TEST(ParseTest, FixedAndFixedInGroup) {
86 std::vector<Part> expected_parts = {
87 Part(PartType::kFixed, "/foo", Modifier::kNone),
88 };
89 RunParseTest("/{foo}", expected_parts);
90}
91
92TEST(ParseTest, FixedInGroupAndFixed) {
93 std::vector<Part> expected_parts = {
94 Part(PartType::kFixed, "/foo", Modifier::kNone),
95 };
96 RunParseTest("{/}foo", expected_parts);
97}
98
99TEST(ParseTest, FixedInGroupAndFixedInGroup) {
100 std::vector<Part> expected_parts = {
101 Part(PartType::kFixed, "/foo", Modifier::kNone),
102 };
103 RunParseTest("{/}{foo}", expected_parts);
104}
105
Ben Kelly0e5c63e82020-11-12 21:24:08106TEST(ParseTest, FixedAndEmptyGroup) {
107 std::vector<Part> expected_parts = {
Ben Kellyccecd872021-07-27 18:37:17108 Part(PartType::kFixed, "/foo", Modifier::kNone),
Ben Kelly0e5c63e82020-11-12 21:24:08109 };
110 RunParseTest("/f{}oo", expected_parts);
111}
112
113TEST(ParseTest, FixedInGroupWithOptionalModifier) {
114 std::vector<Part> expected_parts = {
115 Part(PartType::kFixed, "/foo", Modifier::kOptional),
116 };
117 RunParseTest("{/foo}?", expected_parts);
118}
119
120TEST(ParseTest, FixedInGroupWithZeroOrMoreModifier) {
121 std::vector<Part> expected_parts = {
122 Part(PartType::kFixed, "/foo", Modifier::kZeroOrMore),
123 };
124 RunParseTest("{/foo}*", expected_parts);
125}
126
127TEST(ParseTest, FixedInGroupWithOneOrMoreModifier) {
128 std::vector<Part> expected_parts = {
129 Part(PartType::kFixed, "/foo", Modifier::kOneOrMore),
130 };
131 RunParseTest("{/foo}+", expected_parts);
132}
133
134TEST(ParseTest, FixedInEarlyTerminatedGroup) {
Ben Kelly18869af752021-07-14 02:08:25135 RunParseTest("{/foo", absl::InvalidArgumentError("expected '}'"));
Ben Kelly0e5c63e82020-11-12 21:24:08136}
137
138TEST(ParseTest, FixedInUnbalancedGroup) {
Ben Kelly18869af752021-07-14 02:08:25139 RunParseTest("{/foo?", absl::InvalidArgumentError("expected '}'"));
Ben Kelly0e5c63e82020-11-12 21:24:08140}
141
142TEST(ParseTest, FixedWithModifier) {
Ben Kelly18869af752021-07-14 02:08:25143 RunParseTest("/foo?", absl::InvalidArgumentError("Unexpected modifier"));
Ben Kelly0e5c63e82020-11-12 21:24:08144}
145
146TEST(ParseTest, Regex) {
147 std::vector<Part> expected_parts = {
148 Part(PartType::kFixed, "/f", Modifier::kNone),
149 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"", "oo", /*suffix=*/"",
150 Modifier::kNone),
151 };
152 RunParseTest("/f(oo)", expected_parts);
153}
154
155TEST(ParseTest, RegexInGroup) {
156 std::vector<Part> expected_parts = {
157 Part(PartType::kFixed, "/f", Modifier::kNone),
158 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"", "oo", /*suffix=*/"",
159 Modifier::kNone),
160 };
161 RunParseTest("/f{(oo)}", expected_parts);
162}
163
164TEST(ParseTest, RegexWithPrefixAndSuffixInGroup) {
165 std::vector<Part> expected_parts = {
166 Part(PartType::kFixed, "/", Modifier::kNone),
167 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"f", "o", /*suffix=*/"o",
168 Modifier::kNone),
169 };
170 RunParseTest("/{f(o)o}", expected_parts);
171}
172
173TEST(ParseTest, RegexAndRegexInGroup) {
Ben Kelly18869af752021-07-14 02:08:25174 RunParseTest("/f{(o)(o)}", absl::InvalidArgumentError("expected '}'"));
Ben Kelly0e5c63e82020-11-12 21:24:08175}
176
177TEST(ParseTest, RegexWithPrefix) {
178 std::vector<Part> expected_parts = {
179 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"/", "foo", /*suffix=*/"",
180 Modifier::kNone),
181 };
182 RunParseTest("/(foo)", expected_parts);
183}
184
185TEST(ParseTest, RegexWithNameAndPrefix) {
186 std::vector<Part> expected_parts = {
187 Part(PartType::kFixed, "/foo", Modifier::kNone),
188 Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"/", "[^/]+?",
189 /*suffix=*/"", Modifier::kNone),
190 };
191 RunParseTest("/foo/:bar([^/]+?)", expected_parts);
192}
193
194TEST(ParseTest, RegexWithNameAndPrefixInGroup) {
195 std::vector<Part> expected_parts = {
196 Part(PartType::kFixed, "/foo/", Modifier::kNone),
197 Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"", "[^/]+?",
198 /*suffix=*/"", Modifier::kNone),
199 };
200 RunParseTest("/foo/{:bar([^/]+?)}", expected_parts);
201}
202
203TEST(ParseTest, RegexWithModifier) {
204 std::vector<Part> expected_parts = {
205 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"/", "foo",
206 /*suffix=*/"", Modifier::kOptional),
207 };
208 RunParseTest("/(foo)?", expected_parts);
209}
210
211TEST(ParseTest, RegexLikeFullWildcard) {
212 std::vector<Part> expected_parts = {
Ben Kellyd228e3c2021-02-24 00:43:40213 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
Ben Kelly0e5c63e82020-11-12 21:24:08214 /*suffix=*/"", Modifier::kNone),
215 };
216 RunParseTest("/(.*)", expected_parts);
217}
218
Ben Kellyd228e3c2021-02-24 00:43:40219TEST(ParseTest, Wildcard) {
220 std::vector<Part> expected_parts = {
221 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
222 /*suffix=*/"", Modifier::kNone),
223 };
224 RunParseTest("/*", expected_parts);
225}
226
227TEST(ParseTest, WildcardWithModifierStar) {
228 std::vector<Part> expected_parts = {
229 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
230 /*suffix=*/"", Modifier::kZeroOrMore),
231 };
232 RunParseTest("/**", expected_parts);
233}
234
235TEST(ParseTest, WildcardWithModifierPlus) {
236 std::vector<Part> expected_parts = {
237 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
238 /*suffix=*/"", Modifier::kOneOrMore),
239 };
240 RunParseTest("/*+", expected_parts);
241}
242
243TEST(ParseTest, WildcardWithModifierQuestion) {
244 std::vector<Part> expected_parts = {
245 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
246 /*suffix=*/"", Modifier::kOptional),
247 };
248 RunParseTest("/*?", expected_parts);
249}
250
251TEST(ParseTest, WildcardFollowingWildcardWithModifierStart) {
252 std::vector<Part> expected_parts = {
253 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
254 /*suffix=*/"", Modifier::kZeroOrMore),
255 Part(PartType::kFullWildcard, /*name=*/"1", /*prefix=*/"", /*value=*/"",
256 /*suffix=*/"", Modifier::kNone),
257 };
258 RunParseTest("/***", expected_parts);
259}
260
261TEST(ParseTest, WildcardWithMultipleModifiersPlus) {
Ben Kelly18869af752021-07-14 02:08:25262 RunParseTest("/**+", absl::InvalidArgumentError("expected end of pattern"));
Ben Kellyd228e3c2021-02-24 00:43:40263}
264
265TEST(ParseTest, WildcardWithMultipleModifiersQuestion) {
Ben Kelly18869af752021-07-14 02:08:25266 RunParseTest("/**?", absl::InvalidArgumentError("expected end of pattern"));
Ben Kellyd228e3c2021-02-24 00:43:40267}
268
269TEST(ParseTest, WildcardInGroup) {
270 std::vector<Part> expected_parts = {
271 Part(PartType::kFixed, "/f", Modifier::kNone),
272 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"", /*value=*/"",
273 /*suffix=*/"", Modifier::kNone),
274 };
275 RunParseTest("/f{*}", expected_parts);
276}
277
278TEST(ParseTest, WildcardWithPrefixAndSuffixInGroup) {
279 std::vector<Part> expected_parts = {
280 Part(PartType::kFixed, "/", Modifier::kNone),
281 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"f", /*value=*/"",
282 /*suffix=*/"o", Modifier::kNone),
283 };
284 RunParseTest("/{f*o}", expected_parts);
285}
286
Ben Kelly0e5c63e82020-11-12 21:24:08287TEST(ParseTest, Name) {
288 std::vector<Part> expected_parts = {
289 Part(PartType::kFixed, "/foo", Modifier::kNone),
290 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
291 /*value=*/"", /*suffix=*/"", Modifier::kNone),
292 };
293 RunParseTest("/foo:bar", expected_parts);
294}
295
Ben Kelly02c1d172021-03-16 15:33:25296TEST(ParseTest, NameStartsWithNumber) {
297 RunParseTest("/foo/:0", absl::InvalidArgumentError("Missing parameter name"));
298}
299
Ben Kelly0e5c63e82020-11-12 21:24:08300TEST(ParseTest, NameInGroup) {
301 std::vector<Part> expected_parts = {
302 Part(PartType::kFixed, "/foo", Modifier::kNone),
303 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
304 /*value=*/"", /*suffix=*/"", Modifier::kNone),
305 };
306 RunParseTest("/foo{:bar}", expected_parts);
307}
308
309TEST(ParseTest, NameAndNameInGroup) {
Ben Kelly18869af752021-07-14 02:08:25310 RunParseTest("/foo{:bar:baz}", absl::InvalidArgumentError("expected '}'"));
Ben Kelly0e5c63e82020-11-12 21:24:08311}
312
313TEST(ParseTest, NameWithPrefixAndSuffixInGroup) {
314 std::vector<Part> expected_parts = {
315 Part(PartType::kFixed, "/foo/", Modifier::kNone),
316 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"data_",
317 /*value=*/"", /*suffix=*/".jpg", Modifier::kNone),
318 };
319 RunParseTest("/foo/{data_:bar.jpg}", expected_parts);
320}
321
322TEST(ParseTest, NameWithPrefix) {
323 std::vector<Part> expected_parts = {
324 Part(PartType::kFixed, "/foo", Modifier::kNone),
325 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"/",
326 /*value=*/"", /*suffix=*/"", Modifier::kNone),
327 };
328 RunParseTest("/foo/:bar", expected_parts);
329}
330
331TEST(ParseTest, NameWithEscapedPrefix) {
332 std::vector<Part> expected_parts = {
333 Part(PartType::kFixed, "/foo/", Modifier::kNone),
334 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
335 /*value=*/"", /*suffix=*/"", Modifier::kNone),
336 };
337 RunParseTest("/foo\\/:bar", expected_parts);
338}
339
340TEST(ParseTest, NameWithCustomRegex) {
341 std::vector<Part> expected_parts = {
342 Part(PartType::kFixed, "/foo", Modifier::kNone),
343 Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"", "[^/]+?",
344 /*suffix=*/"", Modifier::kNone),
345 };
346 RunParseTest("/foo:bar([^/]+?)", expected_parts);
347}
348
349TEST(ParseTest, NameWithModifier) {
350 std::vector<Part> expected_parts = {
351 Part(PartType::kSegmentWildcard, /*name=*/"foo", /*prefix=*/"/",
352 /*value=*/"", /*suffix=*/"", Modifier::kOptional),
353 };
354 RunParseTest("/:foo?", expected_parts);
355}
356
Ben Kellyd228e3c2021-02-24 00:43:40357TEST(ParseTest, NameWithModifierStarAndWildcard) {
358 std::vector<Part> expected_parts = {
359 Part(PartType::kSegmentWildcard, /*name=*/"foo", /*prefix=*/"/",
360 /*value=*/"", /*suffix=*/"", Modifier::kZeroOrMore),
361 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"",
362 /*value=*/"", /*suffix=*/"", Modifier::kNone),
363 };
364 RunParseTest("/:foo**", expected_parts);
365}
366
367TEST(ParseTest, NameWithModifierStarAndModifierQuestion) {
Ben Kelly18869af752021-07-14 02:08:25368 RunParseTest("/:foo*?",
369 absl::InvalidArgumentError("expected end of pattern"));
Ben Kellyd228e3c2021-02-24 00:43:40370}
371
372TEST(ParseTest, NameWithModifierStarAndModifierPlus) {
Ben Kelly18869af752021-07-14 02:08:25373 RunParseTest("/:foo*+",
374 absl::InvalidArgumentError("expected end of pattern"));
Ben Kellyd228e3c2021-02-24 00:43:40375}
376
Ben Kelly003c5482021-08-17 19:28:17377TEST(ParseTest, DuplicateName) {
378 RunParseTest("/:foo/:foo", absl::InvalidArgumentError("Duplicate"));
379}
380
Ben Kelly0e5c63e82020-11-12 21:24:08381} // namespace liburlpattern