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