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