Home | History | Annotate | Download | only in activity_log
      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/synchronization/waitable_event.h"
     10 #include "base/test/simple_test_clock.h"
     11 #include "chrome/browser/extensions/activity_log/activity_log.h"
     12 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
     13 #include "chrome/browser/extensions/extension_service.h"
     14 #include "chrome/browser/extensions/test_extension_system.h"
     15 #include "chrome/common/chrome_constants.h"
     16 #include "chrome/common/chrome_switches.h"
     17 #include "chrome/common/extensions/extension_builder.h"
     18 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     19 #include "chrome/test/base/testing_profile.h"
     20 #include "content/public/test/test_browser_thread_bundle.h"
     21 #include "sql/statement.h"
     22 #include "testing/gtest/include/gtest/gtest.h"
     23 
     24 #if defined(OS_CHROMEOS)
     25 #include "chrome/browser/chromeos/login/user_manager.h"
     26 #include "chrome/browser/chromeos/settings/cros_settings.h"
     27 #include "chrome/browser/chromeos/settings/device_settings_service.h"
     28 #endif
     29 
     30 namespace extensions {
     31 
     32 class FullStreamUIPolicyTest : public testing::Test {
     33  public:
     34   FullStreamUIPolicyTest()
     35       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
     36         saved_cmdline_(CommandLine::NO_PROGRAM) {
     37 #if defined OS_CHROMEOS
     38     test_user_manager_.reset(new chromeos::ScopedTestUserManager());
     39 #endif
     40     CommandLine command_line(CommandLine::NO_PROGRAM);
     41     saved_cmdline_ = *CommandLine::ForCurrentProcess();
     42     profile_.reset(new TestingProfile());
     43     CommandLine::ForCurrentProcess()->AppendSwitch(
     44         switches::kEnableExtensionActivityLogging);
     45     CommandLine::ForCurrentProcess()->AppendSwitch(
     46         switches::kEnableExtensionActivityLogTesting);
     47     extension_service_ = static_cast<TestExtensionSystem*>(
     48         ExtensionSystem::Get(profile_.get()))->CreateExtensionService
     49             (&command_line, base::FilePath(), false);
     50   }
     51 
     52   virtual ~FullStreamUIPolicyTest() {
     53 #if defined OS_CHROMEOS
     54     test_user_manager_.reset();
     55 #endif
     56     base::RunLoop().RunUntilIdle();
     57     profile_.reset(NULL);
     58     base::RunLoop().RunUntilIdle();
     59     // Restore the original command line and undo the affects of SetUp().
     60     *CommandLine::ForCurrentProcess() = saved_cmdline_;
     61   }
     62 
     63   // A helper function to call ReadData on a policy object and wait for the
     64   // results to be processed.
     65   void CheckReadData(
     66       ActivityLogPolicy* policy,
     67       const std::string& extension_id,
     68       const int day,
     69       const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
     70     // Submit a request to the policy to read back some data, and call the
     71     // checker function when results are available.  This will happen on the
     72     // database thread.
     73     policy->ReadData(
     74         extension_id,
     75         day,
     76         base::Bind(&FullStreamUIPolicyTest::CheckWrapper,
     77                    checker,
     78                    base::MessageLoop::current()->QuitClosure()));
     79 
     80     // Set up a timeout that will trigger after 5 seconds; if we haven't
     81     // received any results by then assume that the test is broken.
     82     base::CancelableClosure timeout(
     83         base::Bind(&FullStreamUIPolicyTest::TimeoutCallback));
     84     base::MessageLoop::current()->PostDelayedTask(
     85         FROM_HERE, timeout.callback(), base::TimeDelta::FromSeconds(5));
     86 
     87     // Wait for results; either the checker or the timeout callbacks should
     88     // cause the main loop to exit.
     89     base::MessageLoop::current()->Run();
     90 
     91     timeout.Cancel();
     92   }
     93 
     94   static void CheckWrapper(
     95       const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker,
     96       const base::Closure& done,
     97       scoped_ptr<Action::ActionVector> results) {
     98     checker.Run(results.Pass());
     99     done.Run();
    100   }
    101 
    102   static void TimeoutCallback() {
    103     base::MessageLoop::current()->QuitWhenIdle();
    104     FAIL() << "Policy test timed out waiting for results";
    105   }
    106 
    107   static void RetrieveActions_LogAndFetchActions(
    108       scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
    109     ASSERT_EQ(2, static_cast<int>(i->size()));
    110   }
    111 
    112   static void Arguments_Present(scoped_ptr<Action::ActionVector> i) {
    113     scoped_refptr<Action> last = i->front();
    114     std::string args =
    115         "ID=odlameecjipmbmbejkplpemijjgpljce CATEGORY=api_call "
    116         "API=extension.connect ARGS=[\"hello\",\"world\"]";
    117     ASSERT_EQ(args, last->PrintForDebug());
    118   }
    119 
    120   static void Arguments_GetTodaysActions(
    121       scoped_ptr<Action::ActionVector> actions) {
    122     std::string api_print =
    123         "ID=punky CATEGORY=api_call API=brewster ARGS=[\"woof\"]";
    124     std::string dom_print =
    125         "ID=punky CATEGORY=dom_access API=lets ARGS=[\"vamoose\"] "
    126         "PAGE_URL=http://www.google.com/";
    127     ASSERT_EQ(2, static_cast<int>(actions->size()));
    128     ASSERT_EQ(dom_print, actions->at(0)->PrintForDebug());
    129     ASSERT_EQ(api_print, actions->at(1)->PrintForDebug());
    130   }
    131 
    132   static void Arguments_GetOlderActions(
    133       scoped_ptr<Action::ActionVector> actions) {
    134     std::string api_print =
    135         "ID=punky CATEGORY=api_call API=brewster ARGS=[\"woof\"]";
    136     std::string dom_print =
    137         "ID=punky CATEGORY=dom_access API=lets ARGS=[\"vamoose\"] "
    138         "PAGE_URL=http://www.google.com/";
    139     ASSERT_EQ(2, static_cast<int>(actions->size()));
    140     ASSERT_EQ(dom_print, actions->at(0)->PrintForDebug());
    141     ASSERT_EQ(api_print, actions->at(1)->PrintForDebug());
    142   }
    143 
    144  protected:
    145   ExtensionService* extension_service_;
    146   scoped_ptr<TestingProfile> profile_;
    147   content::TestBrowserThreadBundle thread_bundle_;
    148   // Used to preserve a copy of the original command line.
    149   // The test framework will do this itself as well. However, by then,
    150   // it is too late to call ActivityLog::RecomputeLoggingIsEnabled() in
    151   // TearDown().
    152   CommandLine saved_cmdline_;
    153 
    154 #if defined OS_CHROMEOS
    155   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
    156   chromeos::ScopedTestCrosSettings test_cros_settings_;
    157   scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
    158 #endif
    159 };
    160 
    161 TEST_F(FullStreamUIPolicyTest, Construct) {
    162   ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
    163   scoped_refptr<const Extension> extension =
    164       ExtensionBuilder()
    165           .SetManifest(DictionaryBuilder()
    166                        .Set("name", "Test extension")
    167                        .Set("version", "1.0.0")
    168                        .Set("manifest_version", 2))
    169           .Build();
    170   extension_service_->AddExtension(extension.get());
    171   scoped_ptr<base::ListValue> args(new base::ListValue());
    172   scoped_refptr<Action> action = new Action(extension->id(),
    173                                             base::Time::Now(),
    174                                             Action::ACTION_API_CALL,
    175                                             "tabs.testMethod");
    176   action->set_args(args.Pass());
    177   policy->ProcessAction(action);
    178   policy->Close();
    179 }
    180 
    181 TEST_F(FullStreamUIPolicyTest, LogAndFetchActions) {
    182   ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
    183   scoped_refptr<const Extension> extension =
    184       ExtensionBuilder()
    185           .SetManifest(DictionaryBuilder()
    186                        .Set("name", "Test extension")
    187                        .Set("version", "1.0.0")
    188                        .Set("manifest_version", 2))
    189           .Build();
    190   extension_service_->AddExtension(extension.get());
    191   GURL gurl("http://www.google.com");
    192 
    193   // Write some API calls
    194   scoped_refptr<Action> action_api = new Action(extension->id(),
    195                                                 base::Time::Now(),
    196                                                 Action::ACTION_API_CALL,
    197                                                 "tabs.testMethod");
    198   action_api->set_args(make_scoped_ptr(new base::ListValue()));
    199   policy->ProcessAction(action_api);
    200 
    201   scoped_refptr<Action> action_dom = new Action(extension->id(),
    202                                                 base::Time::Now(),
    203                                                 Action::ACTION_DOM_ACCESS,
    204                                                 "document.write");
    205   action_dom->set_args(make_scoped_ptr(new base::ListValue()));
    206   action_dom->set_page_url(gurl);
    207   policy->ProcessAction(action_dom);
    208 
    209   CheckReadData(
    210       policy,
    211       extension->id(),
    212       0,
    213       base::Bind(&FullStreamUIPolicyTest::RetrieveActions_LogAndFetchActions));
    214 
    215   policy->Close();
    216 }
    217 
    218 TEST_F(FullStreamUIPolicyTest, LogWithArguments) {
    219   ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
    220   scoped_refptr<const Extension> extension =
    221       ExtensionBuilder()
    222           .SetManifest(DictionaryBuilder()
    223                        .Set("name", "Test extension")
    224                        .Set("version", "1.0.0")
    225                        .Set("manifest_version", 2))
    226           .Build();
    227   extension_service_->AddExtension(extension.get());
    228 
    229   scoped_ptr<base::ListValue> args(new base::ListValue());
    230   args->Set(0, new base::StringValue("hello"));
    231   args->Set(1, new base::StringValue("world"));
    232   scoped_refptr<Action> action = new Action(extension->id(),
    233                                             base::Time::Now(),
    234                                             Action::ACTION_API_CALL,
    235                                             "extension.connect");
    236   action->set_args(args.Pass());
    237 
    238   policy->ProcessAction(action);
    239   CheckReadData(policy,
    240                 extension->id(),
    241                 0,
    242                 base::Bind(&FullStreamUIPolicyTest::Arguments_Present));
    243   policy->Close();
    244 }
    245 
    246 TEST_F(FullStreamUIPolicyTest, GetTodaysActions) {
    247   ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
    248 
    249   // Use a mock clock to ensure that events are not recorded on the wrong day
    250   // when the test is run close to local midnight.  Note: Ownership is passed
    251   // to the policy, but we still keep a pointer locally.  The policy will take
    252   // care of destruction; this is safe since the policy outlives all our
    253   // accesses to the mock clock.
    254   base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
    255   mock_clock->SetNow(base::Time::Now().LocalMidnight() +
    256                      base::TimeDelta::FromHours(12));
    257   policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
    258 
    259   // Record some actions
    260   scoped_refptr<Action> action =
    261       new Action("punky",
    262                  mock_clock->Now() - base::TimeDelta::FromMinutes(40),
    263                  Action::ACTION_API_CALL,
    264                  "brewster");
    265   action->mutable_args()->AppendString("woof");
    266   policy->ProcessAction(action);
    267 
    268   action =
    269       new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
    270   action->mutable_args()->AppendString("vamoose");
    271   action->set_page_url(GURL("http://www.google.com"));
    272   policy->ProcessAction(action);
    273 
    274   action = new Action(
    275       "scoobydoo", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
    276   action->mutable_args()->AppendString("vamoose");
    277   action->set_page_url(GURL("http://www.google.com"));
    278   policy->ProcessAction(action);
    279 
    280   CheckReadData(
    281       policy,
    282       "punky",
    283       0,
    284       base::Bind(&FullStreamUIPolicyTest::Arguments_GetTodaysActions));
    285   policy->Close();
    286 }
    287 
    288 // Check that we can read back less recent actions in the db.
    289 TEST_F(FullStreamUIPolicyTest, GetOlderActions) {
    290   ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
    291 
    292   // Use a mock clock to ensure that events are not recorded on the wrong day
    293   // when the test is run close to local midnight.
    294   base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
    295   mock_clock->SetNow(base::Time::Now().LocalMidnight() +
    296                      base::TimeDelta::FromHours(12));
    297   policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
    298 
    299   // Record some actions
    300   scoped_refptr<Action> action =
    301       new Action("punky",
    302                  mock_clock->Now() - base::TimeDelta::FromDays(3) -
    303                      base::TimeDelta::FromMinutes(40),
    304                  Action::ACTION_API_CALL,
    305                  "brewster");
    306   action->mutable_args()->AppendString("woof");
    307   policy->ProcessAction(action);
    308 
    309   action = new Action("punky",
    310                       mock_clock->Now() - base::TimeDelta::FromDays(3),
    311                       Action::ACTION_DOM_ACCESS,
    312                       "lets");
    313   action->mutable_args()->AppendString("vamoose");
    314   action->set_page_url(GURL("http://www.google.com"));
    315   policy->ProcessAction(action);
    316 
    317   action = new Action("punky",
    318                       mock_clock->Now(),
    319                       Action::ACTION_DOM_ACCESS,
    320                       "lets");
    321   action->mutable_args()->AppendString("too new");
    322   action->set_page_url(GURL("http://www.google.com"));
    323   policy->ProcessAction(action);
    324 
    325   action = new Action("punky",
    326                       mock_clock->Now() - base::TimeDelta::FromDays(7),
    327                       Action::ACTION_DOM_ACCESS,
    328                       "lets");
    329   action->mutable_args()->AppendString("too old");
    330   action->set_page_url(GURL("http://www.google.com"));
    331   policy->ProcessAction(action);
    332 
    333   CheckReadData(
    334       policy,
    335       "punky",
    336       3,
    337       base::Bind(&FullStreamUIPolicyTest::Arguments_GetOlderActions));
    338   policy->Close();
    339 }
    340 
    341 }  // namespace extensions
    342