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