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