1 // Copyright 2019 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 "util/alarm.h"
6 
7 #include <algorithm>
8 #include <chrono>
9 
10 #include "gtest/gtest.h"
11 #include "platform/test/fake_clock.h"
12 #include "platform/test/fake_task_runner.h"
13 #include "util/chrono_helpers.h"
14 
15 namespace openscreen {
16 namespace {
17 
18 class AlarmTest : public testing::Test {
19  public:
clock()20   FakeClock* clock() { return &clock_; }
task_runner()21   FakeTaskRunner* task_runner() { return &task_runner_; }
alarm()22   Alarm* alarm() { return &alarm_; }
23 
24  private:
25   FakeClock clock_{Clock::now()};
26   FakeTaskRunner task_runner_{&clock_};
27   Alarm alarm_{&FakeClock::now, &task_runner_};
28 };
29 
TEST_F(AlarmTest,RunsTaskAsClockAdvances)30 TEST_F(AlarmTest, RunsTaskAsClockAdvances) {
31   constexpr Clock::duration kDelay = milliseconds(20);
32 
33   const Clock::time_point alarm_time = FakeClock::now() + kDelay;
34   Clock::time_point actual_run_time{};
35   alarm()->Schedule([&]() { actual_run_time = FakeClock::now(); }, alarm_time);
36   // Confirm the lambda did not run immediately.
37   ASSERT_EQ(Clock::time_point{}, actual_run_time);
38 
39   // Confirm the lambda does not run until the necessary delay has elapsed.
40   clock()->Advance(kDelay / 2);
41   ASSERT_EQ(Clock::time_point{}, actual_run_time);
42 
43   // Confirm the lambda is called when the necessary delay has elapsed.
44   clock()->Advance(kDelay / 2);
45   ASSERT_EQ(alarm_time, actual_run_time);
46 
47   // Confirm the lambda is only run once.
48   clock()->Advance(kDelay * 100);
49   ASSERT_EQ(alarm_time, actual_run_time);
50 }
51 
TEST_F(AlarmTest,RunsTaskImmediately)52 TEST_F(AlarmTest, RunsTaskImmediately) {
53   const Clock::time_point expected_run_time = FakeClock::now();
54   Clock::time_point actual_run_time{};
55   alarm()->Schedule([&]() { actual_run_time = FakeClock::now(); },
56                     Alarm::kImmediately);
57   // Confirm the lambda did not run yet, since it should run asynchronously, in
58   // a separate TaskRunner task.
59   ASSERT_EQ(Clock::time_point{}, actual_run_time);
60 
61   // Confirm the lambda runs without the clock having to tick forward.
62   task_runner()->RunTasksUntilIdle();
63   ASSERT_EQ(expected_run_time, actual_run_time);
64 
65   // Confirm the lambda is only run once.
66   clock()->Advance(seconds(2));
67   ASSERT_EQ(expected_run_time, actual_run_time);
68 }
69 
TEST_F(AlarmTest,CancelsTaskWhenGoingOutOfScope)70 TEST_F(AlarmTest, CancelsTaskWhenGoingOutOfScope) {
71   constexpr Clock::duration kDelay = milliseconds(20);
72   constexpr Clock::time_point kNever{};
73 
74   Clock::time_point actual_run_time{};
75   {
76     Alarm scoped_alarm(&FakeClock::now, task_runner());
77     const Clock::time_point alarm_time = FakeClock::now() + kDelay;
78     scoped_alarm.Schedule([&]() { actual_run_time = FakeClock::now(); },
79                           alarm_time);
80     // |scoped_alarm| is destroyed.
81   }
82 
83   // Confirm the lambda has never and will never run.
84   ASSERT_EQ(kNever, actual_run_time);
85   clock()->Advance(kDelay * 100);
86   ASSERT_EQ(kNever, actual_run_time);
87 }
88 
TEST_F(AlarmTest,Cancels)89 TEST_F(AlarmTest, Cancels) {
90   constexpr Clock::duration kDelay = milliseconds(20);
91 
92   const Clock::time_point alarm_time = FakeClock::now() + kDelay;
93   Clock::time_point actual_run_time{};
94   alarm()->Schedule([&]() { actual_run_time = FakeClock::now(); }, alarm_time);
95 
96   // Advance the clock for half the delay, and confirm the lambda has not run
97   // yet.
98   clock()->Advance(kDelay / 2);
99   ASSERT_EQ(Clock::time_point{}, actual_run_time);
100 
101   // Cancel and then advance the clock well past the delay, and confirm the
102   // lambda has never run.
103   alarm()->Cancel();
104   clock()->Advance(kDelay * 100);
105   ASSERT_EQ(Clock::time_point{}, actual_run_time);
106 }
107 
TEST_F(AlarmTest,CancelsAndRearms)108 TEST_F(AlarmTest, CancelsAndRearms) {
109   constexpr Clock::duration kShorterDelay = milliseconds(10);
110   constexpr Clock::duration kLongerDelay = milliseconds(100);
111 
112   // Run the test twice: Once when scheduling first with a long delay, then a
113   // shorter delay; and once when scheduling first with a short delay, then a
114   // longer delay. This is to test Alarm's internal scheduling/firing logic.
115   for (int do_longer_then_shorter = 0; do_longer_then_shorter <= 1;
116        ++do_longer_then_shorter) {
117     const auto delay1 = do_longer_then_shorter ? kLongerDelay : kShorterDelay;
118     const auto delay2 = do_longer_then_shorter ? kShorterDelay : kLongerDelay;
119 
120     int count1 = 0;
121     alarm()->Schedule([&]() { ++count1; }, FakeClock::now() + delay1);
122 
123     // Advance the clock for half of |delay1|, and confirm the lambda that
124     // increments the variable does not run.
125     ASSERT_EQ(0, count1);
126     clock()->Advance(delay1 / 2);
127     ASSERT_EQ(0, count1);
128 
129     // Schedule a different lambda, that increments a different variable, to run
130     // after |delay2|.
131     int count2 = 0;
132     alarm()->Schedule([&]() { ++count2; }, FakeClock::now() + delay2);
133 
134     // Confirm the second scheduling will fire at the right moment.
135     clock()->Advance(delay2 / 2);
136     ASSERT_EQ(0, count2);
137     clock()->Advance(delay2 / 2);
138     ASSERT_EQ(1, count2);
139 
140     // Confirm the second scheduling never fires a second time, and also that
141     // the first one doesn't fire.
142     clock()->Advance(std::max(delay1, delay2) * 100);
143     ASSERT_EQ(0, count1);
144     ASSERT_EQ(1, count2);
145   }
146 }
147 
148 }  // namespace
149 }  // namespace openscreen
150