/* * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fcp/base/simulated_clock.h" #include #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/time/civil_time.h" #include "absl/time/time.h" namespace fcp { namespace { using ::testing::ElementsAre; using ::testing::Eq; // Simple callback waiter that runs the function on Wakeup. class CallbackWaiter : public Clock::Waiter { public: explicit CallbackWaiter(std::function callback) : callback_(std::move(callback)) {} void WakeUp() override { callback_(); } private: std::function callback_; }; // Simple test waiter that adds its ID to the provided vector when WakeUp is // called. This is used to verify that waiters are woken up in the right order. class TestWaiter : public CallbackWaiter { public: explicit TestWaiter(int id, std::vector* output) : CallbackWaiter([=]() { output->push_back(id); }) {} }; absl::Time GetTestInitialTime() { return absl::FromCivil(absl::CivilDay(2020, 1, 1), absl::LocalTimeZone()); } TEST(SimulatedClockTest, GetAndUpdateNow) { absl::Time t = absl::UnixEpoch(); SimulatedClock clock; EXPECT_THAT(clock.Now(), t); absl::Time t2 = GetTestInitialTime(); SimulatedClock clock2(t2); EXPECT_THAT(clock2.Now(), t2); absl::Time t3 = t2 + absl::Seconds(42); clock2.AdvanceTime(absl::Seconds(42)); EXPECT_THAT(clock2.Now(), t3); absl::Time t4 = t3 + absl::Seconds(18); clock2.SetTime(t4); EXPECT_THAT(clock2.Now(), Eq(t4)); } // Verifies that waiters with future deadlines are not triggered unless the // time is advanced. TEST(SimulatedClockTest, FutureDeadline) { std::vector output; absl::Time t = GetTestInitialTime(); SimulatedClock clock(t); clock.WakeupWithDeadline(t + absl::Seconds(1), std::make_shared(1, &output)); EXPECT_THAT(output, ElementsAre()); clock.AdvanceTime(absl::Seconds(1)); EXPECT_THAT(output, ElementsAre(1)); // Advancing time again doesn't trigger the same waiter again. clock.AdvanceTime(absl::Seconds(1)); EXPECT_THAT(output, ElementsAre(1)); } // Verifies that the order of waiters with maching deadlines is preserved // when their wake-up is triggered. TEST(SimulatedClockTest, MatchingDeadlines) { std::vector output; absl::Time t = GetTestInitialTime(); SimulatedClock clock(t); absl::Time t1 = t + absl::Seconds(1); absl::Time t2 = t + absl::Seconds(2); clock.WakeupWithDeadline(t1, std::make_shared(1, &output)); clock.WakeupWithDeadline(t2, std::make_shared(2, &output)); clock.WakeupWithDeadline(t1, std::make_shared(3, &output)); clock.WakeupWithDeadline(t2, std::make_shared(4, &output)); clock.WakeupWithDeadline(t1, std::make_shared(5, &output)); // Trigger all waiters. clock.AdvanceTime(absl::Seconds(2)); EXPECT_THAT(output, ElementsAre(1, 3, 5, 2, 4)); } // Verifies that waiters with current or past deadlines are triggered promptly. TEST(SimulatedClockTest, PastAndCurrentDeadlines) { std::vector output; absl::Time t = absl::FromCivil(absl::CivilDay(2020, 1, 1), absl::LocalTimeZone()); SimulatedClock clock(t); clock.WakeupWithDeadline(t, std::make_shared(1, &output)); clock.WakeupWithDeadline(t - absl::Seconds(1), std::make_shared(2, &output)); EXPECT_THAT(output, ElementsAre(1, 2)); } // Verifies that only expired waiters are triggered. TEST(SimulatedClockTest, MultipleWaiters) { std::vector output; absl::Time t = GetTestInitialTime(); SimulatedClock clock(t); clock.WakeupWithDeadline(t + absl::Seconds(30), std::make_shared(1, &output)); clock.WakeupWithDeadline(t + absl::Seconds(20), std::make_shared(2, &output)); clock.WakeupWithDeadline(t + absl::Seconds(10), std::make_shared(3, &output)); // Advance by 15 seconds clock.AdvanceTime(absl::Seconds(15)); // Advance by another 5 seconds clock.AdvanceTime(absl::Seconds(5)); // Only waiters 3 and 2 should be triggered. EXPECT_THAT(output, ElementsAre(3, 2)); } // Verifies that a new timer can be scheduled when anoter timer is triggered. TEST(SimulatedClockTest, RecursiveWakeup) { std::vector output; absl::Time t = GetTestInitialTime(); SimulatedClock clock(t); clock.WakeupWithDeadline(t + absl::Seconds(20), std::make_shared(1, &output)); clock.WakeupWithDeadline( t + absl::Seconds(20), std::make_shared([&]() { output.push_back(2); clock.WakeupWithDeadline(t + absl::Seconds(15), std::make_shared(3, &output)); })); clock.AdvanceTime(absl::Seconds(20)); // Both waiters are triggered because the #3 one is already expired when // inserted recursively by waiter #2. EXPECT_THAT(output, ElementsAre(1, 2, 3)); } // Verifies that a long taking Wakeup notification results in triggering // other waiters that expire later. TEST(SimulatedClockTest, LongRunningWakeup) { std::vector output; absl::Time t = GetTestInitialTime(); SimulatedClock clock(t); clock.WakeupWithDeadline(t + absl::Seconds(10), std::make_shared(1, &output)); clock.WakeupWithDeadline( t + absl::Seconds(20), std::make_shared([&]() { output.push_back(2); clock.AdvanceTime(absl::Seconds(10)); })); clock.WakeupWithDeadline(t + absl::Seconds(30), std::make_shared(3, &output)); // Advance time by 20 second, which will advance time by another 10 seconds // when waking up waiter #2. clock.AdvanceTime(absl::Seconds(20)); EXPECT_THAT(output, ElementsAre(1, 2, 3)); } } // namespace } // namespace fcp