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