OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "base/files/file_path.h" | 5 #include "base/files/file_path.h" |
6 #include "base/scoped_observer.h" | 6 #include "base/scoped_observer.h" |
7 #include "base/strings/string_number_conversions.h" | 7 #include "base/strings/string_number_conversions.h" |
| 8 #include "base/strings/string_util.h" |
8 #include "base/strings/stringprintf.h" | 9 #include "base/strings/stringprintf.h" |
9 #include "chrome/browser/extensions/activity_log/activity_actions.h" | 10 #include "chrome/browser/extensions/activity_log/activity_actions.h" |
10 #include "chrome/browser/extensions/activity_log/activity_log.h" | 11 #include "chrome/browser/extensions/activity_log/activity_log.h" |
11 #include "chrome/browser/extensions/activity_log/ad_network_database.h" | 12 #include "chrome/browser/extensions/activity_log/ad_network_database.h" |
12 #include "chrome/browser/extensions/extension_browsertest.h" | 13 #include "chrome/browser/extensions/extension_browsertest.h" |
13 #include "chrome/browser/extensions/extension_test_message_listener.h" | 14 #include "chrome/browser/extensions/extension_test_message_listener.h" |
14 #include "chrome/test/base/ui_test_utils.h" | 15 #include "chrome/test/base/ui_test_utils.h" |
15 #include "extensions/common/extension.h" | 16 #include "extensions/common/extension.h" |
16 #include "net/test/embedded_test_server/embedded_test_server.h" | 17 #include "net/test/embedded_test_server/embedded_test_server.h" |
17 #include "net/test/embedded_test_server/http_response.h" | 18 #include "net/test/embedded_test_server/http_response.h" |
18 #include "url/gurl.h" | 19 #include "url/gurl.h" |
19 | 20 |
20 namespace net { | 21 namespace net { |
21 namespace test_server { | 22 namespace test_server { |
22 struct HttpRequest; | 23 struct HttpRequest; |
23 } | 24 } |
24 } | 25 } |
25 | 26 |
26 namespace extensions { | 27 namespace extensions { |
27 | 28 |
28 namespace { | 29 namespace { |
29 | 30 |
30 // The "ad network" that we are using. Any src or href equal to this should be | 31 // The "ad network" that we are using. Any src or href equal to this should be |
31 // considered an ad network. | 32 // considered an ad network. |
32 const char kAdNetwork[] = "http://www.known-ads.adnetwork"; | 33 const char kAdNetwork1[] = "http://www.known-ads.adnetwork"; |
| 34 const char kAdNetwork2[] = "http://www.also-known-ads.adnetwork"; |
33 | 35 |
34 // The current stage of the test. | 36 // The current stage of the test. |
35 enum Stage { | 37 enum Stage { |
36 BEFORE_RESET, // We are about to reset the page. | 38 BEFORE_RESET, // We are about to reset the page. |
37 RESETTING, // We are resetting the page. | 39 RESETTING, // We are resetting the page. |
38 TESTING // The reset is complete, and we are testing. | 40 TESTING // The reset is complete, and we are testing. |
39 }; | 41 }; |
40 | 42 |
41 // The string sent by the test to indicate that the page reset will begin. | 43 // The string sent by the test to indicate that the page reset will begin. |
42 const char kResetBeginString[] = "Page Reset Begin"; | 44 const char kResetBeginString[] = "Page Reset Begin"; |
43 // The string sent by the test to indicate that page reset is complete. | 45 // The string sent by the test to indicate that page reset is complete. |
44 const char kResetEndString[] = "Page Reset End"; | 46 const char kResetEndString[] = "Page Reset End"; |
45 // The string sent by the test to indicate a JS error was caught in the test. | 47 // The string sent by the test to indicate a JS error was caught in the test. |
46 const char kJavascriptErrorString[] = "Testing Error"; | 48 const char kJavascriptErrorString[] = "Testing Error"; |
47 // The string sent by the test to indicate that we have concluded the full test. | 49 // The string sent by the test to indicate that we have concluded the full test. |
48 const char kTestCompleteString[] = "Test Complete"; | 50 const char kTestCompleteString[] = "Test Complete"; |
49 | 51 |
| 52 std::string InjectionTypeToString(Action::InjectionType type) { |
| 53 switch (type) { |
| 54 case Action::NO_AD_INJECTION: |
| 55 return "No Ad Injection"; |
| 56 case Action::INJECTION_NEW_AD: |
| 57 return "Injection New Ad"; |
| 58 case Action::INJECTION_REMOVED_AD: |
| 59 return "Injection Removed Ad"; |
| 60 case Action::INJECTION_REPLACED_AD: |
| 61 return "Injection Replaced Ad"; |
| 62 case Action::INJECTION_LIKELY_NEW_AD: |
| 63 return "Injection Likely New Ad"; |
| 64 case Action::INJECTION_LIKELY_REPLACED_AD: |
| 65 return "Injection Likely Replaced Ad"; |
| 66 case Action::NUM_INJECTION_TYPES: |
| 67 return "Num Injection Types"; |
| 68 } |
| 69 return std::string(); |
| 70 } |
| 71 |
50 // An implementation of ActivityLog::Observer that, for every action, sends it | 72 // An implementation of ActivityLog::Observer that, for every action, sends it |
51 // through Action::DidInjectAd(). This will keep track of the observed | 73 // through Action::DidInjectAd(). This will keep track of the observed |
52 // injections, and can be enabled or disabled as needed (for instance, this | 74 // injections, and can be enabled or disabled as needed (for instance, this |
53 // should be disabled while we are resetting the page). | 75 // should be disabled while we are resetting the page). |
54 class ActivityLogObserver : public ActivityLog::Observer { | 76 class ActivityLogObserver : public ActivityLog::Observer { |
55 public: | 77 public: |
56 explicit ActivityLogObserver(content::BrowserContext* context); | 78 explicit ActivityLogObserver(content::BrowserContext* context); |
57 virtual ~ActivityLogObserver(); | 79 virtual ~ActivityLogObserver(); |
58 | 80 |
59 void set_enabled(bool enabled) { enabled_ = enabled; } | 81 // Disable the observer (e.g., to reset the page). |
60 size_t injection_count() const { return injection_count_; } | 82 void disable() { enabled_ = false; } |
| 83 |
| 84 // Enable the observer, resetting the state. |
| 85 void enable() { |
| 86 injection_type_ = Action::NO_AD_INJECTION; |
| 87 found_multiple_injections_ = false; |
| 88 enabled_ = true; |
| 89 } |
| 90 |
| 91 Action::InjectionType injection_type() const { return injection_type_; } |
| 92 |
| 93 bool found_multiple_injections() const { return found_multiple_injections_; } |
61 | 94 |
62 private: | 95 private: |
63 virtual void OnExtensionActivity(scoped_refptr<Action> action) OVERRIDE; | 96 virtual void OnExtensionActivity(scoped_refptr<Action> action) OVERRIDE; |
64 | 97 |
65 ScopedObserver<ActivityLog, ActivityLog::Observer> scoped_observer_; | 98 ScopedObserver<ActivityLog, ActivityLog::Observer> scoped_observer_; |
| 99 |
| 100 // The associated BrowserContext. |
66 content::BrowserContext* context_; | 101 content::BrowserContext* context_; |
67 size_t injection_count_; | 102 |
| 103 // The type of the last injection. |
| 104 Action::InjectionType injection_type_; |
| 105 |
| 106 // Whether or not we found multiple injection types (which shouldn't happen). |
| 107 bool found_multiple_injections_; |
| 108 |
| 109 // Whether or not the observer is enabled. |
68 bool enabled_; | 110 bool enabled_; |
69 }; | 111 }; |
70 | 112 |
71 ActivityLogObserver::ActivityLogObserver(content::BrowserContext* context) | 113 ActivityLogObserver::ActivityLogObserver(content::BrowserContext* context) |
72 : scoped_observer_(this), | 114 : scoped_observer_(this), |
73 context_(context), | 115 context_(context), |
74 injection_count_(0u), | 116 injection_type_(Action::NO_AD_INJECTION), |
| 117 found_multiple_injections_(false), |
75 enabled_(false) { | 118 enabled_(false) { |
76 ActivityLog::GetInstance(context_)->AddObserver(this); | 119 ActivityLog::GetInstance(context_)->AddObserver(this); |
77 } | 120 } |
78 | 121 |
79 ActivityLogObserver::~ActivityLogObserver() {} | 122 ActivityLogObserver::~ActivityLogObserver() {} |
80 | 123 |
81 void ActivityLogObserver::OnExtensionActivity(scoped_refptr<Action> action) { | 124 void ActivityLogObserver::OnExtensionActivity(scoped_refptr<Action> action) { |
82 if (enabled_ && action->DidInjectAd(NULL /* no rappor service */) != | 125 if (!enabled_) |
83 Action::NO_AD_INJECTION) { | 126 return; |
84 ++injection_count_; | 127 |
| 128 Action::InjectionType type = |
| 129 action->DidInjectAd(NULL /* no rappor service */); |
| 130 if (type != Action::NO_AD_INJECTION) { |
| 131 if (injection_type_ != Action::NO_AD_INJECTION) |
| 132 found_multiple_injections_ = true; |
| 133 injection_type_ = type; |
85 } | 134 } |
86 } | 135 } |
87 | 136 |
88 // A mock for the AdNetworkDatabase. This simply says that the URL | 137 // A mock for the AdNetworkDatabase. This simply says that the URL |
89 // http://www.known-ads.adnetwork is an ad network, and nothing else is. | 138 // http://www.known-ads.adnetwork is an ad network, and nothing else is. |
90 class TestAdNetworkDatabase : public AdNetworkDatabase { | 139 class TestAdNetworkDatabase : public AdNetworkDatabase { |
91 public: | 140 public: |
92 TestAdNetworkDatabase(); | 141 TestAdNetworkDatabase(); |
93 virtual ~TestAdNetworkDatabase(); | 142 virtual ~TestAdNetworkDatabase(); |
94 | 143 |
95 private: | 144 private: |
96 virtual bool IsAdNetwork(const GURL& url) const OVERRIDE; | 145 virtual bool IsAdNetwork(const GURL& url) const OVERRIDE; |
97 | 146 |
98 GURL ad_network_url_; | 147 GURL ad_network_url1_; |
| 148 GURL ad_network_url2_; |
99 }; | 149 }; |
100 | 150 |
101 TestAdNetworkDatabase::TestAdNetworkDatabase() : ad_network_url_(kAdNetwork) {} | 151 TestAdNetworkDatabase::TestAdNetworkDatabase() : ad_network_url1_(kAdNetwork1), |
| 152 ad_network_url2_(kAdNetwork2) { |
| 153 } |
| 154 |
102 TestAdNetworkDatabase::~TestAdNetworkDatabase() {} | 155 TestAdNetworkDatabase::~TestAdNetworkDatabase() {} |
103 | 156 |
104 bool TestAdNetworkDatabase::IsAdNetwork(const GURL& url) const { | 157 bool TestAdNetworkDatabase::IsAdNetwork(const GURL& url) const { |
105 return url == ad_network_url_; | 158 return url == ad_network_url1_ || url == ad_network_url2_; |
106 } | 159 } |
107 | 160 |
108 scoped_ptr<net::test_server::HttpResponse> HandleRequest( | 161 scoped_ptr<net::test_server::HttpResponse> HandleRequest( |
109 const net::test_server::HttpRequest& request) { | 162 const net::test_server::HttpRequest& request) { |
110 scoped_ptr<net::test_server::BasicHttpResponse> response( | 163 scoped_ptr<net::test_server::BasicHttpResponse> response( |
111 new net::test_server::BasicHttpResponse()); | 164 new net::test_server::BasicHttpResponse()); |
112 response->set_code(net::HTTP_OK); | 165 response->set_code(net::HTTP_OK); |
113 return response.PassAs<net::test_server::HttpResponse>(); | 166 return response.PassAs<net::test_server::HttpResponse>(); |
114 } | 167 } |
115 | 168 |
(...skipping 18 matching lines...) Expand all Loading... |
134 | 187 |
135 // Handle a JS error encountered in a test. | 188 // Handle a JS error encountered in a test. |
136 testing::AssertionResult HandleJSError(const std::string& message); | 189 testing::AssertionResult HandleJSError(const std::string& message); |
137 | 190 |
138 const base::FilePath& test_data_dir() { return test_data_dir_; } | 191 const base::FilePath& test_data_dir() { return test_data_dir_; } |
139 | 192 |
140 ExtensionTestMessageListener* listener() { return listener_.get(); } | 193 ExtensionTestMessageListener* listener() { return listener_.get(); } |
141 | 194 |
142 ActivityLogObserver* observer() { return observer_.get(); } | 195 ActivityLogObserver* observer() { return observer_.get(); } |
143 | 196 |
144 void set_expected_injections(size_t expected_injections) { | |
145 expected_injections_ = expected_injections; | |
146 } | |
147 | |
148 private: | 197 private: |
149 // The name of the last completed test; used in case of unexpected failure for | 198 // The name of the last completed test; used in case of unexpected failure for |
150 // debugging. | 199 // debugging. |
151 std::string last_test_; | 200 std::string last_test_; |
152 | 201 |
153 // The number of expected injections. | |
154 size_t expected_injections_; | |
155 | |
156 // A listener for any messages from our ad-injecting extension. | 202 // A listener for any messages from our ad-injecting extension. |
157 scoped_ptr<ExtensionTestMessageListener> listener_; | 203 scoped_ptr<ExtensionTestMessageListener> listener_; |
158 | 204 |
159 // An observer to be alerted when we detect ad injection. | 205 // An observer to be alerted when we detect ad injection. |
160 scoped_ptr<ActivityLogObserver> observer_; | 206 scoped_ptr<ActivityLogObserver> observer_; |
161 | 207 |
162 // The current stage of the test. | 208 // The current stage of the test. |
163 Stage stage_; | 209 Stage stage_; |
164 }; | 210 }; |
165 | 211 |
166 AdInjectionBrowserTest::AdInjectionBrowserTest() | 212 AdInjectionBrowserTest::AdInjectionBrowserTest() : stage_(BEFORE_RESET) { |
167 : expected_injections_(0u), stage_(BEFORE_RESET) {} | 213 } |
168 | 214 |
169 AdInjectionBrowserTest::~AdInjectionBrowserTest() {} | 215 AdInjectionBrowserTest::~AdInjectionBrowserTest() { |
| 216 } |
170 | 217 |
171 void AdInjectionBrowserTest::SetUpOnMainThread() { | 218 void AdInjectionBrowserTest::SetUpOnMainThread() { |
172 ExtensionBrowserTest::SetUpOnMainThread(); | 219 ExtensionBrowserTest::SetUpOnMainThread(); |
173 | 220 |
174 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); | 221 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
175 embedded_test_server()->RegisterRequestHandler(base::Bind(&HandleRequest)); | 222 embedded_test_server()->RegisterRequestHandler(base::Bind(&HandleRequest)); |
176 | 223 |
177 test_data_dir_ = | 224 test_data_dir_ = |
178 test_data_dir_.AppendASCII("activity_log").AppendASCII("ad_injection"); | 225 test_data_dir_.AppendASCII("activity_log").AppendASCII("ad_injection"); |
179 observer_.reset(new ActivityLogObserver(profile())); | 226 observer_.reset(new ActivityLogObserver(profile())); |
(...skipping 21 matching lines...) Expand all Loading... |
201 } | 248 } |
202 | 249 |
203 testing::AssertionResult AdInjectionBrowserTest::HandleResetBeginStage() { | 250 testing::AssertionResult AdInjectionBrowserTest::HandleResetBeginStage() { |
204 if (stage_ != BEFORE_RESET) { | 251 if (stage_ != BEFORE_RESET) { |
205 return testing::AssertionFailure() | 252 return testing::AssertionFailure() |
206 << "In incorrect stage. Last Test: " << last_test_; | 253 << "In incorrect stage. Last Test: " << last_test_; |
207 } | 254 } |
208 | 255 |
209 // Stop looking for ad injection, since some of the reset could be considered | 256 // Stop looking for ad injection, since some of the reset could be considered |
210 // ad injection. | 257 // ad injection. |
211 observer()->set_enabled(false); | 258 observer()->disable(); |
212 stage_ = RESETTING; | 259 stage_ = RESETTING; |
213 return testing::AssertionSuccess(); | 260 return testing::AssertionSuccess(); |
214 } | 261 } |
215 | 262 |
216 testing::AssertionResult AdInjectionBrowserTest::HandleResetEndStage() { | 263 testing::AssertionResult AdInjectionBrowserTest::HandleResetEndStage() { |
217 if (stage_ != RESETTING) { | 264 if (stage_ != RESETTING) { |
218 return testing::AssertionFailure() | 265 return testing::AssertionFailure() |
219 << "In incorrect stage. Last test: " << last_test_; | 266 << "In incorrect stage. Last test: " << last_test_; |
220 } | 267 } |
221 | 268 |
222 // Look for ad injection again, now that the reset is over. | 269 // Look for ad injection again, now that the reset is over. |
223 observer()->set_enabled(true); | 270 observer()->enable(); |
224 stage_ = TESTING; | 271 stage_ = TESTING; |
225 return testing::AssertionSuccess(); | 272 return testing::AssertionSuccess(); |
226 } | 273 } |
227 | 274 |
228 testing::AssertionResult AdInjectionBrowserTest::HandleTestingStage( | 275 testing::AssertionResult AdInjectionBrowserTest::HandleTestingStage( |
229 const std::string& message) { | 276 const std::string& message) { |
230 if (stage_ != TESTING) { | 277 if (stage_ != TESTING) { |
231 return testing::AssertionFailure() | 278 return testing::AssertionFailure() |
232 << "In incorrect stage. Last test: " << last_test_; | 279 << "In incorrect stage. Last test: " << last_test_; |
233 } | 280 } |
234 | 281 |
235 // The format for a testing message is: | 282 // The format for a testing message is: |
236 // "<test_name>:<expected_change>" | 283 // "<test_name>:<expected_change>" |
237 // where <test_name> is the name of the test and <expected_change> is | 284 // where <test_name> is the name of the test and <expected_change> is |
238 // either -1 for no ad injection (to test against false positives) or the | 285 // either -1 for no ad injection (to test against false positives) or the |
239 // number corresponding to ad_detection::InjectionType. | 286 // number corresponding to ad_detection::InjectionType. |
240 size_t sep = message.find(':'); | 287 size_t sep = message.find(':'); |
241 int expected_change = -1; | 288 int expected_change = -1; |
242 if (sep == std::string::npos || | 289 if (sep == std::string::npos || |
243 !base::StringToInt(message.substr(sep + 1), &expected_change) || | 290 !base::StringToInt(message.substr(sep + 1), &expected_change) || |
244 (expected_change < Action::NO_AD_INJECTION || | 291 (expected_change < Action::NO_AD_INJECTION || |
245 expected_change >= Action::NUM_INJECTION_TYPES)) { | 292 expected_change >= Action::NUM_INJECTION_TYPES)) { |
246 return testing::AssertionFailure() | 293 return testing::AssertionFailure() |
247 << "Invalid message received for testing stage: " << message; | 294 << "Invalid message received for testing stage: " << message; |
248 } | 295 } |
249 | 296 |
250 last_test_ = message.substr(0, sep); | 297 last_test_ = message.substr(0, sep); |
251 | 298 |
252 // TODO(rdevlin.cronin): Currently, we lump all kinds of ad injection into | 299 Action::InjectionType expected_injection = |
253 // one counter, because we can't differentiate (or catch all of them). Change | 300 static_cast<Action::InjectionType>(expected_change); |
254 // this when we can. | |
255 // Increment the expected change, and compare. | |
256 if (expected_change != Action::NO_AD_INJECTION) | |
257 ++expected_injections_; | |
258 std::string error; | 301 std::string error; |
259 if (expected_injections_ != observer()->injection_count()) { | 302 if (observer()->found_multiple_injections()) { |
| 303 error = "Found multiple injection types. " |
| 304 "Only one injection is expected per test."; |
| 305 } else if (expected_injection != observer()->injection_type()) { |
260 // We need these static casts, because size_t is different on different | 306 // We need these static casts, because size_t is different on different |
261 // architectures, and printf becomes unhappy. | 307 // architectures, and printf becomes unhappy. |
262 error = | 308 error = base::StringPrintf( |
263 base::StringPrintf("Injection Count Mismatch: Expected %u, Actual %u", | 309 "Incorrect Injection Found: Expected: %s, Actual: %s", |
264 static_cast<unsigned int>(expected_injections_), | 310 InjectionTypeToString(expected_injection).c_str(), |
265 static_cast<unsigned int>( | 311 InjectionTypeToString(observer()->injection_type()).c_str()); |
266 observer()->injection_count())); | |
267 } | 312 } |
268 | 313 |
269 stage_ = BEFORE_RESET; | 314 stage_ = BEFORE_RESET; |
270 | 315 |
271 if (!error.empty()) | 316 if (!error.empty()) { |
272 return testing::AssertionFailure() << error; | 317 return testing::AssertionFailure() |
| 318 << "Error in Test '" << last_test_ << "': " << error; |
| 319 } |
273 | 320 |
274 return testing::AssertionSuccess(); | 321 return testing::AssertionSuccess(); |
275 } | 322 } |
276 | 323 |
277 testing::AssertionResult AdInjectionBrowserTest::HandleJSError( | 324 testing::AssertionResult AdInjectionBrowserTest::HandleJSError( |
278 const std::string& message) { | 325 const std::string& message) { |
279 // The format for a testing message is: | 326 // The format for a testing message is: |
280 // "Testing Error:<test_name>:<error>" | 327 // "Testing Error:<test_name>:<error>" |
281 // where <test_name> is the name of the test and <error> is the error which | 328 // where <test_name> is the name of the test and <error> is the error which |
282 // was encountered. | 329 // was encountered. |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 ASSERT_TRUE(HandleResetEndStage()); | 368 ASSERT_TRUE(HandleResetEndStage()); |
322 } else if (!message.compare( | 369 } else if (!message.compare( |
323 0, strlen(kJavascriptErrorString), kJavascriptErrorString)) { | 370 0, strlen(kJavascriptErrorString), kJavascriptErrorString)) { |
324 EXPECT_TRUE(HandleJSError(message)); | 371 EXPECT_TRUE(HandleJSError(message)); |
325 } else if (message == kTestCompleteString) { | 372 } else if (message == kTestCompleteString) { |
326 break; // We're done! | 373 break; // We're done! |
327 } else { // We're in some kind of test. | 374 } else { // We're in some kind of test. |
328 EXPECT_TRUE(HandleTestingStage(message)); | 375 EXPECT_TRUE(HandleTestingStage(message)); |
329 } | 376 } |
330 | 377 |
331 // We set the expected injections to be whatever they actually are so that | |
332 // we only fail one test, instead of all subsequent tests. | |
333 set_expected_injections(observer()->injection_count()); | |
334 | |
335 // In all cases (except for "Test Complete", in which case we already | 378 // In all cases (except for "Test Complete", in which case we already |
336 // break'ed), we reply with a continue message. | 379 // break'ed), we reply with a continue message. |
337 listener()->Reply("Continue"); | 380 listener()->Reply("Continue"); |
338 listener()->Reset(); | 381 listener()->Reset(); |
339 } | 382 } |
340 } | 383 } |
341 | 384 |
342 // TODO(rdevlin.cronin): We test a good amount of ways of injecting ads with | 385 // TODO(rdevlin.cronin): We test a good amount of ways of injecting ads with |
343 // the above test, but more is better in testing. | 386 // the above test, but more is better in testing. |
344 // See crbug.com/357204. | 387 // See crbug.com/357204. |
345 | 388 |
346 } // namespace extensions | 389 } // namespace extensions |
OLD | NEW |