1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/url_matcher/url_matcher_factory.h"
7 #include "base/basictypes.h"
8 #include "base/format_macros.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/values.h"
11 #include "components/url_matcher/url_matcher_constants.h"
12 #include "testing/gtest/include/gtest/gtest.h"
15 namespace url_matcher
{
17 namespace keys
= url_matcher_constants
;
19 TEST(URLMatcherFactoryTest
, CreateFromURLFilterDictionary
) {
23 scoped_refptr
<URLMatcherConditionSet
> result
;
25 // Invalid key: {"invalid": "foobar"}
26 base::DictionaryValue invalid_condition
;
27 invalid_condition
.SetString("invalid", "foobar");
29 // Invalid value type: {"hostSuffix": []}
30 base::DictionaryValue invalid_condition2
;
31 invalid_condition2
.Set(keys::kHostSuffixKey
, new base::ListValue
);
33 // Invalid regex value: {"urlMatches": "*"}
34 base::DictionaryValue invalid_condition3
;
35 invalid_condition3
.SetString(keys::kURLMatchesKey
, "*");
37 // Invalid regex value: {"originAndPathMatches": "*"}
38 base::DictionaryValue invalid_condition4
;
39 invalid_condition4
.SetString(keys::kOriginAndPathMatchesKey
, "*");
43 // "port_range": [80, [1000, 1010]],
44 // "schemes": ["http"],
45 // "hostSuffix": "example.com"
46 // "hostPrefix": "www"
49 // Port range: Allow 80;1000-1010.
50 base::ListValue
* port_range
= new base::ListValue();
51 port_range
->Append(base::Value::CreateIntegerValue(1000));
52 port_range
->Append(base::Value::CreateIntegerValue(1010));
53 base::ListValue
* port_ranges
= new base::ListValue();
54 port_ranges
->Append(base::Value::CreateIntegerValue(80));
55 port_ranges
->Append(port_range
);
57 base::ListValue
* scheme_list
= new base::ListValue();
58 scheme_list
->Append(base::Value::CreateStringValue("http"));
60 base::DictionaryValue valid_condition
;
61 valid_condition
.SetString(keys::kHostSuffixKey
, "example.com");
62 valid_condition
.SetString(keys::kHostPrefixKey
, "www");
63 valid_condition
.Set(keys::kPortsKey
, port_ranges
);
64 valid_condition
.Set(keys::kSchemesKey
, scheme_list
);
66 // Test wrong condition name passed.
68 result
= URLMatcherFactory::CreateFromURLFilterDictionary(
69 matcher
.condition_factory(), &invalid_condition
, 1, &error
);
70 EXPECT_FALSE(error
.empty());
71 EXPECT_FALSE(result
.get());
73 // Test wrong datatype in hostSuffix.
75 result
= URLMatcherFactory::CreateFromURLFilterDictionary(
76 matcher
.condition_factory(), &invalid_condition2
, 2, &error
);
77 EXPECT_FALSE(error
.empty());
78 EXPECT_FALSE(result
.get());
80 // Test invalid regex in urlMatches.
82 result
= URLMatcherFactory::CreateFromURLFilterDictionary(
83 matcher
.condition_factory(), &invalid_condition3
, 3, &error
);
84 EXPECT_FALSE(error
.empty());
85 EXPECT_FALSE(result
.get());
88 result
= URLMatcherFactory::CreateFromURLFilterDictionary(
89 matcher
.condition_factory(), &invalid_condition4
, 4, &error
);
90 EXPECT_FALSE(error
.empty());
91 EXPECT_FALSE(result
.get());
95 result
= URLMatcherFactory::CreateFromURLFilterDictionary(
96 matcher
.condition_factory(), &valid_condition
, 100, &error
);
98 ASSERT_TRUE(result
.get());
100 URLMatcherConditionSet::Vector conditions
;
101 conditions
.push_back(result
);
102 matcher
.AddConditionSets(conditions
);
104 EXPECT_EQ(1u, matcher
.MatchURL(GURL("http://www.example.com")).size());
105 EXPECT_EQ(1u, matcher
.MatchURL(GURL("http://www.example.com:80")).size());
106 EXPECT_EQ(1u, matcher
.MatchURL(GURL("http://www.example.com:1000")).size());
108 EXPECT_EQ(0u, matcher
.MatchURL(GURL("https://www.example.com:80")).size());
110 EXPECT_EQ(0u, matcher
.MatchURL(GURL("http://www.example.com:81")).size());
111 // Unfulfilled host prefix.
112 EXPECT_EQ(0u, matcher
.MatchURL(GURL("http://mail.example.com:81")).size());
115 // Using upper case letters for scheme and host values is currently an error.
116 // See more context at http://crbug.com/160702#c6 .
117 TEST(URLMatcherFactoryTest
, UpperCase
) {
120 scoped_refptr
<URLMatcherConditionSet
> result
;
122 // {"hostContains": "exaMple"}
123 base::DictionaryValue invalid_condition1
;
124 invalid_condition1
.SetString(keys::kHostContainsKey
, "exaMple");
126 // {"hostSuffix": ".Com"}
127 base::DictionaryValue invalid_condition2
;
128 invalid_condition2
.SetString(keys::kHostSuffixKey
, ".Com");
130 // {"hostPrefix": "WWw."}
131 base::DictionaryValue invalid_condition3
;
132 invalid_condition3
.SetString(keys::kHostPrefixKey
, "WWw.");
134 // {"hostEquals": "WWW.example.Com"}
135 base::DictionaryValue invalid_condition4
;
136 invalid_condition4
.SetString(keys::kHostEqualsKey
, "WWW.example.Com");
138 // {"scheme": ["HTTP"]}
139 base::ListValue
* scheme_list
= new base::ListValue();
140 scheme_list
->Append(base::Value::CreateStringValue("HTTP"));
141 base::DictionaryValue invalid_condition5
;
142 invalid_condition5
.Set(keys::kSchemesKey
, scheme_list
);
144 const base::DictionaryValue
* invalid_conditions
[] = {
152 for (size_t i
= 0; i
< arraysize(invalid_conditions
); ++i
) {
154 result
= URLMatcherFactory::CreateFromURLFilterDictionary(
155 matcher
.condition_factory(), invalid_conditions
[i
], 1, &error
);
156 EXPECT_FALSE(error
.empty()) << "in iteration " << i
;
157 EXPECT_FALSE(result
.get()) << "in iteration " << i
;
161 // This class wraps a case sensitivity test for a single UrlFilter condition.
162 class UrlConditionCaseTest
{
164 // The condition is identified by the key |condition_key|. If that key is
165 // associated with string values, then |use_list_of_strings| should be false,
166 // if the key is associated with list-of-string values, then
167 // |use_list_of_strings| should be true. In |url| is the URL to test against.
168 UrlConditionCaseTest(const char* condition_key
,
169 bool use_list_of_strings
,
170 const std::string
& expected_value
,
171 const std::string
& incorrect_case_value
,
173 bool lower_case_enforced
,
175 : condition_key_(condition_key
),
176 use_list_of_strings_(use_list_of_strings
),
177 expected_value_(expected_value
),
178 incorrect_case_value_(incorrect_case_value
),
179 expected_result_for_wrong_case_(ExpectedResult(case_sensitive
,
180 lower_case_enforced
)),
183 ~UrlConditionCaseTest() {}
185 // Match the condition against |url_|. Checks via EXPECT_* macros that
186 // |expected_value_| matches always, and that |incorrect_case_value_| matches
187 // iff |case_sensitive_| is false.
191 enum ResultType
{ OK
, NOT_FULFILLED
, CREATE_FAILURE
};
193 // What is the expected result of |CheckCondition| if a wrong-case |value|
194 // containing upper case letters is supplied.
195 static ResultType
ExpectedResult(bool case_sensitive
,
196 bool lower_case_enforced
) {
197 if (lower_case_enforced
)
198 return CREATE_FAILURE
;
200 return NOT_FULFILLED
;
204 // Test the condition |condition_key_| = |value| against |url_|.
205 // Check, via EXPECT_* macros, that either the condition cannot be constructed
206 // at all, or that the condition is not fulfilled, or that it is fulfilled,
207 // depending on the value of |expected_result|.
208 void CheckCondition(const std::string
& value
,
209 ResultType expected_result
) const;
211 const char* condition_key_
;
212 const bool use_list_of_strings_
;
213 const std::string
& expected_value_
;
214 const std::string
& incorrect_case_value_
;
215 const ResultType expected_result_for_wrong_case_
;
218 // Allow implicit copy and assign, because a public copy constructor is
219 // needed, but never used (!), for the definition of arrays of this class.
222 void UrlConditionCaseTest::Test() const {
223 CheckCondition(expected_value_
, OK
);
224 CheckCondition(incorrect_case_value_
, expected_result_for_wrong_case_
);
227 void UrlConditionCaseTest::CheckCondition(
228 const std::string
& value
,
229 UrlConditionCaseTest::ResultType expected_result
) const {
230 base::DictionaryValue condition
;
231 if (use_list_of_strings_
) {
232 base::ListValue
* list
= new base::ListValue();
233 list
->Append(base::Value::CreateStringValue(value
));
234 condition
.SetWithoutPathExpansion(condition_key_
, list
);
236 condition
.SetStringWithoutPathExpansion(condition_key_
, value
);
241 scoped_refptr
<URLMatcherConditionSet
> result
;
243 result
= URLMatcherFactory::CreateFromURLFilterDictionary(
244 matcher
.condition_factory(), &condition
, 1, &error
);
245 if (expected_result
== CREATE_FAILURE
) {
246 EXPECT_FALSE(error
.empty());
247 EXPECT_FALSE(result
.get());
250 EXPECT_EQ("", error
);
251 ASSERT_TRUE(result
.get());
253 URLMatcherConditionSet::Vector conditions
;
254 conditions
.push_back(result
);
255 matcher
.AddConditionSets(conditions
);
256 EXPECT_EQ((expected_result
== OK
? 1u : 0u), matcher
.MatchURL(url_
).size())
257 << "while matching condition " << condition_key_
<< " with value "
258 << value
<< " against url " << url_
;
261 // This tests that the UrlFilter handles case sensitivity on various parts of
263 TEST(URLMatcherFactoryTest
, CaseSensitivity
) {
264 const std::string
kScheme("https");
265 const std::string
kSchemeUpper("HTTPS");
266 const std::string
kHost("www.example.com");
267 const std::string
kHostUpper("WWW.EXAMPLE.COM");
268 const std::string
kPath("/path");
269 const std::string
kPathUpper("/PATH");
270 const std::string
kQuery("?option=value&A=B");
271 const std::string
kQueryUpper("?OPTION=VALUE&A=B");
272 const std::string
kUrl(kScheme
+ "://" + kHost
+ ":1234" + kPath
+ kQuery
);
273 const std::string
kUrlUpper(
274 kSchemeUpper
+ "://" + kHostUpper
+ ":1234" + kPathUpper
+ kQueryUpper
);
275 const GURL
url(kUrl
);
276 // Note: according to RFC 3986, and RFC 1034, schema and host, respectively
277 // should be case insensitive. See crbug.com/160702#6 for why we still
278 // require them to be case sensitive in UrlFilter, and enforce lower case.
279 const bool kIsSchemeLowerCaseEnforced
= true;
280 const bool kIsHostLowerCaseEnforced
= true;
281 const bool kIsPathLowerCaseEnforced
= false;
282 const bool kIsQueryLowerCaseEnforced
= false;
283 const bool kIsUrlLowerCaseEnforced
= false;
284 const bool kIsSchemeCaseSensitive
= true;
285 const bool kIsHostCaseSensitive
= true;
286 const bool kIsPathCaseSensitive
= true;
287 const bool kIsQueryCaseSensitive
= true;
288 const bool kIsUrlCaseSensitive
= kIsSchemeCaseSensitive
||
289 kIsHostCaseSensitive
||
290 kIsPathCaseSensitive
||
291 kIsQueryCaseSensitive
;
293 const UrlConditionCaseTest case_tests
[] = {
294 UrlConditionCaseTest(keys::kSchemesKey
, true, kScheme
, kSchemeUpper
,
295 kIsSchemeCaseSensitive
, kIsSchemeLowerCaseEnforced
,
297 UrlConditionCaseTest(keys::kHostContainsKey
, false, kHost
, kHostUpper
,
298 kIsHostCaseSensitive
, kIsHostLowerCaseEnforced
, url
),
299 UrlConditionCaseTest(keys::kHostEqualsKey
, false, kHost
, kHostUpper
,
300 kIsHostCaseSensitive
, kIsHostLowerCaseEnforced
, url
),
301 UrlConditionCaseTest(keys::kHostPrefixKey
, false, kHost
, kHostUpper
,
302 kIsHostCaseSensitive
, kIsHostLowerCaseEnforced
, url
),
303 UrlConditionCaseTest(keys::kHostSuffixKey
, false, kHost
, kHostUpper
,
304 kIsHostCaseSensitive
, kIsHostLowerCaseEnforced
, url
),
305 UrlConditionCaseTest(keys::kPathContainsKey
, false, kPath
, kPathUpper
,
306 kIsPathCaseSensitive
, kIsPathLowerCaseEnforced
, url
),
307 UrlConditionCaseTest(keys::kPathEqualsKey
, false, kPath
, kPathUpper
,
308 kIsPathCaseSensitive
, kIsPathLowerCaseEnforced
, url
),
309 UrlConditionCaseTest(keys::kPathPrefixKey
, false, kPath
, kPathUpper
,
310 kIsPathCaseSensitive
, kIsPathLowerCaseEnforced
, url
),
311 UrlConditionCaseTest(keys::kPathSuffixKey
, false, kPath
, kPathUpper
,
312 kIsPathCaseSensitive
, kIsPathLowerCaseEnforced
, url
),
313 UrlConditionCaseTest(keys::kQueryContainsKey
, false, kQuery
, kQueryUpper
,
314 kIsQueryCaseSensitive
, kIsQueryLowerCaseEnforced
, url
),
315 UrlConditionCaseTest(keys::kQueryEqualsKey
, false, kQuery
, kQueryUpper
,
316 kIsQueryCaseSensitive
, kIsQueryLowerCaseEnforced
, url
),
317 UrlConditionCaseTest(keys::kQueryPrefixKey
, false, kQuery
, kQueryUpper
,
318 kIsQueryCaseSensitive
, kIsQueryLowerCaseEnforced
, url
),
319 UrlConditionCaseTest(keys::kQuerySuffixKey
, false, kQuery
, kQueryUpper
,
320 kIsQueryCaseSensitive
, kIsQueryLowerCaseEnforced
, url
),
321 // Excluding kURLMatchesKey because case sensitivity can be specified in the
323 UrlConditionCaseTest(keys::kURLContainsKey
, false, kUrl
, kUrlUpper
,
324 kIsUrlCaseSensitive
, kIsUrlLowerCaseEnforced
, url
),
325 UrlConditionCaseTest(keys::kURLEqualsKey
, false, kUrl
, kUrlUpper
,
326 kIsUrlCaseSensitive
, kIsUrlLowerCaseEnforced
, url
),
327 UrlConditionCaseTest(keys::kURLPrefixKey
, false, kUrl
, kUrlUpper
,
328 kIsUrlCaseSensitive
, kIsUrlLowerCaseEnforced
, url
),
329 UrlConditionCaseTest(keys::kURLSuffixKey
, false, kUrl
, kUrlUpper
,
330 kIsUrlCaseSensitive
, kIsUrlLowerCaseEnforced
, url
),
333 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(case_tests
); ++i
) {
334 SCOPED_TRACE(base::StringPrintf("Iteration: %" PRIuS
, i
));
335 case_tests
[i
].Test();
339 } // namespace url_matcher