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.
4
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"
26
27#if defined(OS_CHROMEOS)
28#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
29#include "chrome/browser/chromeos/settings/cros_settings.h"
30#include "chrome/browser/chromeos/settings/device_settings_service.h"
31#endif
32
33using content::BrowserThread;
34
35namespace extensions {
36
37class CountingPolicyTest : public testing::Test {
38 public:
39  CountingPolicyTest()
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());
44#endif
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);
53  }
54
55  virtual ~CountingPolicyTest() {
56#if defined OS_CHROMEOS
57    test_user_manager_.reset();
58#endif
59    base::RunLoop().RunUntilIdle();
60    profile_.reset(NULL);
61    base::RunLoop().RunUntilIdle();
62    // Restore the original command line and undo the affects of SetUp().
63    *CommandLine::ForCurrentProcess() = saved_cmdline_;
64  }
65
66  // Wait for the task queue for the specified thread to empty.
67  void WaitOnThread(const BrowserThread::ID& thread) {
68    BrowserThread::PostTaskAndReply(
69        thread,
70        FROM_HERE,
71        base::Bind(&base::DoNothing),
72        base::MessageLoop::current()->QuitClosure());
73    base::MessageLoop::current()->Run();
74  }
75
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.
78  void CheckReadData(
79      ActivityLogDatabasePolicy* policy,
80      const std::string& extension_id,
81      int day,
82      const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
83    CheckReadFilteredData(
84        policy, extension_id, Action::ACTION_ANY, "", "", "", day, checker);
85  }
86
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,
96      int day,
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
100    // database thread.
101    policy->ReadFilteredData(
102        extension_id,
103        type,
104        api_name,
105        page_url,
106        arg_url,
107        day,
108        base::Bind(&CountingPolicyTest::CheckWrapper,
109                   checker,
110                   base::MessageLoop::current()->QuitClosure()));
111
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());
118
119    // Wait for results; either the checker or the timeout callbacks should
120    // cause the main loop to exit.
121    base::MessageLoop::current()->Run();
122
123    timeout.Cancel();
124  }
125
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,
129                                    int string_size,
130                                    int url_size) {
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));
136
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));
141  }
142
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
147    // changes.
148    ASSERT_LE(policy->queued_actions_.size(), 200U);
149  }
150
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());
156    done.Run();
157  }
158
159  static void TimeoutCallback() {
160    base::MessageLoop::current()->QuitWhenIdle();
161    FAIL() << "Policy test timed out waiting for results";
162  }
163
164  static void RetrieveActions_FetchFilteredActions0(
165      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
166    ASSERT_EQ(0, static_cast<int>(i->size()));
167  }
168
169  static void RetrieveActions_FetchFilteredActions1(
170      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
171    ASSERT_EQ(1, static_cast<int>(i->size()));
172  }
173
174  static void RetrieveActions_FetchFilteredActions2(
175      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
176    ASSERT_EQ(2, static_cast<int>(i->size()));
177  }
178
179  static void RetrieveActions_FetchFilteredActions300(
180      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
181    ASSERT_EQ(300, static_cast<int>(i->size()));
182  }
183
184  static void Arguments_Stripped(scoped_ptr<Action::ActionVector> i) {
185    scoped_refptr<Action> last = i->front();
186    CheckAction(*last.get(),
187                "odlameecjipmbmbejkplpemijjgpljce",
188                Action::ACTION_API_CALL,
189                "extension.connect",
190                "[\"hello\",\"world\"]",
191                "",
192                "",
193                "",
194                1);
195  }
196
197  static void Arguments_GetSinglesAction(
198      scoped_ptr<Action::ActionVector> actions) {
199    ASSERT_EQ(1, static_cast<int>(actions->size()));
200    CheckAction(*actions->at(0).get(),
201                "punky",
202                Action::ACTION_DOM_ACCESS,
203                "lets",
204                "",
205                "http://www.google.com/",
206                "",
207                "",
208                1);
209  }
210
211  static void Arguments_GetTodaysActions(
212      scoped_ptr<Action::ActionVector> actions) {
213    ASSERT_EQ(3, static_cast<int>(actions->size()));
214    CheckAction(*actions->at(0).get(),
215                "punky",
216                Action::ACTION_API_CALL,
217                "brewster",
218                "",
219                "",
220                "",
221                "",
222                2);
223    CheckAction(*actions->at(1).get(),
224                "punky",
225                Action::ACTION_DOM_ACCESS,
226                "lets",
227                "",
228                "http://www.google.com/",
229                "",
230                "",
231                1);
232    CheckAction(*actions->at(2).get(),
233                "punky",
234                Action::ACTION_API_CALL,
235                "extension.sendMessage",
236                "[\"not\",\"stripped\"]",
237                "",
238                "",
239                "",
240                1);
241  }
242
243  static void Arguments_GetOlderActions(
244      scoped_ptr<Action::ActionVector> actions) {
245    ASSERT_EQ(2, static_cast<int>(actions->size()));
246    CheckAction(*actions->at(0).get(),
247                "punky",
248                Action::ACTION_DOM_ACCESS,
249                "lets",
250                "",
251                "http://www.google.com/",
252                "",
253                "",
254                1);
255    CheckAction(*actions->at(1).get(),
256                "punky",
257                Action::ACTION_API_CALL,
258                "brewster",
259                "",
260                "",
261                "",
262                "",
263                1);
264  }
265
266  static void Arguments_CheckMergeCount(
267      int count,
268      scoped_ptr<Action::ActionVector> actions) {
269    if (count > 0) {
270      ASSERT_EQ(1u, actions->size());
271      CheckAction(*actions->at(0).get(),
272                  "punky",
273                  Action::ACTION_API_CALL,
274                  "brewster",
275                  "",
276                  "",
277                  "",
278                  "",
279                  count);
280    } else {
281      ASSERT_EQ(0u, actions->size());
282    }
283  }
284
285  static void Arguments_CheckMergeCountAndTime(
286      int count,
287      const base::Time& time,
288      scoped_ptr<Action::ActionVector> actions) {
289    if (count > 0) {
290      ASSERT_EQ(1u, actions->size());
291      CheckAction(*actions->at(0).get(),
292                  "punky",
293                  Action::ACTION_API_CALL,
294                  "brewster",
295                  "",
296                  "",
297                  "",
298                  "",
299                  count);
300      ASSERT_EQ(time, actions->at(0)->time());
301    } else {
302      ASSERT_EQ(0u, actions->size());
303    }
304  }
305
306  static void AllURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
307    ASSERT_EQ(2, static_cast<int>(actions->size()));
308    CheckAction(*actions->at(0).get(),
309                "punky",
310                Action::ACTION_DOM_ACCESS,
311                "lets",
312                "",
313                "",
314                "",
315                "",
316                1);
317    CheckAction(*actions->at(1).get(),
318                "punky",
319                Action::ACTION_DOM_ACCESS,
320                "lets",
321                "",
322                "",
323                "",
324                "",
325                1);
326  }
327
328  static void SomeURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
329    // These will be in the vector in reverse time order.
330    ASSERT_EQ(5, static_cast<int>(actions->size()));
331    CheckAction(*actions->at(0).get(),
332                "punky",
333                Action::ACTION_DOM_ACCESS,
334                "lets",
335                "",
336                "http://www.google.com/",
337                "Google",
338                "http://www.args-url.com/",
339                1);
340    CheckAction(*actions->at(1).get(),
341                "punky",
342                Action::ACTION_DOM_ACCESS,
343                "lets",
344                "",
345                "http://www.google.com/",
346                "Google",
347                "",
348                1);
349    CheckAction(*actions->at(2).get(),
350                "punky",
351                Action::ACTION_DOM_ACCESS,
352                "lets",
353                "",
354                "",
355                "",
356                "",
357                1);
358    CheckAction(*actions->at(3).get(),
359                "punky",
360                Action::ACTION_DOM_ACCESS,
361                "lets",
362                "",
363                "",
364                "",
365                "http://www.google.com/",
366                1);
367    CheckAction(*actions->at(4).get(),
368                "punky",
369                Action::ACTION_DOM_ACCESS,
370                "lets",
371                "",
372                "",
373                "",
374                "",
375                1);
376  }
377
378  static void CheckDuplicates(scoped_ptr<Action::ActionVector> actions) {
379    ASSERT_EQ(2u, actions->size());
380    int total_count = 0;
381    for (size_t i = 0; i < actions->size(); i++) {
382      total_count += actions->at(i)->count();
383    }
384    ASSERT_EQ(3, total_count);
385  }
386
387  static void CheckAction(const Action& action,
388                          const std::string& expected_id,
389                          const Action::ActionType& expected_type,
390                          const std::string& expected_api_name,
391                          const std::string& expected_args_str,
392                          const std::string& expected_page_url,
393                          const std::string& expected_page_title,
394                          const std::string& expected_arg_url,
395                          int expected_count) {
396    ASSERT_EQ(expected_id, action.extension_id());
397    ASSERT_EQ(expected_type, action.action_type());
398    ASSERT_EQ(expected_api_name, action.api_name());
399    ASSERT_EQ(expected_args_str,
400              ActivityLogPolicy::Util::Serialize(action.args()));
401    ASSERT_EQ(expected_page_url, action.SerializePageUrl());
402    ASSERT_EQ(expected_page_title, action.page_title());
403    ASSERT_EQ(expected_arg_url, action.SerializeArgUrl());
404    ASSERT_EQ(expected_count, action.count());
405    ASSERT_NE(-1, action.action_id());
406  }
407
408  // A helper function initializes the policy with a number of actions, calls
409  // RemoveActions on a policy object and then checks the result of the
410  // deletion.
411  void CheckRemoveActions(
412      ActivityLogDatabasePolicy* policy,
413      const std::vector<int64>& action_ids,
414      const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
415
416    // Use a mock clock to ensure that events are not recorded on the wrong day
417    // when the test is run close to local midnight.
418    base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
419    mock_clock->SetNow(base::Time::Now().LocalMidnight() +
420                       base::TimeDelta::FromHours(12));
421    policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
422
423    // Record some actions
424    scoped_refptr<Action> action =
425        new Action("punky1",
426                   mock_clock->Now() - base::TimeDelta::FromMinutes(40),
427                   Action::ACTION_DOM_ACCESS,
428                   "lets1");
429    action->mutable_args()->AppendString("vamoose1");
430    action->set_page_url(GURL("http://www.google1.com"));
431    action->set_page_title("Google1");
432    action->set_arg_url(GURL("http://www.args-url1.com"));
433    policy->ProcessAction(action);
434    // Record the same action twice, so there are multiple entries in the
435    // database.
436    policy->ProcessAction(action);
437
438    action = new Action("punky2",
439                        mock_clock->Now() - base::TimeDelta::FromMinutes(30),
440                        Action::ACTION_API_CALL,
441                        "lets2");
442    action->mutable_args()->AppendString("vamoose2");
443    action->set_page_url(GURL("http://www.google2.com"));
444    action->set_page_title("Google2");
445    action->set_arg_url(GURL("http://www.args-url2.com"));
446    policy->ProcessAction(action);
447    // Record the same action twice, so there are multiple entries in the
448    // database.
449    policy->ProcessAction(action);
450
451    // Submit a request to delete actions.
452    policy->RemoveActions(action_ids);
453
454    // Check the result of the deletion. The checker function gets all
455    // activities in the database.
456    CheckReadData(policy, "", -1, checker);
457
458    // Clean database.
459    policy->DeleteDatabase();
460  }
461
462  static void AllActionsDeleted(scoped_ptr<Action::ActionVector> actions) {
463    ASSERT_EQ(0, static_cast<int>(actions->size()));
464  }
465
466  static void NoActionsDeleted(scoped_ptr<Action::ActionVector> actions) {
467    // These will be in the vector in reverse time order.
468    ASSERT_EQ(2, static_cast<int>(actions->size()));
469    CheckAction(*actions->at(0).get(),
470                "punky2",
471                Action::ACTION_API_CALL,
472                "lets2",
473                "",
474                "http://www.google2.com/",
475                "Google2",
476                "http://www.args-url2.com/",
477                2);
478    ASSERT_EQ(2, actions->at(0)->action_id());
479    CheckAction(*actions->at(1).get(),
480                "punky1",
481                Action::ACTION_DOM_ACCESS,
482                "lets1",
483                "",
484                "http://www.google1.com/",
485                "Google1",
486                "http://www.args-url1.com/",
487                2);
488    ASSERT_EQ(1, actions->at(1)->action_id());
489  }
490
491  static void Action1Deleted(scoped_ptr<Action::ActionVector> actions) {
492    // These will be in the vector in reverse time order.
493    ASSERT_EQ(1, static_cast<int>(actions->size()));
494    CheckAction(*actions->at(0).get(),
495                "punky2",
496                Action::ACTION_API_CALL,
497                "lets2",
498                "",
499                "http://www.google2.com/",
500                "Google2",
501                "http://www.args-url2.com/",
502                2);
503    ASSERT_EQ(2, actions->at(0)->action_id());
504  }
505
506  static void Action2Deleted(scoped_ptr<Action::ActionVector> actions) {
507    // These will be in the vector in reverse time order.
508    ASSERT_EQ(1, static_cast<int>(actions->size()));
509    CheckAction(*actions->at(0).get(),
510                "punky1",
511                Action::ACTION_DOM_ACCESS,
512                "lets1",
513                "",
514                "http://www.google1.com/",
515                "Google1",
516                "http://www.args-url1.com/",
517                2);
518    ASSERT_EQ(1, actions->at(0)->action_id());
519  }
520
521 protected:
522  ExtensionService* extension_service_;
523  scoped_ptr<TestingProfile> profile_;
524  content::TestBrowserThreadBundle thread_bundle_;
525  // Used to preserve a copy of the original command line.
526  // The test framework will do this itself as well. However, by then,
527  // it is too late to call ActivityLog::RecomputeLoggingIsEnabled() in
528  // TearDown().
529  CommandLine saved_cmdline_;
530
531#if defined OS_CHROMEOS
532  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
533  chromeos::ScopedTestCrosSettings test_cros_settings_;
534  scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
535#endif
536};
537
538TEST_F(CountingPolicyTest, Construct) {
539  ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
540  policy->Init();
541  scoped_refptr<const Extension> extension =
542      ExtensionBuilder()
543          .SetManifest(DictionaryBuilder()
544                       .Set("name", "Test extension")
545                       .Set("version", "1.0.0")
546                       .Set("manifest_version", 2))
547          .Build();
548  extension_service_->AddExtension(extension.get());
549  scoped_ptr<base::ListValue> args(new base::ListValue());
550  scoped_refptr<Action> action = new Action(extension->id(),
551                                            base::Time::Now(),
552                                            Action::ACTION_API_CALL,
553                                            "tabs.testMethod");
554  action->set_args(args.Pass());
555  policy->ProcessAction(action);
556  policy->Close();
557}
558
559TEST_F(CountingPolicyTest, LogWithStrippedArguments) {
560  ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
561  policy->Init();
562  scoped_refptr<const Extension> extension =
563      ExtensionBuilder()
564          .SetManifest(DictionaryBuilder()
565                       .Set("name", "Test extension")
566                       .Set("version", "1.0.0")
567                       .Set("manifest_version", 2))
568          .Build();
569  extension_service_->AddExtension(extension.get());
570
571  scoped_ptr<base::ListValue> args(new base::ListValue());
572  args->Set(0, new base::StringValue("hello"));
573  args->Set(1, new base::StringValue("world"));
574  scoped_refptr<Action> action = new Action(extension->id(),
575                                            base::Time::Now(),
576                                            Action::ACTION_API_CALL,
577                                            "extension.connect");
578  action->set_args(args.Pass());
579
580  policy->ProcessAction(action);
581  CheckReadData(policy,
582                extension->id(),
583                0,
584                base::Bind(&CountingPolicyTest::Arguments_Stripped));
585  policy->Close();
586}
587
588TEST_F(CountingPolicyTest, GetTodaysActions) {
589  CountingPolicy* policy = new CountingPolicy(profile_.get());
590  policy->Init();
591  // Disable row expiration for this test by setting a time before any actions
592  // we generate.
593  policy->set_retention_time(base::TimeDelta::FromDays(14));
594
595  // Use a mock clock to ensure that events are not recorded on the wrong day
596  // when the test is run close to local midnight.  Note: Ownership is passed
597  // to the policy, but we still keep a pointer locally.  The policy will take
598  // care of destruction; this is safe since the policy outlives all our
599  // accesses to the mock clock.
600  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
601  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
602                     base::TimeDelta::FromHours(12));
603  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
604
605  // Record some actions
606  scoped_refptr<Action> action =
607      new Action("punky",
608                 mock_clock->Now() - base::TimeDelta::FromMinutes(40),
609                 Action::ACTION_API_CALL,
610                 "brewster");
611  action->mutable_args()->AppendString("woof");
612  policy->ProcessAction(action);
613
614  action = new Action("punky",
615                      mock_clock->Now() - base::TimeDelta::FromMinutes(30),
616                      Action::ACTION_API_CALL,
617                      "brewster");
618  action->mutable_args()->AppendString("meow");
619  policy->ProcessAction(action);
620
621  action = new Action("punky",
622                      mock_clock->Now() - base::TimeDelta::FromMinutes(20),
623                      Action::ACTION_API_CALL,
624                      "extension.sendMessage");
625  action->mutable_args()->AppendString("not");
626  action->mutable_args()->AppendString("stripped");
627  policy->ProcessAction(action);
628
629  action =
630      new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
631  action->mutable_args()->AppendString("vamoose");
632  action->set_page_url(GURL("http://www.google.com"));
633  policy->ProcessAction(action);
634
635  action = new Action(
636      "scoobydoo", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
637  action->mutable_args()->AppendString("vamoose");
638  action->set_page_url(GURL("http://www.google.com"));
639  policy->ProcessAction(action);
640
641  CheckReadData(
642      policy,
643      "punky",
644      0,
645      base::Bind(&CountingPolicyTest::Arguments_GetTodaysActions));
646  policy->Close();
647}
648
649// Check that we can read back less recent actions in the db.
650TEST_F(CountingPolicyTest, GetOlderActions) {
651  CountingPolicy* policy = new CountingPolicy(profile_.get());
652  policy->Init();
653  policy->set_retention_time(base::TimeDelta::FromDays(14));
654
655  // Use a mock clock to ensure that events are not recorded on the wrong day
656  // when the test is run close to local midnight.
657  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
658  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
659                     base::TimeDelta::FromHours(12));
660  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
661
662  // Record some actions
663  scoped_refptr<Action> action =
664      new Action("punky",
665                 mock_clock->Now() - base::TimeDelta::FromDays(3) -
666                     base::TimeDelta::FromMinutes(40),
667                 Action::ACTION_API_CALL,
668                 "brewster");
669  action->mutable_args()->AppendString("woof");
670  policy->ProcessAction(action);
671
672  action = new Action("punky",
673                      mock_clock->Now() - base::TimeDelta::FromDays(3),
674                      Action::ACTION_DOM_ACCESS,
675                      "lets");
676  action->mutable_args()->AppendString("vamoose");
677  action->set_page_url(GURL("http://www.google.com"));
678  policy->ProcessAction(action);
679
680  action = new Action("punky",
681                      mock_clock->Now(),
682                      Action::ACTION_DOM_ACCESS,
683                      "lets");
684  action->mutable_args()->AppendString("too new");
685  action->set_page_url(GURL("http://www.google.com"));
686  policy->ProcessAction(action);
687
688  action = new Action("punky",
689                      mock_clock->Now() - base::TimeDelta::FromDays(7),
690                      Action::ACTION_DOM_ACCESS,
691                      "lets");
692  action->mutable_args()->AppendString("too old");
693  action->set_page_url(GURL("http://www.google.com"));
694  policy->ProcessAction(action);
695
696  CheckReadData(
697      policy,
698      "punky",
699      3,
700      base::Bind(&CountingPolicyTest::Arguments_GetOlderActions));
701
702  policy->Close();
703}
704
705TEST_F(CountingPolicyTest, LogAndFetchFilteredActions) {
706  ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
707  policy->Init();
708  scoped_refptr<const Extension> extension =
709      ExtensionBuilder()
710          .SetManifest(DictionaryBuilder()
711                       .Set("name", "Test extension")
712                       .Set("version", "1.0.0")
713                       .Set("manifest_version", 2))
714          .Build();
715  extension_service_->AddExtension(extension.get());
716  GURL gurl("http://www.google.com");
717
718  // Write some API calls
719  scoped_refptr<Action> action_api = new Action(extension->id(),
720                                                base::Time::Now(),
721                                                Action::ACTION_API_CALL,
722                                                "tabs.testMethod");
723  action_api->set_args(make_scoped_ptr(new base::ListValue()));
724  policy->ProcessAction(action_api);
725
726  scoped_refptr<Action> action_dom = new Action(extension->id(),
727                                                base::Time::Now(),
728                                                Action::ACTION_DOM_ACCESS,
729                                                "document.write");
730  action_dom->set_args(make_scoped_ptr(new base::ListValue()));
731  action_dom->set_page_url(gurl);
732  policy->ProcessAction(action_dom);
733
734  CheckReadFilteredData(
735      policy,
736      extension->id(),
737      Action::ACTION_API_CALL,
738      "tabs.testMethod",
739      "",
740      "",
741      -1,
742      base::Bind(
743          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
744
745  CheckReadFilteredData(
746      policy,
747      "",
748      Action::ACTION_DOM_ACCESS,
749      "",
750      "",
751      "",
752      -1,
753      base::Bind(
754          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
755
756  CheckReadFilteredData(
757      policy,
758      "",
759      Action::ACTION_DOM_ACCESS,
760      "",
761      "http://www.google.com/",
762      "",
763      -1,
764      base::Bind(
765          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
766
767  CheckReadFilteredData(
768      policy,
769      "",
770      Action::ACTION_DOM_ACCESS,
771      "",
772      "http://www.google.com",
773      "",
774      -1,
775      base::Bind(
776          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
777
778  CheckReadFilteredData(
779      policy,
780      "",
781      Action::ACTION_DOM_ACCESS,
782      "",
783      "http://www.goo",
784      "",
785      -1,
786      base::Bind(
787          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
788
789  CheckReadFilteredData(
790      policy,
791      extension->id(),
792      Action::ACTION_ANY,
793      "",
794      "",
795      "",
796      -1,
797      base::Bind(
798          &CountingPolicyTest::RetrieveActions_FetchFilteredActions2));
799
800  policy->Close();
801}
802
803// Check that merging of actions only occurs within the same day, not across
804// days, and that old data can be expired from the database.
805TEST_F(CountingPolicyTest, MergingAndExpiring) {
806  CountingPolicy* policy = new CountingPolicy(profile_.get());
807  policy->Init();
808  // Initially disable expiration by setting a retention time before any
809  // actions we generate.
810  policy->set_retention_time(base::TimeDelta::FromDays(14));
811
812  // Use a mock clock to ensure that events are not recorded on the wrong day
813  // when the test is run close to local midnight.
814  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
815  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
816                    base::TimeDelta::FromHours(12));
817  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
818
819  // The first two actions should be merged; the last one is on a separate day
820  // and should not be.
821  scoped_refptr<Action> action =
822      new Action("punky",
823                 mock_clock->Now() - base::TimeDelta::FromDays(3) -
824                     base::TimeDelta::FromMinutes(40),
825                 Action::ACTION_API_CALL,
826                 "brewster");
827  policy->ProcessAction(action);
828
829  action = new Action("punky",
830                      mock_clock->Now() - base::TimeDelta::FromDays(3) -
831                          base::TimeDelta::FromMinutes(20),
832                      Action::ACTION_API_CALL,
833                      "brewster");
834  policy->ProcessAction(action);
835
836  action = new Action("punky",
837                      mock_clock->Now() - base::TimeDelta::FromDays(2) -
838                          base::TimeDelta::FromMinutes(20),
839                      Action::ACTION_API_CALL,
840                      "brewster");
841  policy->ProcessAction(action);
842
843  CheckReadData(policy,
844                "punky",
845                3,
846                base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 2));
847  CheckReadData(policy,
848                "punky",
849                2,
850                base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 1));
851
852  // Clean actions before midnight two days ago.  Force expiration to run by
853  // clearing last_database_cleaning_time_ and submitting a new action.
854  policy->set_retention_time(base::TimeDelta::FromDays(2));
855  policy->last_database_cleaning_time_ = base::Time();
856  action = new Action("punky",
857                      mock_clock->Now(),
858                      Action::ACTION_API_CALL,
859                      "brewster");
860  policy->ProcessAction(action);
861
862  CheckReadData(policy,
863                "punky",
864                3,
865                base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 0));
866  CheckReadData(policy,
867                "punky",
868                2,
869                base::Bind(&CountingPolicyTest::Arguments_CheckMergeCount, 1));
870
871  policy->Close();
872}
873
874// Test cleaning of old data in the string and URL tables.
875TEST_F(CountingPolicyTest, StringTableCleaning) {
876  CountingPolicy* policy = new CountingPolicy(profile_.get());
877  policy->Init();
878  // Initially disable expiration by setting a retention time before any
879  // actions we generate.
880  policy->set_retention_time(base::TimeDelta::FromDays(14));
881
882  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
883  mock_clock->SetNow(base::Time::Now());
884  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
885
886  // Insert an action; this should create entries in both the string table (for
887  // the extension and API name) and the URL table (for page_url).
888  scoped_refptr<Action> action =
889      new Action("punky",
890                 mock_clock->Now() - base::TimeDelta::FromDays(7),
891                 Action::ACTION_API_CALL,
892                 "brewster");
893  action->set_page_url(GURL("http://www.google.com/"));
894  policy->ProcessAction(action);
895
896  // Add an action which will not be expired, so that some strings will remain
897  // in use.
898  action = new Action(
899      "punky", mock_clock->Now(), Action::ACTION_API_CALL, "tabs.create");
900  policy->ProcessAction(action);
901
902  // There should now be three strings ("punky", "brewster", "tabs.create") and
903  // one URL in the tables.
904  policy->Flush();
905  policy->ScheduleAndForget(policy,
906                            &CountingPolicyTest::CheckStringTableSizes,
907                            3,
908                            1);
909  WaitOnThread(BrowserThread::DB);
910
911  // Trigger a cleaning.  The oldest action is expired when we submit a
912  // duplicate of the newer action.  After this, there should be two strings
913  // and no URLs.
914  policy->set_retention_time(base::TimeDelta::FromDays(2));
915  policy->last_database_cleaning_time_ = base::Time();
916  policy->ProcessAction(action);
917  policy->Flush();
918  policy->ScheduleAndForget(policy,
919                            &CountingPolicyTest::CheckStringTableSizes,
920                            2,
921                            0);
922  WaitOnThread(BrowserThread::DB);
923
924  policy->Close();
925}
926
927// A stress test for memory- and database-based merging of actions.  Submit
928// multiple items, not in chronological order, spanning a few days.  Check that
929// items are merged properly and final timestamps are correct.
930TEST_F(CountingPolicyTest, MoreMerging) {
931  CountingPolicy* policy = new CountingPolicy(profile_.get());
932  policy->Init();
933  policy->set_retention_time(base::TimeDelta::FromDays(14));
934
935  // Use a mock clock to ensure that events are not recorded on the wrong day
936  // when the test is run close to local midnight.
937  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
938  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
939                    base::TimeDelta::FromHours(12));
940  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
941
942  // Create an action 2 days ago, then 1 day ago, then 2 days ago.  Make sure
943  // that we end up with two merged records (one for each day), and each has
944  // the appropriate timestamp.  These merges should happen in the database
945  // since the date keeps changing.
946  base::Time time1 =
947      mock_clock->Now() - base::TimeDelta::FromDays(2) -
948      base::TimeDelta::FromMinutes(40);
949  base::Time time2 =
950      mock_clock->Now() - base::TimeDelta::FromDays(1) -
951      base::TimeDelta::FromMinutes(40);
952  base::Time time3 =
953      mock_clock->Now() - base::TimeDelta::FromDays(2) -
954      base::TimeDelta::FromMinutes(20);
955
956  scoped_refptr<Action> action =
957      new Action("punky", time1, Action::ACTION_API_CALL, "brewster");
958  policy->ProcessAction(action);
959
960  action = new Action("punky", time2, Action::ACTION_API_CALL, "brewster");
961  policy->ProcessAction(action);
962
963  action = new Action("punky", time3, Action::ACTION_API_CALL, "brewster");
964  policy->ProcessAction(action);
965
966  CheckReadData(
967      policy,
968      "punky",
969      2,
970      base::Bind(
971          &CountingPolicyTest::Arguments_CheckMergeCountAndTime, 2, time3));
972  CheckReadData(
973      policy,
974      "punky",
975      1,
976      base::Bind(
977          &CountingPolicyTest::Arguments_CheckMergeCountAndTime, 1, time2));
978
979  // Create three actions today, where the merges should happen in memory.
980  // Again these are not chronological; timestamp time5 should win out since it
981  // is the latest.
982  base::Time time4 = mock_clock->Now() - base::TimeDelta::FromMinutes(60);
983  base::Time time5 = mock_clock->Now() - base::TimeDelta::FromMinutes(20);
984  base::Time time6 = mock_clock->Now() - base::TimeDelta::FromMinutes(40);
985
986  action = new Action("punky", time4, Action::ACTION_API_CALL, "brewster");
987  policy->ProcessAction(action);
988
989  action = new Action("punky", time5, Action::ACTION_API_CALL, "brewster");
990  policy->ProcessAction(action);
991
992  action = new Action("punky", time6, Action::ACTION_API_CALL, "brewster");
993  policy->ProcessAction(action);
994
995  CheckReadData(
996      policy,
997      "punky",
998      0,
999      base::Bind(
1000          &CountingPolicyTest::Arguments_CheckMergeCountAndTime, 3, time5));
1001  policy->Close();
1002}
1003
1004// Check that actions are flushed to disk before letting too many accumulate in
1005// memory.
1006TEST_F(CountingPolicyTest, EarlyFlush) {
1007  CountingPolicy* policy = new CountingPolicy(profile_.get());
1008  policy->Init();
1009
1010  for (int i = 0; i < 500; i++) {
1011    scoped_refptr<Action> action =
1012        new Action("punky",
1013                   base::Time::Now(),
1014                   Action::ACTION_API_CALL,
1015                   base::StringPrintf("apicall_%d", i));
1016    policy->ProcessAction(action);
1017  }
1018
1019  policy->ScheduleAndForget(policy, &CountingPolicyTest::CheckQueueSize);
1020  WaitOnThread(BrowserThread::DB);
1021
1022  policy->Close();
1023}
1024
1025TEST_F(CountingPolicyTest, CapReturns) {
1026  CountingPolicy* policy = new CountingPolicy(profile_.get());
1027  policy->Init();
1028
1029  for (int i = 0; i < 305; i++) {
1030    scoped_refptr<Action> action =
1031        new Action("punky",
1032                   base::Time::Now(),
1033                   Action::ACTION_API_CALL,
1034                   base::StringPrintf("apicall_%d", i));
1035    policy->ProcessAction(action);
1036  }
1037
1038  policy->Flush();
1039  WaitOnThread(BrowserThread::DB);
1040
1041  CheckReadFilteredData(
1042      policy,
1043      "punky",
1044      Action::ACTION_ANY,
1045      "",
1046      "",
1047      "",
1048      -1,
1049      base::Bind(
1050          &CountingPolicyTest::RetrieveActions_FetchFilteredActions300));
1051  policy->Close();
1052}
1053
1054TEST_F(CountingPolicyTest, RemoveAllURLs) {
1055  ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
1056  policy->Init();
1057
1058  // Use a mock clock to ensure that events are not recorded on the wrong day
1059  // when the test is run close to local midnight.
1060  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1061  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1062                     base::TimeDelta::FromHours(12));
1063  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1064
1065  // Record some actions
1066  scoped_refptr<Action> action =
1067      new Action("punky", mock_clock->Now(),
1068                 Action::ACTION_DOM_ACCESS, "lets");
1069  action->mutable_args()->AppendString("vamoose");
1070  action->set_page_url(GURL("http://www.google.com"));
1071  action->set_page_title("Google");
1072  action->set_arg_url(GURL("http://www.args-url.com"));
1073  policy->ProcessAction(action);
1074
1075  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
1076  action = new Action(
1077      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1078  action->mutable_args()->AppendString("vamoose");
1079  action->set_page_url(GURL("http://www.google2.com"));
1080  action->set_page_title("Google");
1081  // Deliberately no arg url set to make sure it stills works if there is no arg
1082  // url.
1083  policy->ProcessAction(action);
1084
1085  // Clean all the URLs.
1086  std::vector<GURL> no_url_restrictions;
1087  policy->RemoveURLs(no_url_restrictions);
1088
1089  CheckReadData(
1090      policy,
1091      "punky",
1092      0,
1093      base::Bind(&CountingPolicyTest::AllURLsRemoved));
1094  policy->Close();
1095}
1096
1097TEST_F(CountingPolicyTest, RemoveSpecificURLs) {
1098  ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
1099  policy->Init();
1100
1101  // Use a mock clock to ensure that events are not recorded on the wrong day
1102  // when the test is run close to local midnight.
1103  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1104  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1105                     base::TimeDelta::FromHours(12));
1106  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1107
1108  // Record some actions
1109  // This should have the page url and args url cleared.
1110  scoped_refptr<Action> action = new Action("punky", mock_clock->Now(),
1111                                            Action::ACTION_DOM_ACCESS, "lets");
1112  action->mutable_args()->AppendString("vamoose");
1113  action->set_page_url(GURL("http://www.google1.com"));
1114  action->set_page_title("Google");
1115  action->set_arg_url(GURL("http://www.google1.com"));
1116  policy->ProcessAction(action);
1117
1118  // This should have the page url cleared but not args url.
1119  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
1120  action = new Action(
1121      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1122  action->mutable_args()->AppendString("vamoose");
1123  action->set_page_url(GURL("http://www.google1.com"));
1124  action->set_page_title("Google");
1125  action->set_arg_url(GURL("http://www.google.com"));
1126  policy->ProcessAction(action);
1127
1128  // This should have the page url cleared. The args url is deliberately not
1129  // set to make sure this doesn't cause any issues.
1130  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
1131  action = new Action(
1132      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1133  action->mutable_args()->AppendString("vamoose");
1134  action->set_page_url(GURL("http://www.google2.com"));
1135  action->set_page_title("Google");
1136  policy->ProcessAction(action);
1137
1138  // This should have the args url cleared but not the page url or page title.
1139  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
1140  action = new Action(
1141      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1142  action->mutable_args()->AppendString("vamoose");
1143  action->set_page_url(GURL("http://www.google.com"));
1144  action->set_page_title("Google");
1145  action->set_arg_url(GURL("http://www.google1.com"));
1146  policy->ProcessAction(action);
1147
1148  // This should have neither cleared.
1149  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
1150  action = new Action(
1151      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1152  action->mutable_args()->AppendString("vamoose");
1153  action->set_page_url(GURL("http://www.google.com"));
1154  action->set_page_title("Google");
1155  action->set_arg_url(GURL("http://www.args-url.com"));
1156  action->set_count(5);
1157  policy->ProcessAction(action);
1158
1159    // Clean some URLs.
1160  std::vector<GURL> urls;
1161  urls.push_back(GURL("http://www.google1.com"));
1162  urls.push_back(GURL("http://www.google2.com"));
1163  urls.push_back(GURL("http://www.url_not_in_db.com"));
1164  policy->RemoveURLs(urls);
1165
1166  CheckReadData(
1167      policy,
1168      "punky",
1169      0,
1170      base::Bind(&CountingPolicyTest::SomeURLsRemoved));
1171  policy->Close();
1172}
1173
1174TEST_F(CountingPolicyTest, RemoveExtensionData) {
1175  CountingPolicy* policy = new CountingPolicy(profile_.get());
1176  policy->Init();
1177
1178  // Use a mock clock to ensure that events are not recorded on the wrong day
1179  // when the test is run close to local midnight.
1180  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1181  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1182                     base::TimeDelta::FromHours(12));
1183  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1184
1185  // Record some actions
1186  scoped_refptr<Action> action = new Action("deleteextensiondata",
1187                                            mock_clock->Now(),
1188                                            Action::ACTION_DOM_ACCESS,
1189                                            "lets");
1190  action->mutable_args()->AppendString("vamoose");
1191  action->set_page_title("Google");
1192  action->set_arg_url(GURL("http://www.google.com"));
1193  policy->ProcessAction(action);
1194  policy->ProcessAction(action);
1195  policy->ProcessAction(action);
1196
1197  scoped_refptr<Action> action2 = new Action("dontdelete",
1198                                             mock_clock->Now(),
1199                                             Action::ACTION_DOM_ACCESS,
1200                                             "lets");
1201  action->mutable_args()->AppendString("vamoose");
1202  action->set_page_title("Google");
1203  action->set_arg_url(GURL("http://www.google.com"));
1204  policy->ProcessAction(action2);
1205
1206  policy->Flush();
1207  policy->RemoveExtensionData("deleteextensiondata");
1208
1209  CheckReadFilteredData(
1210      policy,
1211      "deleteextensiondata",
1212      Action::ACTION_ANY,
1213      "",
1214      "",
1215      "",
1216      -1,
1217      base::Bind(
1218          &CountingPolicyTest::RetrieveActions_FetchFilteredActions0));
1219
1220  CheckReadFilteredData(
1221      policy,
1222      "dontdelete",
1223      Action::ACTION_ANY,
1224      "",
1225      "",
1226      "",
1227      -1,
1228      base::Bind(
1229          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
1230  policy->Close();
1231}
1232
1233TEST_F(CountingPolicyTest, DeleteDatabase) {
1234  CountingPolicy* policy = new CountingPolicy(profile_.get());
1235  policy->Init();
1236  // Disable row expiration for this test by setting a time before any actions
1237  // we generate.
1238  policy->set_retention_time(base::TimeDelta::FromDays(14));
1239
1240  // Use a mock clock to ensure that events are not recorded on the wrong day
1241  // when the test is run close to local midnight.  Note: Ownership is passed
1242  // to the policy, but we still keep a pointer locally.  The policy will take
1243  // care of destruction; this is safe since the policy outlives all our
1244  // accesses to the mock clock.
1245  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1246  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1247                     base::TimeDelta::FromHours(12));
1248  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1249
1250  // Record some actions
1251  scoped_refptr<Action> action =
1252      new Action("punky",
1253                 mock_clock->Now() - base::TimeDelta::FromMinutes(40),
1254                 Action::ACTION_API_CALL,
1255                 "brewster");
1256  action->mutable_args()->AppendString("woof");
1257  policy->ProcessAction(action);
1258
1259  action = new Action("punky",
1260                      mock_clock->Now() - base::TimeDelta::FromMinutes(30),
1261                      Action::ACTION_API_CALL,
1262                      "brewster");
1263  action->mutable_args()->AppendString("meow");
1264  policy->ProcessAction(action);
1265
1266  action = new Action("punky",
1267                      mock_clock->Now() - base::TimeDelta::FromMinutes(20),
1268                      Action::ACTION_API_CALL,
1269                      "extension.sendMessage");
1270  action->mutable_args()->AppendString("not");
1271  action->mutable_args()->AppendString("stripped");
1272  policy->ProcessAction(action);
1273
1274  action =
1275      new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1276  action->mutable_args()->AppendString("vamoose");
1277  action->set_page_url(GURL("http://www.google.com"));
1278  policy->ProcessAction(action);
1279
1280  action = new Action(
1281      "scoobydoo", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1282  action->mutable_args()->AppendString("vamoose");
1283  action->set_page_url(GURL("http://www.google.com"));
1284  policy->ProcessAction(action);
1285
1286  CheckReadData(
1287      policy,
1288      "punky",
1289      0,
1290      base::Bind(&CountingPolicyTest::Arguments_GetTodaysActions));
1291
1292  policy->DeleteDatabase();
1293
1294  CheckReadFilteredData(
1295      policy,
1296      "",
1297      Action::ACTION_ANY,
1298      "",
1299      "",
1300      "",
1301      -1,
1302      base::Bind(
1303          &CountingPolicyTest::RetrieveActions_FetchFilteredActions0));
1304
1305  // The following code tests that the caches of url and string tables were
1306  // cleared by the deletion above.
1307  // https://code.google.com/p/chromium/issues/detail?id=341674.
1308  action =
1309    new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
1310  action->mutable_args()->AppendString("vamoose");
1311  action->set_page_url(GURL("http://www.google.com"));
1312  policy->ProcessAction(action);
1313
1314  CheckReadData(
1315      policy,
1316      "",
1317      -1,
1318      base::Bind(&CountingPolicyTest::Arguments_GetSinglesAction));
1319
1320  policy->DeleteDatabase();
1321
1322  CheckReadFilteredData(
1323      policy,
1324      "",
1325      Action::ACTION_ANY,
1326      "",
1327      "",
1328      "",
1329      -1,
1330      base::Bind(
1331          &CountingPolicyTest::RetrieveActions_FetchFilteredActions0));
1332
1333  policy->Close();
1334}
1335
1336// Tests that duplicate rows in the activity log database are handled properly
1337// when updating counts.
1338TEST_F(CountingPolicyTest, DuplicateRows) {
1339  CountingPolicy* policy = new CountingPolicy(profile_.get());
1340  policy->Init();
1341  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
1342  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
1343                     base::TimeDelta::FromHours(12));
1344  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
1345
1346  // Record two actions with distinct URLs.
1347  scoped_refptr<Action> action;
1348  action = new Action(
1349      "punky", mock_clock->Now(), Action::ACTION_API_CALL, "brewster");
1350  action->set_page_url(GURL("http://www.google.com"));
1351  policy->ProcessAction(action);
1352
1353  action = new Action(
1354      "punky", mock_clock->Now(), Action::ACTION_API_CALL, "brewster");
1355  action->set_page_url(GURL("http://www.google.co.uk"));
1356  policy->ProcessAction(action);
1357
1358  // Manipulate the database to clear the URLs, so that we end up with
1359  // duplicate rows.
1360  std::vector<GURL> no_url_restrictions;
1361  policy->RemoveURLs(no_url_restrictions);
1362
1363  // Record one more action, with no URL.  This should increment the count on
1364  // one, and exactly one, of the existing rows.
1365  action = new Action(
1366      "punky", mock_clock->Now(), Action::ACTION_API_CALL, "brewster");
1367  policy->ProcessAction(action);
1368
1369  CheckReadData(
1370      policy,
1371      "punky",
1372      0,
1373      base::Bind(&CountingPolicyTest::CheckDuplicates));
1374  policy->Close();
1375}
1376
1377TEST_F(CountingPolicyTest, RemoveActions) {
1378  ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
1379  policy->Init();
1380
1381  std::vector<int64> action_ids;
1382
1383  CheckRemoveActions(
1384      policy, action_ids, base::Bind(&CountingPolicyTest::NoActionsDeleted));
1385
1386  action_ids.push_back(-1);
1387  action_ids.push_back(-10);
1388  action_ids.push_back(0);
1389  action_ids.push_back(5);
1390  action_ids.push_back(10);
1391  CheckRemoveActions(
1392      policy, action_ids, base::Bind(&CountingPolicyTest::NoActionsDeleted));
1393  action_ids.clear();
1394
1395  for (int i = 0; i < 50; i++) {
1396    action_ids.push_back(i + 3);
1397  }
1398  CheckRemoveActions(
1399      policy, action_ids, base::Bind(&CountingPolicyTest::NoActionsDeleted));
1400  action_ids.clear();
1401
1402  // CheckRemoveActions pushes two actions to the Activity Log database with IDs
1403  // 1 and 2.
1404  action_ids.push_back(1);
1405  action_ids.push_back(2);
1406  CheckRemoveActions(
1407      policy, action_ids, base::Bind(&CountingPolicyTest::AllActionsDeleted));
1408  action_ids.clear();
1409
1410  action_ids.push_back(1);
1411  CheckRemoveActions(
1412      policy, action_ids, base::Bind(&CountingPolicyTest::Action1Deleted));
1413  action_ids.clear();
1414
1415  action_ids.push_back(2);
1416  CheckRemoveActions(
1417      policy, action_ids, base::Bind(&CountingPolicyTest::Action2Deleted));
1418  action_ids.clear();
1419
1420  policy->Close();
1421}
1422
1423}  // namespace extensions
1424