Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc

Issue 10874029: Adding condition attributes for response headers to Declarative WebRequest (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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 whether the response headers from |url_request| satisfy the match
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 const std::string& key,
237 net::URLRequest* url_request,
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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kResponseHeadersKey, &url_request, &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, keys::kExcludeResponseHeadersKey, &url_request, &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, keys::kExcludeResponseHeadersKey, &url_request, &result);
419 EXPECT_FALSE(result);
420 }
421
159 } // namespace extensions 422 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698