root/components/url_matcher/url_matcher_factory_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. TEST
  2. TEST
  3. url_
  4. ExpectedResult
  5. CheckCondition
  6. TEST

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

#include "components/url_matcher/url_matcher_factory.h"

#include "base/basictypes.h"
#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "components/url_matcher/url_matcher_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace url_matcher {

namespace keys = url_matcher_constants;

TEST(URLMatcherFactoryTest, CreateFromURLFilterDictionary) {
  URLMatcher matcher;

  std::string error;
  scoped_refptr<URLMatcherConditionSet> result;

  // Invalid key: {"invalid": "foobar"}
  base::DictionaryValue invalid_condition;
  invalid_condition.SetString("invalid", "foobar");

  // Invalid value type: {"hostSuffix": []}
  base::DictionaryValue invalid_condition2;
  invalid_condition2.Set(keys::kHostSuffixKey, new base::ListValue);

  // Invalid regex value: {"urlMatches": "*"}
  base::DictionaryValue invalid_condition3;
  invalid_condition3.SetString(keys::kURLMatchesKey, "*");

  // Invalid regex value: {"originAndPathMatches": "*"}
  base::DictionaryValue invalid_condition4;
  invalid_condition4.SetString(keys::kOriginAndPathMatchesKey, "*");

  // Valid values:
  // {
  //   "port_range": [80, [1000, 1010]],
  //   "schemes": ["http"],
  //   "hostSuffix": "example.com"
  //   "hostPrefix": "www"
  // }

  // Port range: Allow 80;1000-1010.
  base::ListValue* port_range = new base::ListValue();
  port_range->Append(base::Value::CreateIntegerValue(1000));
  port_range->Append(base::Value::CreateIntegerValue(1010));
  base::ListValue* port_ranges = new base::ListValue();
  port_ranges->Append(base::Value::CreateIntegerValue(80));
  port_ranges->Append(port_range);

  base::ListValue* scheme_list = new base::ListValue();
  scheme_list->Append(base::Value::CreateStringValue("http"));

  base::DictionaryValue valid_condition;
  valid_condition.SetString(keys::kHostSuffixKey, "example.com");
  valid_condition.SetString(keys::kHostPrefixKey, "www");
  valid_condition.Set(keys::kPortsKey, port_ranges);
  valid_condition.Set(keys::kSchemesKey, scheme_list);

  // Test wrong condition name passed.
  error.clear();
  result = URLMatcherFactory::CreateFromURLFilterDictionary(
      matcher.condition_factory(), &invalid_condition, 1, &error);
  EXPECT_FALSE(error.empty());
  EXPECT_FALSE(result.get());

  // Test wrong datatype in hostSuffix.
  error.clear();
  result = URLMatcherFactory::CreateFromURLFilterDictionary(
      matcher.condition_factory(), &invalid_condition2, 2, &error);
  EXPECT_FALSE(error.empty());
  EXPECT_FALSE(result.get());

  // Test invalid regex in urlMatches.
  error.clear();
  result = URLMatcherFactory::CreateFromURLFilterDictionary(
      matcher.condition_factory(), &invalid_condition3, 3, &error);
  EXPECT_FALSE(error.empty());
  EXPECT_FALSE(result.get());

  error.clear();
  result = URLMatcherFactory::CreateFromURLFilterDictionary(
      matcher.condition_factory(), &invalid_condition4, 4, &error);
  EXPECT_FALSE(error.empty());
  EXPECT_FALSE(result.get());

  // Test success.
  error.clear();
  result = URLMatcherFactory::CreateFromURLFilterDictionary(
      matcher.condition_factory(), &valid_condition, 100, &error);
  EXPECT_EQ("", error);
  ASSERT_TRUE(result.get());

  URLMatcherConditionSet::Vector conditions;
  conditions.push_back(result);
  matcher.AddConditionSets(conditions);

  EXPECT_EQ(1u, matcher.MatchURL(GURL("http://www.example.com")).size());
  EXPECT_EQ(1u, matcher.MatchURL(GURL("http://www.example.com:80")).size());
  EXPECT_EQ(1u, matcher.MatchURL(GURL("http://www.example.com:1000")).size());
  // Wrong scheme.
  EXPECT_EQ(0u, matcher.MatchURL(GURL("https://www.example.com:80")).size());
  // Wrong port.
  EXPECT_EQ(0u, matcher.MatchURL(GURL("http://www.example.com:81")).size());
  // Unfulfilled host prefix.
  EXPECT_EQ(0u, matcher.MatchURL(GURL("http://mail.example.com:81")).size());
}

// Using upper case letters for scheme and host values is currently an error.
// See more context at http://crbug.com/160702#c6 .
TEST(URLMatcherFactoryTest, UpperCase) {
  URLMatcher matcher;
  std::string error;
  scoped_refptr<URLMatcherConditionSet> result;

  // {"hostContains": "exaMple"}
  base::DictionaryValue invalid_condition1;
  invalid_condition1.SetString(keys::kHostContainsKey, "exaMple");

  // {"hostSuffix": ".Com"}
  base::DictionaryValue invalid_condition2;
  invalid_condition2.SetString(keys::kHostSuffixKey, ".Com");

  // {"hostPrefix": "WWw."}
  base::DictionaryValue invalid_condition3;
  invalid_condition3.SetString(keys::kHostPrefixKey, "WWw.");

  // {"hostEquals": "WWW.example.Com"}
  base::DictionaryValue invalid_condition4;
  invalid_condition4.SetString(keys::kHostEqualsKey, "WWW.example.Com");

  // {"scheme": ["HTTP"]}
  base::ListValue* scheme_list = new base::ListValue();
  scheme_list->Append(base::Value::CreateStringValue("HTTP"));
  base::DictionaryValue invalid_condition5;
  invalid_condition5.Set(keys::kSchemesKey, scheme_list);

  const base::DictionaryValue* invalid_conditions[] = {
    &invalid_condition1,
    &invalid_condition2,
    &invalid_condition3,
    &invalid_condition4,
    &invalid_condition5
  };

  for (size_t i = 0; i < arraysize(invalid_conditions); ++i) {
    error.clear();
    result = URLMatcherFactory::CreateFromURLFilterDictionary(
        matcher.condition_factory(), invalid_conditions[i], 1, &error);
    EXPECT_FALSE(error.empty()) << "in iteration " << i;
    EXPECT_FALSE(result.get()) << "in iteration " << i;
  }
}

// This class wraps a case sensitivity test for a single UrlFilter condition.
class UrlConditionCaseTest {
 public:
  // The condition is identified by the key |condition_key|. If that key is
  // associated with string values, then |use_list_of_strings| should be false,
  // if the key is associated with list-of-string values, then
  // |use_list_of_strings| should be true. In |url| is the URL to test against.
  UrlConditionCaseTest(const char* condition_key,
                       bool use_list_of_strings,
                       const std::string& expected_value,
                       const std::string& incorrect_case_value,
                       bool case_sensitive,
                       bool lower_case_enforced,
                       const GURL& url)
      : condition_key_(condition_key),
        use_list_of_strings_(use_list_of_strings),
        expected_value_(expected_value),
        incorrect_case_value_(incorrect_case_value),
        expected_result_for_wrong_case_(ExpectedResult(case_sensitive,
                                                       lower_case_enforced)),
        url_(url) {}

  ~UrlConditionCaseTest() {}

  // Match the condition against |url_|. Checks via EXPECT_* macros that
  // |expected_value_| matches always, and that |incorrect_case_value_| matches
  // iff |case_sensitive_| is false.
  void Test() const;

 private:
  enum ResultType { OK, NOT_FULFILLED, CREATE_FAILURE };

  // What is the expected result of |CheckCondition| if a wrong-case |value|
  // containing upper case letters is supplied.
  static ResultType ExpectedResult(bool case_sensitive,
                                   bool lower_case_enforced) {
    if (lower_case_enforced)
      return CREATE_FAILURE;
    if (case_sensitive)
      return NOT_FULFILLED;
    return OK;
  }

  // Test the condition |condition_key_| = |value| against |url_|.
  // Check, via EXPECT_* macros, that either the condition cannot be constructed
  // at all, or that the condition is not fulfilled, or that it is fulfilled,
  // depending on the value of |expected_result|.
  void CheckCondition(const std::string& value,
                      ResultType expected_result) const;

  const char* condition_key_;
  const bool use_list_of_strings_;
  const std::string& expected_value_;
  const std::string& incorrect_case_value_;
  const ResultType expected_result_for_wrong_case_;
  const GURL& url_;

  // Allow implicit copy and assign, because a public copy constructor is
  // needed, but never used (!), for the definition of arrays of this class.
};

void UrlConditionCaseTest::Test() const {
  CheckCondition(expected_value_, OK);
  CheckCondition(incorrect_case_value_, expected_result_for_wrong_case_);
}

void UrlConditionCaseTest::CheckCondition(
    const std::string& value,
    UrlConditionCaseTest::ResultType expected_result) const {
  base::DictionaryValue condition;
  if (use_list_of_strings_) {
    base::ListValue* list = new base::ListValue();
    list->Append(base::Value::CreateStringValue(value));
    condition.SetWithoutPathExpansion(condition_key_, list);
  } else {
    condition.SetStringWithoutPathExpansion(condition_key_, value);
  }

  URLMatcher matcher;
  std::string error;
  scoped_refptr<URLMatcherConditionSet> result;

  result = URLMatcherFactory::CreateFromURLFilterDictionary(
      matcher.condition_factory(), &condition, 1, &error);
  if (expected_result == CREATE_FAILURE) {
    EXPECT_FALSE(error.empty());
    EXPECT_FALSE(result.get());
    return;
  }
  EXPECT_EQ("", error);
  ASSERT_TRUE(result.get());

  URLMatcherConditionSet::Vector conditions;
  conditions.push_back(result);
  matcher.AddConditionSets(conditions);
  EXPECT_EQ((expected_result == OK ? 1u : 0u), matcher.MatchURL(url_).size())
      << "while matching condition " << condition_key_ << " with value "
      << value  << " against url " << url_;
}

// This tests that the UrlFilter handles case sensitivity on various parts of
// URLs correctly.
TEST(URLMatcherFactoryTest, CaseSensitivity) {
  const std::string kScheme("https");
  const std::string kSchemeUpper("HTTPS");
  const std::string kHost("www.example.com");
  const std::string kHostUpper("WWW.EXAMPLE.COM");
  const std::string kPath("/path");
  const std::string kPathUpper("/PATH");
  const std::string kQuery("?option=value&A=B");
  const std::string kQueryUpper("?OPTION=VALUE&A=B");
  const std::string kUrl(kScheme + "://" + kHost + ":1234" + kPath + kQuery);
  const std::string kUrlUpper(
      kSchemeUpper + "://" + kHostUpper + ":1234" + kPathUpper + kQueryUpper);
  const GURL url(kUrl);
  // Note: according to RFC 3986, and RFC 1034, schema and host, respectively
  // should be case insensitive. See crbug.com/160702#6 for why we still
  // require them to be case sensitive in UrlFilter, and enforce lower case.
  const bool kIsSchemeLowerCaseEnforced = true;
  const bool kIsHostLowerCaseEnforced = true;
  const bool kIsPathLowerCaseEnforced = false;
  const bool kIsQueryLowerCaseEnforced = false;
  const bool kIsUrlLowerCaseEnforced = false;
  const bool kIsSchemeCaseSensitive = true;
  const bool kIsHostCaseSensitive = true;
  const bool kIsPathCaseSensitive = true;
  const bool kIsQueryCaseSensitive = true;
  const bool kIsUrlCaseSensitive = kIsSchemeCaseSensitive ||
                                   kIsHostCaseSensitive ||
                                   kIsPathCaseSensitive ||
                                   kIsQueryCaseSensitive;

  const UrlConditionCaseTest case_tests[] = {
    UrlConditionCaseTest(keys::kSchemesKey, true, kScheme, kSchemeUpper,
                         kIsSchemeCaseSensitive, kIsSchemeLowerCaseEnforced,
                         url),
    UrlConditionCaseTest(keys::kHostContainsKey, false, kHost, kHostUpper,
                         kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kHostEqualsKey, false, kHost, kHostUpper,
                         kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kHostPrefixKey, false, kHost, kHostUpper,
                         kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kHostSuffixKey, false, kHost, kHostUpper,
                         kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kPathContainsKey, false, kPath, kPathUpper,
                         kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kPathEqualsKey, false, kPath, kPathUpper,
                         kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kPathPrefixKey, false, kPath, kPathUpper,
                         kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kPathSuffixKey, false, kPath, kPathUpper,
                         kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kQueryContainsKey, false, kQuery, kQueryUpper,
                         kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kQueryEqualsKey, false, kQuery, kQueryUpper,
                         kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kQueryPrefixKey, false, kQuery, kQueryUpper,
                         kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kQuerySuffixKey, false, kQuery, kQueryUpper,
                         kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url),
    // Excluding kURLMatchesKey because case sensitivity can be specified in the
    // RE2 expression.
    UrlConditionCaseTest(keys::kURLContainsKey, false, kUrl, kUrlUpper,
                         kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kURLEqualsKey, false, kUrl, kUrlUpper,
                         kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kURLPrefixKey, false, kUrl, kUrlUpper,
                         kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url),
    UrlConditionCaseTest(keys::kURLSuffixKey, false, kUrl, kUrlUpper,
                         kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url),
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(case_tests); ++i) {
    SCOPED_TRACE(base::StringPrintf("Iteration: %" PRIuS, i));
    case_tests[i].Test();
  }
}

}  // namespace url_matcher

/* [<][>][^][v][top][bottom][index][help] */