1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // 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"
6 #include "testing/gtest/include/gtest/gtest.h"
7 #include "third_party/liburlpattern/pattern.h"
8
9 namespace liburlpattern {
10
RunParseTest(absl::string_view pattern,absl::StatusOr<std::vector<Part>> expected)11 void RunParseTest(absl::string_view pattern,
12 absl::StatusOr<std::vector<Part>> expected) {
13 auto result = Parse(pattern);
14 ASSERT_EQ(result.ok(), expected.ok())
15 << "parse status '" << result.status() << "' for: " << pattern;
16 if (!expected.ok()) {
17 ASSERT_EQ(result.status().code(), expected.status().code())
18 << "parse status code for: " << pattern;
19 EXPECT_NE(result.status().message().find(expected.status().message()),
20 std::string::npos)
21 << "parse message '" << result.status().message()
22 << "' does not contain '" << expected.status().message()
23 << "' for: " << pattern;
24 return;
25 }
26 const auto& expected_part_list = expected.value();
27 const auto& part_list = result.value().PartList();
28 EXPECT_EQ(part_list.size(), expected_part_list.size())
29 << "parser should produce expected number of parts for: " << pattern;
30 for (size_t i = 0; i < part_list.size() && i < expected_part_list.size();
31 ++i) {
32 EXPECT_EQ(part_list[i], expected_part_list[i])
33 << "token at index " << i << " wrong for: " << pattern;
34 }
35 }
36
TEST(ParseTest,EmptyPattern)37 TEST(ParseTest, EmptyPattern) {
38 RunParseTest("", std::vector<Part>());
39 }
40
TEST(ParseTest,InvalidChar)41 TEST(ParseTest, InvalidChar) {
42 RunParseTest("/foo/ßar", absl::InvalidArgumentError("Invalid character"));
43 }
44
TEST(ParseTest,Fixed)45 TEST(ParseTest, Fixed) {
46 std::vector<Part> expected_parts = {
47 Part(PartType::kFixed, "/foo", Modifier::kNone),
48 };
49 RunParseTest("/foo", expected_parts);
50 }
51
TEST(ParseTest,FixedInGroup)52 TEST(ParseTest, FixedInGroup) {
53 std::vector<Part> expected_parts = {
54 Part(PartType::kFixed, "/foo", Modifier::kNone),
55 };
56 RunParseTest("{/foo}", expected_parts);
57 }
58
TEST(ParseTest,FixedAndEmptyGroup)59 TEST(ParseTest, FixedAndEmptyGroup) {
60 std::vector<Part> expected_parts = {
61 Part(PartType::kFixed, "/f", Modifier::kNone),
62 Part(PartType::kFixed, "oo", Modifier::kNone),
63 };
64 RunParseTest("/f{}oo", expected_parts);
65 }
66
TEST(ParseTest,FixedInGroupWithOptionalModifier)67 TEST(ParseTest, FixedInGroupWithOptionalModifier) {
68 std::vector<Part> expected_parts = {
69 Part(PartType::kFixed, "/foo", Modifier::kOptional),
70 };
71 RunParseTest("{/foo}?", expected_parts);
72 }
73
TEST(ParseTest,FixedInGroupWithZeroOrMoreModifier)74 TEST(ParseTest, FixedInGroupWithZeroOrMoreModifier) {
75 std::vector<Part> expected_parts = {
76 Part(PartType::kFixed, "/foo", Modifier::kZeroOrMore),
77 };
78 RunParseTest("{/foo}*", expected_parts);
79 }
80
TEST(ParseTest,FixedInGroupWithOneOrMoreModifier)81 TEST(ParseTest, FixedInGroupWithOneOrMoreModifier) {
82 std::vector<Part> expected_parts = {
83 Part(PartType::kFixed, "/foo", Modifier::kOneOrMore),
84 };
85 RunParseTest("{/foo}+", expected_parts);
86 }
87
TEST(ParseTest,FixedInEarlyTerminatedGroup)88 TEST(ParseTest, FixedInEarlyTerminatedGroup) {
89 RunParseTest("{/foo", absl::InvalidArgumentError("expected CLOSE"));
90 }
91
TEST(ParseTest,FixedInUnbalancedGroup)92 TEST(ParseTest, FixedInUnbalancedGroup) {
93 RunParseTest("{/foo?", absl::InvalidArgumentError("expected CLOSE"));
94 }
95
TEST(ParseTest,FixedWithModifier)96 TEST(ParseTest, FixedWithModifier) {
97 RunParseTest("/foo?", absl::InvalidArgumentError("Unexpected MODIFIER"));
98 }
99
TEST(ParseTest,Regex)100 TEST(ParseTest, Regex) {
101 std::vector<Part> expected_parts = {
102 Part(PartType::kFixed, "/f", Modifier::kNone),
103 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"", "oo", /*suffix=*/"",
104 Modifier::kNone),
105 };
106 RunParseTest("/f(oo)", expected_parts);
107 }
108
TEST(ParseTest,RegexInGroup)109 TEST(ParseTest, RegexInGroup) {
110 std::vector<Part> expected_parts = {
111 Part(PartType::kFixed, "/f", Modifier::kNone),
112 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"", "oo", /*suffix=*/"",
113 Modifier::kNone),
114 };
115 RunParseTest("/f{(oo)}", expected_parts);
116 }
117
TEST(ParseTest,RegexWithPrefixAndSuffixInGroup)118 TEST(ParseTest, RegexWithPrefixAndSuffixInGroup) {
119 std::vector<Part> expected_parts = {
120 Part(PartType::kFixed, "/", Modifier::kNone),
121 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"f", "o", /*suffix=*/"o",
122 Modifier::kNone),
123 };
124 RunParseTest("/{f(o)o}", expected_parts);
125 }
126
TEST(ParseTest,RegexAndRegexInGroup)127 TEST(ParseTest, RegexAndRegexInGroup) {
128 RunParseTest("/f{(o)(o)}", absl::InvalidArgumentError("expected CLOSE"));
129 }
130
TEST(ParseTest,RegexWithPrefix)131 TEST(ParseTest, RegexWithPrefix) {
132 std::vector<Part> expected_parts = {
133 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"/", "foo", /*suffix=*/"",
134 Modifier::kNone),
135 };
136 RunParseTest("/(foo)", expected_parts);
137 }
138
TEST(ParseTest,RegexWithNameAndPrefix)139 TEST(ParseTest, RegexWithNameAndPrefix) {
140 std::vector<Part> expected_parts = {
141 Part(PartType::kFixed, "/foo", Modifier::kNone),
142 Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"/", "[^/]+?",
143 /*suffix=*/"", Modifier::kNone),
144 };
145 RunParseTest("/foo/:bar([^/]+?)", expected_parts);
146 }
147
TEST(ParseTest,RegexWithNameAndPrefixInGroup)148 TEST(ParseTest, RegexWithNameAndPrefixInGroup) {
149 std::vector<Part> expected_parts = {
150 Part(PartType::kFixed, "/foo/", Modifier::kNone),
151 Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"", "[^/]+?",
152 /*suffix=*/"", Modifier::kNone),
153 };
154 RunParseTest("/foo/{:bar([^/]+?)}", expected_parts);
155 }
156
TEST(ParseTest,RegexWithModifier)157 TEST(ParseTest, RegexWithModifier) {
158 std::vector<Part> expected_parts = {
159 Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"/", "foo",
160 /*suffix=*/"", Modifier::kOptional),
161 };
162 RunParseTest("/(foo)?", expected_parts);
163 }
164
TEST(ParseTest,RegexLikeFullWildcard)165 TEST(ParseTest, RegexLikeFullWildcard) {
166 std::vector<Part> expected_parts = {
167 Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", "",
168 /*suffix=*/"", Modifier::kNone),
169 };
170 RunParseTest("/(.*)", expected_parts);
171 }
172
TEST(ParseTest,Name)173 TEST(ParseTest, Name) {
174 std::vector<Part> expected_parts = {
175 Part(PartType::kFixed, "/foo", Modifier::kNone),
176 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
177 /*value=*/"", /*suffix=*/"", Modifier::kNone),
178 };
179 RunParseTest("/foo:bar", expected_parts);
180 }
181
TEST(ParseTest,NameInGroup)182 TEST(ParseTest, NameInGroup) {
183 std::vector<Part> expected_parts = {
184 Part(PartType::kFixed, "/foo", Modifier::kNone),
185 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
186 /*value=*/"", /*suffix=*/"", Modifier::kNone),
187 };
188 RunParseTest("/foo{:bar}", expected_parts);
189 }
190
TEST(ParseTest,NameAndNameInGroup)191 TEST(ParseTest, NameAndNameInGroup) {
192 RunParseTest("/foo{:bar:baz}", absl::InvalidArgumentError("expected CLOSE"));
193 }
194
TEST(ParseTest,NameWithPrefixAndSuffixInGroup)195 TEST(ParseTest, NameWithPrefixAndSuffixInGroup) {
196 std::vector<Part> expected_parts = {
197 Part(PartType::kFixed, "/foo/", Modifier::kNone),
198 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"data_",
199 /*value=*/"", /*suffix=*/".jpg", Modifier::kNone),
200 };
201 RunParseTest("/foo/{data_:bar.jpg}", expected_parts);
202 }
203
TEST(ParseTest,NameWithPrefix)204 TEST(ParseTest, NameWithPrefix) {
205 std::vector<Part> expected_parts = {
206 Part(PartType::kFixed, "/foo", Modifier::kNone),
207 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"/",
208 /*value=*/"", /*suffix=*/"", Modifier::kNone),
209 };
210 RunParseTest("/foo/:bar", expected_parts);
211 }
212
TEST(ParseTest,NameWithEscapedPrefix)213 TEST(ParseTest, NameWithEscapedPrefix) {
214 std::vector<Part> expected_parts = {
215 Part(PartType::kFixed, "/foo/", Modifier::kNone),
216 Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
217 /*value=*/"", /*suffix=*/"", Modifier::kNone),
218 };
219 RunParseTest("/foo\\/:bar", expected_parts);
220 }
221
TEST(ParseTest,NameWithCustomRegex)222 TEST(ParseTest, NameWithCustomRegex) {
223 std::vector<Part> expected_parts = {
224 Part(PartType::kFixed, "/foo", Modifier::kNone),
225 Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"", "[^/]+?",
226 /*suffix=*/"", Modifier::kNone),
227 };
228 RunParseTest("/foo:bar([^/]+?)", expected_parts);
229 }
230
TEST(ParseTest,NameWithModifier)231 TEST(ParseTest, NameWithModifier) {
232 std::vector<Part> expected_parts = {
233 Part(PartType::kSegmentWildcard, /*name=*/"foo", /*prefix=*/"/",
234 /*value=*/"", /*suffix=*/"", Modifier::kOptional),
235 };
236 RunParseTest("/:foo?", expected_parts);
237 }
238
239 } // namespace liburlpattern
240