Add support for not automatically starting applications

There are cases where we want a debug application (web server is a
reasonable example) which doesn't start up automatically, but can be
started on demand.  Add an "autostart" to the application config entry
to signal this.

Change-Id: Id88ec8a164f42adb73902e2f0ba30c9d1ecea5c9
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/starter/starter_test.cc b/aos/starter/starter_test.cc
index dea27e9..f174032 100644
--- a/aos/starter/starter_test.cc
+++ b/aos/starter/starter_test.cc
@@ -12,6 +12,9 @@
 
 using aos::testing::ArtifactPath;
 
+namespace aos {
+namespace starter {
+
 TEST(StarterdTest, StartStopTest) {
   const std::string config_file =
       ArtifactPath("aos/events/pingpong_config.json");
@@ -69,7 +72,8 @@
               ASSERT_TRUE(aos::starter::SendCommandBlocking(
                   aos::starter::Command::STOP, "ping", config_msg,
                   std::chrono::seconds(3)));
-            }).detach();
+            })
+                .detach();
             test_stage = 3;
             break;
           }
@@ -196,3 +200,78 @@
   starter.Cleanup();
   starterd_thread.join();
 }
+
+TEST(StarterdTest, Autostart) {
+  const std::string config_file =
+      ArtifactPath("aos/events/pingpong_config.json");
+
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+      aos::configuration::ReadConfig(config_file);
+
+  const std::string test_dir = aos::testing::TestTmpDir();
+
+  auto new_config = aos::configuration::MergeWithConfig(
+      &config.message(), absl::StrFormat(
+                             R"({"applications": [
+                                  {
+                                    "name": "ping",
+                                    "executable_name": "%s",
+                                    "args": ["--shm_base", "%s/aos"],
+                                    "autostart": false
+                                  },
+                                  {
+                                    "name": "pong",
+                                    "executable_name": "%s",
+                                    "args": ["--shm_base", "%s/aos"]
+                                  }
+                                ]})",
+                             ArtifactPath("aos/events/ping"), test_dir,
+                             ArtifactPath("aos/events/pong"), test_dir));
+
+  const aos::Configuration *config_msg = &new_config.message();
+
+  // Set up starter with config file
+  aos::starter::Starter starter(config_msg);
+
+  // Create an event loop to watch for the application starting up.
+  aos::ShmEventLoop watcher_loop(config_msg);
+  watcher_loop.SkipAosLog();
+
+  watcher_loop
+      .AddTimer([&watcher_loop] {
+        watcher_loop.Exit();
+        FAIL();
+      })
+      ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(7));
+
+  watcher_loop.MakeWatcher(
+      "/aos", [&watcher_loop](const aos::starter::Status &status) {
+        const aos::starter::ApplicationStatus *ping_app_status =
+            FindApplicationStatus(status, "ping");
+        const aos::starter::ApplicationStatus *pong_app_status =
+            FindApplicationStatus(status, "pong");
+        if (ping_app_status == nullptr || pong_app_status == nullptr) {
+          return;
+        }
+
+        if (ping_app_status->has_state() &&
+            ping_app_status->state() != aos::starter::State::STOPPED) {
+          watcher_loop.Exit();
+          FAIL();
+        }
+        if (pong_app_status->has_state() &&
+            pong_app_status->state() == aos::starter::State::RUNNING) {
+          watcher_loop.Exit();
+          SUCCEED();
+        }
+      });
+
+  std::thread starterd_thread([&starter] { starter.Run(); });
+  watcher_loop.Run();
+
+  starter.Cleanup();
+  starterd_thread.join();
+}
+
+}  // namespace starter
+}  // namespace aos