generalized faking queue messages for test control loops and used it
diff --git a/aos/common/controls/control_loop_test.cc b/aos/common/controls/control_loop_test.cc
new file mode 100644
index 0000000..6f4e784
--- /dev/null
+++ b/aos/common/controls/control_loop_test.cc
@@ -0,0 +1,57 @@
+#include "aos/common/controls/control_loop_test.h"
+
+#include "aos/common/messages/robot_state.q.h"
+#include "aos/common/controls/sensor_generation.q.h"
+#include "aos/common/controls/output_check.q.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace testing {
+
+ControlLoopTest::ControlLoopTest() {
+  ::aos::robot_state.Clear();
+  ::aos::controls::sensor_generation.Clear();
+  ::aos::controls::output_check_received.Clear();
+
+  ::aos::controls::sensor_generation.MakeWithBuilder()
+      .reader_pid(254)
+      .cape_resets(5)
+      .Send();
+  ::aos::time::Time::EnableMockTime(::aos::time::Time::InSeconds(0.0));
+
+  SimulateTimestep(false);
+}
+
+ControlLoopTest::~ControlLoopTest() {
+  ::aos::robot_state.Clear();
+  ::aos::controls::sensor_generation.Clear();
+  ::aos::controls::output_check_received.Clear();
+
+  ::aos::time::Time::DisableMockTime();
+}
+
+void ControlLoopTest::SimulateTimestep(bool enabled) {
+  if (sent_robot_state_last_time_) {
+    sent_robot_state_last_time_ = false;
+  } else {
+    ::aos::robot_state.MakeWithBuilder()
+        .enabled(enabled)
+        .autonomous(false)
+        .fake(true)
+        .team_id(971)
+        .Send();
+    sent_robot_state_last_time_ = true;
+  }
+  if (enabled) {
+    // TODO(brians): Actually make this realistic once we figure out what that
+    // means.
+    ::aos::controls::output_check_received.MakeWithBuilder()
+        .pwm_value(0)
+        .pulse_length(0)
+        .Send();
+  }
+  ::aos::time::Time::IncrementMockTime(::aos::time::Time::InMS(10.0));
+}
+
+}  // namespace testing
+}  // namespace aos
diff --git a/aos/common/controls/control_loop_test.h b/aos/common/controls/control_loop_test.h
new file mode 100644
index 0000000..14be01c
--- /dev/null
+++ b/aos/common/controls/control_loop_test.h
@@ -0,0 +1,34 @@
+#ifndef AOS_COMMON_CONTROLS_CONTROL_LOOP_TEST_H_
+#define AOS_COMMON_CONTROLS_CONTROL_LOOP_TEST_H_
+
+#include "gtest/gtest.h"
+
+#include "aos/common/queue_testutils.h"
+
+namespace aos {
+namespace testing {
+
+// Handles setting up the environment that all control loops need to actually
+// run.
+// This includes sending the queue messages and Clear()ing the queues when
+// appropriate.
+// It also includes dealing with ::aos::time.
+class ControlLoopTest : public ::testing::Test {
+ public:
+  ControlLoopTest();
+
+  virtual ~ControlLoopTest();
+
+  // Simulates everything that happens during 1 time step.
+  void SimulateTimestep(bool enabled);
+
+ private:
+  bool sent_robot_state_last_time_ = false;
+
+  ::aos::common::testing::GlobalCoreInstance my_core;
+};
+
+}  // namespace testing
+}  // namespace aos
+
+#endif  // AOS_COMMON_CONTROLS_CONTROL_LOOP_TEST_H_
diff --git a/aos/common/controls/controls.gyp b/aos/common/controls/controls.gyp
index 0945831..15be23c 100644
--- a/aos/common/controls/controls.gyp
+++ b/aos/common/controls/controls.gyp
@@ -1,6 +1,25 @@
 {
   'targets': [
     {
+      'target_name': 'control_loop_test',
+      'type': 'static_library',
+      'sources': [
+        'control_loop_test.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/common/common.gyp:time',
+        '<(AOS)/common/messages/messages.gyp:robot_state',
+        'sensor_generation',
+        'output_check',
+        '<(EXTERNALS):gtest',
+        '<(AOS)/common/common.gyp:queue_testutils',
+      ],
+      'export_dependent_settings': [
+        '<(EXTERNALS):gtest',
+        '<(AOS)/common/common.gyp:queue_testutils',
+      ],
+    },
+    {
       'target_name': 'polytope',
       'type': 'static_library',
       'sources': [