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