// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/registry_controlled_domains/registry_controlled_domain.h"

#include <cstdint>
#include <optional>
#include <string>
#include <string_view>

#include "base/containers/span.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/types/expected_macros.h"
#include "net/base/registry_controlled_domains/effective_tld_names_unittest1-reversed-inc.cc"
#include "net/base/registry_controlled_domains/effective_tld_names_unittest2-reversed-inc.cc"
#include "net/base/registry_controlled_domains/effective_tld_names_unittest3-reversed-inc.cc"
#include "net/base/registry_controlled_domains/effective_tld_names_unittest4-reversed-inc.cc"
#include "net/base/registry_controlled_domains/effective_tld_names_unittest5-reversed-inc.cc"
#include "net/base/registry_controlled_domains/effective_tld_names_unittest6-reversed-inc.cc"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/buildflags.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace {


}  // namespace

namespace net::registry_controlled_domains {

namespace {

std::string GetDomainFromHost(const std::string& host) {
  return GetDomainAndRegistry(host, EXCLUDE_PRIVATE_REGISTRIES);
}

std::optional<std::string> GetRegistryFromURL(
    std::string_view url,
    UnknownRegistryFilter unknown_filter) {
  GURL gurl(url);
  ASSIGN_OR_RETURN(
      std::string_view registry,
      GetRegistry(gurl, unknown_filter, EXCLUDE_PRIVATE_REGISTRIES));
  return std::string(registry);
}

std::optional<std::string> GetRegistryFromURLIncludingPrivate(
    std::string_view url,
    UnknownRegistryFilter unknown_filter) {
  GURL gurl(url);
  ASSIGN_OR_RETURN(
      std::string_view registry,
      GetRegistry(gurl, unknown_filter, INCLUDE_PRIVATE_REGISTRIES));
  return std::string(registry);
}

std::optional<std::string_view> PermissiveGetHostRegistry(
    std::string_view host) {
  return PermissiveGetHostRegistry(host, EXCLUDE_UNKNOWN_REGISTRIES,
                                   EXCLUDE_PRIVATE_REGISTRIES);
}

// Only called when using ICU (avoids unused static function error).
#if !BUILDFLAG(USE_PLATFORM_ICU_ALTERNATIVES)
std::optional<std::u16string_view> PermissiveGetHostRegistry(
    std::u16string_view host) {
  return PermissiveGetHostRegistry(host, EXCLUDE_UNKNOWN_REGISTRIES,
                                   EXCLUDE_PRIVATE_REGISTRIES);
}
#endif

std::optional<std::string_view> GetCanonicalHostRegistry(
    std::string_view host,
    UnknownRegistryFilter unknown_filter) {
  return GetCanonicalHostRegistry(host, unknown_filter,
                                  EXCLUDE_PRIVATE_REGISTRIES);
}

std::optional<std::string_view> GetCanonicalHostRegistryIncludingPrivate(
    std::string_view host) {
  return GetCanonicalHostRegistry(host, EXCLUDE_UNKNOWN_REGISTRIES,
                                  INCLUDE_PRIVATE_REGISTRIES);
}

}  // namespace

class RegistryControlledDomainTest : public testing::Test {
 protected:
  void UseDomainData(base::span<const uint8_t> graph) {
    // This is undone in TearDown.
    SetFindDomainGraphForTesting(graph);
  }

  bool CompareDomains(const std::string& url1, const std::string& url2) {
    SCOPED_TRACE(url1 + " " + url2);
    GURL g1 = GURL(url1);
    GURL g2 = GURL(url2);
    url::Origin o1 = url::Origin::Create(g1);
    url::Origin o2 = url::Origin::Create(g2);
    EXPECT_EQ(SameDomainOrHost(o1, o2, EXCLUDE_PRIVATE_REGISTRIES),
              SameDomainOrHost(g1, g2, EXCLUDE_PRIVATE_REGISTRIES));
    return SameDomainOrHost(g1, g2, EXCLUDE_PRIVATE_REGISTRIES);
  }

  void TearDown() override { ResetFindDomainGraphForTesting(); }
};

TEST_F(RegistryControlledDomainTest, TestHostIsRegistryIdentifier) {
  UseDomainData(test1::kDafsa);
  // A hostname with a label above the eTLD
  EXPECT_FALSE(HostIsRegistryIdentifier("blah.jp", EXCLUDE_PRIVATE_REGISTRIES));
  EXPECT_FALSE(
      HostIsRegistryIdentifier(".blah.jp", INCLUDE_PRIVATE_REGISTRIES));
  EXPECT_FALSE(
      HostIsRegistryIdentifier(".blah.jp.", INCLUDE_PRIVATE_REGISTRIES));
  // A private TLD
  EXPECT_FALSE(HostIsRegistryIdentifier("priv.no", EXCLUDE_PRIVATE_REGISTRIES));
  EXPECT_TRUE(HostIsRegistryIdentifier("priv.no", INCLUDE_PRIVATE_REGISTRIES));
  EXPECT_TRUE(
      HostIsRegistryIdentifier(".priv.no.", INCLUDE_PRIVATE_REGISTRIES));
  // A hostname that is a TLD
  EXPECT_TRUE(HostIsRegistryIdentifier("jp", EXCLUDE_PRIVATE_REGISTRIES));
  EXPECT_TRUE(HostIsRegistryIdentifier("jp", INCLUDE_PRIVATE_REGISTRIES));
  EXPECT_TRUE(HostIsRegistryIdentifier(".jp", EXCLUDE_PRIVATE_REGISTRIES));
  EXPECT_TRUE(HostIsRegistryIdentifier(".jp", INCLUDE_PRIVATE_REGISTRIES));
  EXPECT_TRUE(HostIsRegistryIdentifier(".jp.", EXCLUDE_PRIVATE_REGISTRIES));
  EXPECT_TRUE(HostIsRegistryIdentifier(".jp.", INCLUDE_PRIVATE_REGISTRIES));
  // A hostname that is a TLD specified by a wildcard rule
  EXPECT_TRUE(
      HostIsRegistryIdentifier("blah.bar.jp", INCLUDE_PRIVATE_REGISTRIES));
  EXPECT_FALSE(
      HostIsRegistryIdentifier("blah.blah.bar.jp", EXCLUDE_PRIVATE_REGISTRIES));
}

TEST_F(RegistryControlledDomainTest, TestGetDomainAndRegistry) {
  UseDomainData(test1::kDafsa);

  struct {
    std::string url;
    std::string expected_domain_and_registry;
  } kTestCases[] = {
      {"http://a.baz.jp/file.html", "baz.jp"},
      {"http://a.baz.jp./file.html", "baz.jp."},
      {"http://ac.jp", ""},
      {"http://a.bar.jp", ""},
      {"http://bar.jp", ""},
      {"http://baz.bar.jp", ""},
      {"http://a.b.baz.bar.jp", "a.b.baz.bar.jp"},

      {"http://baz.pref.bar.jp", "pref.bar.jp"},
      {"http://a.b.bar.baz.com.", "b.bar.baz.com."},

      {"http://a.d.c", "a.d.c"},
      {"http://.a.d.c", "a.d.c"},
      {"http://..a.d.c", "a.d.c"},
      {"http://a.b.c", "b.c"},
      {"http://baz.com", "baz.com"},
      {"http://baz.com.", "baz.com."},

      {"", ""},
      {"http://", ""},
      {"file:///C:/file.html", ""},
      {"http://foo.com..", ""},
      {"http://...", ""},
      {"http://192.168.0.1", ""},
      {"http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/", ""},
      {"http://localhost", ""},
      {"http://localhost.", ""},
      {"http:////Comment", ""},
  };
  for (const auto& test_case : kTestCases) {
    const GURL url(test_case.url);
    EXPECT_EQ(test_case.expected_domain_and_registry,
              GetDomainAndRegistry(url, EXCLUDE_PRIVATE_REGISTRIES));
    EXPECT_EQ(test_case.expected_domain_and_registry,
              GetDomainAndRegistry(url::Origin::Create(url),
                                   EXCLUDE_PRIVATE_REGISTRIES));
  }

  // Test std::string version of GetDomainAndRegistry().  Uses the same
  // underpinnings as the GURL version, so this is really more of a check of
  // CanonicalizeHost().
  EXPECT_EQ("baz.jp", GetDomainFromHost("a.baz.jp"));                  // 1
  EXPECT_EQ("baz.jp.", GetDomainFromHost("a.baz.jp."));                // 1
  EXPECT_EQ("", GetDomainFromHost("ac.jp"));                           // 2
  EXPECT_EQ("", GetDomainFromHost("a.bar.jp"));                        // 3
  EXPECT_EQ("", GetDomainFromHost("bar.jp"));                          // 3
  EXPECT_EQ("", GetDomainFromHost("baz.bar.jp"));                      // 3 4
  EXPECT_EQ("a.b.baz.bar.jp", GetDomainFromHost("a.b.baz.bar.jp"));    // 3 4
  EXPECT_EQ("pref.bar.jp", GetDomainFromHost("baz.pref.bar.jp"));      // 5
  EXPECT_EQ("b.bar.baz.com.", GetDomainFromHost("a.b.bar.baz.com."));  // 6
  EXPECT_EQ("a.d.c", GetDomainFromHost("a.d.c"));                      // 7
  EXPECT_EQ("a.d.c", GetDomainFromHost(".a.d.c"));                     // 7
  EXPECT_EQ("a.d.c", GetDomainFromHost("..a.d.c"));                    // 7
  EXPECT_EQ("b.c", GetDomainFromHost("a.b.c"));                        // 7 8
  EXPECT_EQ("baz.com", GetDomainFromHost("baz.com"));                  // none
  EXPECT_EQ("baz.com.", GetDomainFromHost("baz.com."));                // none

  EXPECT_EQ("", GetDomainFromHost(std::string()));
  EXPECT_EQ("", GetDomainFromHost("foo.com.."));
  EXPECT_EQ("", GetDomainFromHost("..."));
  EXPECT_EQ("", GetDomainFromHost("192.168.0.1"));
  EXPECT_EQ("", GetDomainFromHost("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"));
  EXPECT_EQ("", GetDomainFromHost("localhost."));
  EXPECT_EQ("", GetDomainFromHost(".localhost."));
}

// GetDomainAndRegistry is backed by a cache, so make sure that it's working
// correctly.
TEST_F(RegistryControlledDomainTest, TestGetDomainAndRegistryCaching) {
  UseDomainData(test1::kDafsa);

  // Ask the same thing twice, should get the same result.
  EXPECT_EQ("baz.jp", GetDomainFromHost("a.baz.jp"));
  EXPECT_EQ("baz.jp", GetDomainFromHost("a.baz.jp"));

  // Asking 100 different things shouldn't cause any boundary issues.
  for (int i = 0; i < 100; ++i) {
    EXPECT_EQ("baz.jp", GetDomainFromHost(base::StringPrintf("%d.baz.jp", i)));
  }

  // Ask a few more things multiple times, the results should be consistent.
  for (int i = 0; i < 3; i++) {
    EXPECT_EQ("baz.jp", GetDomainFromHost("a.baz.jp"));
    EXPECT_EQ("baz.jp.", GetDomainFromHost("a.baz.jp."));
    EXPECT_EQ("", GetDomainFromHost("ac.jp"));
    EXPECT_EQ("a.b.baz.bar.jp", GetDomainFromHost("a.b.baz.bar.jp"));
    EXPECT_EQ("pref.bar.jp", GetDomainFromHost("baz.pref.bar.jp"));
    EXPECT_EQ("b.bar.baz.com.", GetDomainFromHost("a.b.bar.baz.com."));
  }
}

TEST_F(RegistryControlledDomainTest, TestGetRegistry) {
  UseDomainData(test1::kDafsa);

  // Test GURL version of GetRegistry().
  EXPECT_EQ("jp", GetRegistryFromURL("http://a.baz.jp/file.html",
                                     EXCLUDE_UNKNOWN_REGISTRIES));  // 1
  EXPECT_EQ("jp.", GetRegistryFromURL("http://a.baz.jp./file.html",
                                      EXCLUDE_UNKNOWN_REGISTRIES));  // 1
  EXPECT_EQ("", GetRegistryFromURL("http://ac.jp",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // 2
  EXPECT_EQ("", GetRegistryFromURL("http://a.bar.jp",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // 3
  EXPECT_EQ("", GetRegistryFromURL("http://bar.jp",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // 3
  EXPECT_EQ("jp", GetRegistryFromURL("http://xbar.jp",
                                     EXCLUDE_UNKNOWN_REGISTRIES));  // 1
  EXPECT_EQ("", GetRegistryFromURL("http://baz.bar.jp",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // 3 4
  EXPECT_EQ("", GetRegistryFromURL("http://.baz.bar.jp",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // 3 4
  EXPECT_EQ("", GetRegistryFromURL("http://..baz.bar.jp",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // 3 4
  EXPECT_EQ(".baz.bar.jp",
            GetRegistryFromURL("http://foo..baz.bar.jp",
                               EXCLUDE_UNKNOWN_REGISTRIES));  // 3 4
  EXPECT_EQ("", GetRegistryFromURL("http://xbaz.bar.jp",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // 3
  EXPECT_EQ("xbaz.bar.jp",
            GetRegistryFromURL("http://x.xbaz.bar.jp",
                               EXCLUDE_UNKNOWN_REGISTRIES));  // 3
  EXPECT_EQ("b.baz.bar.jp",
            GetRegistryFromURL("http://a.b.baz.bar.jp",
                               EXCLUDE_UNKNOWN_REGISTRIES));  // 4
  EXPECT_EQ("bar.jp", GetRegistryFromURL("http://baz.pref.bar.jp",
                                         EXCLUDE_UNKNOWN_REGISTRIES));  // 5
  EXPECT_EQ("bar.jp", GetRegistryFromURL("http://z.baz.pref.bar.jp",
                                         EXCLUDE_UNKNOWN_REGISTRIES));  // 5
  EXPECT_EQ("ref.bar.jp", GetRegistryFromURL("http://p.ref.bar.jp",
                                             EXCLUDE_UNKNOWN_REGISTRIES));  // 5
  EXPECT_EQ("", GetRegistryFromURL("http://xpref.bar.jp",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // 5
  EXPECT_EQ("xpref.bar.jp",
            GetRegistryFromURL("http://baz.xpref.bar.jp",
                               EXCLUDE_UNKNOWN_REGISTRIES));  // 5
  EXPECT_EQ("bar.jp", GetRegistryFromURL("http://baz..pref.bar.jp",
                                         EXCLUDE_UNKNOWN_REGISTRIES));  // 5
  EXPECT_EQ("bar.baz.com",
            GetRegistryFromURL("http://a.b.bar.baz.com",
                               EXCLUDE_UNKNOWN_REGISTRIES));  // 6
  EXPECT_EQ("d.c", GetRegistryFromURL("http://a.d.c",
                                      EXCLUDE_UNKNOWN_REGISTRIES));  // 7
  EXPECT_EQ("d.c", GetRegistryFromURL("http://.a.d.c",
                                      EXCLUDE_UNKNOWN_REGISTRIES));  // 7
  EXPECT_EQ("d.c", GetRegistryFromURL("http://..a.d.c",
                                      EXCLUDE_UNKNOWN_REGISTRIES));  // 7
  EXPECT_EQ("c", GetRegistryFromURL("http://a.b.c",
                                    EXCLUDE_UNKNOWN_REGISTRIES));  // 7 8
  EXPECT_EQ("", GetRegistryFromURL("http://baz.com",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // none
  EXPECT_EQ("", GetRegistryFromURL("http://baz.com.",
                                   EXCLUDE_UNKNOWN_REGISTRIES));  // none
  EXPECT_EQ("com", GetRegistryFromURL("http://baz.com",
                                      INCLUDE_UNKNOWN_REGISTRIES));  // none
  EXPECT_EQ("com.", GetRegistryFromURL("http://baz.com.",
                                       INCLUDE_UNKNOWN_REGISTRIES));  // none

  EXPECT_EQ(std::nullopt,
            GetRegistryFromURL(std::string(), EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ(std::nullopt,
            GetRegistryFromURL("http://", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ(std::nullopt, GetRegistryFromURL("file:///C:/file.html",
                                             EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetRegistryFromURL("http://foo.com..", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetRegistryFromURL("http://...", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ(
      "", GetRegistryFromURL("http://192.168.0.1", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetRegistryFromURL("http://localhost", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetRegistryFromURL("http://localhost", INCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ(
      "", GetRegistryFromURL("http://localhost.", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ(
      "", GetRegistryFromURL("http://localhost.", INCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetRegistryFromURL("http:////Comment", EXCLUDE_UNKNOWN_REGISTRIES));

  // Test std::string version of GetRegistry().  Uses the same underpinnings as
  // the GURL version, so this is really more of a check of CanonicalizeHost().
  EXPECT_EQ("jp", GetCanonicalHostRegistry("a.baz.jp",
                                           EXCLUDE_UNKNOWN_REGISTRIES));  // 1
  EXPECT_EQ("jp.", GetCanonicalHostRegistry("a.baz.jp.",
                                            EXCLUDE_UNKNOWN_REGISTRIES));  // 1
  EXPECT_EQ(
      "", GetCanonicalHostRegistry("ac.jp", EXCLUDE_UNKNOWN_REGISTRIES));  // 2
  EXPECT_EQ("", GetCanonicalHostRegistry("a.bar.jp",
                                         EXCLUDE_UNKNOWN_REGISTRIES));  // 3
  EXPECT_EQ(
      "", GetCanonicalHostRegistry("bar.jp", EXCLUDE_UNKNOWN_REGISTRIES));  // 3
  EXPECT_EQ("", GetCanonicalHostRegistry("baz.bar.jp",
                                         EXCLUDE_UNKNOWN_REGISTRIES));  // 3 4
  EXPECT_EQ("b.baz.bar.jp",
            GetCanonicalHostRegistry("a.b.baz.bar.jp",
                                     EXCLUDE_UNKNOWN_REGISTRIES));  // 4
  EXPECT_EQ("bar.jp", GetCanonicalHostRegistry(
                          "baz.pref.bar.jp", EXCLUDE_UNKNOWN_REGISTRIES));  // 5
  EXPECT_EQ("bar.baz.com",
            GetCanonicalHostRegistry("a.b.bar.baz.com",
                                     EXCLUDE_UNKNOWN_REGISTRIES));  // 6
  EXPECT_EQ("d.c", GetCanonicalHostRegistry("a.d.c",
                                            EXCLUDE_UNKNOWN_REGISTRIES));  // 7
  EXPECT_EQ("d.c", GetCanonicalHostRegistry(".a.d.c",
                                            EXCLUDE_UNKNOWN_REGISTRIES));  // 7
  EXPECT_EQ("d.c", GetCanonicalHostRegistry("..a.d.c",
                                            EXCLUDE_UNKNOWN_REGISTRIES));  // 7
  EXPECT_EQ("c", GetCanonicalHostRegistry("a.b.c",
                                          EXCLUDE_UNKNOWN_REGISTRIES));  // 7 8
  EXPECT_EQ("", GetCanonicalHostRegistry("baz.com",
                                         EXCLUDE_UNKNOWN_REGISTRIES));  // none
  EXPECT_EQ("", GetCanonicalHostRegistry("baz.com.",
                                         EXCLUDE_UNKNOWN_REGISTRIES));  // none
  EXPECT_EQ("com", GetCanonicalHostRegistry(
                       "baz.com", INCLUDE_UNKNOWN_REGISTRIES));  // none
  EXPECT_EQ("com.", GetCanonicalHostRegistry(
                        "baz.com.", INCLUDE_UNKNOWN_REGISTRIES));  // none

  EXPECT_EQ(std::nullopt, GetCanonicalHostRegistry(std::string(),
                                                   EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetCanonicalHostRegistry("foo.com..", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetCanonicalHostRegistry("..", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ(
      "", GetCanonicalHostRegistry("192.168.0.1", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetCanonicalHostRegistry("localhost", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetCanonicalHostRegistry("localhost", INCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetCanonicalHostRegistry("localhost.", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetCanonicalHostRegistry("localhost.", INCLUDE_UNKNOWN_REGISTRIES));

  // IDN case.
  EXPECT_EQ("xn--fiqs8s", GetCanonicalHostRegistry("foo.xn--fiqs8s",
                                                   EXCLUDE_UNKNOWN_REGISTRIES));
}

TEST_F(RegistryControlledDomainTest, HostHasRegistryControlledDomain) {
  UseDomainData(test1::kDafsa);

  // Invalid hosts.
  EXPECT_FALSE(HostHasRegistryControlledDomain(
      std::string(), EXCLUDE_UNKNOWN_REGISTRIES, EXCLUDE_PRIVATE_REGISTRIES));
  EXPECT_FALSE(HostHasRegistryControlledDomain(
      "%00asdf", EXCLUDE_UNKNOWN_REGISTRIES, EXCLUDE_PRIVATE_REGISTRIES));

  // Invalid host but valid R.C.D.
  EXPECT_TRUE(HostHasRegistryControlledDomain(
      "%00foo.jp", EXCLUDE_UNKNOWN_REGISTRIES, EXCLUDE_PRIVATE_REGISTRIES));

  // Valid R.C.D. when canonicalized, even with an invalid prefix and an
  // escaped dot.
  EXPECT_TRUE(HostHasRegistryControlledDomain("%00foo.Google%2EjP",
                                              EXCLUDE_UNKNOWN_REGISTRIES,
                                              EXCLUDE_PRIVATE_REGISTRIES));

  // Regular, no match.
  EXPECT_FALSE(HostHasRegistryControlledDomain(
      "bar.notatld", EXCLUDE_UNKNOWN_REGISTRIES, EXCLUDE_PRIVATE_REGISTRIES));

  // Regular, match.
  EXPECT_TRUE(HostHasRegistryControlledDomain(
      "www.Google.Jp", EXCLUDE_UNKNOWN_REGISTRIES, EXCLUDE_PRIVATE_REGISTRIES));
}

TEST_F(RegistryControlledDomainTest, TestSameDomainOrHost) {
  UseDomainData(test2::kDafsa);

  EXPECT_TRUE(CompareDomains("http://a.b.bar.jp/file.html",
                             "http://a.b.bar.jp/file.html"));  // b.bar.jp
  EXPECT_TRUE(CompareDomains("http://a.b.bar.jp/file.html",
                             "http://b.b.bar.jp/file.html"));  // b.bar.jp
  EXPECT_FALSE(CompareDomains("http://a.foo.jp/file.html",     // foo.jp
                              "http://a.not.jp/file.html"));   // not.jp
  EXPECT_FALSE(CompareDomains("http://a.foo.jp/file.html",     // foo.jp
                              "http://a.foo.jp./file.html"));  // foo.jp.
  EXPECT_FALSE(CompareDomains("http://a.com/file.html",        // a.com
                              "http://b.com/file.html"));      // b.com
  EXPECT_TRUE(CompareDomains("http://a.x.com/file.html",
                             "http://b.x.com/file.html"));  // x.com
  EXPECT_TRUE(CompareDomains("http://a.x.com/file.html",
                             "http://.x.com/file.html"));  // x.com
  EXPECT_TRUE(CompareDomains("http://a.x.com/file.html",
                             "http://..b.x.com/file.html"));  // x.com
  EXPECT_TRUE(CompareDomains("http://intranet/file.html",
                             "http://intranet/file.html"));  // intranet
  EXPECT_FALSE(CompareDomains("http://intranet1/file.html",
                              "http://intranet2/file.html"));  // intranet
  EXPECT_TRUE(CompareDomains(
      "http://intranet1.corp.example.com/file.html",
      "http://intranet2.corp.example.com/file.html"));  // intranet
  EXPECT_TRUE(CompareDomains("http://127.0.0.1/file.html",
                             "http://127.0.0.1/file.html"));   // 127.0.0.1
  EXPECT_FALSE(CompareDomains("http://192.168.0.1/file.html",  // 192.168.0.1
                              "http://127.0.0.1/file.html"));  // 127.0.0.1
  EXPECT_FALSE(CompareDomains("file:///C:/file.html",
                              "file:///C:/file.html"));  // no host

  // The trailing dot means different sites - see also
  // https://github.com/mikewest/sec-metadata/issues/15.
  EXPECT_FALSE(
      CompareDomains("https://foo.example.com", "https://foo.example.com."));
}

TEST_F(RegistryControlledDomainTest, TestDefaultData) {
  // Note that no data is set: we're using the default rules.
  EXPECT_EQ("com", GetRegistryFromURL("http://google.com",
                                      EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("edu", GetRegistryFromURL("http://stanford.edu",
                                      EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("gov", GetRegistryFromURL("http://ustreas.gov",
                                      EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("net",
            GetRegistryFromURL("http://icann.net", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("org", GetRegistryFromURL("http://ferretcentral.org",
                                      EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetRegistryFromURL("http://nowhere.notavaliddomain",
                                   EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("notavaliddomain",
            GetRegistryFromURL("http://nowhere.notavaliddomain",
                               INCLUDE_UNKNOWN_REGISTRIES));
}

TEST_F(RegistryControlledDomainTest, TestPrivateRegistryHandling) {
  UseDomainData(test1::kDafsa);

  // Testing the same dataset for INCLUDE_PRIVATE_REGISTRIES and
  // EXCLUDE_PRIVATE_REGISTRIES arguments.
  // For the domain data used for this test, the private registries are
  // 'priv.no' and 'private'.

  // Non-private registries.
  EXPECT_EQ("no",
            GetRegistryFromURL("http://priv.no", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("no", GetRegistryFromURL("http://foo.priv.no",
                                     EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("jp",
            GetRegistryFromURL("http://foo.jp", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("jp", GetRegistryFromURL("http://www.foo.jp",
                                     EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetRegistryFromURL("http://private", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ(
      "", GetRegistryFromURL("http://foo.private", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("",
            GetRegistryFromURL("http://private", INCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("private", GetRegistryFromURL("http://foo.private",
                                          INCLUDE_UNKNOWN_REGISTRIES));

  // Private registries.
  EXPECT_EQ("", GetRegistryFromURLIncludingPrivate("http://priv.no",
                                                   EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("priv.no", GetRegistryFromURLIncludingPrivate(
                           "http://foo.priv.no", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("jp", GetRegistryFromURLIncludingPrivate(
                      "http://foo.jp", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("jp", GetRegistryFromURLIncludingPrivate(
                      "http://www.foo.jp", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetRegistryFromURLIncludingPrivate("http://private",
                                                   EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("private", GetRegistryFromURLIncludingPrivate(
                           "http://foo.private", EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetRegistryFromURLIncludingPrivate("http://private",
                                                   INCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("private", GetRegistryFromURLIncludingPrivate(
                           "http://foo.private", INCLUDE_UNKNOWN_REGISTRIES));
}

TEST_F(RegistryControlledDomainTest, TestDafsaTwoByteOffsets) {
  UseDomainData(test3::kDafsa);

  // Testing to lookup keys in a DAFSA with two byte offsets.
  // This DAFSA is constructed so that labels begin and end with unique
  // characters, which makes it impossible to merge labels. Each inner node
  // is about 100 bytes and a one byte offset can at most add 64 bytes to
  // previous offset. Thus the paths must go over two byte offsets.

  const std::string_view key0 =
      "a.b.6____________________________________________________"
      "________________________________________________6";
  const std::string_view key1 =
      "a.b.7____________________________________________________"
      "________________________________________________7";
  const std::string_view key2 =
      "a.b.a____________________________________________________"
      "________________________________________________8";

  EXPECT_EQ(key0.substr(4),
            GetCanonicalHostRegistry(key0, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetCanonicalHostRegistry(key1, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ(key1.substr(4), GetCanonicalHostRegistryIncludingPrivate(key1));
  EXPECT_EQ("", GetCanonicalHostRegistry(key2, EXCLUDE_UNKNOWN_REGISTRIES));
}

TEST_F(RegistryControlledDomainTest, TestDafsaThreeByteOffsets) {
  UseDomainData(test4::kDafsa);

  // Testing to lookup keys in a DAFSA with three byte offsets.
  // This DAFSA is constructed so that labels begin and end with unique
  // characters, which makes it impossible to merge labels. The byte array
  // has a size of ~54k. A two byte offset can add at most add 8k to the
  // previous offset. Since we can skip only forward in memory, the nodes
  // representing the return values must be located near the end of the byte
  // array. The probability that we can reach from an arbitrary inner node to
  // a return value without using a three byte offset is small (but not zero).
  // The test is repeated with some different keys and with a reasonable
  // probability at least one of the tested paths has go over a three byte
  // offset.

  const std::string_view key0 =
      "a.b.z6___________________________________________________"
      "_________________________________________________z6";
  const std::string_view key1 =
      "a.b.z7___________________________________________________"
      "_________________________________________________z7";
  const std::string_view key2 =
      "a.b.za___________________________________________________"
      "_________________________________________________z8";

  EXPECT_EQ(key0.substr(4),
            GetCanonicalHostRegistry(key0, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetCanonicalHostRegistry(key1, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ(key1.substr(4), GetCanonicalHostRegistryIncludingPrivate(key1));
  EXPECT_EQ("", GetCanonicalHostRegistry(key2, EXCLUDE_UNKNOWN_REGISTRIES));
}

TEST_F(RegistryControlledDomainTest, TestDafsaJoinedPrefixes) {
  UseDomainData(test5::kDafsa);

  // Testing to lookup keys in a DAFSA with compressed prefixes.
  // This DAFSA is constructed from words with similar prefixes but distinct
  // suffixes. The DAFSA will then form a trie with the implicit source node
  // as root.

  const std::string_view key0 = "a.b.ai";
  const std::string_view key1 = "a.b.bj";
  const std::string_view key2 = "a.b.aak";
  const std::string_view key3 = "a.b.bbl";
  const std::string_view key4 = "a.b.aaa";
  const std::string_view key5 = "a.b.bbb";
  const std::string_view key6 = "a.b.aaaam";
  const std::string_view key7 = "a.b.bbbbn";

  EXPECT_EQ("ai", GetCanonicalHostRegistry(key0, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetCanonicalHostRegistry(key1, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("bj", GetCanonicalHostRegistryIncludingPrivate(key1));
  EXPECT_EQ("aak", GetCanonicalHostRegistry(key2, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetCanonicalHostRegistry(key3, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("bbl", GetCanonicalHostRegistryIncludingPrivate(key3));
  EXPECT_EQ("", GetCanonicalHostRegistryIncludingPrivate(key4));
  EXPECT_EQ("", GetCanonicalHostRegistryIncludingPrivate(key5));
  EXPECT_EQ("aaaam",
            GetCanonicalHostRegistry(key6, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("bbbbn",
            GetCanonicalHostRegistry(key7, EXCLUDE_UNKNOWN_REGISTRIES));
}

TEST_F(RegistryControlledDomainTest, TestDafsaJoinedSuffixes) {
  UseDomainData(test6::kDafsa);

  // Testing to lookup keys in a DAFSA with compressed suffixes.
  // This DAFSA is constructed from words with similar suffixes but distinct
  // prefixes. The DAFSA will then form a trie with the implicit sink node as
  // root.

  const std::string_view key0 = "a.b.ia";
  const std::string_view key1 = "a.b.jb";
  const std::string_view key2 = "a.b.kaa";
  const std::string_view key3 = "a.b.lbb";
  const std::string_view key4 = "a.b.aaa";
  const std::string_view key5 = "a.b.bbb";
  const std::string_view key6 = "a.b.maaaa";
  const std::string_view key7 = "a.b.nbbbb";

  EXPECT_EQ("ia", GetCanonicalHostRegistry(key0, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetCanonicalHostRegistry(key1, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("jb", GetCanonicalHostRegistryIncludingPrivate(key1));
  EXPECT_EQ("kaa", GetCanonicalHostRegistry(key2, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("", GetCanonicalHostRegistry(key3, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("lbb", GetCanonicalHostRegistryIncludingPrivate(key3));
  EXPECT_EQ("", GetCanonicalHostRegistryIncludingPrivate(key4));
  EXPECT_EQ("", GetCanonicalHostRegistryIncludingPrivate(key5));
  EXPECT_EQ("maaaa",
            GetCanonicalHostRegistry(key6, EXCLUDE_UNKNOWN_REGISTRIES));
  EXPECT_EQ("nbbbb",
            GetCanonicalHostRegistry(key7, EXCLUDE_UNKNOWN_REGISTRIES));
}

TEST_F(RegistryControlledDomainTest, Permissive) {
  UseDomainData(test1::kDafsa);

  EXPECT_EQ(std::nullopt, PermissiveGetHostRegistry(""));

  // Regular non-canonical host name.
  EXPECT_EQ("Jp", PermissiveGetHostRegistry("Www.Google.Jp"));
  EXPECT_EQ("Jp.", PermissiveGetHostRegistry("Www.Google.Jp."));

  // Empty returns std::nullopt.
  EXPECT_EQ(std::nullopt, PermissiveGetHostRegistry(""));

  // Trailing spaces are counted as part of the hostname, meaning this will
  // not match a known registry.
  EXPECT_EQ("", PermissiveGetHostRegistry("Www.Google.Jp "));

  // Invalid characters at the beginning are OK if the suffix still matches.
  EXPECT_EQ("Jp", PermissiveGetHostRegistry("*%00#?.Jp"));

  // Escaped period, this will add new components (Www.Google.jp).
  EXPECT_EQ("%4Ap", PermissiveGetHostRegistry("Www.Googl%45%2e%4Ap"));

  // Escaped period, the last component is invalid (Www.Google.%p).
  EXPECT_EQ("", PermissiveGetHostRegistry("Www.Googl%45%2e%25p"));

  // The last component is invalid (Www.Google.%p).
  EXPECT_EQ("", PermissiveGetHostRegistry("Www.Google.%25p"));

  // Escaped period. er is a wildcard registry.
  EXPECT_EQ("Googl%45%2eEr", PermissiveGetHostRegistry("Www.Googl%45%2eEr"));

  // Escaped period. The first component is invalid because of %FF%FE.
  // er is a wildcard registry.
  EXPECT_EQ("", PermissiveGetHostRegistry("%EF%2E%FF%FE.er"));

  // First component is invalid. Note that the RCD (test.er) doesn't fall into
  // the middle of a component here, so this doesn't execute the brute search
  // at the end of DoPermissiveGetHostRegistry, nor the is_canonical check right
  // before the search.
  EXPECT_EQ("test.er", PermissiveGetHostRegistry("%EF%2E%FF%FE.test.er"));

// IDN cases (not supported when not linking ICU).
#if !BUILDFLAG(USE_PLATFORM_ICU_ALTERNATIVES)
  EXPECT_EQ("xn--fiqs8s", PermissiveGetHostRegistry("foo.xn--fiqs8s"));
  EXPECT_EQ("xn--fiqs8s.", PermissiveGetHostRegistry("foo.xn--fiqs8s."));
  EXPECT_EQ("%E4%B8%AD%E5%9B%BD",
            PermissiveGetHostRegistry("foo.%E4%B8%AD%E5%9B%BD"));
  EXPECT_EQ("%E4%B8%AD%E5%9B%BD.",
            PermissiveGetHostRegistry("foo.%E4%B8%AD%E5%9B%BD."));
  EXPECT_EQ("\xE4\xB8\xAD\xE5\x9B\xBD",
            PermissiveGetHostRegistry("foo.\xE4\xB8\xAD\xE5\x9B\xBD"));
  EXPECT_EQ("\xE4\xB8\xAD\xE5\x9B\xBD.",
            PermissiveGetHostRegistry("foo.\xE4\xB8\xAD\xE5\x9B\xBD."));
  // UTF-16 IDN.
  EXPECT_EQ(u"\x4e2d\x56fd", PermissiveGetHostRegistry(u"foo.\x4e2d\x56fd"));

  // Fullwidth dot (u+FF0E) that will get canonicalized to a dot.
  EXPECT_EQ("jp", PermissiveGetHostRegistry("Www.Google\xEF\xBC\x8Ejp"));
  // Same but also ending in a fullwidth dot.
  EXPECT_EQ("jp\xEF\xBC\x8E",
            PermissiveGetHostRegistry("Www.Google\xEF\xBC\x8Ejp\xEF\xBC\x8E"));
  // Escaped UTF-8, also with an escaped fullwidth "Jp".
  // "Jp" = U+FF2A, U+FF50, UTF-8 = EF BC AA EF BD 90
  EXPECT_EQ("%EF%BC%AA%EF%BD%90%EF%BC%8E",
            PermissiveGetHostRegistry(
                "Www.Google%EF%BC%8E%EF%BC%AA%EF%BD%90%EF%BC%8E"));
  // UTF-16 (ending in a dot).
  EXPECT_EQ(u"\xFF2A\xFF50\xFF0E",
            PermissiveGetHostRegistry(u"Www.Google\xFF0E\xFF2A\xFF50\xFF0E"));
#endif
}

}  // namespace net::registry_controlled_domains
