1 // Copyright 2013 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 "base/cancelable_callback.h"
6 #include "base/command_line.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/run_loop.h"
9 #include "base/strings/string_split.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/test/simple_test_clock.h"
13 #include "base/test/test_timeouts.h"
14 #include "chrome/browser/extensions/activity_log/activity_log.h"
15 #include "chrome/browser/extensions/activity_log/counting_policy.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/test_extension_system.h"
18 #include "chrome/common/chrome_constants.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
21 #include "chrome/test/base/testing_profile.h"
22 #include "content/public/test/test_browser_thread_bundle.h"
23 #include "extensions/common/extension_builder.h"
24 #include "sql/statement.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 #if defined(OS_CHROMEOS)
28 #include "chrome/browser/chromeos/login/user_manager.h"
29 #include "chrome/browser/chromeos/settings/cros_settings.h"
30 #include "chrome/browser/chromeos/settings/device_settings_service.h"
33 using content::BrowserThread;
35 namespace extensions {
37 class CountingPolicyTest : public testing::Test {
40 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
41 saved_cmdline_(CommandLine::NO_PROGRAM) {
42 #if defined OS_CHROMEOS
43 test_user_manager_.reset(new chromeos::ScopedTestUserManager());
45 CommandLine command_line(CommandLine::NO_PROGRAM);
46 saved_cmdline_ = *CommandLine::ForCurrentProcess();
47 profile_.reset(new TestingProfile());
48 CommandLine::ForCurrentProcess()->AppendSwitch(
49 switches::kEnableExtensionActivityLogging);
50 extension_service_ = static_cast<TestExtensionSystem*>(
51 ExtensionSystem::Get(profile_.get()))->CreateExtensionService
52 (&command_line, base::FilePath(), false);
55 virtual ~CountingPolicyTest() {
56 #if defined OS_CHROMEOS
57 test_user_manager_.reset();
59 base::RunLoop().RunUntilIdle();
61 base::RunLoop().RunUntilIdle();
62 // Restore the original command line and undo the affects of SetUp().
63 *CommandLine::ForCurrentProcess() = saved_cmdline_;
66 // Wait for the task queue for the specified thread to empty.
67 void WaitOnThread(const BrowserThread::ID& thread) {
68 BrowserThread::PostTaskAndReply(
71 base::Bind(&base::DoNothing),
72 base::MessageLoop::current()->QuitClosure());
73 base::MessageLoop::current()->Run();
76 // A wrapper function for CheckReadFilteredData, so that we don't need to
77 // enter empty string values for parameters we don't care about.
79 ActivityLogDatabasePolicy* policy,
80 const std::string& extension_id,
82 const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
83 CheckReadFilteredData(
84 policy, extension_id, Action::ACTION_ANY, "", "", "", day, checker);
87 // A helper function to call ReadFilteredData on a policy object and wait for
88 // the results to be processed.
89 void CheckReadFilteredData(
90 ActivityLogDatabasePolicy* policy,
91 const std::string& extension_id,
92 const Action::ActionType type,
93 const std::string& api_name,
94 const std::string& page_url,
95 const std::string& arg_url,
97 const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
98 // Submit a request to the policy to read back some data, and call the
99 // checker function when results are available. This will happen on the
101 policy->ReadFilteredData(
108 base::Bind(&CountingPolicyTest::CheckWrapper,
110 base::MessageLoop::current()->QuitClosure()));
112 // Set up a timeout for receiving results; if we haven't received anything
113 // when the timeout triggers then assume that the test is broken.
114 base::CancelableClosure timeout(
115 base::Bind(&CountingPolicyTest::TimeoutCallback));
116 base::MessageLoop::current()->PostDelayedTask(
117 FROM_HERE, timeout.callback(), TestTimeouts::action_timeout());
119 // Wait for results; either the checker or the timeout callbacks should
120 // cause the main loop to exit.
121 base::MessageLoop::current()->Run();
126 // A helper function which verifies that the string_ids and url_ids tables in
127 // the database have the specified sizes.
128 static void CheckStringTableSizes(CountingPolicy* policy,
131 sql::Connection* db = policy->GetDatabaseConnection();
132 sql::Statement statement1(db->GetCachedStatement(
133 sql::StatementID(SQL_FROM_HERE), "SELECT COUNT(*) FROM string_ids"));
134 ASSERT_TRUE(statement1.Step());
135 ASSERT_EQ(string_size, statement1.ColumnInt(0));
137 sql::Statement statement2(db->GetCachedStatement(
138 sql::StatementID(SQL_FROM_HERE), "SELECT COUNT(*) FROM url_ids"));
139 ASSERT_TRUE(statement2.Step());
140 ASSERT_EQ(url_size, statement2.ColumnInt(0));
143 // Checks that the number of queued actions to be written out does not exceed
144 // kSizeThresholdForFlush. Runs on the database thread.
145 static void CheckQueueSize(CountingPolicy* policy) {
146 // This should be updated if kSizeThresholdForFlush in activity_database.cc
148 ASSERT_LE(policy->queued_actions_.size(), 200U);
151 static void CheckWrapper(
152 const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker,
153 const base::Closure& done,
154 scoped_ptr<Action::ActionVector> results) {
155 checker.Run(results.Pass());
159 static void TimeoutCallback() {
160 base::MessageLoop::current()->QuitWhenIdle();
161 FAIL() << "Policy test timed out waiting for results";
164 static void RetrieveActions_FetchFilteredActions0(
165 scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
166 ASSERT_EQ(0, static_cast<int>(i->size()));
169 static void RetrieveActions_FetchFilteredActions1(
170 scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
171 ASSERT_EQ(1, static_cast<int>(i->size()));
174 static void RetrieveActions_FetchFilteredActions2(
175 scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
176 ASSERT_EQ(2, static_cast<int>(i->size()));
179 static void RetrieveActions_FetchFilteredActions300(
180 scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
181 ASSERT_EQ(300, static_cast<int>(i->size()));
184 static void Arguments_Stripped(scoped_ptr<Action::ActionVector> i) {
185 scoped_refptr<Action> last = i->front();
186 CheckAction(*last, "odlameecjipmbmbejkplpemijjgpljce",
187 Action::ACTION_API_CALL, "extension.connect",
188 "[\"hello\",\"world\"]", "", "", "", 1);
191 static void Arguments_GetSinglesAction(
192 scoped_ptr<Action::ActionVector> actions) {
193 ASSERT_EQ(1, static_cast<int>(actions->size()));
194 CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
195 "", "http://www.google.com/", "", "", 1);
198 static void Arguments_GetTodaysActions(
199 scoped_ptr<Action::ActionVector> actions) {
200 ASSERT_EQ(3, static_cast<int>(actions->size()));
201 CheckAction(*actions->at(0), "punky", Action::ACTION_API_CALL, "brewster",
203 CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
204 "", "http://www.google.com/", "", "", 1);
205 CheckAction(*actions->at(2), "punky", Action::ACTION_API_CALL,
206 "extension.sendMessage", "[\"not\",\"stripped\"]", "", "", "",
210 static void Arguments_GetOlderActions(
211 scoped_ptr<Action::ActionVector> actions) {
212 ASSERT_EQ(2, static_cast<int>(actions->size()));
213 CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
214 "", "http://www.google.com/", "", "", 1);
215 CheckAction(*actions->at(1), "punky", Action::ACTION_API_CALL, "brewster",
219 static void Arguments_CheckMergeCount(
221 scoped_ptr<Action::ActionVector> actions) {
223 ASSERT_EQ(1u, actions->size());
224 CheckAction(*actions->at(0), "punky", Action::ACTION_API_CALL, "brewster",
225 "", "", "", "", count);
227 ASSERT_EQ(0u, actions->size());
231 static void Arguments_CheckMergeCountAndTime(
233 const base::Time& time,
234 scoped_ptr<Action::ActionVector> actions) {
236 ASSERT_EQ(1u, actions->size());
237 CheckAction(*actions->at(0), "punky", Action::ACTION_API_CALL, "brewster",
238 "", "", "", "", count);
239 ASSERT_EQ(time, actions->at(0)->time());
241 ASSERT_EQ(0u, actions->size());
245 static void AllURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
246 ASSERT_EQ(2, static_cast<int>(actions->size()));
247 CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
249 CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
253 static void SomeURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
254 // These will be in the vector in reverse time order.
255 ASSERT_EQ(5, static_cast<int>(actions->size()));
256 CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
257 "", "http://www.google.com/", "Google",
258 "http://www.args-url.com/", 1);
259 CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
260 "", "http://www.google.com/", "Google", "", 1);
261 CheckAction(*actions->at(2), "punky", Action::ACTION_DOM_ACCESS, "lets",
263 CheckAction(*actions->at(3), "punky", Action::ACTION_DOM_ACCESS, "lets",
264 "", "", "", "http://www.google.com/", 1);
265 CheckAction(*actions->at(4), "punky", Action::ACTION_DOM_ACCESS, "lets",
269 static void CheckDuplicates(scoped_ptr<Action::ActionVector> actions) {
270 ASSERT_EQ(2u, actions->size());
272 for (size_t i = 0; i < actions->size(); i++) {
273 total_count += actions->at(i)->count();
275 ASSERT_EQ(3, total_count);
278 static void CheckAction(const Action& action,
279 const std::string& expected_id,
280 const Action::ActionType& expected_type,
281 const std::string& expected_api_name,
282 const std::string& expected_args_str,
283 const std::string& expected_page_url,
284 const std::string& expected_page_title,
285 const std::string& expected_arg_url,
286 int expected_count) {
287 ASSERT_EQ(expected_id, action.extension_id());
288 ASSERT_EQ(expected_type, action.action_type());
289 ASSERT_EQ(expected_api_name, action.api_name());
290 ASSERT_EQ(expected_args_str,
291 ActivityLogPolicy::Util::Serialize(action.args()));
292 ASSERT_EQ(expected_page_url, action.SerializePageUrl());
293 ASSERT_EQ(expected_page_title, action.page_title());
294 ASSERT_EQ(expected_arg_url, action.SerializeArgUrl());
295 ASSERT_EQ(expected_count, action.count());
296 ASSERT_NE(-1, action.action_id());
299 // A helper function initializes the policy with a number of actions, calls
300 // RemoveActions on a policy object and then checks the result of the
302 void CheckRemoveActions(
303 ActivityLogDatabasePolicy* policy,
304 const std::vector<int64>& action_ids,
305 const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
307 // Use a mock clock to ensure that events are not recorded on the wrong day
308 // when the test is run close to local midnight.
309 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
310 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
311 base::TimeDelta::FromHours(12));
312 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
314 // Record some actions
315 scoped_refptr<Action> action =
317 mock_clock->Now() - base::TimeDelta::FromMinutes(40),
318 Action::ACTION_DOM_ACCESS,
320 action->mutable_args()->AppendString("vamoose1");
321 action->set_page_url(GURL("http://www.google1.com"));
322 action->set_page_title("Google1");
323 action->set_arg_url(GURL("http://www.args-url1.com"));
324 policy->ProcessAction(action);
325 // Record the same action twice, so there are multiple entries in the
327 policy->ProcessAction(action);
329 action = new Action("punky2",
330 mock_clock->Now() - base::TimeDelta::FromMinutes(30),
331 Action::ACTION_API_CALL,
333 action->mutable_args()->AppendString("vamoose2");
334 action->set_page_url(GURL("http://www.google2.com"));
335 action->set_page_title("Google2");
336 action->set_arg_url(GURL("http://www.args-url2.com"));
337 policy->ProcessAction(action);
338 // Record the same action twice, so there are multiple entries in the
340 policy->ProcessAction(action);
342 // Submit a request to delete actions.
343 policy->RemoveActions(action_ids);
345 // Check the result of the deletion. The checker function gets all
346 // activities in the database.
347 CheckReadData(policy, "", -1, checker);
350 policy->DeleteDatabase();
353 static void AllActionsDeleted(scoped_ptr<Action::ActionVector> actions) {
354 ASSERT_EQ(0, static_cast<int>(actions->size()));
357 static void NoActionsDeleted(scoped_ptr<Action::ActionVector> actions) {
358 // These will be in the vector in reverse time order.
359 ASSERT_EQ(2, static_cast<int>(actions->size()));
360 CheckAction(*actions->at(0),
362 Action::ACTION_API_CALL,
365 "http://www.google2.com/",
367 "http://www.args-url2.com/",
369 ASSERT_EQ(2, actions->at(0)->action_id());
370 CheckAction(*actions->at(1),
372 Action::ACTION_DOM_ACCESS,
375 "http://www.google1.com/",
377 "http://www.args-url1.com/",
379 ASSERT_EQ(1, actions->at(1)->action_id());
382 static void Action1Deleted(scoped_ptr<Action::ActionVector> actions) {
383 // These will be in the vector in reverse time order.
384 ASSERT_EQ(1, static_cast<int>(actions->size()));
385 CheckAction(*actions->at(0),
387 Action::ACTION_API_CALL,
390 "http://www.google2.com/",
392 "http://www.args-url2.com/",
394 ASSERT_EQ(2, actions->at(0)->action_id());
397 static void Action2Deleted(scoped_ptr<Action::ActionVector> actions) {
398 // These will be in the vector in reverse time order.
399 ASSERT_EQ(1, static_cast<int>(actions->size()));
400 CheckAction(*actions->at(0),
402 Action::ACTION_DOM_ACCESS,
405 "http://www.google1.com/",
407 "http://www.args-url1.com/",
409 ASSERT_EQ(1, actions->at(0)->action_id());
413 ExtensionService* extension_service_;
414 scoped_ptr<TestingProfile> profile_;
415 content::TestBrowserThreadBundle thread_bundle_;
416 // Used to preserve a copy of the original command line.
417 // The test framework will do this itself as well. However, by then,
418 // it is too late to call ActivityLog::RecomputeLoggingIsEnabled() in
420 CommandLine saved_cmdline_;
422 #if defined OS_CHROMEOS
423 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
424 chromeos::ScopedTestCrosSettings test_cros_settings_;
425 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
429 TEST_F(CountingPolicyTest, Construct) {
430 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
432 scoped_refptr<const Extension> extension =
434 .SetManifest(DictionaryBuilder()
435 .Set("name", "Test extension")
436 .Set("version", "1.0.0")
437 .Set("manifest_version", 2))
439 extension_service_->AddExtension(extension.get());
440 scoped_ptr<base::ListValue> args(new base::ListValue());
441 scoped_refptr<Action> action = new Action(extension->id(),
443 Action::ACTION_API_CALL,
445 action->set_args(args.Pass());
446 policy->ProcessAction(action);
450 TEST_F(CountingPolicyTest, LogWithStrippedArguments) {
451 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
453 scoped_refptr<const Extension> extension =
455 .SetManifest(DictionaryBuilder()
456 .Set("name", "Test extension")
457 .Set("version", "1.0.0")
458 .Set("manifest_version", 2))
460 extension_service_->AddExtension(extension.get());
462 scoped_ptr<base::ListValue> args(new base::ListValue());
463 args->Set(0, new base::StringValue("hello"));
464 args->Set(1, new base::StringValue("world"));
465 scoped_refptr<Action> action = new Action(extension->id(),
467 Action::ACTION_API_CALL,
468 "extension.connect");
469 action->set_args(args.Pass());
471 policy->ProcessAction(action);
472 CheckReadData(policy,
475 base::Bind(&CountingPolicyTest::Arguments_Stripped));
479 TEST_F(CountingPolicyTest, GetTodaysActions) {
480 CountingPolicy* policy = new CountingPolicy(profile_.get());
482 // Disable row expiration for this test by setting a time before any actions
484 policy->set_retention_time(base::TimeDelta::FromDays(14));
486 // Use a mock clock to ensure that events are not recorded on the wrong day
487 // when the test is run close to local midnight. Note: Ownership is passed
488 // to the policy, but we still keep a pointer locally. The policy will take
489 // care of destruction; this is safe since the policy outlives all our
490 // accesses to the mock clock.
491 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
492 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
493 base::TimeDelta::FromHours(12));
494 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
496 // Record some actions
497 scoped_refptr<Action> action =
499 mock_clock->Now() - base::TimeDelta::FromMinutes(40),
500 Action::ACTION_API_CALL,
502 action->mutable_args()->AppendString("woof");
503 policy->ProcessAction(action);
505 action = new Action("punky",
506 mock_clock->Now() - base::TimeDelta::FromMinutes(30),
507 Action::ACTION_API_CALL,
509 action->mutable_args()->AppendString("meow");
510 policy->ProcessAction(action);
512 action = new Action("punky",
513 mock_clock->Now() - base::TimeDelta::FromMinutes(20),
514 Action::ACTION_API_CALL,
515 "extension.sendMessage");
516 action->mutable_args()->AppendString("not");
517 action->mutable_args()->AppendString("stripped");
518 policy->ProcessAction(action);
521 new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
522 action->mutable_args()->AppendString("vamoose");
523 action->set_page_url(GURL("http://www.google.com"));
524 policy->ProcessAction(action);
527 "scoobydoo", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
528 action->mutable_args()->AppendString("vamoose");
529 action->set_page_url(GURL("http://www.google.com"));
530 policy->ProcessAction(action);
536 base::Bind(&CountingPolicyTest::Arguments_GetTodaysActions));
540 // Check that we can read back less recent actions in the db.
541 TEST_F(CountingPolicyTest, GetOlderActions) {
542 CountingPolicy* policy = new CountingPolicy(profile_.get());
544 policy->set_retention_time(base::TimeDelta::FromDays(14));
546 // Use a mock clock to ensure that events are not recorded on the wrong day
547 // when the test is run close to local midnight.
548 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
549 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
550 base::TimeDelta::FromHours(12));
551 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
553 // Record some actions
554 scoped_refptr<Action> action =
556 mock_clock->Now() - base::TimeDelta::FromDays(3) -
557 base::TimeDelta::FromMinutes(40),
558 Action::ACTION_API_CALL,
560 action->mutable_args()->AppendString("woof");
561 policy->ProcessAction(action);
563 action = new Action("punky",
564 mock_clock->Now() - base::TimeDelta::FromDays(3),
565 Action::ACTION_DOM_ACCESS,
567 action->mutable_args()->AppendString("vamoose");
568 action->set_page_url(GURL("http://www.google.com"));
569 policy->ProcessAction(action);
571 action = new Action("punky",
573 Action::ACTION_DOM_ACCESS,
575 action->mutable_args()->AppendString("too new");
576 action->set_page_url(GURL("http://www.google.com"));
577 policy->ProcessAction(action);
579 action = new Action("punky",
580 mock_clock->Now() - base::TimeDelta::FromDays(7),
581 Action::ACTION_DOM_ACCESS,
583 action->mutable_args()->AppendString("too old");
584 action->set_page_url(GURL("http://www.google.com"));
585 policy->ProcessAction(action);
591 base::Bind(&CountingPolicyTest::Arguments_GetOlderActions));
596 TEST_F(CountingPolicyTest, LogAndFetchFilteredActions) {
597 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
599 scoped_refptr<const Extension> extension =
601 .SetManifest(DictionaryBuilder()
602 .Set("name", "Test extension")
603 .Set("version", "1.0.0")
604 .Set("manifest_version", 2))
606 extension_service_->AddExtension(extension.get());
607 GURL gurl("http://www.google.com");
609 // Write some API calls
610 scoped_refptr<Action> action_api = new Action(extension->id(),
612 Action::ACTION_API_CALL,
614 action_api->set_args(make_scoped_ptr(new base::ListValue()));
615 policy->ProcessAction(action_api);
617 scoped_refptr<Action> action_dom = new Action(extension->id(),
619 Action::ACTION_DOM_ACCESS,
621 action_dom->set_args(make_scoped_ptr(new base::ListValue()));
622 action_dom->set_page_url(gurl);
623 policy->ProcessAction(action_dom);
625 CheckReadFilteredData(
628 Action::ACTION_API_CALL,
634 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
636 CheckReadFilteredData(
639 Action::ACTION_DOM_ACCESS,
645 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
647 CheckReadFilteredData(
650 Action::ACTION_DOM_ACCESS,
652 "http://www.google.com/",
656 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
658 CheckReadFilteredData(
661 Action::ACTION_DOM_ACCESS,
663 "http://www.google.com",
667 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
669 CheckReadFilteredData(
672 Action::ACTION_DOM_ACCESS,
678 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
680 CheckReadFilteredData(
689 &CountingPolicyTest::RetrieveActions_FetchFilteredActions2));
694 // Check that merging of actions only occurs within the same day, not across
695 // days, and that old data can be expired from the database.
696 TEST_F(CountingPolicyTest, MergingAndExpiring) {
697 CountingPolicy* policy = new CountingPolicy(profile_.get());
699 // Initially disable expiration by setting a retention time before any
700 // actions we generate.
701 policy->set_retention_time(base::TimeDelta::FromDays(14));
703 // Use a mock clock to ensure that events are not recorded on the wrong day
704 // when the test is run close to local midnight.
705 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
706 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
707 base::TimeDelta::FromHours(12));
708 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
710 // The first two actions should be merged; the last one is on a separate day
711 // and should not be.
712 scoped_refptr<Action> action =
714 mock_clock->Now() - base::TimeDelta::FromDays(3) -
715 base::TimeDelta::FromMinutes(40),
716 Action::ACTION_API_CALL,
718 policy->ProcessAction(action);
720 action = new Action("punky",
721 mock_clock->Now() - base::TimeDelta::FromDays(3) -
722 base::TimeDelta::FromMinutes(20),
723 Action::ACTION_API_CALL,
725 policy->ProcessAction(action);
727 action = new Action("punky",
728 mock_clock->Now() - base::TimeDelta::FromDays(2) -
729 base::TimeDelta::FromMinutes(20),
730 Action::ACTION_API_CALL,
732 policy->ProcessAction(action);
734 CheckReadData(policy,
737 base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 2));
738 CheckReadData(policy,
741 base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 1));
743 // Clean actions before midnight two days ago. Force expiration to run by
744 // clearing last_database_cleaning_time_ and submitting a new action.
745 policy->set_retention_time(base::TimeDelta::FromDays(2));
746 policy->last_database_cleaning_time_ = base::Time();
747 action = new Action("punky",
749 Action::ACTION_API_CALL,
751 policy->ProcessAction(action);
753 CheckReadData(policy,
756 base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 0));
757 CheckReadData(policy,
760 base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 1));
765 // Test cleaning of old data in the string and URL tables.
766 TEST_F(CountingPolicyTest, StringTableCleaning) {
767 CountingPolicy* policy = new CountingPolicy(profile_.get());
769 // Initially disable expiration by setting a retention time before any
770 // actions we generate.
771 policy->set_retention_time(base::TimeDelta::FromDays(14));
773 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
774 mock_clock->SetNow(base::Time::Now());
775 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
777 // Insert an action; this should create entries in both the string table (for
778 // the extension and API name) and the URL table (for page_url).
779 scoped_refptr<Action> action =
781 mock_clock->Now() - base::TimeDelta::FromDays(7),
782 Action::ACTION_API_CALL,
784 action->set_page_url(GURL("http://www.google.com/"));
785 policy->ProcessAction(action);
787 // Add an action which will not be expired, so that some strings will remain
790 "punky", mock_clock->Now(), Action::ACTION_API_CALL, "tabs.create");
791 policy->ProcessAction(action);
793 // There should now be three strings ("punky", "brewster", "tabs.create") and
794 // one URL in the tables.
796 policy->ScheduleAndForget(policy,
797 &CountingPolicyTest::CheckStringTableSizes,
800 WaitOnThread(BrowserThread::DB);
802 // Trigger a cleaning. The oldest action is expired when we submit a
803 // duplicate of the newer action. After this, there should be two strings
805 policy->set_retention_time(base::TimeDelta::FromDays(2));
806 policy->last_database_cleaning_time_ = base::Time();
807 policy->ProcessAction(action);
809 policy->ScheduleAndForget(policy,
810 &CountingPolicyTest::CheckStringTableSizes,
813 WaitOnThread(BrowserThread::DB);
818 // A stress test for memory- and database-based merging of actions. Submit
819 // multiple items, not in chronological order, spanning a few days. Check that
820 // items are merged properly and final timestamps are correct.
821 TEST_F(CountingPolicyTest, MoreMerging) {
822 CountingPolicy* policy = new CountingPolicy(profile_.get());
824 policy->set_retention_time(base::TimeDelta::FromDays(14));
826 // Use a mock clock to ensure that events are not recorded on the wrong day
827 // when the test is run close to local midnight.
828 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
829 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
830 base::TimeDelta::FromHours(12));
831 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
833 // Create an action 2 days ago, then 1 day ago, then 2 days ago. Make sure
834 // that we end up with two merged records (one for each day), and each has
835 // the appropriate timestamp. These merges should happen in the database
836 // since the date keeps changing.
838 mock_clock->Now() - base::TimeDelta::FromDays(2) -
839 base::TimeDelta::FromMinutes(40);
841 mock_clock->Now() - base::TimeDelta::FromDays(1) -
842 base::TimeDelta::FromMinutes(40);
844 mock_clock->Now() - base::TimeDelta::FromDays(2) -
845 base::TimeDelta::FromMinutes(20);
847 scoped_refptr<Action> action =
848 new Action("punky", time1, Action::ACTION_API_CALL, "brewster");
849 policy->ProcessAction(action);
851 action = new Action("punky", time2, Action::ACTION_API_CALL, "brewster");
852 policy->ProcessAction(action);
854 action = new Action("punky", time3, Action::ACTION_API_CALL, "brewster");
855 policy->ProcessAction(action);
862 &CountingPolicyTest::Arguments_CheckMergeCountAndTime, 2, time3));
868 &CountingPolicyTest::Arguments_CheckMergeCountAndTime, 1, time2));
870 // Create three actions today, where the merges should happen in memory.
871 // Again these are not chronological; timestamp time5 should win out since it
873 base::Time time4 = mock_clock->Now() - base::TimeDelta::FromMinutes(60);
874 base::Time time5 = mock_clock->Now() - base::TimeDelta::FromMinutes(20);
875 base::Time time6 = mock_clock->Now() - base::TimeDelta::FromMinutes(40);
877 action = new Action("punky", time4, Action::ACTION_API_CALL, "brewster");
878 policy->ProcessAction(action);
880 action = new Action("punky", time5, Action::ACTION_API_CALL, "brewster");
881 policy->ProcessAction(action);
883 action = new Action("punky", time6, Action::ACTION_API_CALL, "brewster");
884 policy->ProcessAction(action);
891 &CountingPolicyTest::Arguments_CheckMergeCountAndTime, 3, time5));
895 // Check that actions are flushed to disk before letting too many accumulate in
897 TEST_F(CountingPolicyTest, EarlyFlush) {
898 CountingPolicy* policy = new CountingPolicy(profile_.get());
901 for (int i = 0; i < 500; i++) {
902 scoped_refptr<Action> action =
905 Action::ACTION_API_CALL,
906 base::StringPrintf("apicall_%d", i));
907 policy->ProcessAction(action);
910 policy->ScheduleAndForget(policy, &CountingPolicyTest::CheckQueueSize);
911 WaitOnThread(BrowserThread::DB);
916 TEST_F(CountingPolicyTest, CapReturns) {
917 CountingPolicy* policy = new CountingPolicy(profile_.get());
920 for (int i = 0; i < 305; i++) {
921 scoped_refptr<Action> action =
924 Action::ACTION_API_CALL,
925 base::StringPrintf("apicall_%d", i));
926 policy->ProcessAction(action);
930 WaitOnThread(BrowserThread::DB);
932 CheckReadFilteredData(
941 &CountingPolicyTest::RetrieveActions_FetchFilteredActions300));
945 TEST_F(CountingPolicyTest, RemoveAllURLs) {
946 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
949 // Use a mock clock to ensure that events are not recorded on the wrong day
950 // when the test is run close to local midnight.
951 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
952 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
953 base::TimeDelta::FromHours(12));
954 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
956 // Record some actions
957 scoped_refptr<Action> action =
958 new Action("punky", mock_clock->Now(),
959 Action::ACTION_DOM_ACCESS, "lets");
960 action->mutable_args()->AppendString("vamoose");
961 action->set_page_url(GURL("http://www.google.com"));
962 action->set_page_title("Google");
963 action->set_arg_url(GURL("http://www.args-url.com"));
964 policy->ProcessAction(action);
966 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
968 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
969 action->mutable_args()->AppendString("vamoose");
970 action->set_page_url(GURL("http://www.google2.com"));
971 action->set_page_title("Google");
972 // Deliberately no arg url set to make sure it stills works if there is no arg
974 policy->ProcessAction(action);
976 // Clean all the URLs.
977 std::vector<GURL> no_url_restrictions;
978 policy->RemoveURLs(no_url_restrictions);
984 base::Bind(&CountingPolicyTest::AllURLsRemoved));
988 TEST_F(CountingPolicyTest, RemoveSpecificURLs) {
989 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
992 // Use a mock clock to ensure that events are not recorded on the wrong day
993 // when the test is run close to local midnight.
994 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
995 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
996 base::TimeDelta::FromHours(12));
997 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
999 // Record some actions
1000 // This should have the page url and args url cleared.
1001 scoped_refptr<Action> action = new Action("punky", mock_clock->Now(),
1002 Action::ACTION_DOM_ACCESS, "lets");
1003 action->mutable_args()->AppendString("vamoose");
1004 action->set_page_url(GURL("http://www.google1.com"));
1005 action->set_page_title("Google");
1006 action->set_arg_url(GURL("http://www.google1.com"));
1007 policy->ProcessAction(action);
1009 // This should have the page url cleared but not args url.
1010 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
1011 action = new Action(
1012 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1013 action->mutable_args()->AppendString("vamoose");
1014 action->set_page_url(GURL("http://www.google1.com"));
1015 action->set_page_title("Google");
1016 action->set_arg_url(GURL("http://www.google.com"));
1017 policy->ProcessAction(action);
1019 // This should have the page url cleared. The args url is deliberately not
1020 // set to make sure this doesn't cause any issues.
1021 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
1022 action = new Action(
1023 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1024 action->mutable_args()->AppendString("vamoose");
1025 action->set_page_url(GURL("http://www.google2.com"));
1026 action->set_page_title("Google");
1027 policy->ProcessAction(action);
1029 // This should have the args url cleared but not the page url or page title.
1030 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
1031 action = new Action(
1032 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1033 action->mutable_args()->AppendString("vamoose");
1034 action->set_page_url(GURL("http://www.google.com"));
1035 action->set_page_title("Google");
1036 action->set_arg_url(GURL("http://www.google1.com"));
1037 policy->ProcessAction(action);
1039 // This should have neither cleared.
1040 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
1041 action = new Action(
1042 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1043 action->mutable_args()->AppendString("vamoose");
1044 action->set_page_url(GURL("http://www.google.com"));
1045 action->set_page_title("Google");
1046 action->set_arg_url(GURL("http://www.args-url.com"));
1047 action->set_count(5);
1048 policy->ProcessAction(action);
1051 std::vector<GURL> urls;
1052 urls.push_back(GURL("http://www.google1.com"));
1053 urls.push_back(GURL("http://www.google2.com"));
1054 urls.push_back(GURL("http://www.url_not_in_db.com"));
1055 policy->RemoveURLs(urls);
1061 base::Bind(&CountingPolicyTest::SomeURLsRemoved));
1065 TEST_F(CountingPolicyTest, RemoveExtensionData) {
1066 CountingPolicy* policy = new CountingPolicy(profile_.get());
1069 // Use a mock clock to ensure that events are not recorded on the wrong day
1070 // when the test is run close to local midnight.
1071 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1072 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1073 base::TimeDelta::FromHours(12));
1074 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1076 // Record some actions
1077 scoped_refptr<Action> action = new Action("deleteextensiondata",
1079 Action::ACTION_DOM_ACCESS,
1081 action->mutable_args()->AppendString("vamoose");
1082 action->set_page_title("Google");
1083 action->set_arg_url(GURL("http://www.google.com"));
1084 policy->ProcessAction(action);
1085 policy->ProcessAction(action);
1086 policy->ProcessAction(action);
1088 scoped_refptr<Action> action2 = new Action("dontdelete",
1090 Action::ACTION_DOM_ACCESS,
1092 action->mutable_args()->AppendString("vamoose");
1093 action->set_page_title("Google");
1094 action->set_arg_url(GURL("http://www.google.com"));
1095 policy->ProcessAction(action2);
1098 policy->RemoveExtensionData("deleteextensiondata");
1100 CheckReadFilteredData(
1102 "deleteextensiondata",
1109 &CountingPolicyTest::RetrieveActions_FetchFilteredActions0));
1111 CheckReadFilteredData(
1120 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
1124 TEST_F(CountingPolicyTest, DeleteDatabase) {
1125 CountingPolicy* policy = new CountingPolicy(profile_.get());
1127 // Disable row expiration for this test by setting a time before any actions
1129 policy->set_retention_time(base::TimeDelta::FromDays(14));
1131 // Use a mock clock to ensure that events are not recorded on the wrong day
1132 // when the test is run close to local midnight. Note: Ownership is passed
1133 // to the policy, but we still keep a pointer locally. The policy will take
1134 // care of destruction; this is safe since the policy outlives all our
1135 // accesses to the mock clock.
1136 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1137 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1138 base::TimeDelta::FromHours(12));
1139 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1141 // Record some actions
1142 scoped_refptr<Action> action =
1144 mock_clock->Now() - base::TimeDelta::FromMinutes(40),
1145 Action::ACTION_API_CALL,
1147 action->mutable_args()->AppendString("woof");
1148 policy->ProcessAction(action);
1150 action = new Action("punky",
1151 mock_clock->Now() - base::TimeDelta::FromMinutes(30),
1152 Action::ACTION_API_CALL,
1154 action->mutable_args()->AppendString("meow");
1155 policy->ProcessAction(action);
1157 action = new Action("punky",
1158 mock_clock->Now() - base::TimeDelta::FromMinutes(20),
1159 Action::ACTION_API_CALL,
1160 "extension.sendMessage");
1161 action->mutable_args()->AppendString("not");
1162 action->mutable_args()->AppendString("stripped");
1163 policy->ProcessAction(action);
1166 new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1167 action->mutable_args()->AppendString("vamoose");
1168 action->set_page_url(GURL("http://www.google.com"));
1169 policy->ProcessAction(action);
1171 action = new Action(
1172 "scoobydoo", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1173 action->mutable_args()->AppendString("vamoose");
1174 action->set_page_url(GURL("http://www.google.com"));
1175 policy->ProcessAction(action);
1181 base::Bind(&CountingPolicyTest::Arguments_GetTodaysActions));
1183 policy->DeleteDatabase();
1185 CheckReadFilteredData(
1194 &CountingPolicyTest::RetrieveActions_FetchFilteredActions0));
1196 // The following code tests that the caches of url and string tables were
1197 // cleared by the deletion above.
1198 // https://code.google.com/p/chromium/issues/detail?id=341674.
1200 new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1201 action->mutable_args()->AppendString("vamoose");
1202 action->set_page_url(GURL("http://www.google.com"));
1203 policy->ProcessAction(action);
1209 base::Bind(&CountingPolicyTest::Arguments_GetSinglesAction));
1211 policy->DeleteDatabase();
1213 CheckReadFilteredData(
1222 &CountingPolicyTest::RetrieveActions_FetchFilteredActions0));
1227 // Tests that duplicate rows in the activity log database are handled properly
1228 // when updating counts.
1229 TEST_F(CountingPolicyTest, DuplicateRows) {
1230 CountingPolicy* policy = new CountingPolicy(profile_.get());
1232 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1233 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1234 base::TimeDelta::FromHours(12));
1235 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1237 // Record two actions with distinct URLs.
1238 scoped_refptr<Action> action;
1239 action = new Action(
1240 "punky", mock_clock->Now(), Action::ACTION_API_CALL, "brewster");
1241 action->set_page_url(GURL("http://www.google.com"));
1242 policy->ProcessAction(action);
1244 action = new Action(
1245 "punky", mock_clock->Now(), Action::ACTION_API_CALL, "brewster");
1246 action->set_page_url(GURL("http://www.google.co.uk"));
1247 policy->ProcessAction(action);
1249 // Manipulate the database to clear the URLs, so that we end up with
1251 std::vector<GURL> no_url_restrictions;
1252 policy->RemoveURLs(no_url_restrictions);
1254 // Record one more action, with no URL. This should increment the count on
1255 // one, and exactly one, of the existing rows.
1256 action = new Action(
1257 "punky", mock_clock->Now(), Action::ACTION_API_CALL, "brewster");
1258 policy->ProcessAction(action);
1264 base::Bind(&CountingPolicyTest::CheckDuplicates));
1268 TEST_F(CountingPolicyTest, RemoveActions) {
1269 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
1272 std::vector<int64> action_ids;
1275 policy, action_ids, base::Bind(&CountingPolicyTest::NoActionsDeleted));
1277 action_ids.push_back(-1);
1278 action_ids.push_back(-10);
1279 action_ids.push_back(0);
1280 action_ids.push_back(5);
1281 action_ids.push_back(10);
1283 policy, action_ids, base::Bind(&CountingPolicyTest::NoActionsDeleted));
1286 for (int i = 0; i < 50; i++) {
1287 action_ids.push_back(i + 3);
1290 policy, action_ids, base::Bind(&CountingPolicyTest::NoActionsDeleted));
1293 // CheckRemoveActions pushes two actions to the Activity Log database with IDs
1295 action_ids.push_back(1);
1296 action_ids.push_back(2);
1298 policy, action_ids, base::Bind(&CountingPolicyTest::AllActionsDeleted));
1301 action_ids.push_back(1);
1303 policy, action_ids, base::Bind(&CountingPolicyTest::Action1Deleted));
1306 action_ids.push_back(2);
1308 policy, action_ids, base::Bind(&CountingPolicyTest::Action2Deleted));
1314 } // namespace extensions