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