blob: d32855241e517bedd0ce9ff28eceb871b0558a38 [file] [log] [blame]
Austin Schuhbe69cf32020-08-27 11:38:33 -07001#include "aos/events/event_scheduler.h"
2
3#include <chrono>
4
5#include "gtest/gtest.h"
6
Philipp Schrader790cb542023-07-05 21:06:52 -07007#include "aos/network/testing_time_converter.h"
8
Austin Schuhbe69cf32020-08-27 11:38:33 -07009namespace aos {
10
11namespace chrono = std::chrono;
Austin Schuh58646e22021-08-23 23:51:46 -070012using aos::logger::BootTimestamp;
Austin Schuhbe69cf32020-08-27 11:38:33 -070013
Austin Schuh87dd3832021-01-01 23:07:31 -080014// Legacy time converter for keeping old tests working. Has numerical precision
15// problems.
16class SlopeOffsetTimeConverter final : public TimeConverter {
17 public:
18 SlopeOffsetTimeConverter(size_t nodes_count)
19 : distributed_offset_(nodes_count, std::chrono::seconds(0)),
Austin Schuh58646e22021-08-23 23:51:46 -070020 distributed_slope_(nodes_count, 1.0) {
21 uuids_.reserve(nodes_count);
22 while (uuids_.size() < nodes_count) {
23 uuids_.emplace_back(UUID::Random());
24 }
25 }
Austin Schuh87dd3832021-01-01 23:07:31 -080026
27 // Sets the offset between the distributed and monotonic clock.
28 // monotonic = distributed * slope + offset;
29 void SetDistributedOffset(size_t node_index,
30 std::chrono::nanoseconds distributed_offset,
31 double distributed_slope) {
32 distributed_offset_[node_index] = distributed_offset;
33 distributed_slope_[node_index] = distributed_slope;
34 }
35
36 distributed_clock::time_point ToDistributedClock(
Austin Schuh58646e22021-08-23 23:51:46 -070037 size_t node_index, BootTimestamp time) override {
38 CHECK_EQ(time.boot, 0u);
Austin Schuh87dd3832021-01-01 23:07:31 -080039 return distributed_clock::epoch() +
40 std::chrono::duration_cast<std::chrono::nanoseconds>(
41 (time.time_since_epoch() - distributed_offset_[node_index]) /
42 distributed_slope_[node_index]);
43 }
44
Austin Schuh58646e22021-08-23 23:51:46 -070045 BootTimestamp FromDistributedClock(size_t node_index,
46 distributed_clock::time_point time,
47 size_t boot_index) override {
48 CHECK_EQ(boot_index, 0u);
49 return {
50 .boot = 0u,
51 .time = monotonic_clock::epoch() +
52 std::chrono::duration_cast<std::chrono::nanoseconds>(
53 time.time_since_epoch() * distributed_slope_[node_index]) +
54 distributed_offset_[node_index]};
55 }
56
57 UUID boot_uuid(size_t node_index, size_t boot_count) override {
58 CHECK_EQ(boot_count, 0u);
59 return uuids_[node_index];
Austin Schuh87dd3832021-01-01 23:07:31 -080060 }
61
Austin Schuhb7c8d2a2021-07-19 19:22:12 -070062 void ObserveTimePassed(distributed_clock::time_point /*time*/) override {}
63
Austin Schuh87dd3832021-01-01 23:07:31 -080064 private:
65 // Offset to the distributed clock.
66 // distributed = monotonic + offset;
67 std::vector<std::chrono::nanoseconds> distributed_offset_;
68 std::vector<double> distributed_slope_;
Austin Schuh58646e22021-08-23 23:51:46 -070069 std::vector<UUID> uuids_;
Austin Schuh87dd3832021-01-01 23:07:31 -080070};
71
James Kuszmaul86e86c32022-07-21 17:39:47 -070072class FunctionEvent : public EventScheduler::Event {
73 public:
74 FunctionEvent(std::function<void()> fn) : fn_(fn) {}
75
76 void Handle() noexcept override { fn_(); }
77
78 private:
79 std::function<void()> fn_;
80};
81
Austin Schuhbe69cf32020-08-27 11:38:33 -070082// Tests that the default parameters (slope of 1, offest of 0) behave as
83// an identity.
84TEST(EventSchedulerTest, IdentityTimeConversion) {
Austin Schuh87dd3832021-01-01 23:07:31 -080085 SlopeOffsetTimeConverter time(1);
Austin Schuh58646e22021-08-23 23:51:46 -070086 EventScheduler s(0);
Austin Schuh87dd3832021-01-01 23:07:31 -080087 s.SetTimeConverter(0u, &time);
Austin Schuhbe69cf32020-08-27 11:38:33 -070088 EXPECT_EQ(s.FromDistributedClock(distributed_clock::epoch()),
Austin Schuh58646e22021-08-23 23:51:46 -070089 BootTimestamp::epoch());
Austin Schuhbe69cf32020-08-27 11:38:33 -070090
91 EXPECT_EQ(
92 s.FromDistributedClock(distributed_clock::epoch() + chrono::seconds(1)),
Austin Schuh58646e22021-08-23 23:51:46 -070093 BootTimestamp::epoch() + chrono::seconds(1));
Austin Schuhbe69cf32020-08-27 11:38:33 -070094
95 EXPECT_EQ(s.ToDistributedClock(monotonic_clock::epoch()),
96 distributed_clock::epoch());
97
Austin Schuh87dd3832021-01-01 23:07:31 -080098 EXPECT_EQ(s.ToDistributedClock(monotonic_clock::epoch() + chrono::seconds(1)),
99 distributed_clock::epoch() + chrono::seconds(1));
Austin Schuhbe69cf32020-08-27 11:38:33 -0700100}
101
102// Tests that a non-unity slope is computed correctly.
103TEST(EventSchedulerTest, DoubleTimeConversion) {
Austin Schuh87dd3832021-01-01 23:07:31 -0800104 SlopeOffsetTimeConverter time(1);
Austin Schuh58646e22021-08-23 23:51:46 -0700105 EventScheduler s(0);
Austin Schuh87dd3832021-01-01 23:07:31 -0800106 s.SetTimeConverter(0u, &time);
107 time.SetDistributedOffset(0u, std::chrono::seconds(7), 2.0);
Austin Schuhbe69cf32020-08-27 11:38:33 -0700108
109 EXPECT_EQ(s.FromDistributedClock(distributed_clock::epoch()),
Austin Schuh58646e22021-08-23 23:51:46 -0700110 BootTimestamp::epoch() + chrono::seconds(7));
Austin Schuhbe69cf32020-08-27 11:38:33 -0700111
112 EXPECT_EQ(
113 s.FromDistributedClock(distributed_clock::epoch() + chrono::seconds(1)),
Austin Schuh58646e22021-08-23 23:51:46 -0700114 BootTimestamp::epoch() + chrono::seconds(9));
Austin Schuhbe69cf32020-08-27 11:38:33 -0700115
116 EXPECT_EQ(s.ToDistributedClock(monotonic_clock::epoch() + chrono::seconds(7)),
117 distributed_clock::epoch());
118
Austin Schuh87dd3832021-01-01 23:07:31 -0800119 EXPECT_EQ(s.ToDistributedClock(monotonic_clock::epoch() + chrono::seconds(9)),
120 distributed_clock::epoch() + chrono::seconds(1));
Austin Schuhbe69cf32020-08-27 11:38:33 -0700121}
122
James Kuszmaul86e86c32022-07-21 17:39:47 -0700123// Test that RunUntil() stops at the appointed time and returns correctly.
124TEST(EventSchedulerTest, RunUntil) {
125 int counter = 0;
126 EventSchedulerScheduler scheduler_scheduler;
127 EventScheduler scheduler(0);
128 scheduler_scheduler.AddEventScheduler(&scheduler);
129
130 FunctionEvent e([&counter]() { counter += 1; });
131 FunctionEvent quitter(
132 [&scheduler_scheduler]() { scheduler_scheduler.Exit(); });
133 scheduler.Schedule(monotonic_clock::epoch() + chrono::seconds(1), &e);
134 scheduler.Schedule(monotonic_clock::epoch() + chrono::seconds(3), &quitter);
135 scheduler.Schedule(monotonic_clock::epoch() + chrono::seconds(5), &e);
136 ASSERT_TRUE(scheduler_scheduler.RunUntil(
137 realtime_clock::epoch() + std::chrono::seconds(2), &scheduler,
138 []() { return std::chrono::nanoseconds{0}; }));
139 EXPECT_EQ(counter, 1);
140 ASSERT_FALSE(scheduler_scheduler.RunUntil(
141 realtime_clock::epoch() + std::chrono::seconds(4), &scheduler,
142 []() { return std::chrono::nanoseconds{0}; }));
143 EXPECT_EQ(counter, 1);
144 ASSERT_TRUE(scheduler_scheduler.RunUntil(
145 realtime_clock::epoch() + std::chrono::seconds(6), &scheduler,
146 []() { return std::chrono::nanoseconds{0}; }));
147 EXPECT_EQ(counter, 2);
148}
149
150enum class RunMode {
151 kRun,
152 kRunUntil,
153 kRunFor,
154};
155
156// Sets up a parameterized test case that will excercise all three of the Run(),
157// RunFor(), and RunUntil() methods of the EventSchedulerScheduler. This exposes
158// a ParamRunFor() to the test case that will nominally run for the specified
159// time (except for when in kRun mode, where it will just call Run()).
160class EventSchedulerParamTest : public testing::TestWithParam<RunMode> {
161 public:
162 EventSchedulerParamTest() {
163 schedulers_.reserve(kNumNodes);
164 for (size_t ii = 0; ii < kNumNodes; ++ii) {
165 schedulers_.emplace_back(ii);
166 schedulers_.back().SetTimeConverter(ii, &time_);
167 scheduler_scheduler_.AddEventScheduler(&schedulers_.back());
168 }
169 scheduler_scheduler_.SetTimeConverter(&time_);
170 }
171
172 void StartClocksAtEpoch() {
173 time_.AddMonotonic({BootTimestamp::epoch(), BootTimestamp::epoch()});
174 }
175
176 protected:
177 static constexpr size_t kNumNodes = 2;
178
179 void CheckSchedulersRunning(bool running) {
180 for (EventScheduler &scheduler : schedulers_) {
181 EXPECT_EQ(running, scheduler.is_running());
182 }
183 }
184
185 void ParamRunFor(std::chrono::nanoseconds t) {
186 switch (GetParam()) {
187 case RunMode::kRun:
188 scheduler_scheduler_.Run();
189 break;
190 case RunMode::kRunUntil:
191 scheduler_scheduler_.RunUntil(
192 realtime_clock::time_point(
193 schedulers_.at(0).monotonic_now().time_since_epoch() + t),
194 &schedulers_.at(0), []() { return std::chrono::nanoseconds(0); });
195 break;
196 case RunMode::kRunFor:
197 scheduler_scheduler_.RunFor(t);
198 break;
199 }
200 }
201
202 message_bridge::TestingTimeConverter time_{kNumNodes};
203 std::vector<EventScheduler> schedulers_;
204 EventSchedulerScheduler scheduler_scheduler_;
205};
206
207// Tests that we correctly handle exiting during startup.
208TEST_P(EventSchedulerParamTest, ExitOnStartup) {
209 StartClocksAtEpoch();
210 bool observed_handler = false;
211 schedulers_.at(0).ScheduleOnStartup([this, &observed_handler]() {
212 EXPECT_FALSE(schedulers_.at(0).is_running());
213 observed_handler = true;
214 scheduler_scheduler_.Exit();
215 });
216 ParamRunFor(std::chrono::seconds(1));
217 EXPECT_TRUE(observed_handler);
218}
219
220// Test that creating an event and running the scheduler runs the event.
221TEST_P(EventSchedulerParamTest, ScheduleEvent) {
222 StartClocksAtEpoch();
223 int counter = 0;
224
225 FunctionEvent e([&counter]() { counter += 1; });
226 schedulers_.at(0).Schedule(monotonic_clock::epoch() + chrono::seconds(1), &e);
227 ParamRunFor(std::chrono::seconds(1));
228 EXPECT_EQ(counter, 1);
229 auto token = schedulers_.at(0).Schedule(
230 monotonic_clock::epoch() + chrono::seconds(2), &e);
231 schedulers_.at(0).Deschedule(token);
232 ParamRunFor(std::chrono::seconds(2));
233 EXPECT_EQ(counter, 1);
234}
235
236// Tests that a node that would have a negative monotonic time at boot does not
237// get started until later.
238TEST_P(EventSchedulerParamTest, NodeWaitsTillEpochToBoot) {
239 time_.AddNextTimestamp(
240 distributed_clock::epoch(),
241 {BootTimestamp{0, monotonic_clock::epoch()},
242 BootTimestamp{0, monotonic_clock::epoch() - chrono::seconds(1)}});
243 bool observed_startup_0 = false;
244 bool observed_startup_1 = false;
245 bool observed_on_run_1 = false;
246 schedulers_.at(0).ScheduleOnStartup([this, &observed_startup_0]() {
247 observed_startup_0 = true;
248 EXPECT_FALSE(schedulers_.at(0).is_running());
249 EXPECT_FALSE(schedulers_.at(1).is_running());
250 EXPECT_EQ(distributed_clock::epoch(),
251 scheduler_scheduler_.distributed_now());
252 EXPECT_EQ(monotonic_clock::epoch(), schedulers_.at(0).monotonic_now());
253 EXPECT_EQ(monotonic_clock::epoch() - chrono::seconds(1),
254 schedulers_.at(1).monotonic_now());
255 });
256 schedulers_.at(1).ScheduleOnStartup([this, &observed_startup_1]() {
257 observed_startup_1 = true;
258 // Note that we do not *stop* execution on node zero just to get 1 started.
259 EXPECT_TRUE(schedulers_.at(0).is_running());
260 EXPECT_FALSE(schedulers_.at(1).is_running());
261 EXPECT_EQ(distributed_clock::epoch() + chrono::seconds(1),
262 scheduler_scheduler_.distributed_now());
263 EXPECT_EQ(monotonic_clock::epoch() + chrono::seconds(1),
264 schedulers_.at(0).monotonic_now());
265 EXPECT_EQ(monotonic_clock::epoch(), schedulers_.at(1).monotonic_now());
266 });
267 schedulers_.at(1).ScheduleOnRun([this, &observed_on_run_1]() {
268 observed_on_run_1 = true;
269 // Note that we do not *stop* execution on node zero just to get 1 started.
270 EXPECT_TRUE(schedulers_.at(0).is_running());
271 EXPECT_TRUE(schedulers_.at(1).is_running());
272 EXPECT_EQ(distributed_clock::epoch() + chrono::seconds(1),
273 scheduler_scheduler_.distributed_now());
274 EXPECT_EQ(monotonic_clock::epoch() + chrono::seconds(1),
275 schedulers_.at(0).monotonic_now());
276 EXPECT_EQ(monotonic_clock::epoch(), schedulers_.at(1).monotonic_now());
277 });
278
279 FunctionEvent e([]() {});
280 schedulers_.at(0).Schedule(monotonic_clock::epoch() + chrono::seconds(1), &e);
281 ParamRunFor(chrono::seconds(1));
282 EXPECT_TRUE(observed_startup_0);
283 EXPECT_TRUE(observed_startup_1);
284 EXPECT_TRUE(observed_on_run_1);
285}
286
287// Tests that a node that never boots does not get any of its handlers run.
288TEST_P(EventSchedulerParamTest, NodeNeverBootsIfAlwaysNegative) {
289 time_.AddNextTimestamp(
290 distributed_clock::epoch(),
291 {BootTimestamp{0, monotonic_clock::epoch()},
292 BootTimestamp{0, monotonic_clock::epoch() - chrono::seconds(10)}});
293 bool observed_startup_0 = false;
294 schedulers_.at(0).ScheduleOnStartup([this, &observed_startup_0]() {
295 observed_startup_0 = true;
296 EXPECT_FALSE(schedulers_.at(0).is_running());
297 EXPECT_FALSE(schedulers_.at(1).is_running());
298 EXPECT_EQ(distributed_clock::epoch(),
299 scheduler_scheduler_.distributed_now());
300 EXPECT_EQ(monotonic_clock::epoch(), schedulers_.at(0).monotonic_now());
301 EXPECT_EQ(monotonic_clock::epoch() - chrono::seconds(10),
302 schedulers_.at(1).monotonic_now());
303 });
304 schedulers_.at(1).ScheduleOnStartup(
305 []() { FAIL() << "Should never have hit startup handlers for node 1."; });
306 schedulers_.at(1).ScheduleOnRun(
307 []() { FAIL() << "Should never have hit OnRun handlers for node 1."; });
308 schedulers_.at(1).set_stopped(
309 []() { FAIL() << "Should never have hit stopped handlers for node 1."; });
310
311 FunctionEvent e([this]() { scheduler_scheduler_.Exit(); });
312 schedulers_.at(0).Schedule(monotonic_clock::epoch() + chrono::seconds(1), &e);
313 ParamRunFor(chrono::seconds(1));
314 EXPECT_TRUE(observed_startup_0);
315}
316
317// Checks for regressions in how the startup/shutdown handlers behave.
318TEST_P(EventSchedulerParamTest, StartupShutdownHandlers) {
319 StartClocksAtEpoch();
320 time_.AddNextTimestamp(
321 distributed_clock::epoch() + chrono::seconds(3),
322 {BootTimestamp{0, monotonic_clock::epoch() + chrono::seconds(3)},
323 BootTimestamp{0, monotonic_clock::epoch() + chrono::seconds(3)}});
324 time_.RebootAt(0, distributed_clock::epoch() + chrono::seconds(4));
325 // Expected behavior:
326 // If all handlers get called during a reboot, they should sequence as:
327 // * is_running_ = false
328 // * stopped()
329 // * on_shutdown()
330 // * on_startup()
331 // * started()
332 // * is_running_ = true
333 // * OnRun()
334 //
335 // on_shutdown handlers should not get called at end of execution (e.g., when
336 // TemporarilyStopAndRun is called)--only when a node reboots.
337 //
338 // startup and OnRun handlers get cleared after being called once; these are
339 // also the only handlers that can have more than one handler registered.
340 //
341 // Create counters for all the handlers on the 0 node. Create separate a/b
342 // counters for the handlers that can/should get cleared.
343 int shutdown_counter = 0;
344 int stopped_counter = 0;
345 int startup_counter_a = 0;
346 int startup_counter_b = 0;
347 int started_counter = 0;
348 int on_run_counter_a = 0;
349 int on_run_counter_b = 0;
350
351 schedulers_.at(1).set_on_shutdown([]() {
352 FAIL() << "Should never reach the node 1 shutdown handler, since it never "
353 "reboots.";
354 });
355
356 auto startup_handler_a = [this, &startup_counter_a]() {
357 EXPECT_FALSE(schedulers_.at(0).is_running());
358 ++startup_counter_a;
359 };
360
361 auto startup_handler_b = [this, &startup_counter_b]() {
362 EXPECT_FALSE(schedulers_.at(0).is_running());
363 ++startup_counter_b;
364 };
365
366 auto on_run_handler_a = [this, &on_run_counter_a]() {
367 EXPECT_TRUE(schedulers_.at(0).is_running());
368 ++on_run_counter_a;
369 };
370
371 auto on_run_handler_b = [this, &on_run_counter_b]() {
372 EXPECT_TRUE(schedulers_.at(0).is_running());
373 ++on_run_counter_b;
374 };
375
376 schedulers_.at(0).set_stopped([this, &stopped_counter]() {
377 EXPECT_FALSE(schedulers_.at(0).is_running());
378 ++stopped_counter;
379 });
380 schedulers_.at(0).set_on_shutdown(
381 [this, &shutdown_counter, startup_handler_a, on_run_handler_a]() {
382 EXPECT_FALSE(schedulers_.at(0).is_running());
383 schedulers_.at(0).ScheduleOnStartup(startup_handler_a);
384 schedulers_.at(0).ScheduleOnRun(on_run_handler_a);
385 ++shutdown_counter;
386 });
387 schedulers_.at(0).ScheduleOnStartup(startup_handler_a);
388 schedulers_.at(0).set_started([this, &started_counter]() {
389 EXPECT_FALSE(schedulers_.at(0).is_running());
390 ++started_counter;
391 });
392 schedulers_.at(0).ScheduleOnRun(on_run_handler_a);
393
394 FunctionEvent e([]() {});
395 schedulers_.at(0).Schedule(monotonic_clock::epoch() + chrono::seconds(1), &e);
396 ParamRunFor(std::chrono::seconds(1));
397 EXPECT_EQ(shutdown_counter, 0);
398 EXPECT_EQ(stopped_counter, 1);
399 EXPECT_EQ(started_counter, 1);
400 EXPECT_EQ(startup_counter_a, 1);
401 EXPECT_EQ(on_run_counter_a, 1);
402 EXPECT_EQ(startup_counter_b, 0);
403 EXPECT_EQ(on_run_counter_b, 0);
404
405 // In the middle, execute a TemporarilyStopAndRun. Use it to re-register the
406 // startup handlers.
407 schedulers_.at(0).ScheduleOnStartup(startup_handler_b);
408 schedulers_.at(0).ScheduleOnRun(on_run_handler_b);
409 FunctionEvent stop_and_run([this, startup_handler_a, on_run_handler_a]() {
410 scheduler_scheduler_.TemporarilyStopAndRun(
411 [this, startup_handler_a, on_run_handler_a]() {
412 schedulers_.at(0).ScheduleOnStartup(startup_handler_a);
413 schedulers_.at(0).ScheduleOnRun(on_run_handler_a);
414 });
415 });
416 schedulers_.at(1).Schedule(monotonic_clock::epoch() + chrono::seconds(2),
417 &stop_and_run);
418 ParamRunFor(std::chrono::seconds(1));
419 EXPECT_EQ(shutdown_counter, 0);
420 EXPECT_EQ(stopped_counter, 3);
421 EXPECT_EQ(started_counter, 3);
422 EXPECT_EQ(startup_counter_a, 2);
423 EXPECT_EQ(on_run_counter_a, 2);
424 EXPECT_EQ(startup_counter_b, 1);
425 EXPECT_EQ(on_run_counter_b, 1);
426
427 // Next, execute a reboot in the middle of running and confirm that things
428 // tally correctly. We do not re-register the startup/on_run handlers before
429 // starting here, but do in the shutdown handler, so should see the A handlers
430 // increment.
431 // We need to schedule at least one event so that the reboot is actually
432 // observable (otherwise Run() will just terminate immediately, since there
433 // are no scheduled events that could possibly observe the reboot anyways).
434 schedulers_.at(1).Schedule(monotonic_clock::epoch() + chrono::seconds(5), &e);
435 ParamRunFor(std::chrono::seconds(5));
436 EXPECT_EQ(shutdown_counter, 1);
437 EXPECT_EQ(stopped_counter, 5);
438 EXPECT_EQ(started_counter, 5);
439 EXPECT_EQ(startup_counter_a, 3);
440 EXPECT_EQ(on_run_counter_a, 3);
441 EXPECT_EQ(startup_counter_b, 1);
442 EXPECT_EQ(on_run_counter_b, 1);
443}
444
445// Test that descheduling an already scheduled event doesn't run the event.
446TEST_P(EventSchedulerParamTest, DescheduleEvent) {
447 StartClocksAtEpoch();
448 int counter = 0;
449 FunctionEvent e([&counter]() { counter += 1; });
450 auto token = schedulers_.at(0).Schedule(
451 monotonic_clock::epoch() + chrono::seconds(1), &e);
452 schedulers_.at(0).Deschedule(token);
453 ParamRunFor(std::chrono::seconds(2));
454 EXPECT_EQ(counter, 0);
455}
456
457// Test that TemporarilyStopAndRun respects and preserves running.
458TEST_P(EventSchedulerParamTest, TemporarilyStopAndRun) {
459 StartClocksAtEpoch();
460 int counter = 0;
461
462 scheduler_scheduler_.TemporarilyStopAndRun([this]() {
463 SCOPED_TRACE("StopAndRun while stopped.");
464 CheckSchedulersRunning(false);
465 });
466 {
467 SCOPED_TRACE("After StopAndRun while stopped.");
468 CheckSchedulersRunning(false);
469 }
470
471 FunctionEvent e([&]() {
472 counter += 1;
473 {
474 SCOPED_TRACE("Before StopAndRun while running.");
475 CheckSchedulersRunning(true);
476 }
477 scheduler_scheduler_.TemporarilyStopAndRun([&]() {
478 SCOPED_TRACE("StopAndRun while running.");
479 CheckSchedulersRunning(false);
480 });
481 {
482 SCOPED_TRACE("After StopAndRun while running.");
483 CheckSchedulersRunning(true);
484 }
485 });
486 schedulers_.at(0).Schedule(monotonic_clock::epoch() + chrono::seconds(1), &e);
487 ParamRunFor(std::chrono::seconds(1));
488 EXPECT_EQ(counter, 1);
489}
490
491// Test that TemporarilyStopAndRun leaves stopped nodes stopped.
492TEST_P(EventSchedulerParamTest, TemporarilyStopAndRunStaggeredStart) {
493 time_.AddNextTimestamp(
494 distributed_clock::epoch(),
495 {BootTimestamp{0, monotonic_clock::epoch()},
496 BootTimestamp{0, monotonic_clock::epoch() - chrono::seconds(10)}});
497 int counter = 0;
498
499 schedulers_[1].ScheduleOnRun([]() { FAIL(); });
500 schedulers_[1].ScheduleOnStartup([]() { FAIL(); });
501 schedulers_[1].set_on_shutdown([]() { FAIL(); });
502 schedulers_[1].set_started([]() { FAIL(); });
503 schedulers_[1].set_stopped([]() { FAIL(); });
504
505 FunctionEvent e([this, &counter]() {
506 counter += 1;
507 EXPECT_TRUE(schedulers_[0].is_running());
508 EXPECT_FALSE(schedulers_[1].is_running());
509 scheduler_scheduler_.TemporarilyStopAndRun([&]() {
510 SCOPED_TRACE("StopAndRun while running.");
511 CheckSchedulersRunning(false);
512 });
513 EXPECT_TRUE(schedulers_[0].is_running());
514 EXPECT_FALSE(schedulers_[1].is_running());
515 });
516 FunctionEvent exiter([this]() { scheduler_scheduler_.Exit(); });
517 schedulers_.at(0).Schedule(monotonic_clock::epoch() + chrono::seconds(1), &e);
518 schedulers_.at(0).Schedule(monotonic_clock::epoch() + chrono::seconds(2),
519 &exiter);
520 ParamRunFor(std::chrono::seconds(1));
521 EXPECT_EQ(counter, 1);
522}
523
524INSTANTIATE_TEST_SUITE_P(EventSchedulerParamTest, EventSchedulerParamTest,
525 testing::Values(RunMode::kRun, RunMode::kRunFor));
526
Austin Schuhbe69cf32020-08-27 11:38:33 -0700527} // namespace aos