OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit ion_attribute.h" | 5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit ion_attribute.h" |
6 | 6 |
7 #include "base/basictypes.h" | |
7 #include "base/file_path.h" | 8 #include "base/file_path.h" |
8 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
9 #include "base/values.h" | 10 #include "base/values.h" |
10 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta nts.h" | 11 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta nts.h" |
11 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rule.h " | 12 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rule.h " |
12 #include "content/public/browser/resource_request_info.h" | 13 #include "content/public/browser/resource_request_info.h" |
13 #include "net/url_request/url_request_test_util.h" | 14 #include "net/url_request/url_request_test_util.h" |
14 #include "net/test/test_server.h" | 15 #include "net/test/test_server.h" |
15 #include "testing/gtest/include/gtest/gtest.h" | 16 #include "testing/gtest/include/gtest/gtest.h" |
16 | 17 |
18 using base::DictionaryValue; | |
19 using base::ListValue; | |
20 using base::StringValue; | |
21 using base::Value; | |
22 | |
17 namespace { | 23 namespace { |
18 const char kUnknownConditionName[] = "unknownType"; | 24 const char kUnknownConditionName[] = "unknownType"; |
19 } // namespace | 25 } // namespace |
20 | 26 |
21 namespace extensions { | 27 namespace extensions { |
22 | 28 |
23 namespace keys = declarative_webrequest_constants; | 29 namespace keys = declarative_webrequest_constants; |
24 | 30 |
25 TEST(WebRequestConditionAttributeTest, CreateConditionAttribute) { | 31 TEST(WebRequestConditionAttributeTest, CreateConditionAttribute) { |
26 // Necessary for TestURLRequest. | 32 // Necessary for TestURLRequest. |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
149 scoped_ptr<WebRequestConditionAttribute> attribute_unexcluded = | 155 scoped_ptr<WebRequestConditionAttribute> attribute_unexcluded = |
150 WebRequestConditionAttribute::Create( | 156 WebRequestConditionAttribute::Create( |
151 keys::kExcludeContentTypeKey, &content_types, &error); | 157 keys::kExcludeContentTypeKey, &content_types, &error); |
152 EXPECT_EQ("", error); | 158 EXPECT_EQ("", error); |
153 ASSERT_TRUE(attribute_unexcluded.get()); | 159 ASSERT_TRUE(attribute_unexcluded.get()); |
154 EXPECT_TRUE(attribute_unexcluded->IsFulfilled( | 160 EXPECT_TRUE(attribute_unexcluded->IsFulfilled( |
155 WebRequestRule::RequestData(&url_request, ON_HEADERS_RECEIVED, | 161 WebRequestRule::RequestData(&url_request, ON_HEADERS_RECEIVED, |
156 url_request.response_headers()))); | 162 url_request.response_headers()))); |
157 } | 163 } |
158 | 164 |
165 namespace { | |
166 | |
167 // Builds a vector of vectors of string pointers from an array of strings. | |
168 // |array| is in fact a sequence of arrays. The array |sizes| captures the sizes | |
169 // of all parts of |array|, and |size| is the length of |sizes| itself. | |
170 // Example (this is pseudo-code, not C++): | |
171 // array = { "a", "b", "c", "d", "e", "f" } | |
172 // sizes = { 2, 0, 4 } | |
173 // size = 3 | |
174 // results in out == { {&"a", &"b"}, {}, {&"c", &"d", &"e", &"f"} } | |
175 void GetArrayAsVector(const std::string array[], | |
176 const size_t sizes[], | |
177 const size_t size, | |
178 std::vector< std::vector<const std::string*> >* out) { | |
179 out->clear(); | |
180 size_t next = 0; | |
181 for (size_t i = 0; i < size; ++i) { | |
182 out->push_back(std::vector<const std::string*>()); | |
183 for (size_t j = next; j < next + sizes[i]; ++j) { | |
184 out->back().push_back(&(array[j])); | |
185 } | |
186 next += sizes[i]; | |
187 } | |
188 } | |
189 | |
190 // Builds a DictionaryValue from an array of the form {name1, value1, name2, | |
191 // value2, ...}. Values for the same key are grouped in a ListValue. | |
192 scoped_ptr<DictionaryValue> GetDictionaryFromArray( | |
193 const std::vector<const std::string*>& array) { | |
194 const size_t length = array.size(); | |
195 CHECK(length % 2 == 0); | |
196 | |
197 scoped_ptr<DictionaryValue> dictionary(new DictionaryValue); | |
198 for (size_t i = 0; i < length; i += 2) { | |
199 const std::string* name = array[i]; | |
200 const std::string* value = array[i+1]; | |
201 if (dictionary->HasKey(*name)) { | |
202 Value* entry = NULL; | |
203 ListValue* list = NULL; | |
204 if (!dictionary->GetWithoutPathExpansion(*name, &entry)) | |
205 return scoped_ptr<DictionaryValue>(NULL); | |
206 switch (entry->GetType()) { | |
207 case Value::TYPE_STRING: // Replace the present string with a list. | |
208 list = new ListValue; | |
209 // Ignoring return value, we already verified the entry is there. | |
210 dictionary->RemoveWithoutPathExpansion(*name, &entry); | |
211 list->Append(entry); | |
212 list->Append(Value::CreateStringValue(*value)); | |
213 dictionary->SetWithoutPathExpansion(*name, list); | |
214 break; | |
215 case Value::TYPE_LIST: // Just append to the list. | |
216 CHECK(entry->GetAsList(&list)); | |
217 list->Append(Value::CreateStringValue(*value)); | |
218 break; | |
219 default: | |
220 NOTREACHED(); // We never put other Values here. | |
221 return scoped_ptr<DictionaryValue>(NULL); | |
222 } | |
223 } else { | |
224 dictionary->SetString(*name, *value); | |
225 } | |
226 } | |
227 return dictionary.Pass(); | |
228 } | |
229 | |
230 // Returns how the response headers from |url_request| satisfy the match | |
battre
2012/08/24 16:24:41
s/how/whether/?
vabr (Chromium)
2012/08/27 12:11:50
Done.
| |
231 // criteria given in |tests|. For at least one |i| all tests from |tests[i]| | |
232 // must pass. If |positive_test| is true, the dictionary is interpreted as the | |
233 // containsHeaders property of a RequestMatcher, otherwise as | |
234 // doesNotContainHeaders. | |
235 void MatchAndCheck(const std::vector< std::vector<const std::string*> >& tests, | |
236 net::URLRequest* url_request, | |
237 const std::string& key, | |
battre
2012/08/24 16:24:41
I would change the order: key + tests make up the
vabr (Chromium)
2012/08/27 12:11:50
Done.
| |
238 bool* result) { | |
239 ListValue contains_headers; | |
240 for (size_t i = 0; i < tests.size(); ++i) { | |
241 scoped_ptr<DictionaryValue> temp(GetDictionaryFromArray(tests[i])); | |
242 ASSERT_TRUE(temp.get() != NULL); | |
243 contains_headers.Append(temp.release()); | |
244 } | |
245 | |
246 std::string error; | |
247 scoped_ptr<WebRequestConditionAttribute> attribute = | |
248 WebRequestConditionAttribute::Create(key, &contains_headers, &error); | |
249 ASSERT_EQ("", error); | |
250 ASSERT_TRUE(attribute.get() != NULL); | |
251 | |
252 *result = attribute->IsFulfilled(WebRequestRule::RequestData( | |
253 url_request, ON_HEADERS_RECEIVED, url_request->response_headers())); | |
254 } | |
255 | |
256 } // namespace | |
257 | |
258 // Here we test WebRequestConditionAttributeResponseHeaders for: | |
259 // 1. Correct implementation of prefix/suffix/contains/equals matching. | |
260 // 2. Performing logical disjunction (||) between multiple specifications. | |
261 // 3. Negating the match in case of 'doesNotContainHeaders'. | |
262 TEST(WebRequestConditionAttributeTest, Headers) { | |
263 // Necessary for TestURLRequest. | |
264 MessageLoop message_loop(MessageLoop::TYPE_IO); | |
265 | |
266 net::TestServer test_server( | |
267 net::TestServer::TYPE_HTTP, | |
268 net::TestServer::kLocalhost, | |
269 FilePath(FILE_PATH_LITERAL( | |
270 "chrome/test/data/extensions/api_test/webrequest/declarative"))); | |
271 ASSERT_TRUE(test_server.Start()); | |
272 | |
273 TestURLRequestContext context; | |
274 TestDelegate delegate; | |
275 TestURLRequest url_request(test_server.GetURL("files/headers.html"), | |
276 &delegate, &context); | |
277 url_request.Start(); | |
278 MessageLoop::current()->Run(); | |
279 | |
280 // In all the tests below we assume that the server includes the headers | |
281 // Custom-Header: custom/value | |
282 // Custom-Header-B: valueA | |
283 // Custom-Header-B: valueB | |
284 // Custom-Header-C: valueC, valueD | |
285 // Custom-Header-D: | |
286 // in the response, but does not include "Non-existing: void". | |
287 | |
288 std::vector< std::vector<const std::string*> > tests; | |
289 bool result; | |
290 | |
291 // 1.a. -- All these tests should pass. | |
292 const std::string kPassingCondition[] = { | |
293 keys::kNamePrefixKey, "Custom", | |
294 keys::kNameSuffixKey, "m-header", // Header names are case insensitive. | |
295 keys::kValueContainsKey, "alu", | |
296 keys::kValueEqualsKey, "custom/value" | |
297 }; | |
298 const size_t kPassingConditionSizes[] = { arraysize(kPassingCondition) }; | |
299 GetArrayAsVector(kPassingCondition, kPassingConditionSizes, 1u, &tests); | |
300 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
301 EXPECT_TRUE(result); | |
302 | |
303 // 1.b. -- None of the following tests in the discjunction should pass. | |
304 const std::string kFailCondition[] = { | |
305 keys::kNamePrefixKey, " Custom", // Test 1. | |
306 keys::kNameContainsKey, " -", // Test 2. | |
307 keys::kValueSuffixKey, "alu", // Test 3. | |
308 keys::kValueEqualsKey, "custom" // Test 4. | |
309 }; | |
310 const size_t kFailConditionSizes[] = { 2u, 2u, 2u, 2u }; | |
311 GetArrayAsVector(kFailCondition, kFailConditionSizes, 4u, &tests); | |
312 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
313 EXPECT_FALSE(result); | |
314 | |
315 // 1.c. -- This should fail (mixing name and value from different headers) | |
316 const std::string kMixingCondition[] = { | |
317 keys::kNameSuffixKey, "Header-B", | |
318 keys::kValueEqualsKey, "custom/value" | |
319 }; | |
320 const size_t kMixingConditionSizes[] = { arraysize(kMixingCondition) }; | |
321 GetArrayAsVector(kMixingCondition, kMixingConditionSizes, 1u, &tests); | |
322 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
323 EXPECT_FALSE(result); | |
324 | |
325 // 1.d. -- Test handling multiple values for one header (both should pass). | |
326 const std::string kMoreValues1[] = { | |
327 keys::kNameEqualsKey, "Custom-header-b", | |
328 keys::kValueEqualsKey, "valueA" | |
329 }; | |
330 const size_t kMoreValues1Sizes[] = { arraysize(kMoreValues1) }; | |
331 GetArrayAsVector(kMoreValues1, kMoreValues1Sizes, 1u, &tests); | |
332 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
333 EXPECT_TRUE(result); | |
334 const std::string kMoreValues2[] = { | |
335 keys::kNameEqualsKey, "Custom-header-b", | |
336 keys::kValueEqualsKey, "valueB" | |
337 }; | |
338 const size_t kMoreValues2Sizes[] = { arraysize(kMoreValues2) }; | |
339 GetArrayAsVector(kMoreValues2, kMoreValues2Sizes, 1u, &tests); | |
340 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
341 EXPECT_TRUE(result); | |
342 | |
343 // 1.e. -- This should fail as conjunction but pass as disjunction. | |
344 const std::string kConflict[] = { | |
345 keys::kNameSuffixKey, "Header", // True for some header. | |
346 keys::kNameContainsKey, "Header-B" // True for a different header. | |
347 }; | |
348 // First disjunction, no conflict. | |
349 const size_t kNoConflictSizes[] = { 2u, 2u }; | |
350 GetArrayAsVector(kConflict, kNoConflictSizes, 2u, &tests); | |
351 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
352 EXPECT_TRUE(result); | |
353 // Then conjunction, conflict. | |
354 const size_t kConflictSizes[] = { arraysize(kConflict) }; | |
355 GetArrayAsVector(kConflict, kConflictSizes, 1u, &tests); | |
356 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
357 EXPECT_FALSE(result); | |
358 | |
359 // 1.f. -- This should pass, checking for correct treatment of ',' in values. | |
360 const std::string kComma[] = { | |
361 keys::kNameSuffixKey, "Header-C", | |
362 keys::kValueEqualsKey, "valueC, valueD" | |
363 }; | |
364 const size_t kCommaSizes[] = { arraysize(kComma) }; | |
365 GetArrayAsVector(kComma, kCommaSizes, 1u, &tests); | |
366 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
367 EXPECT_TRUE(result); | |
368 | |
369 // 1.g. -- This should pass, empty values are values as well. | |
370 const std::string kEmpty[] = { | |
371 keys::kNameEqualsKey, "custom-header-d", | |
372 keys::kValueEqualsKey, "" | |
373 }; | |
374 const size_t kEmptySizes[] = { arraysize(kEmpty) }; | |
375 GetArrayAsVector(kEmpty, kEmptySizes, 1u, &tests); | |
376 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
377 EXPECT_TRUE(result); | |
378 | |
379 // 1.h. -- Values are case-sensitive, this should fail | |
380 const std::string kLowercase[] = { | |
381 keys::kNameEqualsKey, "Custom-header-b", | |
382 keys::kValueEqualsKey, "valuea" // valuea != valueA | |
383 }; | |
384 const size_t kLowercaseSizes[] = { arraysize(kLowercase) }; | |
385 GetArrayAsVector(kLowercase, kLowercaseSizes, 1u, &tests); | |
386 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
387 EXPECT_FALSE(result); | |
388 | |
389 // 2.a. -- This should pass as disjunction, because one of the tests passes. | |
390 const std::string kDisjunction[] = { | |
391 keys::kNamePrefixKey, "Non-existing", // This one fails. | |
392 keys::kNameSuffixKey, "Non-existing", // This one fails. | |
393 keys::kValueEqualsKey, "void", // This one fails. | |
394 keys::kValueContainsKey, "alu" // This passes. | |
395 }; | |
396 const size_t kDisjunctionSizes[] = { 2u, 2u, 2u, 2u }; | |
397 GetArrayAsVector(kDisjunction, kDisjunctionSizes, 4u, &tests); | |
398 MatchAndCheck(tests, &url_request, keys::kResponseHeadersKey, &result); | |
399 EXPECT_TRUE(result); | |
400 | |
401 // 3.a. -- This should pass. | |
402 const std::string kNonExistent[] = { | |
403 keys::kNameEqualsKey, "Non-existing", | |
404 keys::kValueEqualsKey, "void" | |
405 }; | |
406 const size_t kNonExistentSizes[] = { arraysize(kNonExistent) }; | |
407 GetArrayAsVector(kNonExistent, kNonExistentSizes, 1u, &tests); | |
408 MatchAndCheck(tests, &url_request, keys::kExcludeResponseHeadersKey, &result); | |
409 EXPECT_TRUE(result); | |
410 | |
411 // 3.b. -- This should fail. | |
412 const std::string kExisting[] = { | |
413 keys::kNameEqualsKey, "custom-header-b", | |
414 keys::kValueEqualsKey, "valueB" | |
415 }; | |
416 const size_t kExistingSize[] = { arraysize(kExisting) }; | |
417 GetArrayAsVector(kExisting, kExistingSize, 1u, &tests); | |
418 MatchAndCheck(tests, &url_request, keys::kExcludeResponseHeadersKey, &result); | |
419 EXPECT_FALSE(result); | |
420 } | |
421 | |
159 } // namespace extensions | 422 } // namespace extensions |
OLD | NEW |