xref: /aosp_15_r20/external/pigweed/pw_chrono/system_timer_facade_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include <chrono>
16 
17 #include "pw_chrono/system_clock.h"
18 #include "pw_chrono/system_timer.h"
19 #include "pw_sync/thread_notification.h"
20 #include "pw_unit_test/framework.h"
21 
22 using namespace std::chrono_literals;
23 
24 namespace pw::chrono {
25 namespace {
26 
27 // We can't control the SystemClock's period configuration, so just in case
28 // duration cannot be accurately expressed in integer ticks, round the
29 // duration up.
30 constexpr SystemClock::duration kRoundedArbitraryShortDuration =
31     SystemClock::for_at_least(42ms);
32 constexpr SystemClock::duration kRoundedArbitraryLongDuration =
33     SystemClock::for_at_least(1s);
34 
ShouldNotBeInvoked(SystemClock::time_point)35 void ShouldNotBeInvoked(SystemClock::time_point) { FAIL(); }
36 
TEST(SystemTimer,CancelInactive)37 TEST(SystemTimer, CancelInactive) {
38   SystemTimer timer(ShouldNotBeInvoked);
39   timer.Cancel();
40 }
41 
TEST(SystemTimer,CancelExplicitly)42 TEST(SystemTimer, CancelExplicitly) {
43   SystemTimer timer(ShouldNotBeInvoked);
44   timer.InvokeAfter(kRoundedArbitraryLongDuration);
45   timer.Cancel();
46 }
47 
TEST(SystemTimer,CancelThroughDestruction)48 TEST(SystemTimer, CancelThroughDestruction) {
49   SystemTimer timer(ShouldNotBeInvoked);
50   timer.InvokeAfter(kRoundedArbitraryLongDuration);
51 }
52 
TEST(SystemTimer,CancelThroughRescheduling)53 TEST(SystemTimer, CancelThroughRescheduling) {
54   SystemTimer timer(ShouldNotBeInvoked);
55   timer.InvokeAfter(kRoundedArbitraryLongDuration);
56   // Cancel the first with this rescheduling.
57   timer.InvokeAfter(kRoundedArbitraryLongDuration);
58   timer.Cancel();
59 }
60 
61 // Helper class to let test cases easily instantiate a timer with a handler
62 // and its own context.
63 class TimerWithHandler {
64  public:
TimerWithHandler()65   TimerWithHandler()
66       : timer_([this](SystemClock::time_point expired_deadline) {
67           this->OnExpiryCallback(expired_deadline);
68         }) {}
69   virtual ~TimerWithHandler() = default;
70 
71   // To be implemented by the test case.
72   virtual void OnExpiryCallback(SystemClock::time_point expired_deadline) = 0;
73 
timer()74   SystemTimer& timer() { return timer_; }
75 
76  private:
77   SystemTimer timer_;
78 };
79 
TEST(SystemTimer,StaticInvokeAt)80 TEST(SystemTimer, StaticInvokeAt) {
81   class TimerWithContext : public TimerWithHandler {
82    public:
83     void OnExpiryCallback(SystemClock::time_point expired_deadline) override {
84       EXPECT_GE(SystemClock::now(), expired_deadline);
85       EXPECT_EQ(expired_deadline, expected_deadline);
86       callback_ran_notification.release();
87     }
88 
89     SystemClock::time_point expected_deadline;
90     sync::ThreadNotification callback_ran_notification;
91   };
92   static TimerWithContext uut;
93 
94   uut.expected_deadline = SystemClock::now() + kRoundedArbitraryShortDuration;
95   uut.timer().InvokeAt(uut.expected_deadline);
96   uut.callback_ran_notification.acquire();
97 
98   // Ensure you can re-use the timer.
99   uut.expected_deadline = SystemClock::now() + kRoundedArbitraryShortDuration;
100   uut.timer().InvokeAt(uut.expected_deadline);
101   uut.callback_ran_notification.acquire();
102 }
103 
TEST(SystemTimer,InvokeAt)104 TEST(SystemTimer, InvokeAt) {
105   class TimerWithContext : public TimerWithHandler {
106    public:
107     void OnExpiryCallback(SystemClock::time_point expired_deadline) override {
108       EXPECT_GE(SystemClock::now(), expired_deadline);
109       EXPECT_EQ(expired_deadline, expected_deadline);
110       callback_ran_notification.release();
111     }
112 
113     SystemClock::time_point expected_deadline;
114     sync::ThreadNotification callback_ran_notification;
115   };
116   TimerWithContext uut;
117 
118   uut.expected_deadline = SystemClock::now() + kRoundedArbitraryShortDuration;
119   uut.timer().InvokeAt(uut.expected_deadline);
120   uut.callback_ran_notification.acquire();
121 
122   // Ensure you can re-use the timer.
123   uut.expected_deadline = SystemClock::now() + kRoundedArbitraryShortDuration;
124   uut.timer().InvokeAt(uut.expected_deadline);
125   uut.callback_ran_notification.acquire();
126 
127   // Ensure scheduling it in the past causes it to execute immediately.
128   uut.expected_deadline = SystemClock::now() - SystemClock::duration(1);
129   uut.timer().InvokeAt(uut.expected_deadline);
130   uut.callback_ran_notification.acquire();
131 }
132 
TEST(SystemTimer,InvokeAfter)133 TEST(SystemTimer, InvokeAfter) {
134   class TimerWithContext : public TimerWithHandler {
135    public:
136     void OnExpiryCallback(SystemClock::time_point expired_deadline) override {
137       EXPECT_GE(SystemClock::now(), expired_deadline);
138       EXPECT_GE(expired_deadline, expected_min_deadline);
139       callback_ran_notification.release();
140     }
141 
142     SystemClock::time_point expected_min_deadline;
143     sync::ThreadNotification callback_ran_notification;
144   };
145   TimerWithContext uut;
146 
147   uut.expected_min_deadline =
148       SystemClock::TimePointAfterAtLeast(kRoundedArbitraryShortDuration);
149   uut.timer().InvokeAfter(kRoundedArbitraryShortDuration);
150   uut.callback_ran_notification.acquire();
151 
152   // Ensure you can re-use the timer.
153   uut.expected_min_deadline =
154       SystemClock::TimePointAfterAtLeast(kRoundedArbitraryShortDuration);
155   uut.timer().InvokeAfter(kRoundedArbitraryShortDuration);
156   uut.callback_ran_notification.acquire();
157 
158   // Ensure scheduling it immediately works.
159   uut.expected_min_deadline = SystemClock::now();
160   uut.timer().InvokeAfter(SystemClock::duration(0));
161   uut.callback_ran_notification.acquire();
162 }
163 
TEST(SystemTimer,CancelFromCallback)164 TEST(SystemTimer, CancelFromCallback) {
165   class TimerWithContext : public TimerWithHandler {
166    public:
167     void OnExpiryCallback(SystemClock::time_point) override {
168       timer().Cancel();
169       callback_ran_notification.release();
170     }
171 
172     sync::ThreadNotification callback_ran_notification;
173   };
174   TimerWithContext uut;
175 
176   uut.timer().InvokeAfter(kRoundedArbitraryShortDuration);
177   uut.callback_ran_notification.acquire();
178 }
179 
TEST(SystemTimer,RescheduleAndCancelFromCallback)180 TEST(SystemTimer, RescheduleAndCancelFromCallback) {
181   class TimerWithContext : public TimerWithHandler {
182    public:
183     void OnExpiryCallback(SystemClock::time_point) override {
184       timer().InvokeAfter(kRoundedArbitraryShortDuration);
185       timer().Cancel();
186       callback_ran_notification.release();
187     }
188 
189     sync::ThreadNotification callback_ran_notification;
190   };
191   TimerWithContext uut;
192 
193   uut.timer().InvokeAfter(kRoundedArbitraryShortDuration);
194   uut.callback_ran_notification.acquire();
195 }
196 
TEST(SystemTimer,RescheduleFromCallback)197 TEST(SystemTimer, RescheduleFromCallback) {
198   class TimerWithContext : public TimerWithHandler {
199    public:
200     void OnExpiryCallback(SystemClock::time_point expired_deadline) override {
201       EXPECT_GE(SystemClock::now(), expired_deadline);
202 
203       EXPECT_EQ(expired_deadline, expected_deadline);
204       invocation_count++;
205       ASSERT_LE(invocation_count, kRequiredInvocations);
206       if (invocation_count < kRequiredInvocations) {
207         expected_deadline = expired_deadline + kPeriod;
208         timer().InvokeAt(expected_deadline);
209       } else {
210         callbacks_done_notification.release();
211       }
212     }
213 
214     const uint8_t kRequiredInvocations = 5;
215     const SystemClock::duration kPeriod = kRoundedArbitraryShortDuration;
216     uint8_t invocation_count = 0;
217     SystemClock::time_point expected_deadline;
218     sync::ThreadNotification callbacks_done_notification;
219   };
220   TimerWithContext uut;
221 
222   uut.expected_deadline = SystemClock::now() + kRoundedArbitraryShortDuration;
223   uut.timer().InvokeAt(uut.expected_deadline);
224   uut.callbacks_done_notification.acquire();
225 }
226 
TEST(SystemTimer,DoubleRescheduleFromCallback)227 TEST(SystemTimer, DoubleRescheduleFromCallback) {
228   class TimerWithContext : public TimerWithHandler {
229    public:
230     void OnExpiryCallback(SystemClock::time_point expired_deadline) override {
231       EXPECT_GE(SystemClock::now(), expired_deadline);
232 
233       EXPECT_EQ(expired_deadline, expected_deadline);
234       invocation_count++;
235       ASSERT_LE(invocation_count, kExpectedInvocations);
236       if (invocation_count == 1) {
237         expected_deadline = expired_deadline + kPeriod;
238         timer().InvokeAt(expected_deadline);
239         timer().InvokeAt(expected_deadline);
240       } else {
241         callbacks_done_notification.release();
242       }
243     }
244 
245     const uint8_t kExpectedInvocations = 2;
246     const SystemClock::duration kPeriod = kRoundedArbitraryShortDuration;
247     uint8_t invocation_count = 0;
248     SystemClock::time_point expected_deadline;
249     sync::ThreadNotification callbacks_done_notification;
250   };
251   TimerWithContext uut;
252 
253   uut.expected_deadline = SystemClock::now() + kRoundedArbitraryShortDuration;
254   uut.timer().InvokeAt(uut.expected_deadline);
255   uut.callbacks_done_notification.acquire();
256 }
257 
258 }  // namespace
259 }  // namespace pw::chrono
260