OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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. | |
4 | |
5 #include "chrome/common/extensions/matcher/url_matcher_factory.h" | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "base/format_macros.h" | |
9 #include "base/stringprintf.h" | |
10 #include "base/values.h" | |
11 #include "chrome/common/extensions/matcher/url_matcher_constants.h" | |
12 #include "googleurl/src/gurl.h" | |
13 #include "testing/gtest/include/gtest/gtest.h" | |
14 | |
15 namespace extensions { | |
16 | |
17 namespace keys = url_matcher_constants; | |
18 | |
19 TEST(URLMatcherFactoryTest, CreateFromURLFilterDictionary) { | |
20 URLMatcher matcher; | |
21 | |
22 std::string error; | |
23 scoped_refptr<URLMatcherConditionSet> result; | |
24 | |
25 // Invalid key: {"invalid": "foobar"} | |
26 DictionaryValue invalid_condition; | |
27 invalid_condition.SetString("invalid", "foobar"); | |
28 | |
29 // Invalid value type: {"hostSuffix": []} | |
30 DictionaryValue invalid_condition2; | |
31 invalid_condition2.Set(keys::kHostSuffixKey, new ListValue); | |
32 | |
33 // Invalid regex value: {"urlMatches": "*"} | |
34 DictionaryValue invalid_condition3; | |
35 invalid_condition3.SetString(keys::kURLMatchesKey, "*"); | |
36 | |
37 // Valid values: | |
38 // { | |
39 // "port_range": [80, [1000, 1010]], | |
40 // "schemes": ["http"], | |
41 // "hostSuffix": "example.com" | |
42 // "hostPrefix": "www" | |
43 // } | |
44 | |
45 // Port range: Allow 80;1000-1010. | |
46 ListValue* port_range = new ListValue(); | |
47 port_range->Append(Value::CreateIntegerValue(1000)); | |
48 port_range->Append(Value::CreateIntegerValue(1010)); | |
49 ListValue* port_ranges = new ListValue(); | |
50 port_ranges->Append(Value::CreateIntegerValue(80)); | |
51 port_ranges->Append(port_range); | |
52 | |
53 ListValue* scheme_list = new ListValue(); | |
54 scheme_list->Append(Value::CreateStringValue("http")); | |
55 | |
56 DictionaryValue valid_condition; | |
57 valid_condition.SetString(keys::kHostSuffixKey, "example.com"); | |
58 valid_condition.SetString(keys::kHostPrefixKey, "www"); | |
59 valid_condition.Set(keys::kPortsKey, port_ranges); | |
60 valid_condition.Set(keys::kSchemesKey, scheme_list); | |
61 | |
62 // Test wrong condition name passed. | |
63 error.clear(); | |
64 result = URLMatcherFactory::CreateFromURLFilterDictionary( | |
65 matcher.condition_factory(), &invalid_condition, 1, &error); | |
66 EXPECT_FALSE(error.empty()); | |
67 EXPECT_FALSE(result.get()); | |
68 | |
69 // Test wrong datatype in hostSuffix. | |
70 error.clear(); | |
71 result = URLMatcherFactory::CreateFromURLFilterDictionary( | |
72 matcher.condition_factory(), &invalid_condition2, 2, &error); | |
73 EXPECT_FALSE(error.empty()); | |
74 EXPECT_FALSE(result.get()); | |
75 | |
76 // Test invalid regex in urlMatches. | |
77 error.clear(); | |
78 result = URLMatcherFactory::CreateFromURLFilterDictionary( | |
79 matcher.condition_factory(), &invalid_condition3, 3, &error); | |
80 EXPECT_FALSE(error.empty()); | |
81 EXPECT_FALSE(result.get()); | |
82 | |
83 // Test success. | |
84 error.clear(); | |
85 result = URLMatcherFactory::CreateFromURLFilterDictionary( | |
86 matcher.condition_factory(), &valid_condition, 100, &error); | |
87 EXPECT_EQ("", error); | |
88 ASSERT_TRUE(result.get()); | |
89 | |
90 URLMatcherConditionSet::Vector conditions; | |
91 conditions.push_back(result); | |
92 matcher.AddConditionSets(conditions); | |
93 | |
94 EXPECT_EQ(1u, matcher.MatchURL(GURL("http://www.example.com")).size()); | |
95 EXPECT_EQ(1u, matcher.MatchURL(GURL("http://www.example.com:80")).size()); | |
96 EXPECT_EQ(1u, matcher.MatchURL(GURL("http://www.example.com:1000")).size()); | |
97 // Wrong scheme. | |
98 EXPECT_EQ(0u, matcher.MatchURL(GURL("https://www.example.com:80")).size()); | |
99 // Wrong port. | |
100 EXPECT_EQ(0u, matcher.MatchURL(GURL("http://www.example.com:81")).size()); | |
101 // Unfulfilled host prefix. | |
102 EXPECT_EQ(0u, matcher.MatchURL(GURL("http://mail.example.com:81")).size()); | |
103 } | |
104 | |
105 // Using upper case letters for scheme and host values is currently an error. | |
106 // See more context at http://crbug.com/160702#c6 . | |
107 TEST(URLMatcherFactoryTest, UpperCase) { | |
108 URLMatcher matcher; | |
109 std::string error; | |
110 scoped_refptr<URLMatcherConditionSet> result; | |
111 | |
112 // {"hostContains": "exaMple"} | |
113 DictionaryValue invalid_condition1; | |
114 invalid_condition1.SetString(keys::kHostContainsKey, "exaMple"); | |
115 | |
116 // {"hostSuffix": ".Com"} | |
117 DictionaryValue invalid_condition2; | |
118 invalid_condition2.SetString(keys::kHostSuffixKey, ".Com"); | |
119 | |
120 // {"hostPrefix": "WWw."} | |
121 DictionaryValue invalid_condition3; | |
122 invalid_condition3.SetString(keys::kHostPrefixKey, "WWw."); | |
123 | |
124 // {"hostEquals": "WWW.example.Com"} | |
125 DictionaryValue invalid_condition4; | |
126 invalid_condition4.SetString(keys::kHostEqualsKey, "WWW.example.Com"); | |
127 | |
128 // {"scheme": ["HTTP"]} | |
129 ListValue* scheme_list = new ListValue(); | |
130 scheme_list->Append(Value::CreateStringValue("HTTP")); | |
131 DictionaryValue invalid_condition5; | |
132 invalid_condition5.Set(keys::kSchemesKey, scheme_list); | |
133 | |
134 const DictionaryValue* invalid_conditions[] = { | |
135 &invalid_condition1, | |
136 &invalid_condition2, | |
137 &invalid_condition3, | |
138 &invalid_condition4, | |
139 &invalid_condition5 | |
140 }; | |
141 | |
142 for (size_t i = 0; i < arraysize(invalid_conditions); ++i) { | |
143 error.clear(); | |
144 result = URLMatcherFactory::CreateFromURLFilterDictionary( | |
145 matcher.condition_factory(), invalid_conditions[i], 1, &error); | |
146 EXPECT_FALSE(error.empty()) << "in iteration " << i; | |
147 EXPECT_FALSE(result.get()) << "in iteration " << i; | |
148 } | |
149 } | |
150 | |
151 // This class wraps a case sensitivity test for a single UrlFilter condition. | |
152 class UrlConditionCaseTest { | |
153 public: | |
154 // The condition is identified by the key |condition_key|. If that key is | |
155 // associated with string values, then |use_list_of_strings| should be false, | |
156 // if the key is associated with list-of-string values, then | |
157 // |use_list_of_strings| should be true. In |url| is the URL to test against. | |
158 UrlConditionCaseTest(const char* condition_key, | |
159 bool use_list_of_strings, | |
160 const std::string& expected_value, | |
161 const std::string& incorrect_case_value, | |
162 bool case_sensitive, | |
163 bool lower_case_enforced, | |
164 const GURL& url) | |
165 : condition_key_(condition_key), | |
166 use_list_of_strings_(use_list_of_strings), | |
167 expected_value_(expected_value), | |
168 incorrect_case_value_(incorrect_case_value), | |
169 expected_result_for_wrong_case_(ExpectedResult(case_sensitive, | |
170 lower_case_enforced)), | |
171 url_(url) {} | |
172 | |
173 ~UrlConditionCaseTest() {} | |
174 | |
175 // Match the condition against |url_|. Checks via EXPECT_* macros that | |
176 // |expected_value_| matches always, and that |incorrect_case_value_| matches | |
177 // iff |case_sensitive_| is false. | |
178 void Test() const; | |
179 | |
180 private: | |
181 enum ResultType { OK, NOT_FULFILLED, CREATE_FAILURE }; | |
182 | |
183 // What is the expected result of |CheckCondition| if a wrong-case |value| | |
184 // containing upper case letters is supplied. | |
185 static ResultType ExpectedResult(bool case_sensitive, | |
186 bool lower_case_enforced) { | |
187 if (lower_case_enforced) | |
188 return CREATE_FAILURE; | |
189 if (case_sensitive) | |
190 return NOT_FULFILLED; | |
191 return OK; | |
192 } | |
193 | |
194 // Test the condition |condition_key_| = |value| against |url_|. | |
195 // Check, via EXPECT_* macros, that either the condition cannot be constructed | |
196 // at all, or that the condition is not fulfilled, or that it is fulfilled, | |
197 // depending on the value of |expected_result|. | |
198 void CheckCondition(const std::string& value, | |
199 ResultType expected_result) const; | |
200 | |
201 const char* condition_key_; | |
202 const bool use_list_of_strings_; | |
203 const std::string& expected_value_; | |
204 const std::string& incorrect_case_value_; | |
205 const ResultType expected_result_for_wrong_case_; | |
206 const GURL& url_; | |
207 | |
208 // Allow implicit copy and assign, because a public copy constructor is | |
209 // needed, but never used (!), for the definition of arrays of this class. | |
210 }; | |
211 | |
212 void UrlConditionCaseTest::Test() const { | |
213 CheckCondition(expected_value_, OK); | |
214 CheckCondition(incorrect_case_value_, expected_result_for_wrong_case_); | |
215 } | |
216 | |
217 void UrlConditionCaseTest::CheckCondition( | |
218 const std::string& value, | |
219 UrlConditionCaseTest::ResultType expected_result) const { | |
220 DictionaryValue condition; | |
221 if (use_list_of_strings_) { | |
222 ListValue* list = new ListValue(); | |
223 list->Append(Value::CreateStringValue(value)); | |
224 condition.SetWithoutPathExpansion(condition_key_, list); | |
225 } else { | |
226 condition.SetStringWithoutPathExpansion(condition_key_, value); | |
227 } | |
228 | |
229 URLMatcher matcher; | |
230 std::string error; | |
231 scoped_refptr<URLMatcherConditionSet> result; | |
232 | |
233 result = URLMatcherFactory::CreateFromURLFilterDictionary( | |
234 matcher.condition_factory(), &condition, 1, &error); | |
235 if (expected_result == CREATE_FAILURE) { | |
236 EXPECT_FALSE(error.empty()); | |
237 EXPECT_FALSE(result); | |
238 return; | |
239 } | |
240 EXPECT_EQ("", error); | |
241 ASSERT_TRUE(result.get()); | |
242 | |
243 URLMatcherConditionSet::Vector conditions; | |
244 conditions.push_back(result); | |
245 matcher.AddConditionSets(conditions); | |
246 EXPECT_EQ((expected_result == OK ? 1u : 0u), matcher.MatchURL(url_).size()) | |
247 << "while matching condition " << condition_key_ << " with value " | |
248 << value << " against url " << url_; | |
249 } | |
250 | |
251 // This tests that the UrlFilter handles case sensitivity on various parts of | |
252 // URLs correctly. | |
253 TEST(URLMatcherFactoryTest, CaseSensitivity) { | |
254 const std::string kScheme("https"); | |
255 const std::string kSchemeUpper("HTTPS"); | |
256 const std::string kHost("www.example.com"); | |
257 const std::string kHostUpper("WWW.EXAMPLE.COM"); | |
258 const std::string kPath("/path"); | |
259 const std::string kPathUpper("/PATH"); | |
260 const std::string kQuery("?option=value&A=B"); | |
261 const std::string kQueryUpper("?OPTION=VALUE&A=B"); | |
262 const std::string kUrl(kScheme + "://" + kHost + ":1234" + kPath + kQuery); | |
263 const std::string kUrlUpper( | |
264 kSchemeUpper + "://" + kHostUpper + ":1234" + kPathUpper + kQueryUpper); | |
265 const GURL url(kUrl); | |
266 // Note: according to RFC 3986, and RFC 1034, schema and host, respectively | |
267 // should be case insensitive. See crbug.com/160702#6 for why we still | |
268 // require them to be case sensitive in UrlFilter, and enforce lower case. | |
269 const bool kIsSchemeLowerCaseEnforced = true; | |
270 const bool kIsHostLowerCaseEnforced = true; | |
271 const bool kIsPathLowerCaseEnforced = false; | |
272 const bool kIsQueryLowerCaseEnforced = false; | |
273 const bool kIsUrlLowerCaseEnforced = false; | |
274 const bool kIsSchemeCaseSensitive = true; | |
275 const bool kIsHostCaseSensitive = true; | |
276 const bool kIsPathCaseSensitive = true; | |
277 const bool kIsQueryCaseSensitive = true; | |
278 const bool kIsUrlCaseSensitive = kIsSchemeCaseSensitive || | |
279 kIsHostCaseSensitive || | |
280 kIsPathCaseSensitive || | |
281 kIsQueryCaseSensitive; | |
282 | |
283 const UrlConditionCaseTest case_tests[] = { | |
284 UrlConditionCaseTest(keys::kSchemesKey, true, kScheme, kSchemeUpper, | |
285 kIsSchemeCaseSensitive, kIsSchemeLowerCaseEnforced, | |
286 url), | |
287 UrlConditionCaseTest(keys::kHostContainsKey, false, kHost, kHostUpper, | |
288 kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url), | |
289 UrlConditionCaseTest(keys::kHostEqualsKey, false, kHost, kHostUpper, | |
290 kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url), | |
291 UrlConditionCaseTest(keys::kHostPrefixKey, false, kHost, kHostUpper, | |
292 kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url), | |
293 UrlConditionCaseTest(keys::kHostSuffixKey, false, kHost, kHostUpper, | |
294 kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url), | |
295 UrlConditionCaseTest(keys::kPathContainsKey, false, kPath, kPathUpper, | |
296 kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url), | |
297 UrlConditionCaseTest(keys::kPathEqualsKey, false, kPath, kPathUpper, | |
298 kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url), | |
299 UrlConditionCaseTest(keys::kPathPrefixKey, false, kPath, kPathUpper, | |
300 kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url), | |
301 UrlConditionCaseTest(keys::kPathSuffixKey, false, kPath, kPathUpper, | |
302 kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url), | |
303 UrlConditionCaseTest(keys::kQueryContainsKey, false, kQuery, kQueryUpper, | |
304 kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url), | |
305 UrlConditionCaseTest(keys::kQueryEqualsKey, false, kQuery, kQueryUpper, | |
306 kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url), | |
307 UrlConditionCaseTest(keys::kQueryPrefixKey, false, kQuery, kQueryUpper, | |
308 kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url), | |
309 UrlConditionCaseTest(keys::kQuerySuffixKey, false, kQuery, kQueryUpper, | |
310 kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url), | |
311 // Excluding kURLMatchesKey because case sensitivity can be specified in the | |
312 // RE2 expression. | |
313 UrlConditionCaseTest(keys::kURLContainsKey, false, kUrl, kUrlUpper, | |
314 kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url), | |
315 UrlConditionCaseTest(keys::kURLEqualsKey, false, kUrl, kUrlUpper, | |
316 kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url), | |
317 UrlConditionCaseTest(keys::kURLPrefixKey, false, kUrl, kUrlUpper, | |
318 kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url), | |
319 UrlConditionCaseTest(keys::kURLSuffixKey, false, kUrl, kUrlUpper, | |
320 kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url), | |
321 }; | |
322 | |
323 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(case_tests); ++i) { | |
324 SCOPED_TRACE(base::StringPrintf("Iteration: %" PRIuS, i)); | |
325 case_tests[i].Test(); | |
326 } | |
327 } | |
328 | |
329 } // namespace extensions | |
OLD | NEW |