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