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