Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / extensions / activity_log / counting_policy_unittest.cc
blob354d0e68c82e8d83d1b1cd56442d35627512cf08
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"
31 #endif
33 namespace extensions {
35 class CountingPolicyTest : public testing::Test {
36 public:
37 CountingPolicyTest()
38 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
39 saved_cmdline_(CommandLine::NO_PROGRAM) {
40 #if defined OS_CHROMEOS
41 test_user_manager_.reset(new chromeos::ScopedTestUserManager());
42 #endif
43 CommandLine command_line(CommandLine::NO_PROGRAM);
44 saved_cmdline_ = *CommandLine::ForCurrentProcess();
45 profile_.reset(new TestingProfile());
46 CommandLine::ForCurrentProcess()->AppendSwitch(
47 switches::kEnableExtensionActivityLogging);
48 extension_service_ = static_cast<TestExtensionSystem*>(
49 ExtensionSystem::Get(profile_.get()))->CreateExtensionService
50 (&command_line, base::FilePath(), false);
53 virtual ~CountingPolicyTest() {
54 #if defined OS_CHROMEOS
55 test_user_manager_.reset();
56 #endif
57 base::RunLoop().RunUntilIdle();
58 profile_.reset(NULL);
59 base::RunLoop().RunUntilIdle();
60 // Restore the original command line and undo the affects of SetUp().
61 *CommandLine::ForCurrentProcess() = saved_cmdline_;
64 // Wait for the task queue for the specified thread to empty.
65 void WaitOnThread(const content::BrowserThread::ID& thread) {
66 BrowserThread::PostTaskAndReply(
67 thread,
68 FROM_HERE,
69 base::Bind(&base::DoNothing),
70 base::MessageLoop::current()->QuitClosure());
71 base::MessageLoop::current()->Run();
74 // A wrapper function for CheckReadFilteredData, so that we don't need to
75 // enter empty string values for parameters we don't care about.
76 void CheckReadData(
77 ActivityLogDatabasePolicy* policy,
78 const std::string& extension_id,
79 int day,
80 const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
81 CheckReadFilteredData(
82 policy, extension_id, Action::ACTION_ANY, "", "", "", day, checker);
85 // A helper function to call ReadFilteredData on a policy object and wait for
86 // the results to be processed.
87 void CheckReadFilteredData(
88 ActivityLogDatabasePolicy* policy,
89 const std::string& extension_id,
90 const Action::ActionType type,
91 const std::string& api_name,
92 const std::string& page_url,
93 const std::string& arg_url,
94 int day,
95 const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
96 // Submit a request to the policy to read back some data, and call the
97 // checker function when results are available. This will happen on the
98 // database thread.
99 policy->ReadFilteredData(
100 extension_id,
101 type,
102 api_name,
103 page_url,
104 arg_url,
105 day,
106 base::Bind(&CountingPolicyTest::CheckWrapper,
107 checker,
108 base::MessageLoop::current()->QuitClosure()));
110 // Set up a timeout for receiving results; if we haven't received anything
111 // when the timeout triggers then assume that the test is broken.
112 base::CancelableClosure timeout(
113 base::Bind(&CountingPolicyTest::TimeoutCallback));
114 base::MessageLoop::current()->PostDelayedTask(
115 FROM_HERE, timeout.callback(), TestTimeouts::action_timeout());
117 // Wait for results; either the checker or the timeout callbacks should
118 // cause the main loop to exit.
119 base::MessageLoop::current()->Run();
121 timeout.Cancel();
124 // A helper function which verifies that the string_ids and url_ids tables in
125 // the database have the specified sizes.
126 static void CheckStringTableSizes(CountingPolicy* policy,
127 int string_size,
128 int url_size) {
129 sql::Connection* db = policy->GetDatabaseConnection();
130 sql::Statement statement1(db->GetCachedStatement(
131 sql::StatementID(SQL_FROM_HERE), "SELECT COUNT(*) FROM string_ids"));
132 ASSERT_TRUE(statement1.Step());
133 ASSERT_EQ(string_size, statement1.ColumnInt(0));
135 sql::Statement statement2(db->GetCachedStatement(
136 sql::StatementID(SQL_FROM_HERE), "SELECT COUNT(*) FROM url_ids"));
137 ASSERT_TRUE(statement2.Step());
138 ASSERT_EQ(url_size, statement2.ColumnInt(0));
141 // Checks that the number of queued actions to be written out does not exceed
142 // kSizeThresholdForFlush. Runs on the database thread.
143 static void CheckQueueSize(CountingPolicy* policy) {
144 // This should be updated if kSizeThresholdForFlush in activity_database.cc
145 // changes.
146 ASSERT_LE(policy->queued_actions_.size(), 200U);
149 static void CheckWrapper(
150 const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker,
151 const base::Closure& done,
152 scoped_ptr<Action::ActionVector> results) {
153 checker.Run(results.Pass());
154 done.Run();
157 static void TimeoutCallback() {
158 base::MessageLoop::current()->QuitWhenIdle();
159 FAIL() << "Policy test timed out waiting for results";
162 static void RetrieveActions_FetchFilteredActions0(
163 scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
164 ASSERT_EQ(0, static_cast<int>(i->size()));
167 static void RetrieveActions_FetchFilteredActions1(
168 scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
169 ASSERT_EQ(1, static_cast<int>(i->size()));
172 static void RetrieveActions_FetchFilteredActions2(
173 scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
174 ASSERT_EQ(2, static_cast<int>(i->size()));
177 static void RetrieveActions_FetchFilteredActions300(
178 scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
179 ASSERT_EQ(300, static_cast<int>(i->size()));
182 static void Arguments_Stripped(scoped_ptr<Action::ActionVector> i) {
183 scoped_refptr<Action> last = i->front();
184 CheckAction(*last, "odlameecjipmbmbejkplpemijjgpljce",
185 Action::ACTION_API_CALL, "extension.connect",
186 "[\"hello\",\"world\"]", "", "", "", 1);
189 static void Arguments_GetTodaysActions(
190 scoped_ptr<Action::ActionVector> actions) {
191 ASSERT_EQ(3, static_cast<int>(actions->size()));
192 CheckAction(*actions->at(0), "punky", Action::ACTION_API_CALL, "brewster",
193 "", "", "", "", 2);
194 CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
195 "", "http://www.google.com/", "", "", 1);
196 CheckAction(*actions->at(2), "punky", Action::ACTION_API_CALL,
197 "extension.sendMessage", "[\"not\",\"stripped\"]", "", "", "",
201 static void Arguments_GetOlderActions(
202 scoped_ptr<Action::ActionVector> actions) {
203 ASSERT_EQ(2, static_cast<int>(actions->size()));
204 CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
205 "", "http://www.google.com/", "", "", 1);
206 CheckAction(*actions->at(1), "punky", Action::ACTION_API_CALL, "brewster",
207 "", "", "", "", 1);
210 static void Arguments_CheckMergeCount(
211 int count,
212 scoped_ptr<Action::ActionVector> actions) {
213 if (count > 0) {
214 ASSERT_EQ(1u, actions->size());
215 CheckAction(*actions->at(0), "punky", Action::ACTION_API_CALL, "brewster",
216 "", "", "", "", count);
217 } else {
218 ASSERT_EQ(0u, actions->size());
222 static void Arguments_CheckMergeCountAndTime(
223 int count,
224 const base::Time& time,
225 scoped_ptr<Action::ActionVector> actions) {
226 if (count > 0) {
227 ASSERT_EQ(1u, actions->size());
228 CheckAction(*actions->at(0), "punky", Action::ACTION_API_CALL, "brewster",
229 "", "", "", "", count);
230 ASSERT_EQ(time, actions->at(0)->time());
231 } else {
232 ASSERT_EQ(0u, actions->size());
236 static void AllURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
237 ASSERT_EQ(2, static_cast<int>(actions->size()));
238 CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
239 "", "", "", "", 1);
240 CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
241 "", "", "", "", 1);
244 static void SomeURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
245 // These will be in the vector in reverse time order.
246 ASSERT_EQ(5, static_cast<int>(actions->size()));
247 CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
248 "", "http://www.google.com/", "Google",
249 "http://www.args-url.com/", 1);
250 CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
251 "", "http://www.google.com/", "Google", "", 1);
252 CheckAction(*actions->at(2), "punky", Action::ACTION_DOM_ACCESS, "lets",
253 "", "", "", "", 1);
254 CheckAction(*actions->at(3), "punky", Action::ACTION_DOM_ACCESS, "lets",
255 "", "", "", "http://www.google.com/", 1);
256 CheckAction(*actions->at(4), "punky", Action::ACTION_DOM_ACCESS, "lets",
257 "", "", "", "", 1);
260 static void CheckDuplicates(scoped_ptr<Action::ActionVector> actions) {
261 ASSERT_EQ(2u, actions->size());
262 int total_count = 0;
263 for (size_t i = 0; i < actions->size(); i++) {
264 total_count += actions->at(i)->count();
266 ASSERT_EQ(3, total_count);
269 static void CheckAction(const Action& action,
270 const std::string& expected_id,
271 const Action::ActionType& expected_type,
272 const std::string& expected_api_name,
273 const std::string& expected_args_str,
274 const std::string& expected_page_url,
275 const std::string& expected_page_title,
276 const std::string& expected_arg_url,
277 int expected_count) {
278 ASSERT_EQ(expected_id, action.extension_id());
279 ASSERT_EQ(expected_type, action.action_type());
280 ASSERT_EQ(expected_api_name, action.api_name());
281 ASSERT_EQ(expected_args_str,
282 ActivityLogPolicy::Util::Serialize(action.args()));
283 ASSERT_EQ(expected_page_url, action.SerializePageUrl());
284 ASSERT_EQ(expected_page_title, action.page_title());
285 ASSERT_EQ(expected_arg_url, action.SerializeArgUrl());
286 ASSERT_EQ(expected_count, action.count());
289 protected:
290 ExtensionService* extension_service_;
291 scoped_ptr<TestingProfile> profile_;
292 content::TestBrowserThreadBundle thread_bundle_;
293 // Used to preserve a copy of the original command line.
294 // The test framework will do this itself as well. However, by then,
295 // it is too late to call ActivityLog::RecomputeLoggingIsEnabled() in
296 // TearDown().
297 CommandLine saved_cmdline_;
299 #if defined OS_CHROMEOS
300 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
301 chromeos::ScopedTestCrosSettings test_cros_settings_;
302 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
303 #endif
306 TEST_F(CountingPolicyTest, Construct) {
307 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
308 policy->Init();
309 scoped_refptr<const Extension> extension =
310 ExtensionBuilder()
311 .SetManifest(DictionaryBuilder()
312 .Set("name", "Test extension")
313 .Set("version", "1.0.0")
314 .Set("manifest_version", 2))
315 .Build();
316 extension_service_->AddExtension(extension.get());
317 scoped_ptr<base::ListValue> args(new base::ListValue());
318 scoped_refptr<Action> action = new Action(extension->id(),
319 base::Time::Now(),
320 Action::ACTION_API_CALL,
321 "tabs.testMethod");
322 action->set_args(args.Pass());
323 policy->ProcessAction(action);
324 policy->Close();
327 TEST_F(CountingPolicyTest, LogWithStrippedArguments) {
328 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
329 policy->Init();
330 scoped_refptr<const Extension> extension =
331 ExtensionBuilder()
332 .SetManifest(DictionaryBuilder()
333 .Set("name", "Test extension")
334 .Set("version", "1.0.0")
335 .Set("manifest_version", 2))
336 .Build();
337 extension_service_->AddExtension(extension.get());
339 scoped_ptr<base::ListValue> args(new base::ListValue());
340 args->Set(0, new base::StringValue("hello"));
341 args->Set(1, new base::StringValue("world"));
342 scoped_refptr<Action> action = new Action(extension->id(),
343 base::Time::Now(),
344 Action::ACTION_API_CALL,
345 "extension.connect");
346 action->set_args(args.Pass());
348 policy->ProcessAction(action);
349 CheckReadData(policy,
350 extension->id(),
352 base::Bind(&CountingPolicyTest::Arguments_Stripped));
353 policy->Close();
356 TEST_F(CountingPolicyTest, GetTodaysActions) {
357 CountingPolicy* policy = new CountingPolicy(profile_.get());
358 policy->Init();
359 // Disable row expiration for this test by setting a time before any actions
360 // we generate.
361 policy->set_retention_time(base::TimeDelta::FromDays(14));
363 // Use a mock clock to ensure that events are not recorded on the wrong day
364 // when the test is run close to local midnight. Note: Ownership is passed
365 // to the policy, but we still keep a pointer locally. The policy will take
366 // care of destruction; this is safe since the policy outlives all our
367 // accesses to the mock clock.
368 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
369 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
370 base::TimeDelta::FromHours(12));
371 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
373 // Record some actions
374 scoped_refptr<Action> action =
375 new Action("punky",
376 mock_clock->Now() - base::TimeDelta::FromMinutes(40),
377 Action::ACTION_API_CALL,
378 "brewster");
379 action->mutable_args()->AppendString("woof");
380 policy->ProcessAction(action);
382 action = new Action("punky",
383 mock_clock->Now() - base::TimeDelta::FromMinutes(30),
384 Action::ACTION_API_CALL,
385 "brewster");
386 action->mutable_args()->AppendString("meow");
387 policy->ProcessAction(action);
389 action = new Action("punky",
390 mock_clock->Now() - base::TimeDelta::FromMinutes(20),
391 Action::ACTION_API_CALL,
392 "extension.sendMessage");
393 action->mutable_args()->AppendString("not");
394 action->mutable_args()->AppendString("stripped");
395 policy->ProcessAction(action);
397 action =
398 new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
399 action->mutable_args()->AppendString("vamoose");
400 action->set_page_url(GURL("http://www.google.com"));
401 policy->ProcessAction(action);
403 action = new Action(
404 "scoobydoo", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
405 action->mutable_args()->AppendString("vamoose");
406 action->set_page_url(GURL("http://www.google.com"));
407 policy->ProcessAction(action);
409 CheckReadData(
410 policy,
411 "punky",
413 base::Bind(&CountingPolicyTest::Arguments_GetTodaysActions));
414 policy->Close();
417 // Check that we can read back less recent actions in the db.
418 TEST_F(CountingPolicyTest, GetOlderActions) {
419 CountingPolicy* policy = new CountingPolicy(profile_.get());
420 policy->Init();
421 policy->set_retention_time(base::TimeDelta::FromDays(14));
423 // Use a mock clock to ensure that events are not recorded on the wrong day
424 // when the test is run close to local midnight.
425 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
426 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
427 base::TimeDelta::FromHours(12));
428 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
430 // Record some actions
431 scoped_refptr<Action> action =
432 new Action("punky",
433 mock_clock->Now() - base::TimeDelta::FromDays(3) -
434 base::TimeDelta::FromMinutes(40),
435 Action::ACTION_API_CALL,
436 "brewster");
437 action->mutable_args()->AppendString("woof");
438 policy->ProcessAction(action);
440 action = new Action("punky",
441 mock_clock->Now() - base::TimeDelta::FromDays(3),
442 Action::ACTION_DOM_ACCESS,
443 "lets");
444 action->mutable_args()->AppendString("vamoose");
445 action->set_page_url(GURL("http://www.google.com"));
446 policy->ProcessAction(action);
448 action = new Action("punky",
449 mock_clock->Now(),
450 Action::ACTION_DOM_ACCESS,
451 "lets");
452 action->mutable_args()->AppendString("too new");
453 action->set_page_url(GURL("http://www.google.com"));
454 policy->ProcessAction(action);
456 action = new Action("punky",
457 mock_clock->Now() - base::TimeDelta::FromDays(7),
458 Action::ACTION_DOM_ACCESS,
459 "lets");
460 action->mutable_args()->AppendString("too old");
461 action->set_page_url(GURL("http://www.google.com"));
462 policy->ProcessAction(action);
464 CheckReadData(
465 policy,
466 "punky",
468 base::Bind(&CountingPolicyTest::Arguments_GetOlderActions));
470 policy->Close();
473 TEST_F(CountingPolicyTest, LogAndFetchFilteredActions) {
474 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
475 policy->Init();
476 scoped_refptr<const Extension> extension =
477 ExtensionBuilder()
478 .SetManifest(DictionaryBuilder()
479 .Set("name", "Test extension")
480 .Set("version", "1.0.0")
481 .Set("manifest_version", 2))
482 .Build();
483 extension_service_->AddExtension(extension.get());
484 GURL gurl("http://www.google.com");
486 // Write some API calls
487 scoped_refptr<Action> action_api = new Action(extension->id(),
488 base::Time::Now(),
489 Action::ACTION_API_CALL,
490 "tabs.testMethod");
491 action_api->set_args(make_scoped_ptr(new base::ListValue()));
492 policy->ProcessAction(action_api);
494 scoped_refptr<Action> action_dom = new Action(extension->id(),
495 base::Time::Now(),
496 Action::ACTION_DOM_ACCESS,
497 "document.write");
498 action_dom->set_args(make_scoped_ptr(new base::ListValue()));
499 action_dom->set_page_url(gurl);
500 policy->ProcessAction(action_dom);
502 CheckReadFilteredData(
503 policy,
504 extension->id(),
505 Action::ACTION_API_CALL,
506 "tabs.testMethod",
510 base::Bind(
511 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
513 CheckReadFilteredData(
514 policy,
516 Action::ACTION_DOM_ACCESS,
521 base::Bind(
522 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
524 CheckReadFilteredData(
525 policy,
527 Action::ACTION_DOM_ACCESS,
529 "http://www.google.com/",
532 base::Bind(
533 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
535 CheckReadFilteredData(
536 policy,
538 Action::ACTION_DOM_ACCESS,
540 "http://www.google.com",
543 base::Bind(
544 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
546 CheckReadFilteredData(
547 policy,
549 Action::ACTION_DOM_ACCESS,
551 "http://www.goo",
554 base::Bind(
555 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
557 CheckReadFilteredData(
558 policy,
559 extension->id(),
560 Action::ACTION_ANY,
565 base::Bind(
566 &CountingPolicyTest::RetrieveActions_FetchFilteredActions2));
568 policy->Close();
571 // Check that merging of actions only occurs within the same day, not across
572 // days, and that old data can be expired from the database.
573 TEST_F(CountingPolicyTest, MergingAndExpiring) {
574 CountingPolicy* policy = new CountingPolicy(profile_.get());
575 policy->Init();
576 // Initially disable expiration by setting a retention time before any
577 // actions we generate.
578 policy->set_retention_time(base::TimeDelta::FromDays(14));
580 // Use a mock clock to ensure that events are not recorded on the wrong day
581 // when the test is run close to local midnight.
582 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
583 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
584 base::TimeDelta::FromHours(12));
585 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
587 // The first two actions should be merged; the last one is on a separate day
588 // and should not be.
589 scoped_refptr<Action> action =
590 new Action("punky",
591 mock_clock->Now() - base::TimeDelta::FromDays(3) -
592 base::TimeDelta::FromMinutes(40),
593 Action::ACTION_API_CALL,
594 "brewster");
595 policy->ProcessAction(action);
597 action = new Action("punky",
598 mock_clock->Now() - base::TimeDelta::FromDays(3) -
599 base::TimeDelta::FromMinutes(20),
600 Action::ACTION_API_CALL,
601 "brewster");
602 policy->ProcessAction(action);
604 action = new Action("punky",
605 mock_clock->Now() - base::TimeDelta::FromDays(2) -
606 base::TimeDelta::FromMinutes(20),
607 Action::ACTION_API_CALL,
608 "brewster");
609 policy->ProcessAction(action);
611 CheckReadData(policy,
612 "punky",
614 base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 2));
615 CheckReadData(policy,
616 "punky",
618 base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 1));
620 // Clean actions before midnight two days ago. Force expiration to run by
621 // clearing last_database_cleaning_time_ and submitting a new action.
622 policy->set_retention_time(base::TimeDelta::FromDays(2));
623 policy->last_database_cleaning_time_ = base::Time();
624 action = new Action("punky",
625 mock_clock->Now(),
626 Action::ACTION_API_CALL,
627 "brewster");
628 policy->ProcessAction(action);
630 CheckReadData(policy,
631 "punky",
633 base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 0));
634 CheckReadData(policy,
635 "punky",
637 base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 1));
639 policy->Close();
642 // Test cleaning of old data in the string and URL tables.
643 TEST_F(CountingPolicyTest, StringTableCleaning) {
644 CountingPolicy* policy = new CountingPolicy(profile_.get());
645 policy->Init();
646 // Initially disable expiration by setting a retention time before any
647 // actions we generate.
648 policy->set_retention_time(base::TimeDelta::FromDays(14));
650 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
651 mock_clock->SetNow(base::Time::Now());
652 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
654 // Insert an action; this should create entries in both the string table (for
655 // the extension and API name) and the URL table (for page_url).
656 scoped_refptr<Action> action =
657 new Action("punky",
658 mock_clock->Now() - base::TimeDelta::FromDays(7),
659 Action::ACTION_API_CALL,
660 "brewster");
661 action->set_page_url(GURL("http://www.google.com/"));
662 policy->ProcessAction(action);
664 // Add an action which will not be expired, so that some strings will remain
665 // in use.
666 action = new Action(
667 "punky", mock_clock->Now(), Action::ACTION_API_CALL, "tabs.create");
668 policy->ProcessAction(action);
670 // There should now be three strings ("punky", "brewster", "tabs.create") and
671 // one URL in the tables.
672 policy->Flush();
673 policy->ScheduleAndForget(policy,
674 &CountingPolicyTest::CheckStringTableSizes,
677 WaitOnThread(BrowserThread::DB);
679 // Trigger a cleaning. The oldest action is expired when we submit a
680 // duplicate of the newer action. After this, there should be two strings
681 // and no URLs.
682 policy->set_retention_time(base::TimeDelta::FromDays(2));
683 policy->last_database_cleaning_time_ = base::Time();
684 policy->ProcessAction(action);
685 policy->Flush();
686 policy->ScheduleAndForget(policy,
687 &CountingPolicyTest::CheckStringTableSizes,
690 WaitOnThread(BrowserThread::DB);
692 policy->Close();
695 // A stress test for memory- and database-based merging of actions. Submit
696 // multiple items, not in chronological order, spanning a few days. Check that
697 // items are merged properly and final timestamps are correct.
698 TEST_F(CountingPolicyTest, MoreMerging) {
699 CountingPolicy* policy = new CountingPolicy(profile_.get());
700 policy->Init();
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 // Create an action 2 days ago, then 1 day ago, then 2 days ago. Make sure
711 // that we end up with two merged records (one for each day), and each has
712 // the appropriate timestamp. These merges should happen in the database
713 // since the date keeps changing.
714 base::Time time1 =
715 mock_clock->Now() - base::TimeDelta::FromDays(2) -
716 base::TimeDelta::FromMinutes(40);
717 base::Time time2 =
718 mock_clock->Now() - base::TimeDelta::FromDays(1) -
719 base::TimeDelta::FromMinutes(40);
720 base::Time time3 =
721 mock_clock->Now() - base::TimeDelta::FromDays(2) -
722 base::TimeDelta::FromMinutes(20);
724 scoped_refptr<Action> action =
725 new Action("punky", time1, Action::ACTION_API_CALL, "brewster");
726 policy->ProcessAction(action);
728 action = new Action("punky", time2, Action::ACTION_API_CALL, "brewster");
729 policy->ProcessAction(action);
731 action = new Action("punky", time3, Action::ACTION_API_CALL, "brewster");
732 policy->ProcessAction(action);
734 CheckReadData(
735 policy,
736 "punky",
738 base::Bind(
739 &CountingPolicyTest::Arguments_CheckMergeCountAndTime, 2, time3));
740 CheckReadData(
741 policy,
742 "punky",
744 base::Bind(
745 &CountingPolicyTest::Arguments_CheckMergeCountAndTime, 1, time2));
747 // Create three actions today, where the merges should happen in memory.
748 // Again these are not chronological; timestamp time5 should win out since it
749 // is the latest.
750 base::Time time4 = mock_clock->Now() - base::TimeDelta::FromMinutes(60);
751 base::Time time5 = mock_clock->Now() - base::TimeDelta::FromMinutes(20);
752 base::Time time6 = mock_clock->Now() - base::TimeDelta::FromMinutes(40);
754 action = new Action("punky", time4, Action::ACTION_API_CALL, "brewster");
755 policy->ProcessAction(action);
757 action = new Action("punky", time5, Action::ACTION_API_CALL, "brewster");
758 policy->ProcessAction(action);
760 action = new Action("punky", time6, Action::ACTION_API_CALL, "brewster");
761 policy->ProcessAction(action);
763 CheckReadData(
764 policy,
765 "punky",
767 base::Bind(
768 &CountingPolicyTest::Arguments_CheckMergeCountAndTime, 3, time5));
769 policy->Close();
772 // Check that actions are flushed to disk before letting too many accumulate in
773 // memory.
774 TEST_F(CountingPolicyTest, EarlyFlush) {
775 CountingPolicy* policy = new CountingPolicy(profile_.get());
776 policy->Init();
778 for (int i = 0; i < 500; i++) {
779 scoped_refptr<Action> action =
780 new Action("punky",
781 base::Time::Now(),
782 Action::ACTION_API_CALL,
783 base::StringPrintf("apicall_%d", i));
784 policy->ProcessAction(action);
787 policy->ScheduleAndForget(policy, &CountingPolicyTest::CheckQueueSize);
788 WaitOnThread(BrowserThread::DB);
790 policy->Close();
793 TEST_F(CountingPolicyTest, CapReturns) {
794 CountingPolicy* policy = new CountingPolicy(profile_.get());
795 policy->Init();
797 for (int i = 0; i < 305; i++) {
798 scoped_refptr<Action> action =
799 new Action("punky",
800 base::Time::Now(),
801 Action::ACTION_API_CALL,
802 base::StringPrintf("apicall_%d", i));
803 policy->ProcessAction(action);
806 policy->Flush();
807 WaitOnThread(BrowserThread::DB);
809 CheckReadFilteredData(
810 policy,
811 "punky",
812 Action::ACTION_ANY,
817 base::Bind(
818 &CountingPolicyTest::RetrieveActions_FetchFilteredActions300));
819 policy->Close();
822 TEST_F(CountingPolicyTest, RemoveAllURLs) {
823 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
824 policy->Init();
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 // Record some actions
834 scoped_refptr<Action> action =
835 new Action("punky", mock_clock->Now(),
836 Action::ACTION_DOM_ACCESS, "lets");
837 action->mutable_args()->AppendString("vamoose");
838 action->set_page_url(GURL("http://www.google.com"));
839 action->set_page_title("Google");
840 action->set_arg_url(GURL("http://www.args-url.com"));
841 policy->ProcessAction(action);
843 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
844 action = new Action(
845 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
846 action->mutable_args()->AppendString("vamoose");
847 action->set_page_url(GURL("http://www.google2.com"));
848 action->set_page_title("Google");
849 // Deliberately no arg url set to make sure it stills works if there is no arg
850 // url.
851 policy->ProcessAction(action);
853 // Clean all the URLs.
854 std::vector<GURL> no_url_restrictions;
855 policy->RemoveURLs(no_url_restrictions);
857 CheckReadData(
858 policy,
859 "punky",
861 base::Bind(&CountingPolicyTest::AllURLsRemoved));
862 policy->Close();
865 TEST_F(CountingPolicyTest, RemoveSpecificURLs) {
866 ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
867 policy->Init();
869 // Use a mock clock to ensure that events are not recorded on the wrong day
870 // when the test is run close to local midnight.
871 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
872 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
873 base::TimeDelta::FromHours(12));
874 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
876 // Record some actions
877 // This should have the page url and args url cleared.
878 scoped_refptr<Action> action = new Action("punky", mock_clock->Now(),
879 Action::ACTION_DOM_ACCESS, "lets");
880 action->mutable_args()->AppendString("vamoose");
881 action->set_page_url(GURL("http://www.google1.com"));
882 action->set_page_title("Google");
883 action->set_arg_url(GURL("http://www.google1.com"));
884 policy->ProcessAction(action);
886 // This should have the page url cleared but not args url.
887 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
888 action = new Action(
889 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
890 action->mutable_args()->AppendString("vamoose");
891 action->set_page_url(GURL("http://www.google1.com"));
892 action->set_page_title("Google");
893 action->set_arg_url(GURL("http://www.google.com"));
894 policy->ProcessAction(action);
896 // This should have the page url cleared. The args url is deliberately not
897 // set to make sure this doesn't cause any issues.
898 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
899 action = new Action(
900 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
901 action->mutable_args()->AppendString("vamoose");
902 action->set_page_url(GURL("http://www.google2.com"));
903 action->set_page_title("Google");
904 policy->ProcessAction(action);
906 // This should have the args url cleared but not the page url or page title.
907 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
908 action = new Action(
909 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
910 action->mutable_args()->AppendString("vamoose");
911 action->set_page_url(GURL("http://www.google.com"));
912 action->set_page_title("Google");
913 action->set_arg_url(GURL("http://www.google1.com"));
914 policy->ProcessAction(action);
916 // This should have neither cleared.
917 mock_clock->Advance(base::TimeDelta::FromSeconds(1));
918 action = new Action(
919 "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
920 action->mutable_args()->AppendString("vamoose");
921 action->set_page_url(GURL("http://www.google.com"));
922 action->set_page_title("Google");
923 action->set_arg_url(GURL("http://www.args-url.com"));
924 action->set_count(5);
925 policy->ProcessAction(action);
927 // Clean some URLs.
928 std::vector<GURL> urls;
929 urls.push_back(GURL("http://www.google1.com"));
930 urls.push_back(GURL("http://www.google2.com"));
931 urls.push_back(GURL("http://www.url_not_in_db.com"));
932 policy->RemoveURLs(urls);
934 CheckReadData(
935 policy,
936 "punky",
938 base::Bind(&CountingPolicyTest::SomeURLsRemoved));
939 policy->Close();
942 TEST_F(CountingPolicyTest, RemoveExtensionData) {
943 CountingPolicy* policy = new CountingPolicy(profile_.get());
944 policy->Init();
946 // Use a mock clock to ensure that events are not recorded on the wrong day
947 // when the test is run close to local midnight.
948 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
949 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
950 base::TimeDelta::FromHours(12));
951 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
953 // Record some actions
954 scoped_refptr<Action> action = new Action("deleteextensiondata",
955 mock_clock->Now(),
956 Action::ACTION_DOM_ACCESS,
957 "lets");
958 action->mutable_args()->AppendString("vamoose");
959 action->set_page_title("Google");
960 action->set_arg_url(GURL("http://www.google.com"));
961 policy->ProcessAction(action);
962 policy->ProcessAction(action);
963 policy->ProcessAction(action);
965 scoped_refptr<Action> action2 = new Action("dontdelete",
966 mock_clock->Now(),
967 Action::ACTION_DOM_ACCESS,
968 "lets");
969 action->mutable_args()->AppendString("vamoose");
970 action->set_page_title("Google");
971 action->set_arg_url(GURL("http://www.google.com"));
972 policy->ProcessAction(action2);
974 policy->Flush();
975 policy->RemoveExtensionData("deleteextensiondata");
977 CheckReadFilteredData(
978 policy,
979 "deleteextensiondata",
980 Action::ACTION_ANY,
985 base::Bind(
986 &CountingPolicyTest::RetrieveActions_FetchFilteredActions0));
988 CheckReadFilteredData(
989 policy,
990 "dontdelete",
991 Action::ACTION_ANY,
996 base::Bind(
997 &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
998 policy->Close();
1001 TEST_F(CountingPolicyTest, DeleteActions) {
1002 CountingPolicy* policy = new CountingPolicy(profile_.get());
1003 policy->Init();
1004 // Disable row expiration for this test by setting a time before any actions
1005 // we generate.
1006 policy->set_retention_time(base::TimeDelta::FromDays(14));
1008 // Use a mock clock to ensure that events are not recorded on the wrong day
1009 // when the test is run close to local midnight. Note: Ownership is passed
1010 // to the policy, but we still keep a pointer locally. The policy will take
1011 // care of destruction; this is safe since the policy outlives all our
1012 // accesses to the mock clock.
1013 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1014 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1015 base::TimeDelta::FromHours(12));
1016 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1018 // Record some actions
1019 scoped_refptr<Action> action =
1020 new Action("punky",
1021 mock_clock->Now() - base::TimeDelta::FromMinutes(40),
1022 Action::ACTION_API_CALL,
1023 "brewster");
1024 action->mutable_args()->AppendString("woof");
1025 policy->ProcessAction(action);
1027 action = new Action("punky",
1028 mock_clock->Now() - base::TimeDelta::FromMinutes(30),
1029 Action::ACTION_API_CALL,
1030 "brewster");
1031 action->mutable_args()->AppendString("meow");
1032 policy->ProcessAction(action);
1034 action = new Action("punky",
1035 mock_clock->Now() - base::TimeDelta::FromMinutes(20),
1036 Action::ACTION_API_CALL,
1037 "extension.sendMessage");
1038 action->mutable_args()->AppendString("not");
1039 action->mutable_args()->AppendString("stripped");
1040 policy->ProcessAction(action);
1042 action =
1043 new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1044 action->mutable_args()->AppendString("vamoose");
1045 action->set_page_url(GURL("http://www.google.com"));
1046 policy->ProcessAction(action);
1048 action = new Action(
1049 "scoobydoo", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1050 action->mutable_args()->AppendString("vamoose");
1051 action->set_page_url(GURL("http://www.google.com"));
1052 policy->ProcessAction(action);
1054 CheckReadData(
1055 policy,
1056 "punky",
1058 base::Bind(&CountingPolicyTest::Arguments_GetTodaysActions));
1060 policy->DeleteDatabase();
1062 CheckReadFilteredData(
1063 policy,
1065 Action::ACTION_ANY,
1070 base::Bind(
1071 &CountingPolicyTest::RetrieveActions_FetchFilteredActions0));
1073 policy->Close();
1076 // Tests that duplicate rows in the activity log database are handled properly
1077 // when updating counts.
1078 TEST_F(CountingPolicyTest, DuplicateRows) {
1079 CountingPolicy* policy = new CountingPolicy(profile_.get());
1080 policy->Init();
1081 base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1082 mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1083 base::TimeDelta::FromHours(12));
1084 policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1086 // Record two actions with distinct URLs.
1087 scoped_refptr<Action> action;
1088 action = new Action(
1089 "punky", mock_clock->Now(), Action::ACTION_API_CALL, "brewster");
1090 action->set_page_url(GURL("http://www.google.com"));
1091 policy->ProcessAction(action);
1093 action = new Action(
1094 "punky", mock_clock->Now(), Action::ACTION_API_CALL, "brewster");
1095 action->set_page_url(GURL("http://www.google.co.uk"));
1096 policy->ProcessAction(action);
1098 // Manipulate the database to clear the URLs, so that we end up with
1099 // duplicate rows.
1100 std::vector<GURL> no_url_restrictions;
1101 policy->RemoveURLs(no_url_restrictions);
1103 // Record one more action, with no URL. This should increment the count on
1104 // one, and exactly one, of the existing rows.
1105 action = new Action(
1106 "punky", mock_clock->Now(), Action::ACTION_API_CALL, "brewster");
1107 policy->ProcessAction(action);
1109 CheckReadData(
1110 policy,
1111 "punky",
1113 base::Bind(&CountingPolicyTest::CheckDuplicates));
1114 policy->Close();
1117 } // namespace extensions