[session_shell] Convert tests to use RequestStart().

This removes all use of Start() to start stories and replaces it
with RequestStart(), except from the session_shell test. That test
continues to exercise both ways to start stories until Start() is
removed altogether.

Move the existing implementation of SessionShell in the session_shell
test SessionShellImpl to a common location lib/testing where it can be
reused by other tests.

Create a common base class SessionShellBase for all test SessionShell
components that uniformly creates an instance of SessionShellImpl. Also
move setup of SessionShellContext and StoryProvider into the shared base
class, reducing boiler plate across tests.

Convert all test SessionShell components to use SessionShellBase.

Convert all test SessionShell components to use RequestStart() instead
of Start() to start stories. The SessionShellImpl instance in their
base classes handles the ensuiing AttachView() and DetachView calls.

Collateral cleanup:

* Simplify requests for PuppetMaster from environment.

* Uniformly use Signal() rather than Logout() to finish the test.

* Remove scheduling of timeouts that are redundant with the timeout
  in dev_base_shell.

* Remove redundant using declarations for TestPoint.

* Remove one unused declaration of Await.

* Convert component_context test to a sequential flow to
  avoid hitting timeouts, and for consistency with other tests.

* Convert component_context test to use Signal() and Await()
  rather than Get() and Put().

MF-121 #comment Convert tests to use RequestStart().

TESTED=/pkgfs/packages/modular_tests/0/test/run_modular_tests.sh
  Manually verified that without the SessionShellImpl, sessionmgr
  crashes in an FTL_CHECK() when the SessionShell uses RequestStart().

Change-Id: I145a9dfdd7a64ada7b63d56f125cb78c2ec351a7
diff --git a/lib/common/story_provider_watcher_base.cc b/lib/common/story_provider_watcher_base.cc
index f671599..dd070b0 100644
--- a/lib/common/story_provider_watcher_base.cc
+++ b/lib/common/story_provider_watcher_base.cc
@@ -19,8 +19,8 @@
 }
 
 void StoryProviderWatcherBase::Watch(
-    fuchsia::modular::StoryProviderPtr* const story_provider) {
-  (*story_provider)->Watch(binding_.NewBinding());
+    fuchsia::modular::StoryProvider* const story_provider) {
+  story_provider->Watch(binding_.NewBinding());
 }
 
 void StoryProviderWatcherBase::Reset() { binding_.Unbind(); }
diff --git a/lib/common/story_provider_watcher_base.h b/lib/common/story_provider_watcher_base.h
index 4f690bf..131f764 100644
--- a/lib/common/story_provider_watcher_base.h
+++ b/lib/common/story_provider_watcher_base.h
@@ -26,7 +26,7 @@
 
   // Registers itself a watcher on the given story provider. Only one story
   // provider can be watched at a time.
-  void Watch(fuchsia::modular::StoryProviderPtr* story_provider);
+  void Watch(fuchsia::modular::StoryProvider* story_provider);
 
   // Deregisters itself from the watched story provider.
   void Reset();
diff --git a/lib/testing/BUILD.gn b/lib/testing/BUILD.gn
index 7a57107..fa8a8f6 100644
--- a/lib/testing/BUILD.gn
+++ b/lib/testing/BUILD.gn
@@ -10,11 +10,23 @@
   ]
 
   public_deps = [
+    ":component_main",
     "//garnet/public/lib/component/cpp",
     "//garnet/public/lib/fxl",
     "//peridot/lib/fidl:single_service_app",
-    "//peridot/public/lib/app_driver/cpp:app_driver",
     "//peridot/public/lib/integration_testing/cpp",
+  ]
+}
+
+source_set("component_main") {
+  testonly = true
+
+  sources = [
+    "component_main.h",
+  ]
+
+  public_deps = [
+    "//peridot/public/lib/app_driver/cpp:app_driver",
     "//zircon/public/lib/async-loop-cpp",
   ]
 }
@@ -77,6 +89,40 @@
   ]
 }
 
+source_set("session_shell_base") {
+  testonly = true
+
+  sources = [
+    "session_shell_base.h",
+  ]
+
+  public_deps = [
+    ":component_base",
+    ":session_shell_impl",
+  ]
+}
+
+source_set("session_shell_impl") {
+  testonly = true
+
+  sources = [
+    "session_shell_impl.h",
+    "session_shell_impl.cc",
+  ]
+
+  public_deps = [
+    "//garnet/public/fidl/fuchsia.ui.viewsv1",
+    "//garnet/public/lib/fidl/cpp",
+    "//garnet/public/lib/fsl",
+    "//garnet/public/lib/fxl",
+    "//garnet/public/lib/component/cpp",
+    "//garnet/public/lib/fxl",
+    "//peridot/public/fidl/fuchsia.modular",
+    "//peridot/public/lib/integration_testing/cpp",
+    "//zircon/public/lib/async-loop-cpp",
+  ]
+}
+
 source_set("story_controller_mock") {
   testonly = true
 
diff --git a/lib/testing/component_base.h b/lib/testing/component_base.h
index d3ff0b7..cec380d 100644
--- a/lib/testing/component_base.h
+++ b/lib/testing/component_base.h
@@ -5,12 +5,11 @@
 #ifndef PERIDOT_LIB_TESTING_COMPONENT_BASE_H_
 #define PERIDOT_LIB_TESTING_COMPONENT_BASE_H_
 
-#include <lib/app_driver/cpp/app_driver.h>
-#include <lib/async-loop/cpp/loop.h>
 #include <lib/component/cpp/connect.h>
 #include <lib/fxl/memory/weak_ptr.h>
 
 #include "peridot/lib/fidl/single_service_app.h"
+#include "peridot/lib/testing/component_main.h"
 #include "peridot/public/lib/integration_testing/cpp/reporting.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 
@@ -115,42 +114,6 @@
   FXL_DISALLOW_COPY_AND_ASSIGN(ComponentBase);
 };
 
-// A main function for an application that only runs the implementation of a
-// single component used for integration testing. The component implementation
-// Impl usually derives from ComponentBase.
-//
-// Args are either nothing or the instance of a Settings class initialized from
-// the command line arguments.
-//
-// Example use with settings (TestApp and Settings are locally defined classes):
-//
-//   int main(int argc, const char** argv) {
-//     auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
-//     Settings settings(command_line);
-//     modular::testing::ComponentMain<TestApp,
-//     Settings>(std::move(settings)); return 0;
-//   }
-//
-// Example use without settings (TestApp is a locally defined class):
-//
-//   int main(int, const char**) {
-//     modular::testing::ComponentMain<TestApp>();
-//     return 0;
-//   }
-//
-template <typename Impl, typename... Args>
-void ComponentMain(Args... args) {
-  async::Loop loop(&kAsyncLoopConfigAttachToThread);
-
-  auto context = component::StartupContext::CreateFromStartupInfo();
-  modular::AppDriver<Impl> driver(
-      context->outgoing().deprecated_services(),
-      std::make_unique<Impl>(context.get(), std::move(args)...),
-      [&loop] { loop.Quit(); });
-
-  loop.Run();
-}
-
 }  // namespace testing
 }  // namespace modular
 
diff --git a/lib/testing/component_main.h b/lib/testing/component_main.h
new file mode 100644
index 0000000..6f69368
--- /dev/null
+++ b/lib/testing/component_main.h
@@ -0,0 +1,56 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PERIDOT_LIB_TESTING_COMPONENT_MAIN_H_
+#define PERIDOT_LIB_TESTING_COMPONENT_MAIN_H_
+
+#include <lib/app_driver/cpp/app_driver.h>
+#include <lib/async-loop/cpp/loop.h>
+
+namespace modular {
+namespace testing {
+
+// A main function for an application that only runs the implementation of a
+// single component used for integration testing. The component implementation
+// Impl usually derives from ComponentBase.
+//
+// Args are meant to be either nothing or the instance of a Settings class
+// initialized from the command line arguments. They are just passed as
+// additional constructor arguments to Impl.
+//
+// Example use with settings (TestApp and Settings are locally defined classes):
+//
+//   int main(int argc, const char** argv) {
+//     auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
+//     Settings settings(command_line);
+//     modular::testing::ComponentMain<TestApp, Settings>(std::move(settings));
+//     return 0;
+//   }
+//
+// Example use without settings (TestApp is a locally defined class):
+//
+//   int main(int, const char**) {
+//     modular::testing::ComponentMain<TestApp>();
+//     return 0;
+//   }
+//
+// The classes ComponentBase and SessionShellBase defined in this directory are
+// meant to be used as base classes for Impl.
+template <typename Impl, typename... Args>
+void ComponentMain(Args... args) {
+  async::Loop loop(&kAsyncLoopConfigAttachToThread);
+
+  auto context = component::StartupContext::CreateFromStartupInfo();
+  modular::AppDriver<Impl> driver(
+      context->outgoing().deprecated_services(),
+      std::make_unique<Impl>(context.get(), std::move(args)...),
+      [&loop] { loop.Quit(); });
+
+  loop.Run();
+}
+
+}  // namespace testing
+}  // namespace modular
+
+#endif  // PERIDOT_LIB_TESTING_COMPONENT_MAIN_H_
diff --git a/lib/testing/session_shell_base.h b/lib/testing/session_shell_base.h
new file mode 100644
index 0000000..f800523
--- /dev/null
+++ b/lib/testing/session_shell_base.h
@@ -0,0 +1,52 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PERIDOT_LIB_TESTING_SESSION_SHELL_BASE_H_
+#define PERIDOT_LIB_TESTING_SESSION_SHELL_BASE_H_
+
+#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/session_shell_impl.h"
+
+namespace modular {
+namespace testing {
+
+class SessionShellBase : public ComponentBase<void> {
+ public:
+  SessionShellBase(component::StartupContext* const startup_context) :
+      ComponentBase(startup_context) {
+    startup_context->ConnectToEnvironmentService(
+        session_shell_context_.NewRequest());
+
+    session_shell_context_->GetStoryProvider(
+        story_provider_.NewRequest());
+
+    startup_context->outgoing().AddPublicService(
+        session_shell_impl_.GetHandler());
+  }
+
+ protected:
+  modular::testing::SessionShellImpl* session_shell_impl() {
+    return &session_shell_impl_;
+  }
+
+  fuchsia::modular::SessionShellContext* session_shell_context() {
+    return session_shell_context_.get();
+  }
+
+  fuchsia::modular::StoryProvider* story_provider() {
+    return story_provider_.get();
+  }
+
+ private:
+  modular::testing::SessionShellImpl session_shell_impl_;
+  fuchsia::modular::SessionShellContextPtr session_shell_context_;
+  fuchsia::modular::StoryProviderPtr story_provider_;
+
+  FXL_DISALLOW_COPY_AND_ASSIGN(SessionShellBase);
+};
+
+}  // namespace testing
+}  // namespace modular
+
+#endif  // PERIDOT_LIB_TESTING_SESSION_SHELL_BASE_H_
diff --git a/lib/testing/session_shell_impl.cc b/lib/testing/session_shell_impl.cc
new file mode 100644
index 0000000..ac09240
--- /dev/null
+++ b/lib/testing/session_shell_impl.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "peridot/lib/testing/session_shell_impl.h"
+
+#include <lib/async/cpp/task.h>
+#include <lib/async/default.h>
+
+namespace modular {
+namespace testing {
+
+SessionShellImpl::SessionShellImpl() = default;
+SessionShellImpl::~SessionShellImpl() = default;
+
+fidl::InterfaceRequestHandler<fuchsia::modular::SessionShell> SessionShellImpl::GetHandler() {
+  return bindings_.GetHandler(this);
+}
+
+// |SessionShell|
+void SessionShellImpl::AttachView(fuchsia::modular::ViewIdentifier view_id,
+                                  fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner>
+                                  view_owner) {
+  on_attach_view_(std::move(view_id));
+}
+
+// |SessionShell|
+void SessionShellImpl::DetachView(fuchsia::modular::ViewIdentifier view_id,
+                                  std::function<void()> done) {
+  on_detach_view_(std::move(view_id));
+
+  // Used to simulate a sluggish shell that hits the timeout.
+  async::PostDelayedTask(async_get_default_dispatcher(),
+                         std::move(done), detach_delay_);
+}
+
+}  // namespace testing
+}  // namespace modular
diff --git a/lib/testing/session_shell_impl.h b/lib/testing/session_shell_impl.h
new file mode 100644
index 0000000..1a1a51f
--- /dev/null
+++ b/lib/testing/session_shell_impl.h
@@ -0,0 +1,74 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PERIDOT_LIB_TESTING_SESSION_SHELL_IMPL_H_
+#define PERIDOT_LIB_TESTING_SESSION_SHELL_IMPL_H_
+
+#include <fuchsia/modular/cpp/fidl.h>
+#include <fuchsia/ui/viewsv1token/cpp/fidl.h>
+#include <lib/component/cpp/startup_context.h>
+#include <lib/fidl/cpp/binding_set.h>
+#include <lib/fxl/logging.h>
+#include <lib/fxl/macros.h>
+#include <lib/fxl/time/time_delta.h>
+
+namespace modular {
+namespace testing {
+
+// An implementation of the fuchsia.modular.SessionShell FIDL service, to be
+// used in session shell components in integration tests. Usually used through
+// SessionShellBase.
+class SessionShellImpl : fuchsia::modular::SessionShell {
+ public:
+  SessionShellImpl();
+  ~SessionShellImpl() override;
+
+  using ViewId = fuchsia::modular::ViewIdentifier;
+
+  // Produces a handler function that can be used in the outgoing service
+  // provider.
+  fidl::InterfaceRequestHandler<fuchsia::modular::SessionShell> GetHandler();
+
+  // Whenever SessionShell.AttachView() is called, the supplied callback is
+  // invoked with the view ID. The ViewOwner is dropped.
+  void set_on_attach_view(std::function<void(ViewId view_id)> callback) {
+    on_attach_view_ = std::move(callback);
+  }
+
+  // Whenever SessionShell.DetachView() is called, the supplied callback is
+  // invoked with the view ID. The return callback of DetachView() is invoked
+  // asynchronously after a delay that can be configured by the client with
+  // set_detach_delay().
+  void set_on_detach_view(std::function<void(ViewId view_id)> callback) {
+    on_detach_view_ = std::move(callback);
+  }
+
+  // Configures the delay after which the return callback of DetachView() is
+  // invoked. Used to test the timeout behavior of sessionmgr.
+  void set_detach_delay(zx::duration detach_delay) {
+    detach_delay_ = std::move(detach_delay);
+  }
+
+ private:
+  // |SessionShell|
+  void AttachView(fuchsia::modular::ViewIdentifier view_id,
+                  fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner>
+                  view_owner) override;
+
+  // |SessionShell|
+  void DetachView(fuchsia::modular::ViewIdentifier view_id,
+                  std::function<void()> done) override;
+
+  fidl::BindingSet<fuchsia::modular::SessionShell> bindings_;
+  std::function<void(ViewId view_id)> on_attach_view_{[](ViewId) {}};
+  std::function<void(ViewId view_id)> on_detach_view_{[](ViewId) {}};
+  zx::duration detach_delay_{};
+
+  FXL_DISALLOW_COPY_AND_ASSIGN(SessionShellImpl);
+};
+
+}  // namespace testing
+}  // namespace modular
+
+#endif  // PERIDOT_LIB_TESTING_SESSION_SHELL_IMPL_H_
diff --git a/tests/chain/BUILD.gn b/tests/chain/BUILD.gn
index 080c083..3219166 100644
--- a/tests/chain/BUILD.gn
+++ b/tests/chain/BUILD.gn
@@ -29,7 +29,8 @@
     "//peridot/lib/fidl:single_service_app",
     "//peridot/lib/rapidjson",
     "//peridot/public/lib/integration_testing/cpp",
-    "//peridot/lib/testing:component_base",
+    "//peridot/lib/testing:component_main",
+    "//peridot/lib/testing:session_shell_base",
     "//peridot/public/fidl/fuchsia.modular",
     "//peridot/public/lib/context/cpp:formatting",
     "//peridot/tests/common:defs",
diff --git a/tests/chain/chain_test_session_shell.cc b/tests/chain/chain_test_session_shell.cc
index 7a375e7..c6f56bb 100644
--- a/tests/chain/chain_test_session_shell.cc
+++ b/tests/chain/chain_test_session_shell.cc
@@ -17,13 +17,13 @@
 
 #include "peridot/lib/fidl/array_to_string.h"
 #include "peridot/lib/rapidjson/rapidjson.h"
-#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/component_main.h"
+#include "peridot/lib/testing/session_shell_base.h"
 #include "peridot/public/lib/integration_testing/cpp/reporting.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 #include "peridot/tests/chain/defs.h"
 #include "peridot/tests/common/defs.h"
 
-using modular::testing::Await;
 using modular::testing::Put;
 using modular::testing::TestPoint;
 
@@ -32,19 +32,13 @@
 const char kStoryName[] = "story";
 
 // Cf. README.md for what this test does and how.
-class TestApp : public modular::testing::ComponentBase<void> {
+class TestApp : public modular::testing::SessionShellBase {
  public:
   TestApp(component::StartupContext* const startup_context)
-      : ComponentBase(startup_context) {
+      : SessionShellBase(startup_context) {
     TestInit(__FILE__);
 
-    session_shell_context_ = startup_context->ConnectToEnvironmentService<
-        fuchsia::modular::SessionShellContext>();
-    session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
-
-    puppet_master_ =
-        startup_context
-            ->ConnectToEnvironmentService<fuchsia::modular::PuppetMaster>();
+    startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
 
     CreateStory();
   }
@@ -85,16 +79,13 @@
   }
 
   void StartStory() {
-    // Start and show the new story.
-    story_provider_->GetController(kStoryName, story_controller_.NewRequest());
-    fidl::InterfacePtr<fuchsia::ui::viewsv1token::ViewOwner> story_view_binding;
-    story_controller_->Start(story_view_binding.NewRequest());
+    // Request start of the new story.
+    story_provider()->GetController(kStoryName, story_controller_.NewRequest());
+    story_controller_->RequestStart();
   }
 
-  fuchsia::modular::SessionShellContextPtr session_shell_context_;
   fuchsia::modular::PuppetMasterPtr puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
-  fuchsia::modular::StoryProviderPtr story_provider_;
   fidl::StringPtr story_id_;
   fuchsia::modular::StoryControllerPtr story_controller_;
   FXL_DISALLOW_COPY_AND_ASSIGN(TestApp);
diff --git a/tests/component_context/component_context_test_module.cc b/tests/component_context/component_context_test_module.cc
index 64e5f00..11f0e2f 100644
--- a/tests/component_context/component_context_test_module.cc
+++ b/tests/component_context/component_context_test_module.cc
@@ -27,56 +27,15 @@
 
 namespace {
 
-// Execute a trigger after the counter reaches a particular value OR if the
-// count is canceled.
-class CounterTrigger {
- public:
-  CounterTrigger(int count, std::function<void()> trigger)
-      : count_(count), trigger_(std::move(trigger)) {}
-
-  void Step() {
-    if (!finished_) {
-      // If this CHECK triggers, then you've called Step() more times than
-      // you passed for |count| into the constructor.
-      FXL_CHECK(count_ > 0);
-      if (count_ && --count_ == 0) {
-        Finished();
-      }
-    }
-  }
-
-  // It's safe to call Cancel() at any time, even if the trigger has already
-  // executed.
-  void Cancel() { Finished(); }
-
- private:
-  void Finished() {
-    if (!finished_) {
-      finished_ = true;
-      trigger_();
-    }
-  }
-
-  int count_;
-  const std::function<void()> trigger_;
-  bool finished_{};
-
-  FXL_DISALLOW_COPY_AND_ASSIGN(CounterTrigger);
-};
-
 // Cf. README.md for what this test does and how.
 class TestModule {
  public:
   TestPoint initialized_{"Root module initialized"};
-  TestPoint one_agent_connected_{"One agent accepted connection"};
 
   TestModule(modular::ModuleHost* module_host,
              fidl::InterfaceRequest<
                  fuchsia::ui::app::ViewProvider> /*view_provider_request*/)
-      : steps_(
-            kTotalSimultaneousTests,
-            [this, module_host] { Signal(modular::testing::kTestShutdown); }),
-        weak_ptr_factory_(this) {
+      : weak_ptr_factory_(this) {
     modular::testing::Init(module_host->startup_context(), __FILE__);
 
     initialized_.Pass();
@@ -92,13 +51,7 @@
     component::ConnectToService(one_agent_services.get(),
                                 one_agent_interface_.NewRequest());
 
-    Await("one_agent_connected", [this] {
-      one_agent_connected_.Pass();
-      TestMessageQueue(
-          [this] { TestAgentController([this] { steps_.Step(); }); });
-    });
-
-    TestUnstoppableAgent([this] { steps_.Step(); });
+    TestAgentConnected();
   }
 
   TestModule(modular::ModuleHost* const module_host,
@@ -116,11 +69,21 @@
   }
 
  private:
+  TestPoint one_agent_connected_{"One agent accepted connection"};
+
+  void TestAgentConnected() {
+    Await("one_agent_connected", [this] {
+                                   FXL_LOG(INFO) << "TestModule One Agent Connected";
+                                   one_agent_connected_.Pass();
+                                   TestMessageQueue();
+                               });
+  }
+
   TestPoint msg_queue_communicated_{
       "Communicated message between Agent one using a MessageQueue"};
 
-  // Tests message queues. Calls |done_cb| when completed successfully.
-  void TestMessageQueue(std::function<void()> done_cb) {
+  // Tests message queues.
+  void TestMessageQueue() {
     constexpr char kTestMessage[] = "test message!";
 
     component_context_->ObtainMessageQueue("root_msg_queue",
@@ -128,8 +91,8 @@
 
     // MessageQueueManager shouldn't send us anything just yet.
     msg_queue_.RegisterReceiver(
-        [this, done_cb, kTestMessage](const std::string& msg,
-                                      fit::function<void()> ack) {
+        [this, kTestMessage](const std::string& msg,
+                             fit::function<void()> ack) {
           ack();
           // We only want one message.
           msg_queue_.RegisterReceiver(nullptr);
@@ -137,7 +100,7 @@
           if (msg == kTestMessage) {
             msg_queue_communicated_.Pass();
           }
-          done_cb();
+          TestAgentController();
         });
 
     msg_queue_.GetToken([this, kTestMessage](const fidl::StringPtr& token) {
@@ -149,18 +112,18 @@
 
   // Tests fuchsia::modular::AgentController. Calls |done_cb| when completed
   // successfully.
-  void TestAgentController(std::function<void()> done_cb) {
+  void TestAgentController() {
     // Closing the agent controller should trigger the agent to stop.
     one_agent_controller.Unbind();
 
-    Await("one_agent_stopped", [this, done_cb] {
+    Await("one_agent_stopped", [this] {
       one_agent_stopped_.Pass();
-      done_cb();
+      TestUnstoppableAgent();
     });
   }
 
   // Start an agent that will not stop of its own accord.
-  void TestUnstoppableAgent(std::function<void()> done_cb) {
+  void TestUnstoppableAgent() {
     fuchsia::sys::ServiceProviderPtr unstoppable_agent_services;
     component_context_->ConnectToAgent(
         kUnstoppableAgent, unstoppable_agent_services.NewRequest(),
@@ -174,15 +137,13 @@
     async::PostDelayedTask(
         async_get_default_dispatcher(),
         callback::MakeScoped(weak_ptr_factory_.GetWeakPtr(),
-                             [this, done_cb] {
+                             [this] {
                                unstoppable_agent_controller_.Unbind();
-                               done_cb();
+                               Signal(modular::testing::kTestShutdown);
                              }),
         zx::msec(500));
   }
 
-  CounterTrigger steps_;
-
   fuchsia::modular::AgentControllerPtr one_agent_controller;
   ComponentContextTestServicePtr one_agent_interface_;
   fuchsia::modular::ComponentContextPtr component_context_;
diff --git a/tests/component_context/component_context_test_one_agent.cc b/tests/component_context/component_context_test_one_agent.cc
index b982abc..950160b 100644
--- a/tests/component_context/component_context_test_one_agent.cc
+++ b/tests/component_context/component_context_test_one_agent.cc
@@ -14,6 +14,8 @@
 #include "peridot/tests/common/defs.h"
 #include "peridot/tests/component_context/defs.h"
 
+using ::modular::testing::Await;
+using ::modular::testing::Signal;
 using ::modular::testing::TestPoint;
 using ::test::peridot::tests::componentcontext::ComponentContextTestService;
 
@@ -41,7 +43,7 @@
   // Called by AgentDriver.
   void Connect(fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> request) {
     agent_services_.AddBinding(std::move(request));
-    modular::testing::GetStore()->Put("one_agent_connected", "", [] {});
+    Signal("one_agent_connected");
   }
 
   // Called by AgentDriver.
@@ -52,15 +54,15 @@
 
   // Called by AgentDriver.
   void Terminate(const std::function<void()>& done) {
+    FXL_LOG(INFO) << "TestOneAgent::Terminate()";
     // Before reporting that we stop, we wait until two_agent has connected.
-    modular::testing::GetStore()->Get(
-        "two_agent_connected", [this, done](const fidl::StringPtr&) {
+    Await("two_agent_connected", [this, done] {
+          FXL_LOG(INFO) << "TestOneAgent::Terminate() GET";
           // Killing the agent controller should stop it.
           two_agent_controller_.Unbind();
           two_agent_connected_.Pass();
-          modular::testing::GetStore()->Put("one_agent_stopped", "", [done] {
-            modular::testing::Done(done);
-          });
+          Signal("one_agent_stopped");
+          modular::testing::Done(done);
         });
   }
 
diff --git a/tests/component_context/component_context_test_two_agent.cc b/tests/component_context/component_context_test_two_agent.cc
index 9e3aff6..ffc24e9 100644
--- a/tests/component_context/component_context_test_two_agent.cc
+++ b/tests/component_context/component_context_test_two_agent.cc
@@ -12,6 +12,7 @@
 #include "peridot/tests/common/defs.h"
 #include "peridot/tests/component_context/defs.h"
 
+using ::modular::testing::Signal;
 using ::modular::testing::TestPoint;
 
 namespace {
@@ -26,7 +27,7 @@
   // Called by AgentDriver.
   void Connect(
       fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> /*services*/) {
-    modular::testing::GetStore()->Put("two_agent_connected", "", [] {});
+    Signal("two_agent_connected");
   }
 
   // Called by AgentDriver.
diff --git a/tests/last_focus_time/BUILD.gn b/tests/last_focus_time/BUILD.gn
index 6b7d6d5..5ca94bc 100644
--- a/tests/last_focus_time/BUILD.gn
+++ b/tests/last_focus_time/BUILD.gn
@@ -37,7 +37,8 @@
     "//peridot/lib/fidl:array_to_string",
     "//peridot/lib/rapidjson",
     "//peridot/public/lib/integration_testing/cpp",
-    "//peridot/lib/testing:component_base",
+    "//peridot/lib/testing:component_main",
+    "//peridot/lib/testing:session_shell_base",
     "//peridot/public/fidl/fuchsia.modular",
     "//peridot/tests/common:defs",
   ]
diff --git a/tests/last_focus_time/last_focus_time_test_session_shell.cc b/tests/last_focus_time/last_focus_time_test_session_shell.cc
index dba4435..a2a5680 100644
--- a/tests/last_focus_time/last_focus_time_test_session_shell.cc
+++ b/tests/last_focus_time/last_focus_time_test_session_shell.cc
@@ -13,11 +13,13 @@
 #include <lib/fxl/macros.h>
 
 #include "peridot/lib/common/story_provider_watcher_base.h"
-#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/component_main.h"
+#include "peridot/lib/testing/session_shell_base.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 #include "peridot/tests/common/defs.h"
 #include "peridot/tests/last_focus_time/defs.h"
 
+using modular::testing::Signal;
 using modular::testing::TestPoint;
 
 namespace {
@@ -147,22 +149,17 @@
 };
 
 // Cf. README.md for what this test does and how.
-class TestApp : public modular::testing::ComponentBase<void> {
+class TestApp : public modular::testing::SessionShellBase {
  public:
   TestApp(component::StartupContext* const startup_context)
-      : ComponentBase(startup_context) {
+      : SessionShellBase(startup_context) {
     TestInit(__FILE__);
 
-    puppet_master_ =
-        startup_context
-            ->ConnectToEnvironmentService<fuchsia::modular::PuppetMaster>();
-    session_shell_context_ = startup_context->ConnectToEnvironmentService<
-        fuchsia::modular::SessionShellContext>();
-    session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
-    story_provider_watcher_.Watch(&story_provider_);
+    startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
+    story_provider_watcher_.Watch(story_provider());
 
-    session_shell_context_->GetFocusController(focus_controller_.NewRequest());
-    session_shell_context_->GetFocusProvider(focus_provider_.NewRequest());
+    session_shell_context()->GetFocusController(focus_controller_.NewRequest());
+    session_shell_context()->GetFocusProvider(focus_provider_.NewRequest());
     focus_watcher_.Watch(focus_provider_.get());
 
     CreateStory();
@@ -197,12 +194,11 @@
   TestPoint start_story_{"StartStory()"};
 
   void StartStory() {
-    story_provider_->GetController(kStoryName, story_controller_.NewRequest());
+    story_provider()->GetController(kStoryName, story_controller_.NewRequest());
     story_watcher_.Watch(story_controller_.get());
 
-    // Start and show the new story.
-    fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> story_view;
-    story_controller_->Start(story_view.NewRequest());
+    // Request start of the new story.
+    story_controller_->RequestStart();
 
     story_watcher_.Continue([this] {
       start_story_.Pass();
@@ -219,18 +215,13 @@
     story_provider_watcher_.Continue([this] {
       focus_.Pass();
       story_provider_watcher_.Reset();
-      Logout();
+
+      Signal(modular::testing::kTestShutdown);
     });
   }
 
-  void Logout() { session_shell_context_->Logout(); }
-
-  fuchsia::modular::SessionShellContextPtr session_shell_context_;
-
   fuchsia::modular::PuppetMasterPtr puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
-
-  fuchsia::modular::StoryProviderPtr story_provider_;
   StoryProviderWatcherImpl story_provider_watcher_;
 
   fuchsia::modular::StoryControllerPtr story_controller_;
diff --git a/tests/link_context_entities/BUILD.gn b/tests/link_context_entities/BUILD.gn
index 7b4151a..732e8dc 100644
--- a/tests/link_context_entities/BUILD.gn
+++ b/tests/link_context_entities/BUILD.gn
@@ -36,7 +36,8 @@
     "//peridot/lib/fidl:single_service_app",
     "//peridot/lib/rapidjson",
     "//peridot/public/lib/integration_testing/cpp",
-    "//peridot/lib/testing:component_base",
+    "//peridot/lib/testing:component_main",
+    "//peridot/lib/testing:session_shell_base",
     "//peridot/public/fidl/fuchsia.modular",
     "//peridot/public/lib/context/cpp:context_helper",
     "//peridot/public/lib/context/cpp:formatting",
diff --git a/tests/link_context_entities/link_context_entities_test_session_shell.cc b/tests/link_context_entities/link_context_entities_test_session_shell.cc
index d873904..195bad0 100644
--- a/tests/link_context_entities/link_context_entities_test_session_shell.cc
+++ b/tests/link_context_entities/link_context_entities_test_session_shell.cc
@@ -17,12 +17,14 @@
 
 #include "peridot/lib/fidl/array_to_string.h"
 #include "peridot/lib/rapidjson/rapidjson.h"
-#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/component_main.h"
+#include "peridot/lib/testing/session_shell_base.h"
 #include "peridot/public/lib/integration_testing/cpp/reporting.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 #include "peridot/tests/common/defs.h"
 #include "peridot/tests/link_context_entities/defs.h"
 
+using modular::testing::Signal;
 using modular::testing::TestPoint;
 
 namespace {
@@ -81,18 +83,13 @@
 };
 
 // Cf. README.md for what this test does and how.
-class TestApp : public modular::testing::ComponentBase<void> {
+class TestApp : public modular::testing::SessionShellBase {
  public:
   TestApp(component::StartupContext* const startup_context)
-      : ComponentBase(startup_context) {
+      : SessionShellBase(startup_context) {
     TestInit(__FILE__);
 
-    puppet_master_ =
-        startup_context
-            ->ConnectToEnvironmentService<fuchsia::modular::PuppetMaster>();
-    session_shell_context_ = startup_context->ConnectToEnvironmentService<
-        fuchsia::modular::SessionShellContext>();
-    session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
+    startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
 
     fuchsia::modular::IntelligenceServicesPtr intelligence_services;
     startup_context->ConnectToEnvironmentService(
@@ -143,9 +140,8 @@
           ProcessContextValue(value);
         });
 
-    story_provider_->GetController(kStoryName, story_controller_.NewRequest());
-    fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> story_view;
-    story_controller_->Start(story_view.NewRequest());
+    story_provider()->GetController(kStoryName, story_controller_.NewRequest());
+    story_controller_->RequestStart();
 
     start_story_exit_.Pass();
   }
@@ -222,10 +218,9 @@
     }
   }
 
-  void Logout() { session_shell_context_->Logout(); }
-
-  fuchsia::modular::SessionShellContextPtr session_shell_context_;
-  fuchsia::modular::StoryProviderPtr story_provider_;
+  void Logout() {
+    Signal(modular::testing::kTestShutdown);
+  }
 
   fuchsia::modular::PuppetMasterPtr puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
diff --git a/tests/link_data/BUILD.gn b/tests/link_data/BUILD.gn
index 5cc1523..c82eb0d 100644
--- a/tests/link_data/BUILD.gn
+++ b/tests/link_data/BUILD.gn
@@ -36,7 +36,8 @@
     "//peridot/lib/common:names",
     "//peridot/lib/rapidjson",
     "//peridot/public/lib/integration_testing/cpp",
-    "//peridot/lib/testing:component_base",
+    "//peridot/lib/testing:component_main",
+    "//peridot/lib/testing:session_shell_base",
     "//peridot/public/fidl/fuchsia.modular",
     "//peridot/public/fidl/fuchsia.modular.internal",
     "//peridot/tests/common:defs",
diff --git a/tests/link_data/link_data_test_session_shell.cc b/tests/link_data/link_data_test_session_shell.cc
index c2cc5a6..26b8146 100644
--- a/tests/link_data/link_data_test_session_shell.cc
+++ b/tests/link_data/link_data_test_session_shell.cc
@@ -18,7 +18,8 @@
 
 #include "peridot/lib/common/names.h"
 #include "peridot/lib/rapidjson/rapidjson.h"
-#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/component_main.h"
+#include "peridot/lib/testing/session_shell_base.h"
 #include "peridot/public/lib/integration_testing/cpp/reporting.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 #include "peridot/tests/common/defs.h"
@@ -34,19 +35,13 @@
 const char kStoryName[] = "story";
 
 // Cf. README.md for what this test does and how.
-class TestApp : public modular::testing::ComponentBase<void> {
+class TestApp : public modular::testing::SessionShellBase {
  public:
   explicit TestApp(component::StartupContext* const startup_context)
-      : ComponentBase(startup_context) {
+      : SessionShellBase(startup_context) {
     TestInit(__FILE__);
 
-    puppet_master_ =
-        startup_context
-            ->ConnectToEnvironmentService<fuchsia::modular::PuppetMaster>();
-    startup_context
-        ->ConnectToEnvironmentService<fuchsia::modular::SessionShellContext>(
-            session_shell_context_.NewRequest());
-    session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
+    startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
 
     TestStory1();
   }
@@ -88,7 +83,7 @@
   TestPoint story1_get_controller_{"Story1 GetController"};
 
   void TestStory1_GetController() {
-    story_provider_->GetController(kStoryName, story_controller_.NewRequest());
+    story_provider()->GetController(kStoryName, story_controller_.NewRequest());
     story_controller_->GetInfo([this](fuchsia::modular::StoryInfo story_info,
                                       fuchsia::modular::StoryState state) {
       story1_get_controller_.Pass();
@@ -146,8 +141,7 @@
   TestPoint story1_run_module0_link_{"Story1 Run: Module0 link"};
 
   void TestStory1_Run() {
-    fuchsia::ui::viewsv1token::ViewOwnerPtr story_view;
-    story_controller_->Start(story_view.NewRequest());
+    story_controller_->RequestStart();
 
     Await(std::string("module0_link") + ":" + kRootJson1, [this] {
       story1_run_module0_link_.Pass();
@@ -214,8 +208,7 @@
   void TestStory2_Run() {
     story2_run_.Pass();
 
-    fuchsia::ui::viewsv1token::ViewOwnerPtr story_view;
-    story_controller_->Start(story_view.NewRequest());
+    story_controller_->RequestStart();
 
     TestStory2_Wait();
   }
@@ -255,8 +248,6 @@
 
   fuchsia::modular::PuppetMasterPtr puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
-  fuchsia::modular::SessionShellContextPtr session_shell_context_;
-  fuchsia::modular::StoryProviderPtr story_provider_;
   fuchsia::modular::StoryControllerPtr story_controller_;
   fuchsia::modular::LinkPtr module0_link_;
   fuchsia::modular::StoryInfo story_info_;
diff --git a/tests/module_context/BUILD.gn b/tests/module_context/BUILD.gn
index ac79a36..2ba172a 100644
--- a/tests/module_context/BUILD.gn
+++ b/tests/module_context/BUILD.gn
@@ -29,7 +29,8 @@
     "//peridot/lib/fidl:single_service_app",
     "//peridot/lib/rapidjson",
     "//peridot/public/lib/integration_testing/cpp",
-    "//peridot/lib/testing:component_base",
+    "//peridot/lib/testing:component_main",
+    "//peridot/lib/testing:session_shell_base",
     "//peridot/public/fidl/fuchsia.modular",
     "//peridot/public/lib/context/cpp:formatting",
     "//peridot/tests/common:defs",
diff --git a/tests/module_context/module_context_test_session_shell.cc b/tests/module_context/module_context_test_session_shell.cc
index 6a6d57a..9eb5805 100644
--- a/tests/module_context/module_context_test_session_shell.cc
+++ b/tests/module_context/module_context_test_session_shell.cc
@@ -15,7 +15,8 @@
 #include <lib/fxl/macros.h>
 
 #include "peridot/lib/rapidjson/rapidjson.h"
-#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/component_main.h"
+#include "peridot/lib/testing/session_shell_base.h"
 #include "peridot/public/lib/integration_testing/cpp/reporting.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 #include "peridot/tests/common/defs.h"
@@ -39,8 +40,8 @@
                fidl::VectorPtr<fuchsia::modular::OngoingActivityType>) {}) {}
   ~StoryActivityWatcherImpl() override = default;
 
-  void Watch(fuchsia::modular::StoryProviderPtr* const story_provider) {
-    (*story_provider)->WatchActivity(binding_.NewBinding());
+  void Watch(fuchsia::modular::StoryProvider* const story_provider) {
+    story_provider->WatchActivity(binding_.NewBinding());
   }
 
   void OnNotify(std::function<
@@ -67,33 +68,20 @@
   FXL_DISALLOW_COPY_AND_ASSIGN(StoryActivityWatcherImpl);
 };
 
-class TestApp : public modular::testing::ComponentBase<void> {
+class TestApp : public modular::testing::SessionShellBase {
  public:
   TestApp(component::StartupContext* const startup_context)
-      : ComponentBase(startup_context), weak_ptr_factory_(this) {
+      : SessionShellBase(startup_context), weak_ptr_factory_(this) {
     TestInit(__FILE__);
 
-    puppet_master_ =
-        startup_context
-            ->ConnectToEnvironmentService<fuchsia::modular::PuppetMaster>();
-    session_shell_context_ = startup_context->ConnectToEnvironmentService<
-        fuchsia::modular::SessionShellContext>();
-
-    session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
+    startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
 
     CreateStory();
-    async::PostDelayedTask(
-        async_get_default_dispatcher(),
-        callback::MakeScoped(weak_ptr_factory_.GetWeakPtr(),
-                             [this] { session_shell_context_->Logout(); }),
-        zx::msec(modular::testing::kTestTimeoutMilliseconds));
   }
 
   ~TestApp() override = default;
 
  private:
-  using TestPoint = modular::testing::TestPoint;
-
   TestPoint story_create_{"Created story."};
   void CreateStory() {
     fidl::VectorPtr<fuchsia::modular::StoryCommand> commands;
@@ -141,13 +129,13 @@
   TestPoint story_get_controller_{"Story GetController()"};
   // Starts the story and adds two modules to it.
   void StartStory() {
-    story_provider_->GetController(kStoryName, story_controller_.NewRequest());
+    story_provider()->GetController(kStoryName, story_controller_.NewRequest());
     story_controller_.set_error_handler([this](zx_status_t status) {
       FXL_LOG(ERROR) << "Story controller for story " << kStoryName
                      << " died. Does this story exist?";
     });
 
-    story_controller_->Start(story_view_.NewRequest());
+    story_controller_->RequestStart();
     story_controller_->GetInfo(
         [this](fuchsia::modular::StoryInfo, fuchsia::modular::StoryState) {
           story_get_controller_.Pass();
@@ -158,7 +146,7 @@
   TestPoint on_watch_ongoing_activities_dispatched{
       "When a watcher is registered, ongoing activities should be dispatched."};
   void PerformWatchActivity() {
-    story_activity_watcher_.Watch(&story_provider_);
+    story_activity_watcher_.Watch(story_provider());
     story_activity_watcher_.OnNotify(
         [this](
             fidl::StringPtr story_id,
@@ -318,7 +306,8 @@
               if (!is_running) {
                 story_stopped_.Pass();
               }
-              session_shell_context_->Logout();
+
+              Signal(modular::testing::kTestShutdown);
             });
           });
     });
@@ -327,7 +316,7 @@
   // Verifies that the story is stopped when the last module that is part of the
   // story calls ModuleContext.Done and is stopped.
   void IsStoryRunning(std::function<void(bool)> callback) {
-    story_provider_->RunningStories(
+    story_provider()->RunningStories(
         [this, callback](fidl::VectorPtr<fidl::StringPtr> story_ids) {
           bool found_story = false;
           // Check all the running stories to make sure the one created for this
@@ -366,13 +355,9 @@
 
   fuchsia::modular::PuppetMasterPtr puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
-  fuchsia::modular::SessionShellContextPtr session_shell_context_;
-  fuchsia::modular::StoryProviderPtr story_provider_;
   fuchsia::modular::StoryControllerPtr story_controller_;
   StoryActivityWatcherImpl story_activity_watcher_;
 
-  fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> story_view_;
-
   fxl::WeakPtrFactory<TestApp> weak_ptr_factory_;
 
   FXL_DISALLOW_COPY_AND_ASSIGN(TestApp);
diff --git a/tests/session_shell/BUILD.gn b/tests/session_shell/BUILD.gn
index a33a297..2f98ed8 100644
--- a/tests/session_shell/BUILD.gn
+++ b/tests/session_shell/BUILD.gn
@@ -35,7 +35,8 @@
     "//garnet/public/lib/fxl",
     "//peridot/lib/rapidjson",
     "//peridot/public/lib/integration_testing/cpp",
-    "//peridot/lib/testing:component_base",
+    "//peridot/lib/testing:component_main",
+    "//peridot/lib/testing:session_shell_base",
     "//peridot/public/fidl/fuchsia.modular",
     "//peridot/tests/common:defs",
   ]
diff --git a/tests/session_shell/session_shell_test_session_shell.cc b/tests/session_shell/session_shell_test_session_shell.cc
index 22ba007..47e711e 100644
--- a/tests/session_shell/session_shell_test_session_shell.cc
+++ b/tests/session_shell/session_shell_test_session_shell.cc
@@ -22,7 +22,8 @@
 #include <lib/fxl/time/time_delta.h>
 
 #include "peridot/lib/rapidjson/rapidjson.h"
-#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/component_main.h"
+#include "peridot/lib/testing/session_shell_base.h"
 #include "peridot/public/lib/integration_testing/cpp/reporting.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 #include "peridot/tests/common/defs.h"
@@ -33,7 +34,6 @@
 using modular::testing::Signal;
 using modular::testing::TestPoint;
 
-using fuchsia::modular::SessionShell;
 using fuchsia::modular::ViewIdentifier;
 
 namespace {
@@ -47,8 +47,8 @@
 
   // Registers itself a watcher on the given story provider. Only one story
   // provider can be watched at a time.
-  void Watch(fuchsia::modular::StoryProviderPtr* const story_provider) {
-    (*story_provider)->Watch(binding_.NewBinding());
+  void Watch(fuchsia::modular::StoryProvider* const story_provider) {
+    story_provider->Watch(binding_.NewBinding());
   }
 
   // Deregisters itself from the watched story provider.
@@ -140,83 +140,25 @@
 
 // Cf. README.md for what this test does in general and how. The test cases are
 // described in detail in comments below.
-class SessionShellImpl : fuchsia::modular::SessionShell {
- public:
-  SessionShellImpl() = default;
-  ~SessionShellImpl() = default;
-
-  using ViewId = fuchsia::modular::ViewIdentifier;
-
-  fidl::InterfaceRequestHandler<fuchsia::modular::SessionShell> GetHandler() {
-    return bindings_.GetHandler(this);
-  }
-
-  void set_on_attach_view(std::function<void(ViewId view_id)> callback) {
-    on_attach_view_ = callback;
-  }
-
-  void set_on_detach_view(std::function<void(ViewId view_id)> callback) {
-    on_detach_view_ = callback;
-  }
-
-  void set_detach_delay(zx::duration detach_delay) {
-    detach_delay_ = detach_delay;
-  }
-
- private:
-  // |SessionShell|
-  void AttachView(fuchsia::modular::ViewIdentifier view_id,
-                  fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner>
-                  view_owner) override {
-    on_attach_view_(std::move(view_id));
-  }
-
-  // |SessionShell|
-  void DetachView(fuchsia::modular::ViewIdentifier view_id,
-                  std::function<void()> done) override {
-    on_detach_view_(std::move(view_id));
-
-    // Used to simulate a sluggish shell that hits the timeout.
-    async::PostDelayedTask(async_get_default_dispatcher(),
-                           std::move(done), detach_delay_);
-  }
-
-  fidl::BindingSet<fuchsia::modular::SessionShell> bindings_;
-  std::function<void(ViewId view_id)> on_attach_view_{[](ViewId) {}};
-  std::function<void(ViewId view_id)> on_detach_view_{[](ViewId) {}};
-  zx::duration detach_delay_{};
-
-  FXL_DISALLOW_COPY_AND_ASSIGN(SessionShellImpl);
-};
-
-
-// Cf. README.md for what this test does and how.
-class TestApp : public modular::testing::ComponentBase<void>
+class TestApp : public modular::testing::SessionShellBase
 {
  public:
   using ViewId = fuchsia::modular::ViewIdentifier;
 
   explicit TestApp(component::StartupContext* const startup_context)
-      : ComponentBase(startup_context) {
+      : SessionShellBase(startup_context) {
     TestInit(__FILE__);
 
-    startup_context->ConnectToEnvironmentService(
-        session_shell_context_.NewRequest());
     startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
-
-    session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
-    story_provider_state_watcher_.Watch(&story_provider_);
-
-    startup_context->outgoing().AddPublicService(
-        session_shell_impl_.GetHandler());
+    story_provider_state_watcher_.Watch(story_provider());
 
     // Until we use RequestStart() for the first time, there must be no calls on
     // the SessionShell service.
-    session_shell_impl_.set_on_attach_view(
+    session_shell_impl()->set_on_attach_view(
         [](ViewId) {
           Fail("AttachView() called without RequestStart().");
         });
-    session_shell_impl_.set_on_detach_view(
+    session_shell_impl()->set_on_detach_view(
         [](ViewId) {
           Fail("DetachView() called without RequestStart().");
         });
@@ -244,7 +186,7 @@
   TestPoint get_story_info_null_{"StoryProvider.GetStoryInfo() is null"};
 
   void TestStoryProvider_GetStoryInfo_Null() {
-    story_provider_->GetStoryInfo(
+    story_provider()->GetStoryInfo(
         "X", [this](fuchsia::modular::StoryInfoPtr story_info) {
           if (!story_info) {
             get_story_info_null_.Pass();
@@ -261,7 +203,7 @@
   TestPoint get_link_{"SessionShellContext.GetLink()"};
 
   void TestSessionShellContext_GetLink() {
-    session_shell_context_->GetLink(session_shell_link_.NewRequest());
+    session_shell_context()->GetLink(session_shell_link_.NewRequest());
     session_shell_link_->Get(
         nullptr, [this](std::unique_ptr<fuchsia::mem::Buffer> value) {
           get_link_.Pass();
@@ -277,7 +219,7 @@
   TestPoint previous_stories_{"StoryProvider.GetStories()"};
 
   void TestStoryProvider_GetStories() {
-    story_provider_->GetStories(
+    story_provider()->GetStories(
         nullptr, [this](fidl::VectorPtr<fuchsia::modular::StoryInfo> stories) {
           previous_stories_.Pass();
           TestStoryProvider_GetStoryInfo(std::move(stories));
@@ -338,7 +280,7 @@
   TestPoint story1_get_controller_{"Story1 GetController"};
 
   void TestStory1_GetController(fidl::StringPtr story_id) {
-    story_provider_->GetController(story_id, story_controller_.NewRequest());
+    story_provider()->GetController(story_id, story_controller_.NewRequest());
     story_controller_->GetInfo([this](fuchsia::modular::StoryInfo story_info,
                                       fuchsia::modular::StoryState state) {
       story1_get_controller_.Pass();
@@ -401,7 +343,7 @@
   TestPoint story2_get_controller_{"Story2 Get Controller"};
 
   void TestStory2_GetController(fidl::StringPtr story_id) {
-    story_provider_->GetController(story_id, story_controller_.NewRequest());
+    story_provider()->GetController(story_id, story_controller_.NewRequest());
     story_controller_->GetInfo([this](fuchsia::modular::StoryInfo story_info,
                                       fuchsia::modular::StoryState state) {
       story_info_ = std::move(story_info);
@@ -455,7 +397,7 @@
     puppet_master_->DeleteStory(story_info_.id,
                                 [this] { story2_delete_.Pass(); });
 
-    story_provider_->GetStoryInfo(
+    story_provider()->GetStoryInfo(
         story_info_.id, [this](fuchsia::modular::StoryInfoPtr info) {
           TestStory2_InfoAfterDeleteIsNull(std::move(info));
         });
@@ -481,7 +423,7 @@
 
   void TestStory3() {
     story_provider_state_watcher_.Reset();
-    story_provider_state_watcher_.Watch(&story_provider_);
+    story_provider_state_watcher_.Watch(story_provider());
 
     puppet_master_->ControlStory("story3", story_puppet_master_.NewRequest());
 
@@ -500,7 +442,7 @@
   TestPoint story3_get_controller_{"Story3 GetController"};
 
   void TestStory3_GetController(fidl::StringPtr story_id) {
-    story_provider_->GetController(story_id, story_controller_.NewRequest());
+    story_provider()->GetController(story_id, story_controller_.NewRequest());
     story_controller_->GetInfo([this](fuchsia::modular::StoryInfo story_info,
                                       fuchsia::modular::StoryState state) {
       story_info_ = std::move(story_info);
@@ -512,7 +454,7 @@
   TestPoint story3_previous_stories_{"Story3 GetGetStories"};
 
   void TestStory3_GetStories() {
-    story_provider_->GetStories(
+    story_provider()->GetStories(
         nullptr, [this](fidl::VectorPtr<fuchsia::modular::StoryInfo> stories) {
           // Since this is a kind-of-proto story, it shouldn't appear in
           // GetStories calls. Note that we still expect 1 story to be here
@@ -561,7 +503,7 @@
     puppet_master_->DeleteStory(story_info_.id,
                                 [this] { story3_delete_.Pass(); });
 
-    story_provider_->GetStoryInfo(
+    story_provider()->GetStoryInfo(
         story_info_.id, [this](fuchsia::modular::StoryInfoPtr info) {
           TestStory3_InfoAfterDeleteIsNull(std::move(info));
         });
@@ -614,7 +556,7 @@
   TestPoint story4_attach_view_{"Story4 attach View"};
 
   void TestStory4_Run() {
-    story_provider_->GetController("story4", story_controller_.NewRequest());
+    story_provider()->GetController("story4", story_controller_.NewRequest());
     story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info,
                                       fuchsia::modular::StoryState state) {
       story_info_ = std::move(info);
@@ -626,7 +568,7 @@
     // Start and show the new story using RequestStart().
     story_controller_->RequestStart();
 
-    session_shell_impl_.set_on_attach_view(
+    session_shell_impl()->set_on_attach_view(
         [this](ViewId) {
           story4_attach_view_.Pass();
         });
@@ -644,7 +586,7 @@
   TestPoint story4_stop_{"Story4 Stop"};
 
   void TestStory4_Stop() {
-    session_shell_impl_.set_on_detach_view(
+    session_shell_impl()->set_on_detach_view(
         [this](ViewId) {
           story4_detach_view_.Pass();
         });
@@ -663,7 +605,7 @@
     puppet_master_->DeleteStory(story_info_.id,
                                 [this] { story4_delete_.Pass(); });
 
-    story_provider_->GetStoryInfo(
+    story_provider()->GetStoryInfo(
         story_info_.id, [this](fuchsia::modular::StoryInfoPtr info) {
           TestStory4_InfoAfterDeleteIsNull(std::move(info));
         });
@@ -717,7 +659,7 @@
   TestPoint story5_attach_view_{"Story5 attach View"};
 
   void TestStory5_Run() {
-    story_provider_->GetController("story5", story_controller_.NewRequest());
+    story_provider()->GetController("story5", story_controller_.NewRequest());
 
     story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info,
                                       fuchsia::modular::StoryState state) {
@@ -730,7 +672,7 @@
     // Start and show the new story using RequestStart().
     story_controller_->RequestStart();
 
-    session_shell_impl_.set_on_attach_view(
+    session_shell_impl()->set_on_attach_view(
         [this](ViewId) {
           story5_attach_view_.Pass();
         });
@@ -750,9 +692,9 @@
     // Ignore the detach view. The delay is larger than the timeout for the
     // whole test configured in dev_base_shell.cc, so an attempt to wait for
     // this timeout would fail the whole test.
-    session_shell_impl_.set_detach_delay(
+    session_shell_impl()->set_detach_delay(
         zx::msec(modular::testing::kTestTimeoutMilliseconds * 2));
-    session_shell_impl_.set_on_detach_view([](ViewId) {});
+    session_shell_impl()->set_on_detach_view([](ViewId) {});
 
     story_controller_->Stop([this] {
       TeardownStoryController();
@@ -768,7 +710,7 @@
     puppet_master_->DeleteStory(story_info_.id,
                                 [this] { story5_delete_.Pass(); });
 
-    story_provider_->GetStoryInfo(
+    story_provider()->GetStoryInfo(
         story_info_.id, [this](fuchsia::modular::StoryInfoPtr info) {
           TestStory5_InfoAfterDeleteIsNull(std::move(info));
         });
@@ -821,7 +763,7 @@
   TestPoint story6_attach_view_{"Story6 attach View"};
 
   void TestStory6_Run() {
-    story_provider_->GetController("story6", story_controller_.NewRequest());
+    story_provider()->GetController("story6", story_controller_.NewRequest());
 
     story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info,
                                       fuchsia::modular::StoryState state) {
@@ -834,7 +776,7 @@
     // Start and show the new story using RequestStart().
     story_controller_->RequestStart();
 
-    session_shell_impl_.set_on_attach_view(
+    session_shell_impl()->set_on_attach_view(
         [this](ViewId) {
           story6_attach_view_.Pass();
         });
@@ -850,8 +792,8 @@
 
   void TestStory6_Logout() {
     // If we get a DetachView() call during logout, that's a failure.
-    session_shell_impl_.set_detach_delay(zx::sec(0));
-    session_shell_impl_.set_on_detach_view(
+    session_shell_impl()->set_detach_delay(zx::sec(0));
+    session_shell_impl()->set_on_detach_view(
         [](ViewId) {
           Fail("DetachView() Received on Logout");
         });
@@ -861,11 +803,8 @@
 
   void TeardownStoryController() { story_controller_.Unbind(); }
 
-  SessionShellImpl session_shell_impl_;
   StoryProviderStateWatcherImpl story_provider_state_watcher_;
 
-  fuchsia::modular::SessionShellContextPtr session_shell_context_;
-  fuchsia::modular::StoryProviderPtr story_provider_;
   fuchsia::modular::PuppetMasterPtr puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
   fuchsia::modular::StoryControllerPtr story_controller_;
diff --git a/tests/story_shell/BUILD.gn b/tests/story_shell/BUILD.gn
index 60440f5..2537f77 100644
--- a/tests/story_shell/BUILD.gn
+++ b/tests/story_shell/BUILD.gn
@@ -74,7 +74,8 @@
     "//peridot/lib/fidl:clone",
     "//peridot/lib/rapidjson",
     "//peridot/public/lib/integration_testing/cpp",
-    "//peridot/lib/testing:component_base",
+    "//peridot/lib/testing:component_main",
+    "//peridot/lib/testing:session_shell_base",
     "//peridot/public/fidl/fuchsia.modular",
     "//peridot/public/lib/app_driver/cpp",
     "//peridot/public/lib/context/cpp:formatting",
diff --git a/tests/story_shell/story_shell_test_session_shell.cc b/tests/story_shell/story_shell_test_session_shell.cc
index 793b637..0baa65c 100644
--- a/tests/story_shell/story_shell_test_session_shell.cc
+++ b/tests/story_shell/story_shell_test_session_shell.cc
@@ -23,7 +23,8 @@
 #include "peridot/lib/common/names.h"
 #include "peridot/lib/fidl/clone.h"
 #include "peridot/lib/rapidjson/rapidjson.h"
-#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/component_main.h"
+#include "peridot/lib/testing/session_shell_base.h"
 #include "peridot/public/lib/integration_testing/cpp/reporting.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 #include "peridot/tests/common/defs.h"
@@ -31,6 +32,7 @@
 
 using modular::testing::Get;
 using modular::testing::Put;
+using modular::testing::Signal;
 using modular::testing::TestPoint;
 
 namespace {
@@ -39,19 +41,14 @@
 const char kStoryName2[] = "story2";
 
 // Cf. README.md for what this test does and how.
-class TestApp : public modular::testing::ComponentBase<void>,
+class TestApp : public modular::testing::SessionShellBase,
                 fuchsia::modular::SessionShellPresentationProvider {
  public:
   explicit TestApp(component::StartupContext* const startup_context)
-      : ComponentBase(startup_context) {
+      : SessionShellBase(startup_context) {
     TestInit(__FILE__);
 
-    puppet_master_ =
-        startup_context
-            ->ConnectToEnvironmentService<fuchsia::modular::PuppetMaster>();
-    session_shell_context_ = startup_context->ConnectToEnvironmentService<
-        fuchsia::modular::SessionShellContext>();
-    session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
+    startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
 
     startup_context->outgoing()
         .AddPublicService<fuchsia::modular::SessionShellPresentationProvider>(
@@ -146,7 +143,7 @@
   TestPoint story1_run1_{"Story1 Run1"};
 
   void Story1_Run1() {
-    story_provider_->GetController(kStoryName1, story_controller_.NewRequest());
+    story_provider()->GetController(kStoryName1, story_controller_.NewRequest());
 
     // TODO(jphsiao|vardhan): remodel this |proceed_after_6| style of
     // continuation to use Futures instead.
@@ -162,8 +159,7 @@
     Get("root:one:two manifest", proceed_after_6);
     Get("root:one:two ordering", proceed_after_6);
 
-    fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> story_view;
-    story_controller_->Start(story_view.NewRequest());
+    story_controller_->RequestStart();
   }
 
   void Story1_Stop1() {
@@ -185,8 +181,7 @@
     Get("root:one:two manifest", proceed_after_6);
     Get("root:one:two ordering", proceed_after_6);
 
-    fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> story_view;
-    story_controller_->Start(story_view.NewRequest());
+    story_controller_->RequestStart();
   }
 
   void Story1_Stop2() {
@@ -244,9 +239,8 @@
     Get("root:one:two manifest", proceed_after_5);
     Get("root:one:two ordering", proceed_after_5);
 
-    story_provider_->GetController(kStoryName2, story_controller_.NewRequest());
-    fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> story_view;
-    story_controller_->Start(story_view.NewRequest());
+    story_provider()->GetController(kStoryName2, story_controller_.NewRequest());
+    story_controller_->RequestStart();
   }
 
   void Story2_Stop1() {
@@ -267,8 +261,7 @@
     Get("root:one:two manifest", proceed_after_5);
     Get("root:one:two ordering", proceed_after_5);
 
-    fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> story_view;
-    story_controller_->Start(story_view.NewRequest());
+    story_controller_->RequestStart();
   }
 
   bool end_of_story2_{};
@@ -283,14 +276,12 @@
   void MaybeLogout() {
     if (story1_presentation_request_received_ &&
         story2_presentation_request_received_ && end_of_story2_) {
-      session_shell_context_->Logout();
+      Signal(modular::testing::kTestShutdown);
     }
   }
 
   fuchsia::modular::PuppetMasterPtr puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
-  fuchsia::modular::SessionShellContextPtr session_shell_context_;
-  fuchsia::modular::StoryProviderPtr story_provider_;
   fuchsia::modular::StoryControllerPtr story_controller_;
   fidl::BindingSet<fuchsia::modular::SessionShellPresentationProvider>
       presentation_provider_bindings_;
diff --git a/tests/suggestion/BUILD.gn b/tests/suggestion/BUILD.gn
index a33d4ea..ec5a925 100644
--- a/tests/suggestion/BUILD.gn
+++ b/tests/suggestion/BUILD.gn
@@ -55,7 +55,8 @@
   deps = [
     ":defs",
     "//peridot/public/lib/integration_testing/cpp",
-    "//peridot/lib/testing:component_base",
+    "//peridot/lib/testing:component_main",
+    "//peridot/lib/testing:session_shell_base",
     "//peridot/public/fidl/fuchsia.modular",
     "//peridot/tests/common:defs",
   ]
diff --git a/tests/suggestion/suggestion_test_session_shell.cc b/tests/suggestion/suggestion_test_session_shell.cc
index d2f9a53..f6488bf 100644
--- a/tests/suggestion/suggestion_test_session_shell.cc
+++ b/tests/suggestion/suggestion_test_session_shell.cc
@@ -10,7 +10,8 @@
 #include <lib/fxl/logging.h>
 #include <lib/fxl/macros.h>
 
-#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/component_main.h"
+#include "peridot/lib/testing/session_shell_base.h"
 #include "peridot/public/lib/integration_testing/cpp/reporting.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 #include "peridot/tests/common/defs.h"
@@ -26,20 +27,15 @@
 
 // Cf. README.md for what this test does and how.
 class TestApp : fuchsia::modular::NextListener,
-                public modular::testing::ComponentBase<void> {
+                public modular::testing::SessionShellBase {
  public:
   TestApp(component::StartupContext* const startup_context)
-      : ComponentBase(startup_context) {
+      : SessionShellBase(startup_context) {
     TestInit(__FILE__);
 
-    puppet_master_ =
-        startup_context
-            ->ConnectToEnvironmentService<fuchsia::modular::PuppetMaster>();
-    session_shell_context_ = startup_context->ConnectToEnvironmentService<
-        fuchsia::modular::SessionShellContext>();
+    startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
 
-    session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
-    session_shell_context_->GetSuggestionProvider(
+    session_shell_context()->GetSuggestionProvider(
         suggestion_provider_.NewRequest());
 
     suggestion_provider_->SubscribeToNext(
@@ -77,12 +73,12 @@
   }
 
   void StartStory() {
-    story_provider_->GetController(kStoryName, story_controller_.NewRequest());
+    story_provider()->GetController(kStoryName, story_controller_.NewRequest());
     story_controller_.set_error_handler([](zx_status_t status) {
       FXL_LOG(ERROR) << "Story controller for story " << kStoryName
                      << " died. Does this story exist?";
     });
-    story_controller_->Start(view_owner_.NewRequest());
+    story_controller_->RequestStart();
   }
 
   TestPoint received_suggestion_{
@@ -111,9 +107,6 @@
 
   fuchsia::modular::PuppetMasterPtr puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
-  fuchsia::ui::viewsv1token::ViewOwnerPtr view_owner_;
-  fuchsia::modular::SessionShellContextPtr session_shell_context_;
-  fuchsia::modular::StoryProviderPtr story_provider_;
   fuchsia::modular::StoryControllerPtr story_controller_;
   fuchsia::modular::SuggestionProviderPtr suggestion_provider_;
   fidl::BindingSet<fuchsia::modular::NextListener>
diff --git a/tests/trigger/BUILD.gn b/tests/trigger/BUILD.gn
index c41deb0..d04f6ee 100644
--- a/tests/trigger/BUILD.gn
+++ b/tests/trigger/BUILD.gn
@@ -35,7 +35,8 @@
     "//garnet/public/lib/fxl",
     "//peridot/lib/rapidjson",
     "//peridot/public/lib/integration_testing/cpp",
-    "//peridot/lib/testing:component_base",
+    "//peridot/lib/testing:component_main",
+    "//peridot/lib/testing:session_shell_base",
     "//peridot/public/fidl/fuchsia.modular",
     "//peridot/tests/common:defs",
   ]
diff --git a/tests/trigger/trigger_test_session_shell.cc b/tests/trigger/trigger_test_session_shell.cc
index b26c38e..00bfa99 100644
--- a/tests/trigger/trigger_test_session_shell.cc
+++ b/tests/trigger/trigger_test_session_shell.cc
@@ -13,31 +13,28 @@
 #include <lib/fxl/logging.h>
 #include <lib/fxl/macros.h>
 
-#include "peridot/lib/testing/component_base.h"
+#include "peridot/lib/testing/component_main.h"
+#include "peridot/lib/testing/session_shell_base.h"
 #include "peridot/public/lib/integration_testing/cpp/reporting.h"
 #include "peridot/public/lib/integration_testing/cpp/testing.h"
 #include "peridot/tests/common/defs.h"
 #include "peridot/tests/trigger/defs.h"
 
 using ::modular::testing::Await;
+using ::modular::testing::Signal;
 using ::modular::testing::TestPoint;
 
 namespace {
 
 const char kStoryName[] = "story";
 
-class TestApp : public modular::testing::ComponentBase<void> {
+class TestApp : public modular::testing::SessionShellBase {
  public:
   TestApp(component::StartupContext* const startup_context)
-      : ComponentBase(startup_context), weak_ptr_factory_(this) {
+      : SessionShellBase(startup_context), weak_ptr_factory_(this) {
     TestInit(__FILE__);
 
-    puppet_master_ =
-        startup_context
-            ->ConnectToEnvironmentService<fuchsia::modular::PuppetMaster>();
-    session_shell_context_ = startup_context->ConnectToEnvironmentService<
-        fuchsia::modular::SessionShellContext>();
-    session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
+    startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
 
     CreateStory();
   }
@@ -45,8 +42,6 @@
   ~TestApp() override = default;
 
  private:
-  using TestPoint = modular::testing::TestPoint;
-
   TestPoint story_create_{"Created story."};
 
   void CreateStory() {
@@ -68,11 +63,6 @@
           story_create_.Pass();
           StartStory();
         });
-    async::PostDelayedTask(
-        async_get_default_dispatcher(),
-        callback::MakeScoped(weak_ptr_factory_.GetWeakPtr(),
-                             [this] { session_shell_context_->Logout(); }),
-        zx::msec(kTimeoutMilliseconds));
   }
 
   TestPoint got_queue_token_{"Got message queue token."};
@@ -81,13 +71,13 @@
   TestPoint agent_executed_delete_task_{
       "fuchsia::modular::Agent executed message queue task."};
   void StartStory() {
-    story_provider_->GetController(kStoryName, story_controller_.NewRequest());
+    story_provider()->GetController(kStoryName, story_controller_.NewRequest());
     story_controller_.set_error_handler([this](zx_status_t status) {
       FXL_LOG(ERROR) << "Story controller for story " << kStoryName
                      << " died. Does this story exist?";
     });
 
-    story_controller_->Start(story_view_.NewRequest());
+    story_controller_->RequestStart();
 
     // Retrieve the message queue token for the messsage queue that the module
     // created.
@@ -107,7 +97,7 @@
               // test store.
               Await(value, [this] {
                 agent_executed_delete_task_.Pass();
-                session_shell_context_->Logout();
+                Signal(modular::testing::kTestShutdown);
               });
             });
           });
@@ -116,12 +106,8 @@
 
   fuchsia::modular::PuppetMasterPtr puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
-  fuchsia::modular::SessionShellContextPtr session_shell_context_;
-  fuchsia::modular::StoryProviderPtr story_provider_;
   fuchsia::modular::StoryControllerPtr story_controller_;
 
-  fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> story_view_;
-
   fxl::WeakPtrFactory<TestApp> weak_ptr_factory_;
 
   FXL_DISALLOW_COPY_AND_ASSIGN(TestApp);