alarms_api_unittest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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
24typedef extensions::api::alarms::Alarm JsAlarm;
25
26namespace utils = extension_function_test_utils;
27
28namespace extensions {
29
30namespace {
31
32// Test delegate which quits the message loop when an alarm fires.
33class 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    MessageLoop::current()->Quit();
40  }
41
42  std::vector<std::string> alarms_seen;
43};
44
45}  // namespace
46
47class 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_));
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
146TEST_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  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
173TEST_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  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  MessageLoop::current()->Run();
196
197  ASSERT_EQ(2u, alarm_delegate_->alarms_seen.size());
198  EXPECT_EQ("", alarm_delegate_->alarms_seen[0]);
199}
200
201TEST_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  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
224TEST_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  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  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
251TEST_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
267TEST_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
282TEST_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
321TEST_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
348TEST_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  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
391TEST_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
417class 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
428TEST_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    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    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
486TEST_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
507TEST_F(ExtensionAlarmsSchedulingTest, TimerRunning) {
508  EXPECT_FALSE(alarm_manager_->timer_.IsRunning());
509  CreateAlarm("[\"a\", {\"delayInMinutes\": 0.001}]");
510  EXPECT_TRUE(alarm_manager_->timer_.IsRunning());
511  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