| // Copyright 2019 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. |
| |
| #include "util/alarm.h" |
| |
| #include <algorithm> |
| #include <chrono> |
| |
| #include "gtest/gtest.h" |
| #include "platform/test/fake_clock.h" |
| #include "platform/test/fake_task_runner.h" |
| #include "util/chrono_helpers.h" |
| |
| namespace openscreen { |
| namespace { |
| |
| class AlarmTest : public testing::Test { |
| public: |
| FakeClock* clock() { return &clock_; } |
| FakeTaskRunner* task_runner() { return &task_runner_; } |
| Alarm* alarm() { return &alarm_; } |
| |
| private: |
| FakeClock clock_{Clock::now()}; |
| FakeTaskRunner task_runner_{&clock_}; |
| Alarm alarm_{&FakeClock::now, &task_runner_}; |
| }; |
| |
| TEST_F(AlarmTest, RunsTaskAsClockAdvances) { |
| constexpr Clock::duration kDelay = milliseconds(20); |
| |
| const Clock::time_point alarm_time = FakeClock::now() + kDelay; |
| Clock::time_point actual_run_time{}; |
| alarm()->Schedule([&]() { actual_run_time = FakeClock::now(); }, alarm_time); |
| // Confirm the lambda did not run immediately. |
| ASSERT_EQ(Clock::time_point{}, actual_run_time); |
| |
| // Confirm the lambda does not run until the necessary delay has elapsed. |
| clock()->Advance(kDelay / 2); |
| ASSERT_EQ(Clock::time_point{}, actual_run_time); |
| |
| // Confirm the lambda is called when the necessary delay has elapsed. |
| clock()->Advance(kDelay / 2); |
| ASSERT_EQ(alarm_time, actual_run_time); |
| |
| // Confirm the lambda is only run once. |
| clock()->Advance(kDelay * 100); |
| ASSERT_EQ(alarm_time, actual_run_time); |
| } |
| |
| TEST_F(AlarmTest, RunsTaskImmediately) { |
| const Clock::time_point expected_run_time = FakeClock::now(); |
| Clock::time_point actual_run_time{}; |
| alarm()->Schedule([&]() { actual_run_time = FakeClock::now(); }, |
| Alarm::kImmediately); |
| // Confirm the lambda did not run yet, since it should run asynchronously, in |
| // a separate TaskRunner task. |
| ASSERT_EQ(Clock::time_point{}, actual_run_time); |
| |
| // Confirm the lambda runs without the clock having to tick forward. |
| task_runner()->RunTasksUntilIdle(); |
| ASSERT_EQ(expected_run_time, actual_run_time); |
| |
| // Confirm the lambda is only run once. |
| clock()->Advance(seconds(2)); |
| ASSERT_EQ(expected_run_time, actual_run_time); |
| } |
| |
| TEST_F(AlarmTest, CancelsTaskWhenGoingOutOfScope) { |
| constexpr Clock::duration kDelay = milliseconds(20); |
| constexpr Clock::time_point kNever{}; |
| |
| Clock::time_point actual_run_time{}; |
| { |
| Alarm scoped_alarm(&FakeClock::now, task_runner()); |
| const Clock::time_point alarm_time = FakeClock::now() + kDelay; |
| scoped_alarm.Schedule([&]() { actual_run_time = FakeClock::now(); }, |
| alarm_time); |
| // |scoped_alarm| is destroyed. |
| } |
| |
| // Confirm the lambda has never and will never run. |
| ASSERT_EQ(kNever, actual_run_time); |
| clock()->Advance(kDelay * 100); |
| ASSERT_EQ(kNever, actual_run_time); |
| } |
| |
| TEST_F(AlarmTest, Cancels) { |
| constexpr Clock::duration kDelay = milliseconds(20); |
| |
| const Clock::time_point alarm_time = FakeClock::now() + kDelay; |
| Clock::time_point actual_run_time{}; |
| alarm()->Schedule([&]() { actual_run_time = FakeClock::now(); }, alarm_time); |
| |
| // Advance the clock for half the delay, and confirm the lambda has not run |
| // yet. |
| clock()->Advance(kDelay / 2); |
| ASSERT_EQ(Clock::time_point{}, actual_run_time); |
| |
| // Cancel and then advance the clock well past the delay, and confirm the |
| // lambda has never run. |
| alarm()->Cancel(); |
| clock()->Advance(kDelay * 100); |
| ASSERT_EQ(Clock::time_point{}, actual_run_time); |
| } |
| |
| TEST_F(AlarmTest, CancelsAndRearms) { |
| constexpr Clock::duration kShorterDelay = milliseconds(10); |
| constexpr Clock::duration kLongerDelay = milliseconds(100); |
| |
| // Run the test twice: Once when scheduling first with a long delay, then a |
| // shorter delay; and once when scheduling first with a short delay, then a |
| // longer delay. This is to test Alarm's internal scheduling/firing logic. |
| for (int do_longer_then_shorter = 0; do_longer_then_shorter <= 1; |
| ++do_longer_then_shorter) { |
| const auto delay1 = do_longer_then_shorter ? kLongerDelay : kShorterDelay; |
| const auto delay2 = do_longer_then_shorter ? kShorterDelay : kLongerDelay; |
| |
| int count1 = 0; |
| alarm()->Schedule([&]() { ++count1; }, FakeClock::now() + delay1); |
| |
| // Advance the clock for half of |delay1|, and confirm the lambda that |
| // increments the variable does not run. |
| ASSERT_EQ(0, count1); |
| clock()->Advance(delay1 / 2); |
| ASSERT_EQ(0, count1); |
| |
| // Schedule a different lambda, that increments a different variable, to run |
| // after |delay2|. |
| int count2 = 0; |
| alarm()->Schedule([&]() { ++count2; }, FakeClock::now() + delay2); |
| |
| // Confirm the second scheduling will fire at the right moment. |
| clock()->Advance(delay2 / 2); |
| ASSERT_EQ(0, count2); |
| clock()->Advance(delay2 / 2); |
| ASSERT_EQ(1, count2); |
| |
| // Confirm the second scheduling never fires a second time, and also that |
| // the first one doesn't fire. |
| clock()->Advance(std::max(delay1, delay2) * 100); |
| ASSERT_EQ(0, count1); |
| ASSERT_EQ(1, count2); |
| } |
| } |
| |
| } // namespace |
| } // namespace openscreen |