1 // Copyright (c) 2012 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 // This file tests the chrome.alarms extension API. 6 7 #include "base/test/simple_test_clock.h" 8 #include "base/values.h" 9 #include "chrome/browser/extensions/api/alarms/alarm_manager.h" 10 #include "chrome/browser/extensions/api/alarms/alarms_api.h" 11 #include "chrome/browser/extensions/extension_function_test_utils.h" 12 #include "chrome/browser/profiles/profile_manager.h" 13 #include "chrome/browser/ui/browser.h" 14 #include "chrome/browser/ui/tabs/tab_strip_model.h" 15 #include "chrome/common/extensions/background_info.h" 16 #include "chrome/common/extensions/extension_messages.h" 17 #include "chrome/test/base/browser_with_test_window_test.h" 18 #include "content/public/browser/web_contents.h" 19 #include "content/public/test/mock_render_process_host.h" 20 #include "ipc/ipc_test_sink.h" 21 #include "testing/gmock/include/gmock/gmock.h" 22 #include "testing/gtest/include/gtest/gtest.h" 23 24 typedef extensions::api::alarms::Alarm JsAlarm; 25 26 namespace utils = extension_function_test_utils; 27 28 namespace extensions { 29 30 namespace { 31 32 // Test delegate which quits the message loop when an alarm fires. 33 class AlarmDelegate : public AlarmManager::Delegate { 34 public: 35 virtual ~AlarmDelegate() {} 36 virtual void OnAlarm(const std::string& extension_id, 37 const Alarm& alarm) OVERRIDE { 38 alarms_seen.push_back(alarm.js_alarm->name); 39 base::MessageLoop::current()->Quit(); 40 } 41 42 std::vector<std::string> alarms_seen; 43 }; 44 45 } // namespace 46 47 class ExtensionAlarmsTest : public BrowserWithTestWindowTest { 48 public: 49 virtual void SetUp() { 50 BrowserWithTestWindowTest::SetUp(); 51 52 test_clock_ = new base::SimpleTestClock(); 53 alarm_manager_ = AlarmManager::Get(browser()->profile()); 54 alarm_manager_->SetClockForTesting(test_clock_); 55 56 alarm_delegate_ = new AlarmDelegate(); 57 alarm_manager_->set_delegate(alarm_delegate_); 58 59 extension_ = utils::CreateEmptyExtensionWithLocation( 60 extensions::Manifest::UNPACKED); 61 62 // Make sure there's a RenderViewHost for alarms to warn into. 63 AddTab(browser(), BackgroundInfo::GetBackgroundURL(extension_.get())); 64 contents_ = browser()->tab_strip_model()->GetActiveWebContents(); 65 66 test_clock_->SetNow(base::Time::FromDoubleT(10)); 67 } 68 69 base::Value* RunFunctionWithExtension( 70 UIThreadExtensionFunction* function, const std::string& args) { 71 scoped_refptr<UIThreadExtensionFunction> delete_function(function); 72 function->set_extension(extension_.get()); 73 function->SetRenderViewHost(contents_->GetRenderViewHost()); 74 return utils::RunFunctionAndReturnSingleResult(function, args, browser()); 75 } 76 77 base::DictionaryValue* RunFunctionAndReturnDict( 78 UIThreadExtensionFunction* function, const std::string& args) { 79 base::Value* result = RunFunctionWithExtension(function, args); 80 return result ? utils::ToDictionary(result) : NULL; 81 } 82 83 base::ListValue* RunFunctionAndReturnList( 84 UIThreadExtensionFunction* function, const std::string& args) { 85 base::Value* result = RunFunctionWithExtension(function, args); 86 return result ? utils::ToList(result) : NULL; 87 } 88 89 void RunFunction(UIThreadExtensionFunction* function, 90 const std::string& args) { 91 scoped_ptr<base::Value> result(RunFunctionWithExtension(function, args)); 92 } 93 94 std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function, 95 const std::string& args) { 96 function->set_extension(extension_.get()); 97 function->SetRenderViewHost(contents_->GetRenderViewHost()); 98 return utils::RunFunctionAndReturnError(function, args, browser()); 99 } 100 101 void CreateAlarm(const std::string& args) { 102 RunFunction(new AlarmsCreateFunction(test_clock_), args); 103 } 104 105 // Takes a JSON result from a function and converts it to a vector of 106 // JsAlarms. 107 std::vector<linked_ptr<JsAlarm> > ToAlarmList(base::ListValue* value) { 108 std::vector<linked_ptr<JsAlarm> > list; 109 for (size_t i = 0; i < value->GetSize(); ++i) { 110 linked_ptr<JsAlarm> alarm(new JsAlarm); 111 base::DictionaryValue* alarm_value; 112 if (!value->GetDictionary(i, &alarm_value)) { 113 ADD_FAILURE() << "Expected a list of Alarm objects."; 114 return list; 115 } 116 EXPECT_TRUE(JsAlarm::Populate(*alarm_value, alarm.get())); 117 list.push_back(alarm); 118 } 119 return list; 120 } 121 122 // Creates up to 3 alarms using the extension API. 123 void CreateAlarms(size_t num_alarms) { 124 CHECK(num_alarms <= 3); 125 126 const char* kCreateArgs[] = { 127 "[null, {\"periodInMinutes\": 0.001}]", 128 "[\"7\", {\"periodInMinutes\": 7}]", 129 "[\"0\", {\"delayInMinutes\": 0}]", 130 }; 131 for (size_t i = 0; i < num_alarms; ++i) { 132 scoped_ptr<base::DictionaryValue> result(RunFunctionAndReturnDict( 133 new AlarmsCreateFunction(test_clock_), kCreateArgs[i])); 134 EXPECT_FALSE(result.get()); 135 } 136 } 137 138 protected: 139 base::SimpleTestClock* test_clock_; 140 AlarmManager* alarm_manager_; 141 AlarmDelegate* alarm_delegate_; 142 scoped_refptr<extensions::Extension> extension_; 143 content::WebContents* contents_; 144 }; 145 146 TEST_F(ExtensionAlarmsTest, Create) { 147 test_clock_->SetNow(base::Time::FromDoubleT(10)); 148 // Create 1 non-repeating alarm. 149 CreateAlarm("[null, {\"delayInMinutes\": 0}]"); 150 151 const Alarm* alarm = 152 alarm_manager_->GetAlarm(extension_->id(), std::string()); 153 ASSERT_TRUE(alarm); 154 EXPECT_EQ("", alarm->js_alarm->name); 155 EXPECT_DOUBLE_EQ(10000, alarm->js_alarm->scheduled_time); 156 EXPECT_FALSE(alarm->js_alarm->period_in_minutes.get()); 157 158 // Now wait for the alarm to fire. Our test delegate will quit the 159 // MessageLoop when that happens. 160 base::MessageLoop::current()->Run(); 161 162 ASSERT_EQ(1u, alarm_delegate_->alarms_seen.size()); 163 EXPECT_EQ("", alarm_delegate_->alarms_seen[0]); 164 165 // Ensure the alarm is gone. 166 { 167 const AlarmManager::AlarmList* alarms = 168 alarm_manager_->GetAllAlarms(extension_->id()); 169 ASSERT_FALSE(alarms); 170 } 171 } 172 173 TEST_F(ExtensionAlarmsTest, CreateRepeating) { 174 test_clock_->SetNow(base::Time::FromDoubleT(10)); 175 176 // Create 1 repeating alarm. 177 CreateAlarm("[null, {\"periodInMinutes\": 0.001}]"); 178 179 const Alarm* alarm = 180 alarm_manager_->GetAlarm(extension_->id(), std::string()); 181 ASSERT_TRUE(alarm); 182 EXPECT_EQ("", alarm->js_alarm->name); 183 EXPECT_DOUBLE_EQ(10060, alarm->js_alarm->scheduled_time); 184 EXPECT_THAT(alarm->js_alarm->period_in_minutes, 185 testing::Pointee(testing::DoubleEq(0.001))); 186 187 test_clock_->Advance(base::TimeDelta::FromSeconds(1)); 188 // Now wait for the alarm to fire. Our test delegate will quit the 189 // MessageLoop when that happens. 190 base::MessageLoop::current()->Run(); 191 192 test_clock_->Advance(base::TimeDelta::FromSeconds(1)); 193 // Wait again, and ensure the alarm fires again. 194 alarm_manager_->ScheduleNextPoll(); 195 base::MessageLoop::current()->Run(); 196 197 ASSERT_EQ(2u, alarm_delegate_->alarms_seen.size()); 198 EXPECT_EQ("", alarm_delegate_->alarms_seen[0]); 199 } 200 201 TEST_F(ExtensionAlarmsTest, CreateAbsolute) { 202 test_clock_->SetNow(base::Time::FromDoubleT(9.99)); 203 CreateAlarm("[null, {\"when\": 10001}]"); 204 205 const Alarm* alarm = 206 alarm_manager_->GetAlarm(extension_->id(), std::string()); 207 ASSERT_TRUE(alarm); 208 EXPECT_EQ("", alarm->js_alarm->name); 209 EXPECT_DOUBLE_EQ(10001, alarm->js_alarm->scheduled_time); 210 EXPECT_THAT(alarm->js_alarm->period_in_minutes, 211 testing::IsNull()); 212 213 test_clock_->SetNow(base::Time::FromDoubleT(10.1)); 214 // Now wait for the alarm to fire. Our test delegate will quit the 215 // MessageLoop when that happens. 216 base::MessageLoop::current()->Run(); 217 218 ASSERT_FALSE(alarm_manager_->GetAlarm(extension_->id(), std::string())); 219 220 ASSERT_EQ(1u, alarm_delegate_->alarms_seen.size()); 221 EXPECT_EQ("", alarm_delegate_->alarms_seen[0]); 222 } 223 224 TEST_F(ExtensionAlarmsTest, CreateRepeatingWithQuickFirstCall) { 225 test_clock_->SetNow(base::Time::FromDoubleT(9.99)); 226 CreateAlarm("[null, {\"when\": 10001, \"periodInMinutes\": 0.001}]"); 227 228 const Alarm* alarm = 229 alarm_manager_->GetAlarm(extension_->id(), std::string()); 230 ASSERT_TRUE(alarm); 231 EXPECT_EQ("", alarm->js_alarm->name); 232 EXPECT_DOUBLE_EQ(10001, alarm->js_alarm->scheduled_time); 233 EXPECT_THAT(alarm->js_alarm->period_in_minutes, 234 testing::Pointee(testing::DoubleEq(0.001))); 235 236 test_clock_->SetNow(base::Time::FromDoubleT(10.1)); 237 // Now wait for the alarm to fire. Our test delegate will quit the 238 // MessageLoop when that happens. 239 base::MessageLoop::current()->Run(); 240 241 ASSERT_TRUE(alarm_manager_->GetAlarm(extension_->id(), std::string())); 242 EXPECT_THAT(alarm_delegate_->alarms_seen, testing::ElementsAre("")); 243 244 test_clock_->SetNow(base::Time::FromDoubleT(10.7)); 245 base::MessageLoop::current()->Run(); 246 247 ASSERT_TRUE(alarm_manager_->GetAlarm(extension_->id(), std::string())); 248 EXPECT_THAT(alarm_delegate_->alarms_seen, testing::ElementsAre("", "")); 249 } 250 251 TEST_F(ExtensionAlarmsTest, CreateDupe) { 252 test_clock_->SetNow(base::Time::FromDoubleT(10)); 253 254 // Create 2 duplicate alarms. The first should be overridden. 255 CreateAlarm("[\"dup\", {\"delayInMinutes\": 1}]"); 256 CreateAlarm("[\"dup\", {\"delayInMinutes\": 7}]"); 257 258 { 259 const AlarmManager::AlarmList* alarms = 260 alarm_manager_->GetAllAlarms(extension_->id()); 261 ASSERT_TRUE(alarms); 262 EXPECT_EQ(1u, alarms->size()); 263 EXPECT_DOUBLE_EQ(430000, (*alarms)[0].js_alarm->scheduled_time); 264 } 265 } 266 267 TEST_F(ExtensionAlarmsTest, CreateDelayBelowMinimum) { 268 // Create an alarm with delay below the minimum accepted value. 269 CreateAlarm("[\"negative\", {\"delayInMinutes\": -0.2}]"); 270 IPC::TestSink& sink = static_cast<content::MockRenderProcessHost*>( 271 contents_->GetRenderViewHost()->GetProcess())->sink(); 272 const IPC::Message* warning = sink.GetUniqueMessageMatching( 273 ExtensionMsg_AddMessageToConsole::ID); 274 ASSERT_TRUE(warning); 275 content::ConsoleMessageLevel level = content::CONSOLE_MESSAGE_LEVEL_DEBUG; 276 std::string message; 277 ExtensionMsg_AddMessageToConsole::Read(warning, &level, &message); 278 EXPECT_EQ(content::CONSOLE_MESSAGE_LEVEL_WARNING, level); 279 EXPECT_THAT(message, testing::HasSubstr("delay is less than minimum of 1")); 280 } 281 282 TEST_F(ExtensionAlarmsTest, Get) { 283 test_clock_->SetNow(base::Time::FromDoubleT(4)); 284 285 // Create 2 alarms, and make sure we can query them. 286 CreateAlarms(2); 287 288 // Get the default one. 289 { 290 JsAlarm alarm; 291 scoped_ptr<base::DictionaryValue> result(RunFunctionAndReturnDict( 292 new AlarmsGetFunction(), "[null]")); 293 ASSERT_TRUE(result.get()); 294 EXPECT_TRUE(JsAlarm::Populate(*result, &alarm)); 295 EXPECT_EQ("", alarm.name); 296 EXPECT_DOUBLE_EQ(4060, alarm.scheduled_time); 297 EXPECT_THAT(alarm.period_in_minutes, 298 testing::Pointee(testing::DoubleEq(0.001))); 299 } 300 301 // Get "7". 302 { 303 JsAlarm alarm; 304 scoped_ptr<base::DictionaryValue> result(RunFunctionAndReturnDict( 305 new AlarmsGetFunction(), "[\"7\"]")); 306 ASSERT_TRUE(result.get()); 307 EXPECT_TRUE(JsAlarm::Populate(*result, &alarm)); 308 EXPECT_EQ("7", alarm.name); 309 EXPECT_EQ(424000, alarm.scheduled_time); 310 EXPECT_THAT(alarm.period_in_minutes, testing::Pointee(7)); 311 } 312 313 // Get a non-existent one. 314 { 315 std::string error = RunFunctionAndReturnError( 316 new AlarmsGetFunction(), "[\"nobody\"]"); 317 EXPECT_FALSE(error.empty()); 318 } 319 } 320 321 TEST_F(ExtensionAlarmsTest, GetAll) { 322 // Test getAll with 0 alarms. 323 { 324 scoped_ptr<base::ListValue> result(RunFunctionAndReturnList( 325 new AlarmsGetAllFunction(), "[]")); 326 std::vector<linked_ptr<JsAlarm> > alarms = ToAlarmList(result.get()); 327 EXPECT_EQ(0u, alarms.size()); 328 } 329 330 // Create 2 alarms, and make sure we can query them. 331 CreateAlarms(2); 332 333 { 334 scoped_ptr<base::ListValue> result(RunFunctionAndReturnList( 335 new AlarmsGetAllFunction(), "[null]")); 336 std::vector<linked_ptr<JsAlarm> > alarms = ToAlarmList(result.get()); 337 EXPECT_EQ(2u, alarms.size()); 338 339 // Test the "7" alarm. 340 JsAlarm* alarm = alarms[0].get(); 341 if (alarm->name != "7") 342 alarm = alarms[1].get(); 343 EXPECT_EQ("7", alarm->name); 344 EXPECT_THAT(alarm->period_in_minutes, testing::Pointee(7)); 345 } 346 } 347 348 TEST_F(ExtensionAlarmsTest, Clear) { 349 // Clear a non-existent one. 350 { 351 std::string error = RunFunctionAndReturnError( 352 new AlarmsClearFunction(), "[\"nobody\"]"); 353 EXPECT_FALSE(error.empty()); 354 } 355 356 // Create 3 alarms. 357 CreateAlarms(3); 358 359 // Clear all but the 0.001-minute alarm. 360 { 361 RunFunction(new AlarmsClearFunction(), "[\"7\"]"); 362 RunFunction(new AlarmsClearFunction(), "[\"0\"]"); 363 364 const AlarmManager::AlarmList* alarms = 365 alarm_manager_->GetAllAlarms(extension_->id()); 366 ASSERT_TRUE(alarms); 367 EXPECT_EQ(1u, alarms->size()); 368 EXPECT_THAT((*alarms)[0].js_alarm->period_in_minutes, 369 testing::Pointee(0.001)); 370 } 371 372 // Now wait for the alarms to fire, and ensure the cancelled alarms don't 373 // fire. 374 alarm_manager_->ScheduleNextPoll(); 375 base::MessageLoop::current()->Run(); 376 377 ASSERT_EQ(1u, alarm_delegate_->alarms_seen.size()); 378 EXPECT_EQ("", alarm_delegate_->alarms_seen[0]); 379 380 // Ensure the 0.001-minute alarm is still there, since it's repeating. 381 { 382 const AlarmManager::AlarmList* alarms = 383 alarm_manager_->GetAllAlarms(extension_->id()); 384 ASSERT_TRUE(alarms); 385 EXPECT_EQ(1u, alarms->size()); 386 EXPECT_THAT((*alarms)[0].js_alarm->period_in_minutes, 387 testing::Pointee(0.001)); 388 } 389 } 390 391 TEST_F(ExtensionAlarmsTest, ClearAll) { 392 // ClearAll with no alarms set. 393 { 394 scoped_ptr<base::Value> result(RunFunctionWithExtension( 395 new AlarmsClearAllFunction(), "[]")); 396 EXPECT_FALSE(result.get()); 397 } 398 399 // Create 3 alarms. 400 { 401 CreateAlarms(3); 402 const AlarmManager::AlarmList* alarms = 403 alarm_manager_->GetAllAlarms(extension_->id()); 404 ASSERT_TRUE(alarms); 405 EXPECT_EQ(3u, alarms->size()); 406 } 407 408 // Clear them. 409 { 410 RunFunction(new AlarmsClearAllFunction(), "[]"); 411 const AlarmManager::AlarmList* alarms = 412 alarm_manager_->GetAllAlarms(extension_->id()); 413 ASSERT_FALSE(alarms); 414 } 415 } 416 417 class ExtensionAlarmsSchedulingTest : public ExtensionAlarmsTest { 418 public: 419 // Get the time that the alarm named is scheduled to run. 420 base::Time GetScheduledTime(const std::string& alarm_name) { 421 const extensions::Alarm* alarm = 422 alarm_manager_->GetAlarm(extension_->id(), alarm_name); 423 CHECK(alarm); 424 return base::Time::FromJsTime(alarm->js_alarm->scheduled_time); 425 } 426 }; 427 428 TEST_F(ExtensionAlarmsSchedulingTest, PollScheduling) { 429 { 430 CreateAlarm("[\"a\", {\"periodInMinutes\": 6}]"); 431 CreateAlarm("[\"bb\", {\"periodInMinutes\": 8}]"); 432 EXPECT_EQ(GetScheduledTime("a"), alarm_manager_->next_poll_time_); 433 alarm_manager_->RemoveAllAlarms(extension_->id()); 434 } 435 { 436 CreateAlarm("[\"a\", {\"delayInMinutes\": 10}]"); 437 CreateAlarm("[\"bb\", {\"delayInMinutes\": 21}]"); 438 EXPECT_EQ(GetScheduledTime("a"), alarm_manager_->next_poll_time_); 439 alarm_manager_->RemoveAllAlarms(extension_->id()); 440 } 441 { 442 test_clock_->SetNow(base::Time::FromDoubleT(10)); 443 CreateAlarm("[\"a\", {\"periodInMinutes\": 10}]"); 444 Alarm alarm; 445 alarm.js_alarm->name = "bb"; 446 alarm.js_alarm->scheduled_time = 30 * 60000; 447 alarm.js_alarm->period_in_minutes.reset(new double(30)); 448 alarm_manager_->AddAlarmImpl(extension_->id(), alarm); 449 EXPECT_DOUBLE_EQ(GetScheduledTime("a").ToDoubleT(), 450 alarm_manager_->next_poll_time_.ToDoubleT()); 451 alarm_manager_->RemoveAllAlarms(extension_->id()); 452 } 453 { 454 test_clock_->SetNow(base::Time::FromDoubleT(3 * 60 + 1)); 455 Alarm alarm; 456 alarm.js_alarm->name = "bb"; 457 alarm.js_alarm->scheduled_time = 3 * 60000; 458 alarm.js_alarm->period_in_minutes.reset(new double(3)); 459 alarm_manager_->AddAlarmImpl(extension_->id(), alarm); 460 base::MessageLoop::current()->Run(); 461 EXPECT_EQ(alarm_manager_->last_poll_time_ + base::TimeDelta::FromMinutes(3), 462 alarm_manager_->next_poll_time_); 463 alarm_manager_->RemoveAllAlarms(extension_->id()); 464 } 465 { 466 test_clock_->SetNow(base::Time::FromDoubleT(4 * 60 + 1)); 467 CreateAlarm("[\"a\", {\"periodInMinutes\": 2}]"); 468 alarm_manager_->RemoveAlarm(extension_->id(), "a"); 469 Alarm alarm2; 470 alarm2.js_alarm->name = "bb"; 471 alarm2.js_alarm->scheduled_time = 4 * 60000; 472 alarm2.js_alarm->period_in_minutes.reset(new double(4)); 473 alarm_manager_->AddAlarmImpl(extension_->id(), alarm2); 474 Alarm alarm3; 475 alarm3.js_alarm->name = "ccc"; 476 alarm3.js_alarm->scheduled_time = 25 * 60000; 477 alarm3.js_alarm->period_in_minutes.reset(new double(25)); 478 alarm_manager_->AddAlarmImpl(extension_->id(), alarm3); 479 base::MessageLoop::current()->Run(); 480 EXPECT_EQ(alarm_manager_->last_poll_time_ + base::TimeDelta::FromMinutes(4), 481 alarm_manager_->next_poll_time_); 482 alarm_manager_->RemoveAllAlarms(extension_->id()); 483 } 484 } 485 486 TEST_F(ExtensionAlarmsSchedulingTest, ReleasedExtensionPollsInfrequently) { 487 extension_ = utils::CreateEmptyExtensionWithLocation( 488 extensions::Manifest::INTERNAL); 489 test_clock_->SetNow(base::Time::FromJsTime(300000)); 490 CreateAlarm("[\"a\", {\"when\": 300010}]"); 491 CreateAlarm("[\"b\", {\"when\": 340000}]"); 492 493 // On startup (when there's no "last poll"), we let alarms fire as 494 // soon as they're scheduled. 495 EXPECT_DOUBLE_EQ(300010, alarm_manager_->next_poll_time_.ToJsTime()); 496 497 alarm_manager_->last_poll_time_ = base::Time::FromJsTime(290000); 498 // In released extensions, we set the granularity to at least 5 499 // minutes, which makes AddAlarm schedule the next poll after the 500 // extension requested. 501 alarm_manager_->ScheduleNextPoll(); 502 EXPECT_DOUBLE_EQ((alarm_manager_->last_poll_time_ + 503 base::TimeDelta::FromMinutes(1)).ToJsTime(), 504 alarm_manager_->next_poll_time_.ToJsTime()); 505 } 506 507 TEST_F(ExtensionAlarmsSchedulingTest, TimerRunning) { 508 EXPECT_FALSE(alarm_manager_->timer_.IsRunning()); 509 CreateAlarm("[\"a\", {\"delayInMinutes\": 0.001}]"); 510 EXPECT_TRUE(alarm_manager_->timer_.IsRunning()); 511 base::MessageLoop::current()->Run(); 512 EXPECT_FALSE(alarm_manager_->timer_.IsRunning()); 513 CreateAlarm("[\"bb\", {\"delayInMinutes\": 10}]"); 514 EXPECT_TRUE(alarm_manager_->timer_.IsRunning()); 515 alarm_manager_->RemoveAllAlarms(extension_->id()); 516 EXPECT_FALSE(alarm_manager_->timer_.IsRunning()); 517 } 518 519 } // namespace extensions 520