diff --git a/internal/test/BUILD b/internal/test/BUILD index e8c5bf70..e3227fe4 100644 --- a/internal/test/BUILD +++ b/internal/test/BUILD @@ -47,6 +47,7 @@ cc_library( "//internal/platform/implementation:types", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", @@ -76,6 +77,7 @@ cc_test( ":test", "//internal/data:data_manager", "//internal/network:types", + "//internal/platform:types", "//internal/platform/implementation:types", "//internal/platform/implementation/g3", # fixdeps: keep "@com_github_protobuf_matchers//protobuf-matchers", diff --git a/internal/test/fake_clock.cc b/internal/test/fake_clock.cc index 3f464ea6..f737d187 100644 --- a/internal/test/fake_clock.cc +++ b/internal/test/fake_clock.cc @@ -76,4 +76,9 @@ int FakeClock::GetObserversCount() { return observers_.size(); } +void FakeClock::Reset() { + absl::MutexLock lock(&mutex_); + return observers_.clear(); +} + } // namespace nearby diff --git a/internal/test/fake_clock.h b/internal/test/fake_clock.h index 8c077e18..5b7a9af8 100644 --- a/internal/test/fake_clock.h +++ b/internal/test/fake_clock.h @@ -45,6 +45,8 @@ class FakeClock : public Clock { int GetObserversCount() ABSL_LOCKS_EXCLUDED(mutex_); + void Reset() ABSL_LOCKS_EXCLUDED(mutex_); + private: mutable absl::Mutex mutex_; absl::Time now_ ABSL_GUARDED_BY(mutex_); diff --git a/internal/test/fake_clock_test.cc b/internal/test/fake_clock_test.cc index d06966e6..0f57ce56 100644 --- a/internal/test/fake_clock_test.cc +++ b/internal/test/fake_clock_test.cc @@ -35,6 +35,18 @@ TEST(FakeClock, TestFastForward) { EXPECT_EQ(clock.GetObserversCount(), 0); } +TEST(FakeClock, TestReset) { + FakeClock clock; + int count = 0; + auto observer = [&count]() { count++; }; + clock.AddObserver("test", observer); + clock.FastForward(absl::Nanoseconds(1500)); + EXPECT_EQ(count, 1); + EXPECT_EQ(clock.GetObserversCount(), 1); + clock.Reset(); + EXPECT_EQ(clock.GetObserversCount(), 0); +} + TEST(FakeClock, TestObserver) { FakeClock clock; int count = 0; diff --git a/internal/test/fake_timer.cc b/internal/test/fake_timer.cc index 70929e41..08d47e8a 100644 --- a/internal/test/fake_timer.cc +++ b/internal/test/fake_timer.cc @@ -1,4 +1,4 @@ -// Copyright 2021 Google LLC +// Copyright 2021-2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,82 +18,110 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/time/clock.h" #include "absl/time/time.h" +#include "internal/platform/logging.h" +#include "internal/platform/mutex_lock.h" namespace nearby { -FakeTimer::FakeTimer(FakeClock* clock) : clock_(clock) { - id_ = std::to_string(absl::ToUnixNanos(absl::Now())); - clock_->AddObserver(id_, [this]() { ClockUpdated(); }); -} +FakeTimer::FakeTimer(FakeClock* clock) : clock_(clock) {} -FakeTimer::~FakeTimer() { clock_->RemoveObserver(id_); } +FakeTimer::~FakeTimer() { Stop(); } bool FakeTimer::Start(int delay, int period, absl::AnyInvocable callback) { - if (is_started_ || delay < 0 || period < 0) { - return false; - } - delay_ = delay; - period_ = period; - callback_ = std::move(callback); - start_time_ = clock_->Now(); - fired_count_ = 0; - is_started_ = true; - - return true; + MutexLock lock(&mutex_); + return InternalStart(delay, period, std::move(callback)); } bool FakeTimer::Stop() { - is_started_ = false; - return true; + MutexLock lock(&mutex_); + return InternalStop(); } -bool FakeTimer::IsRunning() { return is_started_; } +bool FakeTimer::IsRunning() { + MutexLock lock(&mutex_); + return !timer_data_.id.empty(); +} void FakeTimer::ClockUpdated() { - if (!is_started_) { + MutexLock lock(&mutex_); + TimerData timer_data = timer_data_; + if (timer_data.id.empty()) { return; } absl::Time now = clock_->Now(); - int64_t duration = absl::ToInt64Milliseconds(now - start_time_); - FakeClock* clock = clock_; - // The timer may be released during callback, save period and delay to avoid - // access exception. - int period = period_; - int delay = delay_; - - if (duration >= delay_ && fired_count_ == 0) { - ++fired_count_; - if (callback_ != nullptr) { - callback_(); + if (!timer_data.is_delay_called && + timer_data.last_time == timer_data.create_time && + timer_data.last_time + absl::Milliseconds(timer_data.delay) <= now) { + timer_data.callback(); + if (timer_data.id == timer_data_.id) { + timer_data.is_delay_called = true; + timer_data.last_time = + timer_data.last_time + absl::Milliseconds(timer_data.delay); + timer_data_ = timer_data; } } - if (period == 0 || duration < delay || - ((clock_ != nullptr) && (clock_ != clock))) { + if (timer_data.period == 0 || timer_data.id != timer_data_.id) { + // timer is changed during callback. return; } - int count = (duration - delay) / period; - int should_fire_count = count - fired_count_ + 1; - for (int i = 0; i < should_fire_count; ++i) { - ++fired_count_; - if (callback_ != nullptr) { - callback_(); + timer_data_ = timer_data; + while (timer_data.last_time + absl::Milliseconds(timer_data.period) <= now) { + timer_data.callback(); + if (timer_data.id == timer_data_.id) { + timer_data.last_time += absl::Milliseconds(timer_data.period); + } else { + return; } } + + timer_data_ = timer_data; } bool FakeTimer::FireNow() { - if (IsRunning() && callback_) { - callback_(); + if (IsRunning()) { + timer_data_.callback(); return true; } return false; } +bool FakeTimer::InternalStart(int delay, int period, + absl::AnyInvocable callback) { + if (delay < 0 || period < 0 || callback == nullptr) { + return false; + } + + if (!timer_data_.id.empty()) { + NEARBY_LOGS(ERROR) << __func__ << ": timer is already running"; + return false; + } + + timer_data_.id = std::to_string(absl::ToUnixNanos(absl::Now())); + timer_data_.delay = delay; + timer_data_.period = period; + callback_ = std::move(callback); + timer_data_.callback = [&]() { callback_(); }; + timer_data_.create_time = clock_->Now(); + timer_data_.last_time = timer_data_.create_time; + clock_->AddObserver(timer_data_.id, [this]() { ClockUpdated(); }); + return true; +} + +bool FakeTimer::InternalStop() { + if (timer_data_.id.empty()) { + return true; + } + clock_->RemoveObserver(timer_data_.id); + timer_data_ = {}; + return true; +} + } // namespace nearby diff --git a/internal/test/fake_timer.h b/internal/test/fake_timer.h index 24a7c15e..6f4e2e20 100644 --- a/internal/test/fake_timer.h +++ b/internal/test/fake_timer.h @@ -1,4 +1,4 @@ -// Copyright 2021 Google LLC +// Copyright 2021-2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ #include +#include "internal/platform/mutex.h" #include "internal/platform/timer.h" #include "internal/test/fake_clock.h" @@ -35,16 +36,25 @@ class FakeTimer : public Timer { bool FireNow() override; private: - void ClockUpdated(); + struct TimerData { + std::string id; + int delay; + int period; + bool is_delay_called = false; + absl::Time create_time; + absl::Time last_time; + std::function callback; + }; - std::string id_; - int delay_ = 0; - int period_ = 0; - int fired_count_ = 0; - absl::Time start_time_; - bool is_started_ = false; - absl::AnyInvocable callback_; + void ClockUpdated(); + bool InternalStart(int delay, int period, + absl::AnyInvocable callback); + bool InternalStop(); + + mutable RecursiveMutex mutex_; FakeClock* clock_ = nullptr; + TimerData timer_data_; + absl::AnyInvocable callback_ = nullptr; }; } // namespace nearby diff --git a/internal/test/fake_timer_test.cc b/internal/test/fake_timer_test.cc index 2bbb59b4..89a185b9 100644 --- a/internal/test/fake_timer_test.cc +++ b/internal/test/fake_timer_test.cc @@ -14,10 +14,11 @@ #include "internal/test/fake_timer.h" -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/time/clock.h" #include "absl/time/time.h" +#include "internal/platform/logging.h" +#include "internal/test/fake_task_runner.h" namespace nearby { namespace { @@ -105,10 +106,10 @@ TEST(FakeTimer, TestTimerDestructor) { FakeClock clock; { FakeTimer timer(&clock); - EXPECT_EQ(clock.GetObserversCount(), 1); int count = 0; auto callback = [&count]() { ++count; }; timer.Start(100, 0, callback); + EXPECT_EQ(clock.GetObserversCount(), 1); EXPECT_TRUE(timer.IsRunning()); EXPECT_EQ(count, 0); EXPECT_TRUE(timer.Stop()); @@ -160,5 +161,25 @@ TEST(FakeTimer, StartTimerInTimerProc) { EXPECT_EQ(count, 2); } +TEST(FakeTimer, WaitForRunningTask) { + bool finish = false; + FakeClock clock; + FakeTimer timer1(&clock); + FakeTaskRunner task_runner{&clock, 1}; + + timer1.Start(50, 0, [&]() { + absl::SleepFor(absl::Milliseconds(2000)); + finish = true; + }); + + task_runner.PostTask([&]() { + absl::SleepFor(absl::Milliseconds(10)); + timer1.Stop(); + EXPECT_TRUE(finish); + }); + + clock.FastForward(absl::Milliseconds(50)); +} + } // namespace } // namespace nearby