1 // Copyright 2024 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 "pw_async2/simulated_time_provider.h"
16
17 #include "pw_chrono/system_clock.h"
18 #include "pw_unit_test/framework.h"
19
20 namespace {
21
22 using ::pw::async2::Context;
23 using ::pw::async2::Dispatcher;
24 using ::pw::async2::Pending;
25 using ::pw::async2::Poll;
26 using ::pw::async2::Ready;
27 using ::pw::async2::SimulatedTimeProvider;
28 using ::pw::async2::Task;
29 using ::pw::async2::TimeFuture;
30 using ::pw::chrono::SystemClock;
31 using ::std::chrono_literals::operator""min;
32 using ::std::chrono_literals::operator""h;
33
34 // We can't control the SystemClock's period configuration, so just in case
35 // 42 hours cannot be accurately expressed in integer ticks, round the
36 // duration up.
37 constexpr SystemClock::duration kRoundedArbitraryDuration =
38 SystemClock::for_at_least(42h);
39
TEST(SimulatedTimeProvider,InitialTime)40 TEST(SimulatedTimeProvider, InitialTime) {
41 SimulatedTimeProvider<SystemClock> tp;
42
43 EXPECT_EQ(SystemClock::time_point(SystemClock::duration(0)), tp.now());
44 }
45
TEST(SimulatedTimeProvider,SetTime)46 TEST(SimulatedTimeProvider, SetTime) {
47 SimulatedTimeProvider<SystemClock> tp;
48
49 tp.SetTime(SystemClock::time_point(kRoundedArbitraryDuration));
50 EXPECT_EQ(kRoundedArbitraryDuration, tp.now().time_since_epoch());
51 }
52
TEST(SimulatedTimeProvider,AdvanceTime)53 TEST(SimulatedTimeProvider, AdvanceTime) {
54 SimulatedTimeProvider<SystemClock> tp;
55
56 const SystemClock::time_point before_timestamp = tp.now();
57 tp.AdvanceTime(kRoundedArbitraryDuration);
58 const SystemClock::time_point after_timestamp = tp.now();
59
60 EXPECT_EQ(kRoundedArbitraryDuration, tp.now().time_since_epoch());
61 EXPECT_EQ(kRoundedArbitraryDuration, after_timestamp - before_timestamp);
62 }
63
64 struct WaitTask : public Task {
WaitTask__anon6cb087330111::WaitTask65 WaitTask(TimeFuture<SystemClock>&& future)
66 : future_(std::move(future)), completed_(false) {}
67
DoPend__anon6cb087330111::WaitTask68 Poll<> DoPend(Context& cx) final {
69 if (future_.Pend(cx).IsPending()) {
70 return Pending();
71 }
72 completed_ = true;
73 return Ready();
74 }
75 TimeFuture<SystemClock> future_;
76 bool completed_;
77 };
78
TEST(SimulatedTimeProvider,AdvanceTimeRunsPastTimers)79 TEST(SimulatedTimeProvider, AdvanceTimeRunsPastTimers) {
80 SimulatedTimeProvider<SystemClock> tp;
81 WaitTask task(tp.WaitFor(1h));
82 Dispatcher dispatcher;
83 dispatcher.Post(task);
84 tp.AdvanceTime(30min);
85 EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
86 tp.AdvanceTime(40min);
87 EXPECT_EQ(dispatcher.RunUntilStalled(), Ready());
88 }
89
TEST(SimulatedTimeProvider,AdvanceUntilNextExpirationRunsPastTimers)90 TEST(SimulatedTimeProvider, AdvanceUntilNextExpirationRunsPastTimers) {
91 SimulatedTimeProvider<SystemClock> tp;
92 WaitTask task(tp.WaitFor(1h));
93 Dispatcher dispatcher;
94 dispatcher.Post(task);
95 EXPECT_TRUE(tp.AdvanceUntilNextExpiration());
96 EXPECT_EQ(dispatcher.RunUntilStalled(), Ready());
97 EXPECT_FALSE(tp.AdvanceUntilNextExpiration());
98 }
99
TEST(SimulatedTimeProvider,TimeUntilNextExpirationGetsTimerDelay)100 TEST(SimulatedTimeProvider, TimeUntilNextExpirationGetsTimerDelay) {
101 SimulatedTimeProvider<SystemClock> tp;
102 auto timer = tp.WaitFor(1h);
103 ASSERT_TRUE(tp.TimeUntilNextExpiration().has_value());
104 EXPECT_GE(*tp.TimeUntilNextExpiration(), 1h);
105 }
106
TEST(SimulatedTimeProvider,TimeUntilNextExpirationAfterCompletionReturnsNullopt)107 TEST(SimulatedTimeProvider,
108 TimeUntilNextExpirationAfterCompletionReturnsNullopt) {
109 SimulatedTimeProvider<SystemClock> tp;
110 auto timer = tp.WaitFor(1h);
111 tp.AdvanceTime(90min);
112 EXPECT_FALSE(tp.TimeUntilNextExpiration().has_value());
113 }
114
TEST(SimulatedTimeProvider,TimeUntilNextExpirationAfterDestroyReturnsNullopt)115 TEST(SimulatedTimeProvider, TimeUntilNextExpirationAfterDestroyReturnsNullopt) {
116 SimulatedTimeProvider<SystemClock> tp;
117 {
118 auto timer = tp.WaitFor(1h);
119 }
120 EXPECT_FALSE(tp.TimeUntilNextExpiration().has_value());
121 }
122
TEST(SimulatedTimeProvider,ResetSetsTimerBackToPendingAndFiresAgain)123 TEST(SimulatedTimeProvider, ResetSetsTimerBackToPendingAndFiresAgain) {
124 SimulatedTimeProvider<SystemClock> tp;
125 Dispatcher dispatcher;
126
127 auto timer = tp.WaitFor(1h);
128 EXPECT_TRUE(dispatcher.RunPendableUntilStalled(timer).IsPending());
129 tp.AdvanceTime(90min);
130 EXPECT_TRUE(dispatcher.RunPendableUntilStalled(timer).IsReady());
131 timer.Reset(timer.expiration() + 40min);
132 EXPECT_TRUE(dispatcher.RunPendableUntilStalled(timer).IsPending());
133 tp.AdvanceTime(90min);
134 EXPECT_TRUE(dispatcher.RunPendableUntilStalled(timer).IsReady());
135 }
136
TEST(SimulatedTimeProvider,TimerWithPastExpirationExpiresImmediately)137 TEST(SimulatedTimeProvider, TimerWithPastExpirationExpiresImmediately) {
138 SimulatedTimeProvider<SystemClock> tp;
139 auto start = tp.now();
140 tp.AdvanceTime(90min);
141 auto timer = tp.WaitUntil(start + 30min);
142 Dispatcher dispatcher;
143 EXPECT_TRUE(dispatcher.RunPendableUntilStalled(timer).IsReady());
144 }
145
TEST(SimulatedTimeProvider,MultipleMovedTimersExpireInOrder)146 TEST(SimulatedTimeProvider, MultipleMovedTimersExpireInOrder) {
147 SimulatedTimeProvider<SystemClock> tp;
148 // Insert out-of-order to check that they become sorted.
149 auto t3_init = tp.WaitFor(3h);
150 auto t1_init = tp.WaitFor(1h);
151 auto t2_init = tp.WaitFor(2h);
152 // Move out of order to check that sorting is preserved.
153 WaitTask t2(std::move(t2_init));
154 WaitTask t1(std::move(t1_init));
155 WaitTask t3(std::move(t3_init));
156
157 Dispatcher dispatcher;
158 dispatcher.Post(t1);
159 dispatcher.Post(t2);
160 dispatcher.Post(t3);
161
162 EXPECT_TRUE(dispatcher.RunUntilStalled().IsPending());
163 EXPECT_FALSE(t1.completed_);
164 EXPECT_FALSE(t2.completed_);
165 EXPECT_FALSE(t3.completed_);
166
167 // t1 should expire first.
168 EXPECT_TRUE(tp.AdvanceUntilNextExpiration());
169 EXPECT_TRUE(dispatcher.RunUntilStalled().IsPending());
170 EXPECT_TRUE(t1.completed_);
171 EXPECT_FALSE(t2.completed_);
172 EXPECT_FALSE(t3.completed_);
173
174 // Then t2.
175 EXPECT_TRUE(tp.AdvanceUntilNextExpiration());
176 EXPECT_TRUE(dispatcher.RunUntilStalled().IsPending());
177 EXPECT_TRUE(t1.completed_);
178 EXPECT_TRUE(t2.completed_);
179 EXPECT_FALSE(t3.completed_);
180
181 // Then t3.
182 EXPECT_TRUE(tp.AdvanceUntilNextExpiration());
183 EXPECT_TRUE(dispatcher.RunUntilStalled().IsReady());
184 EXPECT_TRUE(t1.completed_);
185 EXPECT_TRUE(t2.completed_);
186 EXPECT_TRUE(t3.completed_);
187 }
188
189 } // namespace
190