0

[Code Health Rotation] Clean Up URLUtilTest

As part of the code health rotation, we should remove the stale feature
flag `StandardCompliantNonSpecialSchemeURLParsing`. This feature was
launched in M130. See https://crbug.com/388143055 for details.

Bug: 388143055
Change-Id: I49fade5a137ce352faa1753cdd725aecac1a6265
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6171928
Reviewed-by: Adam Rice <ricea@chromium.org>
Commit-Queue: Hayato Ito <hayato@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1407146}
This commit is contained in:
Hayato Ito
2025-01-15 22:56:36 -08:00
committed by Chromium LUCI CQ
parent ea738d26c3
commit 308592ff1d

@ -9,14 +9,12 @@
#include <optional>
#include <string_view>
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest-message.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/third_party/mozilla/url_parse.h"
#include "url/url_canon.h"
#include "url/url_canon_stdstring.h"
#include "url/url_features.h"
#include "url/url_test_utils.h"
namespace url {
@ -30,6 +28,55 @@ class URLUtilTest : public testing::Test {
~URLUtilTest() override = default;
protected:
struct URLCase {
const std::string_view input;
const std::string_view expected;
bool expected_success;
};
struct ResolveRelativeCase {
const std::string_view base;
const std::string_view rel;
std::optional<std::string_view> expected;
};
void TestCanonicalize(const URLCase& url_case) {
std::string canonicalized;
StdStringCanonOutput output(&canonicalized);
Parsed parsed;
bool success =
Canonicalize(url_case.input.data(), url_case.input.size(),
/*trim_path_end=*/false,
/*charset_converter=*/nullptr, &output, &parsed);
output.Complete();
EXPECT_EQ(success, url_case.expected_success);
EXPECT_EQ(output.view(), url_case.expected);
}
void TestResolveRelative(const ResolveRelativeCase& test) {
SCOPED_TRACE(testing::Message()
<< "base: " << test.base << ", rel: " << test.rel);
Parsed base_parsed = ParseNonSpecialURL(test.base);
std::string resolved;
StdStringCanonOutput output(&resolved);
Parsed resolved_parsed;
bool valid = ResolveRelative(test.base.data(), test.base.size(),
base_parsed, test.rel.data(), test.rel.size(),
nullptr, &output, &resolved_parsed);
output.Complete();
if (valid) {
ASSERT_TRUE(test.expected);
EXPECT_EQ(resolved, *test.expected);
} else {
EXPECT_FALSE(test.expected);
}
}
private:
ScopedSchemeRegistryForTests scoped_registry_;
};
@ -610,78 +657,7 @@ TEST_F(URLUtilTest, TestHasInvalidURLEscapeSequences) {
}
}
class URLUtilTypedTest : public ::testing::TestWithParam<bool> {
public:
URLUtilTypedTest()
: use_standard_compliant_non_special_scheme_url_parsing_(GetParam()) {
if (use_standard_compliant_non_special_scheme_url_parsing_) {
scoped_feature_list_.InitAndEnableFeature(
kStandardCompliantNonSpecialSchemeURLParsing);
} else {
scoped_feature_list_.InitAndDisableFeature(
kStandardCompliantNonSpecialSchemeURLParsing);
}
}
protected:
struct URLCase {
const std::string_view input;
const std::string_view expected;
bool expected_success;
};
struct ResolveRelativeCase {
const std::string_view base;
const std::string_view rel;
std::optional<std::string_view> expected;
};
void TestCanonicalize(const URLCase& url_case) {
std::string canonicalized;
StdStringCanonOutput output(&canonicalized);
Parsed parsed;
bool success =
Canonicalize(url_case.input.data(), url_case.input.size(),
/*trim_path_end=*/false,
/*charset_converter=*/nullptr, &output, &parsed);
output.Complete();
EXPECT_EQ(success, url_case.expected_success);
EXPECT_EQ(output.view(), url_case.expected);
}
void TestResolveRelative(const ResolveRelativeCase& test) {
SCOPED_TRACE(testing::Message()
<< "base: " << test.base << ", rel: " << test.rel);
Parsed base_parsed =
url::IsUsingStandardCompliantNonSpecialSchemeURLParsing()
? ParseNonSpecialURL(test.base)
: ParsePathURL(test.base, /*trim_path_end=*/true);
std::string resolved;
StdStringCanonOutput output(&resolved);
Parsed resolved_parsed;
bool valid = ResolveRelative(test.base.data(), test.base.size(),
base_parsed, test.rel.data(), test.rel.size(),
nullptr, &output, &resolved_parsed);
output.Complete();
if (valid) {
ASSERT_TRUE(test.expected);
EXPECT_EQ(resolved, *test.expected);
} else {
EXPECT_FALSE(test.expected);
}
}
bool use_standard_compliant_non_special_scheme_url_parsing_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_P(URLUtilTypedTest, TestResolveRelativeWithNonStandardBase) {
TEST_F(URLUtilTest, TestResolveRelativeWithNonStandardBase) {
// This tests non-standard (in the sense that IsStandard() == false)
// hierarchical schemes.
struct ResolveRelativeCase {
@ -689,9 +665,6 @@ TEST_P(URLUtilTypedTest, TestResolveRelativeWithNonStandardBase) {
const char* rel;
bool is_valid;
const char* out;
// Optional expected output when the feature is enabled.
// If the result doesn't change, you can omit this field.
const char* out_when_non_special_url_feature_is_enabled;
} resolve_non_standard_cases[] = {
// Resolving a relative path against a non-hierarchical URL should fail.
{"scheme:opaque_data", "/path", false, ""},
@ -717,12 +690,9 @@ TEST_P(URLUtilTypedTest, TestResolveRelativeWithNonStandardBase) {
{"custom://Authority:NoCanon/path/", "file.html", true,
"custom://Authority:NoCanon/path/file.html"},
// A path with an authority section gets canonicalized under standard URL
// rules, even though the base was non-standard.
// rules, even though the base was non-standard. Host case sensitivity
// should be preserved and trailing slash after a host soulld be removed.
{"content://content.Provider/", "//other.Provider", true,
"content://other.provider/",
// With the feature enabled:
// - Host case sensitivity should be preserved.
// - Trailing slash after a host is no longer necessary.
"content://other.Provider"},
// Resolving an absolute URL doesn't cause canonicalization of the
// result.
@ -747,10 +717,7 @@ TEST_P(URLUtilTypedTest, TestResolveRelativeWithNonStandardBase) {
SCOPED_TRACE(testing::Message()
<< "base: " << test.base << ", rel: " << test.rel);
Parsed base_parsed = use_standard_compliant_non_special_scheme_url_parsing_
? ParseNonSpecialURL(test.base)
: ParsePathURL(test.base, /*trim_path_end=*/true);
Parsed base_parsed = ParseNonSpecialURL(test.base);
std::string resolved;
StdStringCanonOutput output(&resolved);
Parsed resolved_parsed;
@ -761,141 +728,36 @@ TEST_P(URLUtilTypedTest, TestResolveRelativeWithNonStandardBase) {
EXPECT_EQ(test.is_valid, valid);
if (test.is_valid && valid) {
if (use_standard_compliant_non_special_scheme_url_parsing_ &&
test.out_when_non_special_url_feature_is_enabled) {
EXPECT_EQ(test.out_when_non_special_url_feature_is_enabled, resolved);
} else {
EXPECT_EQ(test.out, resolved);
}
EXPECT_EQ(test.out, resolved);
}
}
}
TEST_P(URLUtilTypedTest, TestNoRefComponent) {
// This test was originally written before full support for non-special URLs
// became available. We need a flag-dependent test here because the test uses
// an internal parse function. See http://crbug.com/40063064 for details.
//
// The test case corresponds to the following user scenario:
//
// > const url = new URL("any#body", "mailto://to/");
// > assertEquals(url.href, "mailto://to/any#body");
//
// TODO(crbug.com/40063064): Remove this test once the flag is enabled.
const std::string_view base = "mailto://to/";
const std::string_view rel = "any#body";
if (use_standard_compliant_non_special_scheme_url_parsing_) {
// We probably don't need to test with the flag enabled, however, including
// a test with the flag enabled would be beneficial for comparison purposes,
// at least until we enable the flag by default.
Parsed base_parsed = ParseNonSpecialURL(base);
std::string resolved;
StdStringCanonOutput output(&resolved);
Parsed resolved_parsed;
bool valid =
ResolveRelative(base.data(), base.size(), base_parsed, rel.data(),
rel.size(), nullptr, &output, &resolved_parsed);
EXPECT_TRUE(valid);
// Note: If the flag is enabled and the correct parsing function is used,
// resolved_parsed.ref becomes valid correctly.
EXPECT_TRUE(resolved_parsed.ref.is_valid());
output.Complete();
EXPECT_EQ(resolved, "mailto://to/any#body");
} else {
// Note: See the description of https://codereview.chromium.org/767713002/
// for the intention of this test, which added this test to record a wrong
// result if a wrong parser function is used. I kept the following original
// comment as is:
//
// The hash-mark must be ignored when mailto: scheme is parsed,
// even if the URL has a base and relative part.
std::string resolved;
StdStringCanonOutput output(&resolved);
Parsed resolved_parsed;
bool valid = ResolveRelative(
base.data(), base.size(), ParsePathURL(base, false), rel.data(),
rel.size(), nullptr, &output, &resolved_parsed);
EXPECT_TRUE(valid);
EXPECT_FALSE(resolved_parsed.ref.is_valid());
}
}
TEST_P(URLUtilTypedTest, Cannolicalize) {
TEST_F(URLUtilTest, Cannolicalize) {
// Verify that the feature flag changes canonicalization behavior,
// focusing on key cases here as comprehesive testing is covered in other unit
// tests.
if (use_standard_compliant_non_special_scheme_url_parsing_) {
URLCase cases[] = {
{"git://host/..", "git://host/", true},
{"git:// /", "git:///", false},
{"git:/..", "git:/", true},
{"mailto:/..", "mailto:/", true},
};
for (const auto& i : cases) {
TestCanonicalize(i);
}
} else {
// Every non-special URL is considered as an opaque path if the feature is
// disabled.
URLCase cases[] = {
{"git://host/..", "git://host/..", true},
{"git:// /", "git:// /", true},
{"git:/..", "git:/..", true},
{"mailto:/..", "mailto:/..", true},
};
for (const auto& i : cases) {
TestCanonicalize(i);
}
URLCase cases[] = {
{"git://host/..", "git://host/", true},
{"git:// /", "git:///", false},
{"git:/..", "git:/", true},
{"mailto:/..", "mailto:/", true},
};
for (const auto& i : cases) {
TestCanonicalize(i);
}
}
TEST_P(URLUtilTypedTest, TestResolveRelativeWithNonSpecialBase) {
// Test flag-dependent behaviors. Existing tests in
// URLUtilTest::TestResolveRelativeWithNonStandardBase cover common cases.
//
// TODO(crbug.com/40063064): Test common cases in this typed test too.
if (use_standard_compliant_non_special_scheme_url_parsing_) {
ResolveRelativeCase cases[] = {
{"scheme://Authority", "path", "scheme://Authority/path"},
};
for (const auto& i : cases) {
TestResolveRelative(i);
}
} else {
ResolveRelativeCase cases[] = {
// It's still possible to get an invalid path URL.
//
// Note: If the flag is enabled, "custom://Invalid:!#Auth/" is an
// invalid URL.
// ResolveRelative() should be never called.
{"custom://Invalid:!#Auth/", "file.html", std::nullopt},
// Resolving should fail if the base URL is authority-based but is
// missing a path component (the '/' at the end).
{"scheme://Authority", "path", std::nullopt},
// In this case, the backslashes will not be canonicalized because it's
// a non-standard URL, but they will be treated as a path separators,
// giving the base URL here a path of "\".
//
// The result here is somewhat arbitrary. One could argue it should be
// either "aaa://a\" or "aaa://a/" since the path is being replaced with
// the "current directory". But in the context of resolving on data
// URLs, adding the requested dot doesn't seem wrong either.
//
// Note: If the flag is enabled, "aaa://a\\" is an invalid URL.
// ResolveRelative() should be never called.
{"aaa://a\\", "aaa:.", "aaa://a\\."}};
for (const auto& i : cases) {
TestResolveRelative(i);
}
TEST_F(URLUtilTest, TestResolveRelativeWithNonSpecialBase) {
ResolveRelativeCase cases[] = {
{"scheme://Authority", "path", "scheme://Authority/path"},
};
for (const auto& i : cases) {
TestResolveRelative(i);
}
}
TEST_P(URLUtilTypedTest, OpaqueNonSpecialScheme) {
TEST_F(URLUtilTest, OpaqueNonSpecialScheme) {
// Ensure that the behavior of "android:" scheme URL is preserved, which is
// not URL Standard compliant.
//
@ -907,15 +769,8 @@ TEST_P(URLUtilTypedTest, OpaqueNonSpecialScheme) {
// Test a "git:" scheme URL for comparison.
res = CanonicalizeSpec("git://a b", false);
if (use_standard_compliant_non_special_scheme_url_parsing_) {
// This is correct behavior because "git://a b" is an invalid URL.
EXPECT_FALSE(res);
} else {
ASSERT_TRUE(res);
EXPECT_EQ(*res, "git://a b");
}
// This is correct behavior because "git://a b" is an invalid URL.
EXPECT_FALSE(res);
}
INSTANTIATE_TEST_SUITE_P(All, URLUtilTypedTest, ::testing::Bool());
} // namespace url