// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file tests the chrome.alarms extension API. #include "base/test/simple_test_clock.h" #include "base/values.h" #include "chrome/browser/extensions/api/alarms/alarm_manager.h" #include "chrome/browser/extensions/api/alarms/alarms_api.h" #include "chrome/browser/extensions/extension_api_unittest.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" #include "content/public/browser/web_contents.h" #include "content/public/test/mock_render_process_host.h" #include "extensions/common/extension_messages.h" #include "ipc/ipc_test_sink.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" typedef extensions::api::alarms::Alarm JsAlarm; namespace utils = extension_function_test_utils; namespace extensions { namespace { // Test delegate which quits the message loop when an alarm fires. class AlarmDelegate : public AlarmManager::Delegate { public: virtual ~AlarmDelegate() {} virtual void OnAlarm(const std::string& extension_id, const Alarm& alarm) OVERRIDE { alarms_seen.push_back(alarm.js_alarm->name); if (base::MessageLoop::current()->is_running()) base::MessageLoop::current()->Quit(); } std::vector alarms_seen; }; } // namespace void RunScheduleNextPoll(AlarmManager* alarm_manager) { alarm_manager->ScheduleNextPoll(); } class ExtensionAlarmsTest : public ExtensionApiUnittest { public: using ExtensionApiUnittest::RunFunction; virtual void SetUp() { ExtensionApiUnittest::SetUp(); test_clock_ = new base::SimpleTestClock(); alarm_manager_ = AlarmManager::Get(browser()->profile()); alarm_manager_->SetClockForTesting(test_clock_); alarm_delegate_ = new AlarmDelegate(); alarm_manager_->set_delegate(alarm_delegate_); // Make sure there's a RenderViewHost for alarms to warn into. CreateBackgroundPage(); test_clock_->SetNow(base::Time::FromDoubleT(10)); } void CreateAlarm(const std::string& args) { RunFunction(new AlarmsCreateFunction(test_clock_), args); } // Takes a JSON result from a function and converts it to a vector of // JsAlarms. std::vector > ToAlarmList(base::ListValue* value) { std::vector > list; for (size_t i = 0; i < value->GetSize(); ++i) { linked_ptr alarm(new JsAlarm); base::DictionaryValue* alarm_value; if (!value->GetDictionary(i, &alarm_value)) { ADD_FAILURE() << "Expected a list of Alarm objects."; return list; } EXPECT_TRUE(JsAlarm::Populate(*alarm_value, alarm.get())); list.push_back(alarm); } return list; } // Creates up to 3 alarms using the extension API. void CreateAlarms(size_t num_alarms) { CHECK(num_alarms <= 3); const char* kCreateArgs[] = { "[null, {\"periodInMinutes\": 0.001}]", "[\"7\", {\"periodInMinutes\": 7}]", "[\"0\", {\"delayInMinutes\": 0}]", }; for (size_t i = 0; i < num_alarms; ++i) { scoped_ptr result(RunFunctionAndReturnDictionary( new AlarmsCreateFunction(test_clock_), kCreateArgs[i])); EXPECT_FALSE(result.get()); } } base::SimpleTestClock* test_clock_; AlarmManager* alarm_manager_; AlarmDelegate* alarm_delegate_; }; void ExtensionAlarmsTestGetAllAlarmsCallback( const AlarmManager::AlarmList* alarms) { // Ensure the alarm is gone. ASSERT_FALSE(alarms); } void ExtensionAlarmsTestGetAlarmCallback( ExtensionAlarmsTest* test, Alarm* alarm) { ASSERT_TRUE(alarm); EXPECT_EQ("", alarm->js_alarm->name); EXPECT_DOUBLE_EQ(10000, alarm->js_alarm->scheduled_time); EXPECT_FALSE(alarm->js_alarm->period_in_minutes.get()); // Now wait for the alarm to fire. Our test delegate will quit the // MessageLoop when that happens. base::MessageLoop::current()->Run(); ASSERT_EQ(1u, test->alarm_delegate_->alarms_seen.size()); EXPECT_EQ("", test->alarm_delegate_->alarms_seen[0]); // Ensure the alarm is gone. test->alarm_manager_->GetAllAlarms(test->extension()->id(), base::Bind( ExtensionAlarmsTestGetAllAlarmsCallback)); } TEST_F(ExtensionAlarmsTest, Create) { test_clock_->SetNow(base::Time::FromDoubleT(10)); // Create 1 non-repeating alarm. CreateAlarm("[null, {\"delayInMinutes\": 0}]"); alarm_manager_->GetAlarm(extension()->id(), std::string(), base::Bind( ExtensionAlarmsTestGetAlarmCallback, this)); } void ExtensionAlarmsTestCreateRepeatingGetAlarmCallback( ExtensionAlarmsTest* test, Alarm* alarm) { ASSERT_TRUE(alarm); EXPECT_EQ("", alarm->js_alarm->name); EXPECT_DOUBLE_EQ(10060, alarm->js_alarm->scheduled_time); EXPECT_THAT(alarm->js_alarm->period_in_minutes, testing::Pointee(testing::DoubleEq(0.001))); test->test_clock_->Advance(base::TimeDelta::FromSeconds(1)); // Now wait for the alarm to fire. Our test delegate will quit the // MessageLoop when that happens. base::MessageLoop::current()->Run(); test->test_clock_->Advance(base::TimeDelta::FromSeconds(1)); // Wait again, and ensure the alarm fires again. RunScheduleNextPoll(test->alarm_manager_); base::MessageLoop::current()->Run(); ASSERT_EQ(2u, test->alarm_delegate_->alarms_seen.size()); EXPECT_EQ("", test->alarm_delegate_->alarms_seen[0]); } TEST_F(ExtensionAlarmsTest, CreateRepeating) { test_clock_->SetNow(base::Time::FromDoubleT(10)); // Create 1 repeating alarm. CreateAlarm("[null, {\"periodInMinutes\": 0.001}]"); alarm_manager_->GetAlarm(extension()->id(), std::string(), base::Bind( ExtensionAlarmsTestCreateRepeatingGetAlarmCallback, this)); } void ExtensionAlarmsTestCreateAbsoluteGetAlarm2Callback( ExtensionAlarmsTest* test, Alarm* alarm) { ASSERT_FALSE(alarm); ASSERT_EQ(1u, test->alarm_delegate_->alarms_seen.size()); EXPECT_EQ("", test->alarm_delegate_->alarms_seen[0]); } void ExtensionAlarmsTestCreateAbsoluteGetAlarm1Callback( ExtensionAlarmsTest* test, Alarm* alarm) { ASSERT_TRUE(alarm); EXPECT_EQ("", alarm->js_alarm->name); EXPECT_DOUBLE_EQ(10001, alarm->js_alarm->scheduled_time); EXPECT_THAT(alarm->js_alarm->period_in_minutes, testing::IsNull()); test->test_clock_->SetNow(base::Time::FromDoubleT(10.1)); // Now wait for the alarm to fire. Our test delegate will quit the // MessageLoop when that happens. base::MessageLoop::current()->Run(); test->alarm_manager_->GetAlarm( test->extension()->id(), std::string(), base::Bind( ExtensionAlarmsTestCreateAbsoluteGetAlarm2Callback, test)); } TEST_F(ExtensionAlarmsTest, CreateAbsolute) { test_clock_->SetNow(base::Time::FromDoubleT(9.99)); CreateAlarm("[null, {\"when\": 10001}]"); alarm_manager_->GetAlarm(extension()->id(), std::string(), base::Bind( ExtensionAlarmsTestCreateAbsoluteGetAlarm1Callback, this)); } void ExtensionAlarmsTestCreateRepeatingWithQuickFirstCallGetAlarm3Callback( ExtensionAlarmsTest* test, Alarm* alarm) { ASSERT_TRUE(alarm); EXPECT_THAT(test->alarm_delegate_->alarms_seen, testing::ElementsAre("", "")); } void ExtensionAlarmsTestCreateRepeatingWithQuickFirstCallGetAlarm2Callback( ExtensionAlarmsTest* test, Alarm* alarm) { ASSERT_TRUE(alarm); EXPECT_THAT(test->alarm_delegate_->alarms_seen, testing::ElementsAre("")); test->test_clock_->SetNow(base::Time::FromDoubleT(10.7)); base::MessageLoop::current()->Run(); test->alarm_manager_->GetAlarm( test->extension()->id(), std::string(), base::Bind( ExtensionAlarmsTestCreateRepeatingWithQuickFirstCallGetAlarm3Callback, test)); } void ExtensionAlarmsTestCreateRepeatingWithQuickFirstCallGetAlarm1Callback( ExtensionAlarmsTest* test, Alarm* alarm) { ASSERT_TRUE(alarm); EXPECT_EQ("", alarm->js_alarm->name); EXPECT_DOUBLE_EQ(10001, alarm->js_alarm->scheduled_time); EXPECT_THAT(alarm->js_alarm->period_in_minutes, testing::Pointee(testing::DoubleEq(0.001))); test->test_clock_->SetNow(base::Time::FromDoubleT(10.1)); // Now wait for the alarm to fire. Our test delegate will quit the // MessageLoop when that happens. base::MessageLoop::current()->Run(); test->alarm_manager_->GetAlarm( test->extension()->id(), std::string(), base::Bind( ExtensionAlarmsTestCreateRepeatingWithQuickFirstCallGetAlarm2Callback, test)); } TEST_F(ExtensionAlarmsTest, CreateRepeatingWithQuickFirstCall) { test_clock_->SetNow(base::Time::FromDoubleT(9.99)); CreateAlarm("[null, {\"when\": 10001, \"periodInMinutes\": 0.001}]"); alarm_manager_->GetAlarm(extension()->id(), std::string(), base::Bind( ExtensionAlarmsTestCreateRepeatingWithQuickFirstCallGetAlarm1Callback, this)); } void ExtensionAlarmsTestCreateDupeGetAllAlarmsCallback( const AlarmManager::AlarmList* alarms) { ASSERT_TRUE(alarms); EXPECT_EQ(1u, alarms->size()); EXPECT_DOUBLE_EQ(430000, (*alarms)[0].js_alarm->scheduled_time); } TEST_F(ExtensionAlarmsTest, CreateDupe) { test_clock_->SetNow(base::Time::FromDoubleT(10)); // Create 2 duplicate alarms. The first should be overridden. CreateAlarm("[\"dup\", {\"delayInMinutes\": 1}]"); CreateAlarm("[\"dup\", {\"delayInMinutes\": 7}]"); alarm_manager_->GetAllAlarms(extension()->id(), base::Bind( ExtensionAlarmsTestCreateDupeGetAllAlarmsCallback)); } TEST_F(ExtensionAlarmsTest, CreateDelayBelowMinimum) { // Create an alarm with delay below the minimum accepted value. CreateAlarm("[\"negative\", {\"delayInMinutes\": -0.2}]"); IPC::TestSink& sink = static_cast( contents()->GetRenderViewHost()->GetProcess())->sink(); const IPC::Message* warning = sink.GetUniqueMessageMatching( ExtensionMsg_AddMessageToConsole::ID); ASSERT_TRUE(warning); content::ConsoleMessageLevel level = content::CONSOLE_MESSAGE_LEVEL_DEBUG; std::string message; ExtensionMsg_AddMessageToConsole::Read(warning, &level, &message); EXPECT_EQ(content::CONSOLE_MESSAGE_LEVEL_WARNING, level); EXPECT_THAT(message, testing::HasSubstr("delay is less than minimum of 1")); } TEST_F(ExtensionAlarmsTest, Get) { test_clock_->SetNow(base::Time::FromDoubleT(4)); // Create 2 alarms, and make sure we can query them. CreateAlarms(2); // Get the default one. { JsAlarm alarm; scoped_ptr result(RunFunctionAndReturnDictionary( new AlarmsGetFunction(), "[null]")); ASSERT_TRUE(result.get()); EXPECT_TRUE(JsAlarm::Populate(*result, &alarm)); EXPECT_EQ("", alarm.name); EXPECT_DOUBLE_EQ(4060, alarm.scheduled_time); EXPECT_THAT(alarm.period_in_minutes, testing::Pointee(testing::DoubleEq(0.001))); } // Get "7". { JsAlarm alarm; scoped_ptr result(RunFunctionAndReturnDictionary( new AlarmsGetFunction(), "[\"7\"]")); ASSERT_TRUE(result.get()); EXPECT_TRUE(JsAlarm::Populate(*result, &alarm)); EXPECT_EQ("7", alarm.name); EXPECT_EQ(424000, alarm.scheduled_time); EXPECT_THAT(alarm.period_in_minutes, testing::Pointee(7)); } // Get a non-existent one. { std::string error = RunFunctionAndReturnError( new AlarmsGetFunction(), "[\"nobody\"]"); EXPECT_FALSE(error.empty()); } } TEST_F(ExtensionAlarmsTest, GetAll) { // Test getAll with 0 alarms. { scoped_ptr result(RunFunctionAndReturnList( new AlarmsGetAllFunction(), "[]")); std::vector > alarms = ToAlarmList(result.get()); EXPECT_EQ(0u, alarms.size()); } // Create 2 alarms, and make sure we can query them. CreateAlarms(2); { scoped_ptr result(RunFunctionAndReturnList( new AlarmsGetAllFunction(), "[null]")); std::vector > alarms = ToAlarmList(result.get()); EXPECT_EQ(2u, alarms.size()); // Test the "7" alarm. JsAlarm* alarm = alarms[0].get(); if (alarm->name != "7") alarm = alarms[1].get(); EXPECT_EQ("7", alarm->name); EXPECT_THAT(alarm->period_in_minutes, testing::Pointee(7)); } } void ExtensionAlarmsTestClearGetAllAlarms2Callback( const AlarmManager::AlarmList* alarms) { // Ensure the 0.001-minute alarm is still there, since it's repeating. ASSERT_TRUE(alarms); EXPECT_EQ(1u, alarms->size()); EXPECT_THAT((*alarms)[0].js_alarm->period_in_minutes, testing::Pointee(0.001)); } void ExtensionAlarmsTestClearGetAllAlarms1Callback( ExtensionAlarmsTest* test, const AlarmManager::AlarmList* alarms) { ASSERT_TRUE(alarms); EXPECT_EQ(1u, alarms->size()); EXPECT_THAT((*alarms)[0].js_alarm->period_in_minutes, testing::Pointee(0.001)); // Now wait for the alarms to fire, and ensure the cancelled alarms don't // fire. test->test_clock_->Advance(base::TimeDelta::FromMilliseconds(60)); RunScheduleNextPoll(test->alarm_manager_); base::MessageLoop::current()->Run(); ASSERT_EQ(1u, test->alarm_delegate_->alarms_seen.size()); EXPECT_EQ("", test->alarm_delegate_->alarms_seen[0]); // Ensure the 0.001-minute alarm is still there, since it's repeating. test->alarm_manager_->GetAllAlarms(test->extension()->id(), base::Bind( ExtensionAlarmsTestClearGetAllAlarms2Callback)); } TEST_F(ExtensionAlarmsTest, Clear) { // Clear a non-existent one. { scoped_ptr result( RunFunctionAndReturnValue(new AlarmsClearFunction(), "[\"nobody\"]")); bool copy_bool_result = false; ASSERT_TRUE(result->GetAsBoolean(©_bool_result)); EXPECT_FALSE(copy_bool_result); } // Create 3 alarms. CreateAlarms(3); // Clear all but the 0.001-minute alarm. { scoped_ptr result( RunFunctionAndReturnValue(new AlarmsClearFunction(), "[\"7\"]")); bool copy_bool_result = false; ASSERT_TRUE(result->GetAsBoolean(©_bool_result)); EXPECT_TRUE(copy_bool_result); } { scoped_ptr result( RunFunctionAndReturnValue(new AlarmsClearFunction(), "[\"0\"]")); bool copy_bool_result = false; ASSERT_TRUE(result->GetAsBoolean(©_bool_result)); EXPECT_TRUE(copy_bool_result); } alarm_manager_->GetAllAlarms(extension()->id(), base::Bind( ExtensionAlarmsTestClearGetAllAlarms1Callback, this)); } void ExtensionAlarmsTestClearAllGetAllAlarms2Callback( const AlarmManager::AlarmList* alarms) { ASSERT_FALSE(alarms); } void ExtensionAlarmsTestClearAllGetAllAlarms1Callback( ExtensionAlarmsTest* test, const AlarmManager::AlarmList* alarms) { ASSERT_TRUE(alarms); EXPECT_EQ(3u, alarms->size()); // Clear them. test->RunFunction(new AlarmsClearAllFunction(), "[]"); test->alarm_manager_->GetAllAlarms( test->extension()->id(), base::Bind( ExtensionAlarmsTestClearAllGetAllAlarms2Callback)); } TEST_F(ExtensionAlarmsTest, ClearAll) { // ClearAll with no alarms set. { scoped_ptr result(RunFunctionAndReturnValue( new AlarmsClearAllFunction(), "[]")); bool copy_bool_result = false; ASSERT_TRUE(result->GetAsBoolean(©_bool_result)); EXPECT_TRUE(copy_bool_result); } // Create 3 alarms. CreateAlarms(3); alarm_manager_->GetAllAlarms(extension()->id(), base::Bind( ExtensionAlarmsTestClearAllGetAllAlarms1Callback, this)); } class ExtensionAlarmsSchedulingTest : public ExtensionAlarmsTest { void GetAlarmCallback(Alarm* alarm) { CHECK(alarm); const base::Time scheduled_time = base::Time::FromJsTime(alarm->js_alarm->scheduled_time); EXPECT_EQ(scheduled_time, alarm_manager_->next_poll_time_); } static void RemoveAlarmCallback (bool success) { EXPECT_TRUE(success); } static void RemoveAllAlarmsCallback () {} public: // Get the time that the alarm named is scheduled to run. void VerifyScheduledTime(const std::string& alarm_name) { alarm_manager_->GetAlarm(extension()->id(), alarm_name, base::Bind( &ExtensionAlarmsSchedulingTest::GetAlarmCallback, base::Unretained(this))); } void RemoveAlarm(const std::string& name) { alarm_manager_->RemoveAlarm( extension()->id(), name, base::Bind(&ExtensionAlarmsSchedulingTest::RemoveAlarmCallback)); } void RemoveAllAlarms () { alarm_manager_->RemoveAllAlarms(extension()->id(), base::Bind( &ExtensionAlarmsSchedulingTest::RemoveAllAlarmsCallback)); } }; TEST_F(ExtensionAlarmsSchedulingTest, PollScheduling) { { CreateAlarm("[\"a\", {\"periodInMinutes\": 6}]"); CreateAlarm("[\"bb\", {\"periodInMinutes\": 8}]"); VerifyScheduledTime("a"); RemoveAllAlarms(); } { CreateAlarm("[\"a\", {\"delayInMinutes\": 10}]"); CreateAlarm("[\"bb\", {\"delayInMinutes\": 21}]"); VerifyScheduledTime("a"); RemoveAllAlarms(); } { test_clock_->SetNow(base::Time::FromDoubleT(10)); CreateAlarm("[\"a\", {\"periodInMinutes\": 10}]"); Alarm alarm; alarm.js_alarm->name = "bb"; alarm.js_alarm->scheduled_time = 30 * 60000; alarm.js_alarm->period_in_minutes.reset(new double(30)); alarm_manager_->AddAlarmImpl(extension()->id(), alarm); VerifyScheduledTime("a"); RemoveAllAlarms(); } { test_clock_->SetNow(base::Time::FromDoubleT(3 * 60 + 1)); Alarm alarm; alarm.js_alarm->name = "bb"; alarm.js_alarm->scheduled_time = 3 * 60000; alarm.js_alarm->period_in_minutes.reset(new double(3)); alarm_manager_->AddAlarmImpl(extension()->id(), alarm); base::MessageLoop::current()->Run(); EXPECT_EQ(base::Time::FromJsTime(3 * 60000) + base::TimeDelta::FromMinutes(3), alarm_manager_->next_poll_time_); RemoveAllAlarms(); } { test_clock_->SetNow(base::Time::FromDoubleT(4 * 60 + 1)); CreateAlarm("[\"a\", {\"periodInMinutes\": 2}]"); RemoveAlarm("a"); Alarm alarm2; alarm2.js_alarm->name = "bb"; alarm2.js_alarm->scheduled_time = 4 * 60000; alarm2.js_alarm->period_in_minutes.reset(new double(4)); alarm_manager_->AddAlarmImpl(extension()->id(), alarm2); Alarm alarm3; alarm3.js_alarm->name = "ccc"; alarm3.js_alarm->scheduled_time = 25 * 60000; alarm3.js_alarm->period_in_minutes.reset(new double(25)); alarm_manager_->AddAlarmImpl(extension()->id(), alarm3); base::MessageLoop::current()->Run(); EXPECT_EQ(base::Time::FromJsTime(4 * 60000) + base::TimeDelta::FromMinutes(4), alarm_manager_->next_poll_time_); RemoveAllAlarms(); } } TEST_F(ExtensionAlarmsSchedulingTest, ReleasedExtensionPollsInfrequently) { set_extension(utils::CreateEmptyExtensionWithLocation( extensions::Manifest::INTERNAL)); test_clock_->SetNow(base::Time::FromJsTime(300000)); CreateAlarm("[\"a\", {\"when\": 300010}]"); CreateAlarm("[\"b\", {\"when\": 340000}]"); // On startup (when there's no "last poll"), we let alarms fire as // soon as they're scheduled. EXPECT_DOUBLE_EQ(300010, alarm_manager_->next_poll_time_.ToJsTime()); alarm_manager_->last_poll_time_ = base::Time::FromJsTime(290000); // In released extensions, we set the granularity to at least 1 // minute, which makes AddAlarm schedule the next poll after the // extension requested. alarm_manager_->ScheduleNextPoll(); EXPECT_DOUBLE_EQ((alarm_manager_->last_poll_time_ + base::TimeDelta::FromMinutes(1)).ToJsTime(), alarm_manager_->next_poll_time_.ToJsTime()); } TEST_F(ExtensionAlarmsSchedulingTest, TimerRunning) { EXPECT_FALSE(alarm_manager_->timer_.IsRunning()); CreateAlarm("[\"a\", {\"delayInMinutes\": 0.001}]"); EXPECT_TRUE(alarm_manager_->timer_.IsRunning()); test_clock_->Advance(base::TimeDelta::FromMilliseconds(60)); base::MessageLoop::current()->Run(); EXPECT_FALSE(alarm_manager_->timer_.IsRunning()); CreateAlarm("[\"bb\", {\"delayInMinutes\": 10}]"); EXPECT_TRUE(alarm_manager_->timer_.IsRunning()); RemoveAllAlarms(); EXPECT_FALSE(alarm_manager_->timer_.IsRunning()); } TEST_F(ExtensionAlarmsSchedulingTest, MinimumGranularity) { set_extension(utils::CreateEmptyExtensionWithLocation( extensions::Manifest::INTERNAL)); test_clock_->SetNow(base::Time::FromJsTime(0)); CreateAlarm("[\"a\", {\"periodInMinutes\": 2}]"); test_clock_->Advance(base::TimeDelta::FromSeconds(1)); CreateAlarm("[\"b\", {\"periodInMinutes\": 2}]"); test_clock_->Advance(base::TimeDelta::FromMinutes(2)); alarm_manager_->last_poll_time_ = base::Time::FromJsTime(2 * 60000); // In released extensions, we set the granularity to at least 1 // minute, which makes scheduler set it to 1 minute, rather than // 1 second later (when b is supposed to go off). alarm_manager_->ScheduleNextPoll(); EXPECT_DOUBLE_EQ((alarm_manager_->last_poll_time_ + base::TimeDelta::FromMinutes(1)).ToJsTime(), alarm_manager_->next_poll_time_.ToJsTime()); } TEST_F(ExtensionAlarmsSchedulingTest, DifferentMinimumGranularities) { test_clock_->SetNow(base::Time::FromJsTime(0)); // Create an alarm to go off in 12 seconds. This uses the default, unpacked // extension - so there is no minimum granularity. CreateAlarm("[\"a\", {\"periodInMinutes\": 0.2}]"); // 12 seconds. // Create a new extension, which is packed, and has a granularity of 1 minute. // CreateAlarm() uses extension_, so keep a ref of the old one around, and // repopulate extension_. scoped_refptr extension2(extension_ref()); set_extension( utils::CreateEmptyExtensionWithLocation(extensions::Manifest::INTERNAL)); CreateAlarm("[\"b\", {\"periodInMinutes\": 2}]"); alarm_manager_->last_poll_time_ = base::Time::FromJsTime(0); alarm_manager_->ScheduleNextPoll(); // The next poll time should be 12 seconds from now - the time at which the // first alarm should go off. EXPECT_DOUBLE_EQ((alarm_manager_->last_poll_time_ + base::TimeDelta::FromSeconds(12)).ToJsTime(), alarm_manager_->next_poll_time_.ToJsTime()); } // Test that scheduled alarms go off at set intervals, even if their actual // trigger is off. TEST_F(ExtensionAlarmsSchedulingTest, RepeatingAlarmsScheduledPredictably) { test_clock_->SetNow(base::Time::FromJsTime(0)); CreateAlarm("[\"a\", {\"periodInMinutes\": 2}]"); alarm_manager_->last_poll_time_ = base::Time::FromJsTime(0); alarm_manager_->ScheduleNextPoll(); // We expect the first poll to happen two minutes from the start. EXPECT_DOUBLE_EQ((alarm_manager_->last_poll_time_ + base::TimeDelta::FromSeconds(120)).ToJsTime(), alarm_manager_->next_poll_time_.ToJsTime()); // Poll more than two minutes later. test_clock_->Advance(base::TimeDelta::FromSeconds(125)); alarm_manager_->PollAlarms(); // The alarm should have triggered once. EXPECT_EQ(1u, alarm_delegate_->alarms_seen.size()); // The next poll should still be scheduled for four minutes from the start, // even though this is less than two minutes since the last alarm. // Last poll was at 125 seconds; next poll should be at 240 seconds. EXPECT_DOUBLE_EQ((alarm_manager_->last_poll_time_ + base::TimeDelta::FromSeconds(115)).ToJsTime(), alarm_manager_->next_poll_time_.ToJsTime()); // Completely miss a scheduled trigger. test_clock_->Advance(base::TimeDelta::FromSeconds(255)); // Total Time: 380s alarm_manager_->PollAlarms(); // The alarm should have triggered again at this last poll. EXPECT_EQ(2u, alarm_delegate_->alarms_seen.size()); // The next poll should be the first poll that hasn't happened and is in-line // with the original scheduling. // Last poll was at 380 seconds; next poll should be at 480 seconds. EXPECT_DOUBLE_EQ((alarm_manager_->last_poll_time_ + base::TimeDelta::FromSeconds(100)).ToJsTime(), alarm_manager_->next_poll_time_.ToJsTime()); } } // namespace extensions