blob: 09e279c74f2e459efded6cedd7fa08800b4264c3 [file] [log] [blame]
Ben Fredricksond69f38b2015-01-28 20:06:15 -08001#include <unistd.h>
2
3#include <memory>
Brian Silvermana2ae62d2015-03-15 15:55:22 -07004#include <thread>
Austin Schuhf2a50ba2016-12-24 16:16:26 -08005#include <chrono>
Ben Fredricksond69f38b2015-01-28 20:06:15 -08006
7#include "gtest/gtest.h"
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -05008
John Park33858a32018-09-28 23:05:48 -07009#include "aos/queue.h"
10#include "aos/actions/actor.h"
11#include "aos/actions/actions.h"
12#include "aos/actions/actions.q.h"
13#include "aos/actions/test_action.q.h"
14#include "aos/event.h"
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050015#include "aos/testing/test_shm.h"
Ben Fredricksond69f38b2015-01-28 20:06:15 -080016
Ben Fredricksond69f38b2015-01-28 20:06:15 -080017namespace aos {
18namespace common {
19namespace actions {
20namespace testing {
21
Austin Schuhf2a50ba2016-12-24 16:16:26 -080022
23namespace chrono = ::std::chrono;
24
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080025class TestActorIndex
26 : public aos::common::actions::ActorBase<actions::TestActionQueueGroup> {
27 public:
28 explicit TestActorIndex(actions::TestActionQueueGroup *s)
29 : aos::common::actions::ActorBase<actions::TestActionQueueGroup>(s) {}
30
31 bool RunAction(const uint32_t &new_index) override {
32 index = new_index;
33 return true;
34 }
35
36 uint32_t index = 0;
37};
38
39::std::unique_ptr<
40 aos::common::actions::TypedAction<actions::TestActionQueueGroup>>
41MakeTestActionIndex(uint32_t index) {
42 return ::std::unique_ptr<
43 aos::common::actions::TypedAction<actions::TestActionQueueGroup>>(
44 new aos::common::actions::TypedAction<actions::TestActionQueueGroup>(
45 &actions::test_action, index));
46}
47
Ben Fredricksond69f38b2015-01-28 20:06:15 -080048class TestActorNOP
49 : public aos::common::actions::ActorBase<actions::TestActionQueueGroup> {
50 public:
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080051 explicit TestActorNOP(actions::TestActionQueueGroup *s)
Ben Fredricksond69f38b2015-01-28 20:06:15 -080052 : actions::ActorBase<actions::TestActionQueueGroup>(s) {}
53
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080054 bool RunAction(const uint32_t &) override { return true; }
Ben Fredricksond69f38b2015-01-28 20:06:15 -080055};
56
57::std::unique_ptr<
58 aos::common::actions::TypedAction<actions::TestActionQueueGroup>>
59MakeTestActionNOP() {
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080060 return MakeTestActionIndex(0);
Ben Fredricksond69f38b2015-01-28 20:06:15 -080061}
62
63class TestActorShouldCancel
64 : public aos::common::actions::ActorBase<actions::TestActionQueueGroup> {
65 public:
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080066 explicit TestActorShouldCancel(actions::TestActionQueueGroup *s)
Ben Fredricksond69f38b2015-01-28 20:06:15 -080067 : aos::common::actions::ActorBase<actions::TestActionQueueGroup>(s) {}
68
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080069 bool RunAction(const uint32_t &) override {
Ben Fredricksond69f38b2015-01-28 20:06:15 -080070 while (!ShouldCancel()) {
71 LOG(FATAL, "NOT CANCELED!!\n");
72 }
Daniel Petti3b1e48f2015-02-15 15:57:53 -080073 return true;
Ben Fredricksond69f38b2015-01-28 20:06:15 -080074 }
75};
76
77::std::unique_ptr<
78 aos::common::actions::TypedAction<actions::TestActionQueueGroup>>
79MakeTestActionShouldCancel() {
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080080 return MakeTestActionIndex(0);
81}
82
83class TestActor2Nop
84 : public aos::common::actions::ActorBase<actions::TestAction2QueueGroup> {
85 public:
86 explicit TestActor2Nop(actions::TestAction2QueueGroup *s)
87 : actions::ActorBase<actions::TestAction2QueueGroup>(s) {}
88
89 bool RunAction(const actions::MyParams &) { return true; }
90};
91
92::std::unique_ptr<
93 aos::common::actions::TypedAction<actions::TestAction2QueueGroup>>
94MakeTestAction2NOP(const actions::MyParams &params) {
Ben Fredricksond69f38b2015-01-28 20:06:15 -080095 return ::std::unique_ptr<
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080096 aos::common::actions::TypedAction<actions::TestAction2QueueGroup>>(
97 new aos::common::actions::TypedAction<actions::TestAction2QueueGroup>(
98 &actions::test_action2, params));
Ben Fredricksond69f38b2015-01-28 20:06:15 -080099}
100
101class ActionTest : public ::testing::Test {
102 protected:
103 ActionTest() {
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800104 actions::test_action.goal.Clear();
105 actions::test_action.status.Clear();
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800106 actions::test_action2.goal.Clear();
107 actions::test_action2.status.Clear();
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800108 }
109
110 virtual ~ActionTest() {
111 actions::test_action.goal.Clear();
112 actions::test_action.status.Clear();
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800113 actions::test_action2.goal.Clear();
114 actions::test_action2.status.Clear();
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800115 }
116
117 // Bring up and down Core.
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -0500118 ::aos::testing::TestSharedMemory my_shm_;
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800119 ::aos::common::actions::ActionQueue action_queue_;
120};
121
122// Tests that the the actions exist in a safe state at startup.
123TEST_F(ActionTest, DoesNothing) {
124 // Tick an empty queue and make sure it was not running.
125 EXPECT_FALSE(action_queue_.Running());
126 action_queue_.Tick();
127 EXPECT_FALSE(action_queue_.Running());
128}
129
Brian Silvermana2ae62d2015-03-15 15:55:22 -0700130// Tests that starting with an old run message in the goal queue actually works.
131// This used to result in the client hanging, waiting for a response to its
132// cancel message.
133TEST_F(ActionTest, StartWithOldGoal) {
134 ASSERT_TRUE(actions::test_action.goal.MakeWithBuilder().run(971).Send());
135
136 TestActorNOP nop_act(&actions::test_action);
137
138 ASSERT_FALSE(actions::test_action.status.FetchLatest());
139 ::std::thread init_thread([&nop_act]() { nop_act.Initialize(); });
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800140 ::std::this_thread::sleep_for(chrono::milliseconds(100));
Brian Silvermana2ae62d2015-03-15 15:55:22 -0700141 ASSERT_TRUE(actions::test_action.goal.MakeWithBuilder().run(1).Send());
142 init_thread.join();
143 ASSERT_TRUE(actions::test_action.status.FetchLatest());
144 EXPECT_EQ(0u, actions::test_action.status->running);
145 EXPECT_EQ(0u, actions::test_action.status->last_running);
146
147 action_queue_.EnqueueAction(MakeTestActionNOP());
148 nop_act.WaitForActionRequest();
149
150 // We started an action and it should be running.
151 EXPECT_TRUE(action_queue_.Running());
152
153 action_queue_.CancelAllActions();
154 action_queue_.Tick();
155
156 EXPECT_TRUE(action_queue_.Running());
157
158 // Run the action so it can signal completion.
159 nop_act.RunIteration();
160 action_queue_.Tick();
161
162 // Make sure it stopped.
163 EXPECT_FALSE(action_queue_.Running());
164}
165
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800166// Tests that the queues are properly configured for testing. Tests that queues
167// work exactly as used in the tests.
168TEST_F(ActionTest, QueueCheck) {
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800169 actions::TestActionQueueGroup *send_side = &actions::test_action;
170 actions::TestActionQueueGroup *recv_side = &actions::test_action;
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800171
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800172 send_side->goal.MakeWithBuilder().run(1).Send();
173
174 EXPECT_TRUE(recv_side->goal.FetchLatest());
175 EXPECT_TRUE(recv_side->goal->run);
176
177 send_side->goal.MakeWithBuilder().run(0).Send();
178
179 EXPECT_TRUE(recv_side->goal.FetchLatest());
180 EXPECT_FALSE(recv_side->goal->run);
181
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800182 send_side->status.MakeWithBuilder().running(5).last_running(6).Send();
183
184 EXPECT_TRUE(recv_side->status.FetchLatest());
185 EXPECT_EQ(5, static_cast<int>(recv_side->status->running));
186 EXPECT_EQ(6, static_cast<int>(recv_side->status->last_running));
187}
188
189// Tests that an action starts and stops.
190TEST_F(ActionTest, ActionQueueWasRunning) {
191 TestActorNOP nop_act(&actions::test_action);
192
193 // Tick an empty queue and make sure it was not running.
194 action_queue_.Tick();
195 EXPECT_FALSE(action_queue_.Running());
196
197 action_queue_.EnqueueAction(MakeTestActionNOP());
198 nop_act.WaitForActionRequest();
199
200 // We started an action and it should be running.
201 EXPECT_TRUE(action_queue_.Running());
202
203 // Tick it and make sure it is still running.
204 action_queue_.Tick();
205 EXPECT_TRUE(action_queue_.Running());
206
207 // Run the action so it can signal completion.
208 nop_act.RunIteration();
209 action_queue_.Tick();
210
211 // Make sure it stopped.
212 EXPECT_FALSE(action_queue_.Running());
213}
214
215// Tests that we can cancel two actions and have them both stop.
216TEST_F(ActionTest, ActionQueueCancelAll) {
217 TestActorNOP nop_act(&actions::test_action);
218
219 // Tick an empty queue and make sure it was not running.
220 action_queue_.Tick();
221 EXPECT_FALSE(action_queue_.Running());
222
223 // Enqueue two actions to test both cancel. We can have an action and a next
224 // action so we want to test that.
225 action_queue_.EnqueueAction(MakeTestActionNOP());
226 action_queue_.EnqueueAction(MakeTestActionNOP());
227 nop_act.WaitForActionRequest();
228 action_queue_.Tick();
229
230 // Check that current and next exist.
231 EXPECT_TRUE(action_queue_.GetCurrentActionState(nullptr, nullptr, nullptr,
232 nullptr, nullptr, nullptr));
233 EXPECT_TRUE(action_queue_.GetNextActionState(nullptr, nullptr, nullptr,
234 nullptr, nullptr, nullptr));
235
236 action_queue_.CancelAllActions();
237 action_queue_.Tick();
238
239 // It should still be running as the actor could not have signaled.
240 EXPECT_TRUE(action_queue_.Running());
241
242 bool sent_started, sent_cancel, interrupted;
243 EXPECT_TRUE(action_queue_.GetCurrentActionState(
244 nullptr, &sent_started, &sent_cancel, &interrupted, nullptr, nullptr));
245 EXPECT_TRUE(sent_started);
246 EXPECT_TRUE(sent_cancel);
247 EXPECT_FALSE(interrupted);
248
249 EXPECT_FALSE(action_queue_.GetNextActionState(nullptr, nullptr, nullptr,
250 nullptr, nullptr, nullptr));
251
252 // Run the action so it can signal completion.
253 nop_act.RunIteration();
254 action_queue_.Tick();
255
256 // Make sure it stopped.
257 EXPECT_FALSE(action_queue_.Running());
258}
259
260// Tests that an action that would block forever stops when canceled.
261TEST_F(ActionTest, ActionQueueCancelOne) {
262 TestActorShouldCancel cancel_act(&actions::test_action);
263
264 // Enqueue blocking action.
265 action_queue_.EnqueueAction(MakeTestActionShouldCancel());
266
267 cancel_act.WaitForActionRequest();
268 action_queue_.Tick();
269 EXPECT_TRUE(action_queue_.Running());
270
271 // Tell action to cancel.
272 action_queue_.CancelCurrentAction();
273 action_queue_.Tick();
274
275 // This will block forever on failure.
276 // TODO(ben): prolly a bad way to fail
277 cancel_act.RunIteration();
278 action_queue_.Tick();
279
280 // It should still be running as the actor could not have signalled.
281 EXPECT_FALSE(action_queue_.Running());
282}
283
284// Tests that an action starts and stops.
285TEST_F(ActionTest, ActionQueueTwoActions) {
286 TestActorNOP nop_act(&actions::test_action);
287
288 // Tick an empty queue and make sure it was not running.
289 action_queue_.Tick();
290 EXPECT_FALSE(action_queue_.Running());
291
292 // Enqueue action to be canceled.
293 action_queue_.EnqueueAction(MakeTestActionNOP());
294 nop_act.WaitForActionRequest();
295 action_queue_.Tick();
296
297 // Should still be running as the actor could not have signalled.
298 EXPECT_TRUE(action_queue_.Running());
299
300 // id for the first time run.
301 uint32_t nop_act_id = 0;
302 // Check the internal state and write down id for later use.
303 bool sent_started, sent_cancel, interrupted;
304 EXPECT_TRUE(action_queue_.GetCurrentActionState(nullptr, &sent_started,
305 &sent_cancel, &interrupted,
306 &nop_act_id, nullptr));
307 EXPECT_TRUE(sent_started);
308 EXPECT_FALSE(sent_cancel);
309 EXPECT_FALSE(interrupted);
310 ASSERT_NE(0u, nop_act_id);
311
312 // Add the next action which should ensure the first stopped.
313 action_queue_.EnqueueAction(MakeTestActionNOP());
314
315 // id for the second run.
316 uint32_t nop_act2_id = 0;
317 // Check the internal state and write down id for later use.
318 EXPECT_TRUE(action_queue_.GetNextActionState(nullptr, &sent_started,
319 &sent_cancel, &interrupted,
320 &nop_act2_id, nullptr));
321 EXPECT_NE(nop_act_id, nop_act2_id);
322 EXPECT_FALSE(sent_started);
323 EXPECT_FALSE(sent_cancel);
324 EXPECT_FALSE(interrupted);
325 ASSERT_NE(0u, nop_act2_id);
326
327 action_queue_.Tick();
328
329 // Run the action so it can signal completion.
330 nop_act.RunIteration();
331 action_queue_.Tick();
332 // Wait for the first id to finish, needed for the correct number of fetches.
333 nop_act.WaitForStop(nop_act_id);
334
335 // Start the next action on the actor side.
336 nop_act.WaitForActionRequest();
337
338 // Check the new action is the right one.
339 uint32_t test_id = 0;
340 EXPECT_TRUE(action_queue_.GetCurrentActionState(
341 nullptr, &sent_started, &sent_cancel, &interrupted, &test_id, nullptr));
342 EXPECT_TRUE(sent_started);
343 EXPECT_FALSE(sent_cancel);
344 EXPECT_FALSE(interrupted);
345 EXPECT_EQ(nop_act2_id, test_id);
346
347 // Make sure it is still going.
348 EXPECT_TRUE(action_queue_.Running());
349
350 // Run the next action so it can accomplish signal completion.
351 nop_act.RunIteration();
352 action_queue_.Tick();
353 nop_act.WaitForStop(nop_act_id);
354
355 // Make sure it stopped.
356 EXPECT_FALSE(action_queue_.Running());
357}
358
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800359// Tests that we do get an index with our goal
360TEST_F(ActionTest, ActionIndex) {
361 TestActorIndex idx_act(&actions::test_action);
362
363 // Tick an empty queue and make sure it was not running.
364 action_queue_.Tick();
365 EXPECT_FALSE(action_queue_.Running());
366
367 // Enqueue action to post index.
368 action_queue_.EnqueueAction(MakeTestActionIndex(5));
369 EXPECT_TRUE(actions::test_action.goal.FetchLatest());
370 EXPECT_EQ(5u, actions::test_action.goal->params);
371 EXPECT_EQ(0u, idx_act.index);
372
373 idx_act.WaitForActionRequest();
374 action_queue_.Tick();
375
376 // Check the new action is the right one.
377 uint32_t test_id = 0;
378 EXPECT_TRUE(action_queue_.GetCurrentActionState(nullptr, nullptr, nullptr,
379 nullptr, &test_id, nullptr));
380
381 // Run the next action so it can accomplish signal completion.
382 idx_act.RunIteration();
383 action_queue_.Tick();
384 idx_act.WaitForStop(test_id);
385 EXPECT_EQ(5u, idx_act.index);
386
387 // Enqueue action to post index.
388 action_queue_.EnqueueAction(MakeTestActionIndex(3));
389 EXPECT_TRUE(actions::test_action.goal.FetchLatest());
390 EXPECT_EQ(3u, actions::test_action.goal->params);
391
392 // Run the next action so it can accomplish signal completion.
393 idx_act.RunIteration();
394 action_queue_.Tick();
395 idx_act.WaitForStop(test_id);
396 EXPECT_EQ(3u, idx_act.index);
397}
398
399// Tests that an action with a structure params works.
400TEST_F(ActionTest, StructParamType) {
401 TestActor2Nop nop_act(&actions::test_action2);
402
403 // Tick an empty queue and make sure it was not running.
404 action_queue_.Tick();
405 EXPECT_FALSE(action_queue_.Running());
406
407 actions::MyParams p;
408 p.param1 = 5.0;
409 p.param2 = 7;
410
411 action_queue_.EnqueueAction(MakeTestAction2NOP(p));
412 nop_act.WaitForActionRequest();
413
414 // We started an action and it should be running.
415 EXPECT_TRUE(action_queue_.Running());
416
417 // Tick it and make sure it is still running.
418 action_queue_.Tick();
419 EXPECT_TRUE(action_queue_.Running());
420
421 // Run the action so it can signal completion.
422 nop_act.RunIteration();
423 action_queue_.Tick();
424
425 // Make sure it stopped.
426 EXPECT_FALSE(action_queue_.Running());
427}
428
Brian Silverman237a5542015-03-29 17:59:29 -0400429// Tests that cancelling an action before the message confirming it started is
430// received works.
431// Situations like this used to lock the action queue up waiting for an action
432// to report that it successfully cancelled.
433// This situation is kind of a race condition, but it happens very consistently
434// when hitting buttons while the robot is in teleop-disabled. To hit the race
435// condition consistently in the test, there are a couple of Events inserted in
436// between various things running.
437TEST_F(ActionTest, CancelBeforeStart) {
438 Event thread_ready, ready_to_start, ready_to_stop;
439 ::std::thread action_thread(
440 [this, &thread_ready, &ready_to_start, &ready_to_stop]() {
441 TestActorNOP nop_act(&actions::test_action);
442 nop_act.Initialize();
443 thread_ready.Set();
444 ready_to_start.Wait();
445 nop_act.WaitForActionRequest();
446 LOG(DEBUG, "got a request to run\n");
447 const uint32_t running_id = nop_act.RunIteration();
448 LOG(DEBUG, "waiting for %" PRIx32 " to be stopped\n", running_id);
449 ready_to_stop.Set();
450 nop_act.WaitForStop(running_id);
451 });
452
453 action_queue_.CancelAllActions();
454 EXPECT_FALSE(action_queue_.Running());
455 thread_ready.Wait();
456 LOG(DEBUG, "starting action\n");
457 action_queue_.EnqueueAction(MakeTestActionNOP());
458 action_queue_.Tick();
459 action_queue_.CancelAllActions();
460 ready_to_start.Set();
461 LOG(DEBUG, "started action\n");
462 EXPECT_TRUE(action_queue_.Running());
463 ready_to_stop.Wait();
464 EXPECT_TRUE(action_queue_.Running());
465 LOG(DEBUG, "action is ready to stop\n");
466
467 action_queue_.Tick();
468 action_queue_.CancelAllActions();
469 EXPECT_FALSE(action_queue_.Running());
470 action_queue_.Tick();
471 action_queue_.CancelAllActions();
472 ASSERT_FALSE(action_queue_.Running());
473 action_thread.join();
474
475 action_queue_.Tick();
476 action_queue_.CancelAllActions();
477 ASSERT_FALSE(action_queue_.Running());
478}
479
480} // namespace testing
481} // namespace actions
482} // namespace common
483} // namespace aos