actually test the problematic early-cancel for actions

I've already fixed several bugs that I think this test would've caught.

Change-Id: I1acee23cca40649efa400d5c36b5907550a1ed64
diff --git a/aos/common/actions/action_test.cc b/aos/common/actions/action_test.cc
index eb863e3..59d8d79 100644
--- a/aos/common/actions/action_test.cc
+++ b/aos/common/actions/action_test.cc
@@ -10,6 +10,7 @@
 #include "aos/common/actions/actions.h"
 #include "aos/common/actions/actions.q.h"
 #include "aos/common/actions/test_action.q.h"
+#include "aos/common/event.h"
 
 using ::aos::time::Time;
 
@@ -424,7 +425,58 @@
   EXPECT_FALSE(action_queue_.Running());
 }
 
-}  // namespace testing.
-}  // namespace actions.
-}  // namespace common.
-}  // namespace aos.
+// Tests that cancelling an action before the message confirming it started is
+// received works.
+// Situations like this used to lock the action queue up waiting for an action
+// to report that it successfully cancelled.
+// This situation is kind of a race condition, but it happens very consistently
+// when hitting buttons while the robot is in teleop-disabled. To hit the race
+// condition consistently in the test, there are a couple of Events inserted in
+// between various things running.
+TEST_F(ActionTest, CancelBeforeStart) {
+  Event thread_ready, ready_to_start, ready_to_stop;
+  ::std::thread action_thread(
+      [this, &thread_ready, &ready_to_start, &ready_to_stop]() {
+        TestActorNOP nop_act(&actions::test_action);
+        nop_act.Initialize();
+        thread_ready.Set();
+        ready_to_start.Wait();
+        nop_act.WaitForActionRequest();
+        LOG(DEBUG, "got a request to run\n");
+        const uint32_t running_id = nop_act.RunIteration();
+        LOG(DEBUG, "waiting for %" PRIx32 " to be stopped\n", running_id);
+        ready_to_stop.Set();
+        nop_act.WaitForStop(running_id);
+      });
+
+  action_queue_.CancelAllActions();
+  EXPECT_FALSE(action_queue_.Running());
+  thread_ready.Wait();
+  LOG(DEBUG, "starting action\n");
+  action_queue_.EnqueueAction(MakeTestActionNOP());
+  action_queue_.Tick();
+  action_queue_.CancelAllActions();
+  ready_to_start.Set();
+  LOG(DEBUG, "started action\n");
+  EXPECT_TRUE(action_queue_.Running());
+  ready_to_stop.Wait();
+  EXPECT_TRUE(action_queue_.Running());
+  LOG(DEBUG, "action is ready to stop\n");
+
+  action_queue_.Tick();
+  action_queue_.CancelAllActions();
+  EXPECT_FALSE(action_queue_.Running());
+  action_queue_.Tick();
+  action_queue_.CancelAllActions();
+  ASSERT_FALSE(action_queue_.Running());
+  action_thread.join();
+
+  action_queue_.Tick();
+  action_queue_.CancelAllActions();
+  ASSERT_FALSE(action_queue_.Running());
+}
+
+}  // namespace testing
+}  // namespace actions
+}  // namespace common
+}  // namespace aos