blob: 2401bc13edff63ffaa09bc9d0d9157f27f099bd5 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file or at https://opensource.org/licenses/MIT.
#include "third_party/liburlpattern/parse.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/strings/str_format.h"
#include "third_party/liburlpattern/pattern.h"
namespace {
absl::StatusOr<std::string> PassThrough(absl::string_view input) {
return std::string(input);
}
} // namespace
namespace liburlpattern {
absl::StatusOr<std::string> ToUpper(absl::string_view input) {
std::string output;
std::transform(input.begin(), input.end(), std::back_inserter(output),
[](unsigned char c) { return std::toupper(c); });
return output;
}
void RunParseTest(absl::string_view pattern,
absl::StatusOr<std::vector<Part>> expected,
EncodeCallback callback = PassThrough) {
auto result = Parse(pattern, std::move(callback));
ASSERT_EQ(result.ok(), expected.ok())
<< "parse status '" << result.status() << "' for: " << pattern;
if (!expected.ok()) {
ASSERT_EQ(result.status().code(), expected.status().code())
<< "parse status code for: " << pattern;
EXPECT_NE(result.status().message().find(expected.status().message()),
std::string::npos)
<< "parse message '" << result.status().message()
<< "' does not contain '" << expected.status().message()
<< "' for: " << pattern;
return;
}
const auto& expected_part_list = expected.value();
const auto& part_list = result.value().PartList();
EXPECT_EQ(part_list.size(), expected_part_list.size())
<< "parser should produce expected number of parts for: " << pattern;
for (size_t i = 0; i < part_list.size() && i < expected_part_list.size();
++i) {
EXPECT_EQ(part_list[i], expected_part_list[i])
<< "token at index " << i << " wrong for: " << pattern;
}
}
TEST(ParseTest, EmptyPattern) {
RunParseTest("", std::vector<Part>());
}
TEST(ParseTest, EncoderCallback) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/FOO/BAR", Modifier::kNone),
};
RunParseTest("/foo/bar", expected_parts, ToUpper);
}
TEST(ParseTest, Fixed) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
};
RunParseTest("/foo", expected_parts);
}
TEST(ParseTest, FixedInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
};
RunParseTest("{/foo}", expected_parts);
}
TEST(ParseTest, FixedAndFixedInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
};
RunParseTest("/{foo}", expected_parts);
}
TEST(ParseTest, FixedInGroupAndFixed) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
};
RunParseTest("{/}foo", expected_parts);
}
TEST(ParseTest, FixedInGroupAndFixedInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
};
RunParseTest("{/}{foo}", expected_parts);
}
TEST(ParseTest, FixedAndEmptyGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
};
RunParseTest("/f{}oo", expected_parts);
}
TEST(ParseTest, FixedInGroupWithOptionalModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kOptional),
};
RunParseTest("{/foo}?", expected_parts);
}
TEST(ParseTest, FixedInGroupWithZeroOrMoreModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kZeroOrMore),
};
RunParseTest("{/foo}*", expected_parts);
}
TEST(ParseTest, FixedInGroupWithOneOrMoreModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kOneOrMore),
};
RunParseTest("{/foo}+", expected_parts);
}
TEST(ParseTest, FixedInEarlyTerminatedGroup) {
RunParseTest("{/foo", absl::InvalidArgumentError("expected '}'"));
}
TEST(ParseTest, FixedInUnbalancedGroup) {
RunParseTest("{/foo?", absl::InvalidArgumentError("expected '}'"));
}
TEST(ParseTest, FixedWithModifier) {
RunParseTest("/foo?", absl::InvalidArgumentError("Unexpected modifier"));
}
TEST(ParseTest, Regex) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/f", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"", "oo", /*suffix=*/"",
Modifier::kNone),
};
RunParseTest("/f(oo)", expected_parts);
}
TEST(ParseTest, RegexInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/f", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"", "oo", /*suffix=*/"",
Modifier::kNone),
};
RunParseTest("/f{(oo)}", expected_parts);
}
TEST(ParseTest, RegexWithPrefixAndSuffixInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"f", "o", /*suffix=*/"o",
Modifier::kNone),
};
RunParseTest("/{f(o)o}", expected_parts);
}
TEST(ParseTest, RegexAndRegexInGroup) {
RunParseTest("/f{(o)(o)}", absl::InvalidArgumentError("expected '}'"));
}
TEST(ParseTest, RegexWithPrefix) {
std::vector<Part> expected_parts = {
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"/", "foo", /*suffix=*/"",
Modifier::kNone),
};
RunParseTest("/(foo)", expected_parts);
}
TEST(ParseTest, RegexWithNameAndPrefix) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"/", "[^/]+?",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo/:bar([^/]+?)", expected_parts);
}
TEST(ParseTest, RegexWithNameAndPrefixInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo/", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"", "[^/]+?",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo/{:bar([^/]+?)}", expected_parts);
}
TEST(ParseTest, RegexWithModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"/", "foo",
/*suffix=*/"", Modifier::kOptional),
};
RunParseTest("/(foo)?", expected_parts);
}
TEST(ParseTest, RegexLikeFullWildcard) {
std::vector<Part> expected_parts = {
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/(.*)", expected_parts);
}
TEST(ParseTest, Wildcard) {
std::vector<Part> expected_parts = {
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/*", expected_parts);
}
TEST(ParseTest, WildcardWithModifierStar) {
std::vector<Part> expected_parts = {
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
/*suffix=*/"", Modifier::kZeroOrMore),
};
RunParseTest("/**", expected_parts);
}
TEST(ParseTest, WildcardWithModifierPlus) {
std::vector<Part> expected_parts = {
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
/*suffix=*/"", Modifier::kOneOrMore),
};
RunParseTest("/*+", expected_parts);
}
TEST(ParseTest, WildcardWithModifierQuestion) {
std::vector<Part> expected_parts = {
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
/*suffix=*/"", Modifier::kOptional),
};
RunParseTest("/*?", expected_parts);
}
TEST(ParseTest, WildcardFollowingWildcardWithModifierStart) {
std::vector<Part> expected_parts = {
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", /*value=*/"",
/*suffix=*/"", Modifier::kZeroOrMore),
Part(PartType::kFullWildcard, /*name=*/"1", /*prefix=*/"", /*value=*/"",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/***", expected_parts);
}
TEST(ParseTest, WildcardWithMultipleModifiersPlus) {
RunParseTest("/**+", absl::InvalidArgumentError("expected end of pattern"));
}
TEST(ParseTest, WildcardWithMultipleModifiersQuestion) {
RunParseTest("/**?", absl::InvalidArgumentError("expected end of pattern"));
}
TEST(ParseTest, WildcardInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/f", Modifier::kNone),
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"", /*value=*/"",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/f{*}", expected_parts);
}
TEST(ParseTest, WildcardWithPrefixAndSuffixInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/", Modifier::kNone),
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"f", /*value=*/"",
/*suffix=*/"o", Modifier::kNone),
};
RunParseTest("/{f*o}", expected_parts);
}
TEST(ParseTest, Name) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
/*value=*/"", /*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo:bar", expected_parts);
}
TEST(ParseTest, NameStartsWithNumber) {
RunParseTest("/foo/:0", absl::InvalidArgumentError("Missing parameter name"));
}
TEST(ParseTest, NameInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
/*value=*/"", /*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo{:bar}", expected_parts);
}
TEST(ParseTest, NameAndNameInGroup) {
RunParseTest("/foo{:bar:baz}", absl::InvalidArgumentError("expected '}'"));
}
TEST(ParseTest, NameWithPrefixAndSuffixInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo/", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"data_",
/*value=*/"", /*suffix=*/".jpg", Modifier::kNone),
};
RunParseTest("/foo/{data_:bar.jpg}", expected_parts);
}
TEST(ParseTest, NameWithPrefix) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"/",
/*value=*/"", /*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo/:bar", expected_parts);
}
TEST(ParseTest, NameWithEscapedPrefix) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo/", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
/*value=*/"", /*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo\\/:bar", expected_parts);
}
TEST(ParseTest, NameWithCustomRegex) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"", "[^/]+?",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo:bar([^/]+?)", expected_parts);
}
TEST(ParseTest, NameWithModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kSegmentWildcard, /*name=*/"foo", /*prefix=*/"/",
/*value=*/"", /*suffix=*/"", Modifier::kOptional),
};
RunParseTest("/:foo?", expected_parts);
}
TEST(ParseTest, NameWithModifierStarAndWildcard) {
std::vector<Part> expected_parts = {
Part(PartType::kSegmentWildcard, /*name=*/"foo", /*prefix=*/"/",
/*value=*/"", /*suffix=*/"", Modifier::kZeroOrMore),
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"",
/*value=*/"", /*suffix=*/"", Modifier::kNone),
};
RunParseTest("/:foo**", expected_parts);
}
TEST(ParseTest, NameWithModifierStarAndModifierQuestion) {
RunParseTest("/:foo*?",
absl::InvalidArgumentError("expected end of pattern"));
}
TEST(ParseTest, NameWithModifierStarAndModifierPlus) {
RunParseTest("/:foo*+",
absl::InvalidArgumentError("expected end of pattern"));
}
TEST(ParseTest, DuplicateName) {
RunParseTest("/:foo/:foo", absl::InvalidArgumentError("Duplicate"));
}
} // namespace liburlpattern