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
3 // found in the LICENSE file.
5 #include "chrome/browser/safe_browsing/incident_reporting_service.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/lazy_instance.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/test/test_simple_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/threading/thread_local.h"
17 #include "chrome/browser/prefs/browser_prefs.h"
18 #include "chrome/browser/safe_browsing/incident_report_uploader.h"
19 #include "chrome/browser/safe_browsing/last_download_finder.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/common/safe_browsing/csd.pb.h"
22 #include "chrome/test/base/testing_browser_process.h"
23 #include "chrome/test/base/testing_pref_service_syncable.h"
24 #include "chrome/test/base/testing_profile.h"
25 #include "chrome/test/base/testing_profile_manager.h"
26 #include "net/url_request/url_request_context_getter.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 // A test fixture that sets up a test task runner and makes it the thread's
30 // runner. The fixture implements a fake envrionment data collector and a fake
32 class IncidentReportingServiceTest : public testing::Test {
34 // An IRS class that allows a test harness to provide a fake environment
35 // collector and report uploader via callbacks.
36 class TestIncidentReportingService
37 : public safe_browsing::IncidentReportingService {
39 typedef base::Callback<void(Profile*)> PreProfileAddCallback;
41 typedef base::Callback<
42 void(safe_browsing::ClientIncidentReport_EnvironmentData*)>
43 CollectEnvironmentCallback;
45 typedef base::Callback<scoped_ptr<safe_browsing::LastDownloadFinder>(
46 const safe_browsing::LastDownloadFinder::LastDownloadCallback&
47 callback)> CreateDownloadFinderCallback;
49 typedef base::Callback<scoped_ptr<safe_browsing::IncidentReportUploader>(
50 const safe_browsing::IncidentReportUploader::OnResultCallback&,
51 const safe_browsing::ClientIncidentReport& report)> StartUploadCallback;
53 TestIncidentReportingService(
54 const scoped_refptr<base::TaskRunner>& task_runner,
55 const PreProfileAddCallback& pre_profile_add_callback,
56 const CollectEnvironmentCallback& collect_environment_callback,
57 const CreateDownloadFinderCallback& create_download_finder_callback,
58 const StartUploadCallback& start_upload_callback)
59 : IncidentReportingService(NULL, NULL),
60 pre_profile_add_callback_(pre_profile_add_callback),
61 collect_environment_callback_(collect_environment_callback),
62 create_download_finder_callback_(create_download_finder_callback),
63 start_upload_callback_(start_upload_callback) {
64 SetCollectEnvironmentHook(&CollectEnvironmentData, task_runner);
65 test_instance_.Get().Set(this);
68 virtual ~TestIncidentReportingService() { test_instance_.Get().Set(NULL); }
71 virtual void OnProfileAdded(Profile* profile) OVERRIDE {
72 pre_profile_add_callback_.Run(profile);
73 safe_browsing::IncidentReportingService::OnProfileAdded(profile);
76 virtual scoped_ptr<safe_browsing::LastDownloadFinder> CreateDownloadFinder(
77 const safe_browsing::LastDownloadFinder::LastDownloadCallback& callback)
79 return create_download_finder_callback_.Run(callback);
82 virtual scoped_ptr<safe_browsing::IncidentReportUploader> StartReportUpload(
83 const safe_browsing::IncidentReportUploader::OnResultCallback& callback,
84 const scoped_refptr<net::URLRequestContextGetter>&
85 request_context_getter,
86 const safe_browsing::ClientIncidentReport& report) OVERRIDE {
87 return start_upload_callback_.Run(callback, report);
91 static TestIncidentReportingService& current() {
92 return *test_instance_.Get().Get();
95 static void CollectEnvironmentData(
96 safe_browsing::ClientIncidentReport_EnvironmentData* data) {
97 current().collect_environment_callback_.Run(data);
100 static base::LazyInstance<base::ThreadLocalPointer<
101 TestIncidentReportingService> >::Leaky test_instance_;
103 PreProfileAddCallback pre_profile_add_callback_;
104 CollectEnvironmentCallback collect_environment_callback_;
105 CreateDownloadFinderCallback create_download_finder_callback_;
106 StartUploadCallback start_upload_callback_;
109 // A type for specifying whether or not a profile created by CreateProfile
110 // participates in safe browsing.
111 enum SafeBrowsingDisposition {
112 SAFE_BROWSING_OPT_OUT,
113 SAFE_BROWSING_OPT_IN,
116 // A type for specifying the action to be taken by the test fixture during
117 // profile initialization (before NOTIFICATION_PROFILE_ADDED is sent).
118 enum OnProfileAdditionAction {
119 ON_PROFILE_ADDITION_NO_ACTION,
120 ON_PROFILE_ADDITION_ADD_INCIDENT, // Add an incident to the service.
123 // A type for specifying the action to be taken by the test fixture when the
124 // service creates a LastDownloadFinder.
125 enum OnCreateDownloadFinderAction {
126 // Post a task that reports a download.
127 ON_CREATE_DOWNLOAD_FINDER_DOWNLOAD_FOUND,
128 // Post a task that reports no downloads found.
129 ON_CREATE_DOWNLOAD_FINDER_NO_DOWNLOADS,
130 // Immediately return due to a lack of eligible profiles.
131 ON_CREATE_DOWNLOAD_FINDER_NO_PROFILES,
134 static const int64 kIncidentTimeMsec;
135 static const char kFakeOsName[];
136 static const char kFakeDownloadToken[];
138 IncidentReportingServiceTest()
139 : task_runner_(new base::TestSimpleTaskRunner),
140 thread_task_runner_handle_(task_runner_),
141 profile_manager_(TestingBrowserProcess::GetGlobal()),
142 instance_(new TestIncidentReportingService(
144 base::Bind(&IncidentReportingServiceTest::PreProfileAdd,
145 base::Unretained(this)),
146 base::Bind(&IncidentReportingServiceTest::CollectEnvironmentData,
147 base::Unretained(this)),
148 base::Bind(&IncidentReportingServiceTest::CreateDownloadFinder,
149 base::Unretained(this)),
150 base::Bind(&IncidentReportingServiceTest::StartUpload,
151 base::Unretained(this)))),
152 on_create_download_finder_action_(
153 ON_CREATE_DOWNLOAD_FINDER_DOWNLOAD_FOUND),
154 upload_result_(safe_browsing::IncidentReportUploader::UPLOAD_SUCCESS),
155 environment_collected_(),
156 download_finder_created_(),
157 download_finder_destroyed_(),
158 uploader_destroyed_() {}
160 virtual void SetUp() OVERRIDE {
161 testing::Test::SetUp();
162 ASSERT_TRUE(profile_manager_.SetUp());
165 // Sets the action to be taken by the test fixture when the service creates a
166 // LastDownloadFinder.
167 void SetCreateDownloadFinderAction(OnCreateDownloadFinderAction action) {
168 on_create_download_finder_action_ = action;
171 // Creates and returns a profile (owned by the profile manager) with or
172 // without safe browsing enabled. An incident will be created within
173 // PreProfileAdd if requested.
174 TestingProfile* CreateProfile(const std::string& profile_name,
175 SafeBrowsingDisposition safe_browsing_opt_in,
176 OnProfileAdditionAction on_addition_action) {
177 // Create prefs for the profile with safe browsing enabled or not.
178 scoped_ptr<TestingPrefServiceSyncable> prefs(
179 new TestingPrefServiceSyncable);
180 chrome::RegisterUserProfilePrefs(prefs->registry());
181 prefs->SetBoolean(prefs::kSafeBrowsingEnabled,
182 safe_browsing_opt_in == SAFE_BROWSING_OPT_IN);
184 // Remember whether or not to create an incident.
185 profile_properties_[profile_name].on_addition_action = on_addition_action;
188 return profile_manager_.CreateTestingProfile(
190 prefs.PassAs<PrefServiceSyncable>(),
191 base::ASCIIToUTF16(profile_name),
192 0, // avatar_id (unused)
193 std::string(), // supervised_user_id (unused)
194 TestingProfile::TestingFactories());
197 // Configures a callback to run when the next upload is started that will post
198 // a task to delete the profile. This task will run before the upload
200 void DeleteProfileOnUpload(Profile* profile) {
201 ASSERT_TRUE(on_start_upload_callback_.is_null());
202 on_start_upload_callback_ =
203 base::Bind(&IncidentReportingServiceTest::DelayedDeleteProfile,
204 base::Unretained(this),
208 // Returns an incident suitable for testing.
209 scoped_ptr<safe_browsing::ClientIncidentReport_IncidentData>
211 scoped_ptr<safe_browsing::ClientIncidentReport_IncidentData> incident(
212 new safe_browsing::ClientIncidentReport_IncidentData());
213 incident->set_incident_time_msec(kIncidentTimeMsec);
214 incident->mutable_tracked_preference();
215 return incident.Pass();
218 // Adds a test incident to the service.
219 void AddTestIncident(Profile* profile) {
220 instance_->GetAddIncidentCallback(profile).Run(MakeTestIncident().Pass());
223 // Confirms that the test incident was uploaded by the service, then clears
224 // the instance for subsequent incidents.
225 void ExpectTestIncidentUploaded() {
226 ASSERT_TRUE(uploaded_report_);
227 ASSERT_EQ(1, uploaded_report_->incident_size());
228 ASSERT_TRUE(uploaded_report_->incident(0).has_incident_time_msec());
229 ASSERT_EQ(kIncidentTimeMsec,
230 uploaded_report_->incident(0).incident_time_msec());
231 ASSERT_TRUE(uploaded_report_->has_environment());
232 ASSERT_TRUE(uploaded_report_->environment().has_os());
233 ASSERT_TRUE(uploaded_report_->environment().os().has_os_name());
234 ASSERT_EQ(std::string(kFakeOsName),
235 uploaded_report_->environment().os().os_name());
236 ASSERT_EQ(std::string(kFakeDownloadToken),
237 uploaded_report_->download().token());
239 uploaded_report_.reset();
242 void AssertNoUpload() { ASSERT_FALSE(uploaded_report_); }
244 bool HasCollectedEnvironmentData() const { return environment_collected_; }
245 bool HasCreatedDownloadFinder() const { return download_finder_created_; }
246 bool DownloadFinderDestroyed() const { return download_finder_destroyed_; }
247 bool UploaderDestroyed() const { return uploader_destroyed_; }
249 scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
250 base::ThreadTaskRunnerHandle thread_task_runner_handle_;
251 TestingProfileManager profile_manager_;
252 scoped_ptr<safe_browsing::IncidentReportingService> instance_;
253 base::Closure on_start_upload_callback_;
254 OnCreateDownloadFinderAction on_create_download_finder_action_;
255 safe_browsing::IncidentReportUploader::Result upload_result_;
256 bool environment_collected_;
257 bool download_finder_created_;
258 scoped_ptr<safe_browsing::ClientIncidentReport> uploaded_report_;
259 bool download_finder_destroyed_;
260 bool uploader_destroyed_;
263 // A fake IncidentReportUploader that posts a task to provide a given response
264 // back to the incident reporting service. It also reports back to the test
265 // harness via a closure when it is deleted by the incident reporting service.
266 class FakeUploader : public safe_browsing::IncidentReportUploader {
269 const base::Closure& on_deleted,
270 const safe_browsing::IncidentReportUploader::OnResultCallback& callback,
271 safe_browsing::IncidentReportUploader::Result result)
272 : safe_browsing::IncidentReportUploader(callback),
273 on_deleted_(on_deleted),
275 // Post a task that will provide the response.
276 base::ThreadTaskRunnerHandle::Get()->PostTask(
278 base::Bind(&FakeUploader::FinishUpload, base::Unretained(this)));
280 virtual ~FakeUploader() { on_deleted_.Run(); }
283 void FinishUpload() {
284 // Callbacks have a tendency to delete the uploader, so no touching
285 // anything after this.
286 callback_.Run(result_,
287 scoped_ptr<safe_browsing::ClientIncidentResponse>());
290 base::Closure on_deleted_;
291 safe_browsing::IncidentReportUploader::Result result_;
293 DISALLOW_COPY_AND_ASSIGN(FakeUploader);
296 class FakeDownloadFinder : public safe_browsing::LastDownloadFinder {
298 static scoped_ptr<safe_browsing::LastDownloadFinder> Create(
299 const base::Closure& on_deleted,
300 scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails>
302 const safe_browsing::LastDownloadFinder::LastDownloadCallback&
304 // Post a task to run the callback.
305 base::ThreadTaskRunnerHandle::Get()->PostTask(
306 FROM_HERE, base::Bind(callback, base::Passed(&download)));
307 return scoped_ptr<safe_browsing::LastDownloadFinder>(
308 new FakeDownloadFinder(on_deleted));
311 virtual ~FakeDownloadFinder() { on_deleted_.Run(); }
314 explicit FakeDownloadFinder(const base::Closure& on_deleted)
315 : on_deleted_(on_deleted) {}
317 base::Closure on_deleted_;
319 DISALLOW_COPY_AND_ASSIGN(FakeDownloadFinder);
322 // Properties for a profile that impact the behavior of the test.
323 struct ProfileProperties {
324 ProfileProperties() : on_addition_action(ON_PROFILE_ADDITION_NO_ACTION) {}
326 // The action taken by the test fixture during profile initialization
327 // (before NOTIFICATION_PROFILE_ADDED is sent).
328 OnProfileAdditionAction on_addition_action;
331 // Returns the name of a profile as provided to CreateProfile.
332 static std::string GetProfileName(Profile* profile) {
333 // Cannot reliably use profile->GetProfileName() since the test needs the
334 // name before the profile manager sets it (which happens after profile
336 return profile->GetPath().BaseName().AsUTF8Unsafe();
339 // Posts a task to delete the profile.
340 void DelayedDeleteProfile(Profile* profile) {
341 base::ThreadTaskRunnerHandle::Get()->PostTask(
343 base::Bind(&TestingProfileManager::DeleteTestingProfile,
344 base::Unretained(&profile_manager_),
345 GetProfileName(profile)));
348 // A callback run by the test fixture when a profile is added. An incident
350 void PreProfileAdd(Profile* profile) {
351 // The instance must have already been created.
352 ASSERT_TRUE(instance_);
353 // Add a test incident to the service if requested.
354 switch (profile_properties_[GetProfileName(profile)].on_addition_action) {
355 case ON_PROFILE_ADDITION_ADD_INCIDENT:
356 AddTestIncident(profile);
360 ON_PROFILE_ADDITION_NO_ACTION,
361 profile_properties_[GetProfileName(profile)].on_addition_action);
366 // A fake CollectEnvironmentData implementation invoked by the service during
368 void CollectEnvironmentData(
369 safe_browsing::ClientIncidentReport_EnvironmentData* data) {
371 static_cast<safe_browsing::ClientIncidentReport_EnvironmentData*>(NULL),
373 data->mutable_os()->set_os_name(kFakeOsName);
374 environment_collected_ = true;
377 // A fake CreateDownloadFinder implementation invoked by the service during
379 scoped_ptr<safe_browsing::LastDownloadFinder> CreateDownloadFinder(
380 const safe_browsing::LastDownloadFinder::LastDownloadCallback& callback) {
381 download_finder_created_ = true;
382 scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails> download;
383 if (on_create_download_finder_action_ ==
384 ON_CREATE_DOWNLOAD_FINDER_NO_PROFILES) {
385 return scoped_ptr<safe_browsing::LastDownloadFinder>();
387 if (on_create_download_finder_action_ ==
388 ON_CREATE_DOWNLOAD_FINDER_DOWNLOAD_FOUND) {
389 download.reset(new safe_browsing::ClientIncidentReport_DownloadDetails);
390 download->set_token(kFakeDownloadToken);
392 return scoped_ptr<safe_browsing::LastDownloadFinder>(
393 FakeDownloadFinder::Create(
394 base::Bind(&IncidentReportingServiceTest::OnDownloadFinderDestroyed,
395 base::Unretained(this)),
400 // A fake StartUpload implementation invoked by the service during operation.
401 scoped_ptr<safe_browsing::IncidentReportUploader> StartUpload(
402 const safe_browsing::IncidentReportUploader::OnResultCallback& callback,
403 const safe_browsing::ClientIncidentReport& report) {
404 // Remember the report that is being uploaded.
405 uploaded_report_.reset(new safe_browsing::ClientIncidentReport(report));
406 // Run and clear the OnStartUpload callback, if provided.
407 if (!on_start_upload_callback_.is_null()) {
408 on_start_upload_callback_.Run();
409 on_start_upload_callback_ = base::Closure();
411 return scoped_ptr<safe_browsing::IncidentReportUploader>(
414 &IncidentReportingServiceTest::OnUploaderDestroyed,
415 base::Unretained(this)),
417 upload_result_)).Pass();
420 void OnDownloadFinderDestroyed() { download_finder_destroyed_ = true; }
421 void OnUploaderDestroyed() { uploader_destroyed_ = true; }
423 // A mapping of profile name to its corresponding properties.
424 std::map<std::string, ProfileProperties> profile_properties_;
428 base::LazyInstance<base::ThreadLocalPointer<
429 IncidentReportingServiceTest::TestIncidentReportingService> >::Leaky
430 IncidentReportingServiceTest::TestIncidentReportingService::test_instance_ =
431 LAZY_INSTANCE_INITIALIZER;
433 const int64 IncidentReportingServiceTest::kIncidentTimeMsec = 47LL;
434 const char IncidentReportingServiceTest::kFakeOsName[] = "fakedows";
435 const char IncidentReportingServiceTest::kFakeDownloadToken[] = "fakedlt";
437 // Tests that an incident added during profile initialization when safe browsing
438 // is on is uploaded.
439 TEST_F(IncidentReportingServiceTest, AddIncident) {
441 "profile1", SAFE_BROWSING_OPT_IN, ON_PROFILE_ADDITION_ADD_INCIDENT);
443 // Let all tasks run.
444 task_runner_->RunUntilIdle();
446 // Verify that environment collection took place.
447 EXPECT_TRUE(HasCollectedEnvironmentData());
449 // Verify that the most recent download was looked for.
450 EXPECT_TRUE(HasCreatedDownloadFinder());
452 // Verify that report upload took place and contained the incident,
453 // environment data, and download details.
454 ExpectTestIncidentUploaded();
456 // Verify that the download finder and the uploader were destroyed.
457 ASSERT_TRUE(DownloadFinderDestroyed());
458 ASSERT_TRUE(UploaderDestroyed());
461 // Tests that an incident added during profile initialization when safe browsing
462 // is off is not uploaded.
463 TEST_F(IncidentReportingServiceTest, NoSafeBrowsing) {
464 // Create the profile, thereby causing the test to begin.
466 "profile1", SAFE_BROWSING_OPT_OUT, ON_PROFILE_ADDITION_ADD_INCIDENT);
468 // Let all tasks run.
469 task_runner_->RunUntilIdle();
471 // Verify that no report upload took place.
475 // Tests that no incident report is uploaded if there is no recent download.
476 TEST_F(IncidentReportingServiceTest, NoDownloadNoUpload) {
477 // Tell the fixture to return no downloads found.
478 SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_NO_DOWNLOADS);
480 // Create the profile, thereby causing the test to begin.
482 "profile1", SAFE_BROWSING_OPT_IN, ON_PROFILE_ADDITION_ADD_INCIDENT);
484 // Let all tasks run.
485 task_runner_->RunUntilIdle();
487 // Verify that the download finder was run but that no report upload took
489 EXPECT_TRUE(HasCreatedDownloadFinder());
491 EXPECT_TRUE(DownloadFinderDestroyed());
494 // Tests that no incident report is uploaded if there is no recent download.
495 TEST_F(IncidentReportingServiceTest, NoProfilesNoUpload) {
496 // Tell the fixture to pretend there are no profiles eligible for finding
498 SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_NO_PROFILES);
500 // Create the profile, thereby causing the test to begin.
502 "profile1", SAFE_BROWSING_OPT_IN, ON_PROFILE_ADDITION_ADD_INCIDENT);
504 // Let all tasks run.
505 task_runner_->RunUntilIdle();
507 // Verify that the download finder was run but that no report upload took
509 EXPECT_TRUE(HasCreatedDownloadFinder());
511 // Although CreateDownloadFinder was called, no instance was returned so there
512 // is nothing to have been destroyed.
513 EXPECT_FALSE(DownloadFinderDestroyed());
516 // Tests that an incident added after upload is not uploaded again.
517 TEST_F(IncidentReportingServiceTest, OnlyOneUpload) {
518 // Create the profile, thereby causing the test to begin.
519 Profile* profile = CreateProfile(
520 "profile1", SAFE_BROWSING_OPT_IN, ON_PROFILE_ADDITION_ADD_INCIDENT);
522 // Let all tasks run.
523 task_runner_->RunUntilIdle();
525 // Verify that report upload took place and contained the incident and
527 ExpectTestIncidentUploaded();
529 // Add the incident to the service again.
530 AddTestIncident(profile);
532 // Let all tasks run.
533 task_runner_->RunUntilIdle();
535 // Verify that no additional report upload took place.
539 // Tests that the same incident added for two different profiles in sequence
540 // results in two uploads.
541 TEST_F(IncidentReportingServiceTest, TwoProfilesTwoUploads) {
542 // Create the profile, thereby causing the test to begin.
544 "profile1", SAFE_BROWSING_OPT_IN, ON_PROFILE_ADDITION_ADD_INCIDENT);
546 // Let all tasks run.
547 task_runner_->RunUntilIdle();
549 // Verify that report upload took place and contained the incident and
551 ExpectTestIncidentUploaded();
553 // Create a second profile with its own incident on addition.
555 "profile2", SAFE_BROWSING_OPT_IN, ON_PROFILE_ADDITION_ADD_INCIDENT);
557 // Let all tasks run.
558 task_runner_->RunUntilIdle();
560 // Verify that a second report upload took place.
561 ExpectTestIncidentUploaded();
564 // Tests that an upload succeeds if the profile is destroyed while it is
566 TEST_F(IncidentReportingServiceTest, ProfileDestroyedDuringUpload) {
567 // Create a profile for which an incident will be added.
568 Profile* profile = CreateProfile(
569 "profile1", SAFE_BROWSING_OPT_IN, ON_PROFILE_ADDITION_ADD_INCIDENT);
571 // Hook up a callback to run when the upload is started that will post a task
572 // to delete the profile. This task will run before the upload finishes.
573 DeleteProfileOnUpload(profile);
575 // Let all tasks run.
576 task_runner_->RunUntilIdle();
578 // Verify that report upload took place and contained the incident and
580 ExpectTestIncidentUploaded();
582 // The lack of a crash indicates that the deleted profile was not accessed by
583 // the service while handling the upload response.
587 // Shutdown during processing
588 // environment colection taking longer than incident delay timer
589 // environment colection taking longer than incident delay timer, and then
590 // another incident arriving