[modular] Allow closing StoryPuppetMaster connections before Execute() has completely finished.

Some clients want to fire-and-forget the Execute() call. This allows
them to do it.

TEST=puppet_master_impl_unittest

Change-Id: I395bc8d5ab172597ed34a393e18efec2245e12bf
diff --git a/bin/sessionmgr/puppet_master/puppet_master_impl.cc b/bin/sessionmgr/puppet_master/puppet_master_impl.cc
index 68c466c..7d10e05 100644
--- a/bin/sessionmgr/puppet_master/puppet_master_impl.cc
+++ b/bin/sessionmgr/puppet_master/puppet_master_impl.cc
@@ -30,15 +30,13 @@
     fidl::StringPtr story_name,
     fidl::InterfaceRequest<fuchsia::modular::StoryPuppetMaster> request) {
   auto controller = std::make_unique<StoryPuppetMasterImpl>(
-      story_name, session_storage_, executor_);
+      story_name, &operations_, session_storage_, executor_);
   story_puppet_masters_.AddBinding(std::move(controller), std::move(request));
 }
 
 void PuppetMasterImpl::DeleteStory(fidl::StringPtr story_name,
                                    DeleteStoryCallback done) {
-  session_storage_->DeleteStory(story_name)->Then([done = std::move(done)] {
-    done();
-  });
+  session_storage_->DeleteStory(story_name)->Then(std::move(done));
 }
 
 void PuppetMasterImpl::GetStories(GetStoriesCallback done) {
diff --git a/bin/sessionmgr/puppet_master/puppet_master_impl.h b/bin/sessionmgr/puppet_master/puppet_master_impl.h
index 6260956..d124bd9 100644
--- a/bin/sessionmgr/puppet_master/puppet_master_impl.h
+++ b/bin/sessionmgr/puppet_master/puppet_master_impl.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include <fuchsia/modular/cpp/fidl.h>
+#include <lib/async/cpp/operation.h>
 #include <lib/fidl/cpp/binding_set.h>
 #include <lib/fxl/memory/weak_ptr.h>
 
@@ -51,6 +52,8 @@
                    std::unique_ptr<StoryPuppetMasterImpl>>
       story_puppet_masters_;
 
+  OperationCollection operations_;
+
   FXL_DISALLOW_COPY_AND_ASSIGN(PuppetMasterImpl);
 };
 
diff --git a/bin/sessionmgr/puppet_master/puppet_master_impl_unittest.cc b/bin/sessionmgr/puppet_master/puppet_master_impl_unittest.cc
index 24176e3..5e1a963 100644
--- a/bin/sessionmgr/puppet_master/puppet_master_impl_unittest.cc
+++ b/bin/sessionmgr/puppet_master/puppet_master_impl_unittest.cc
@@ -74,10 +74,7 @@
   EXPECT_EQ(1, executor_.execute_count());
   EXPECT_EQ(fuchsia::modular::ExecuteStatus::OK, result.status);
 
-  // Executor gets the internal story id. It is auto-generated, so we simply see
-  // that we got one.
-  EXPECT_TRUE(executor_.last_story_id() &&
-              executor_.last_story_id()->size() > 0);
+  EXPECT_EQ("foo", executor_.last_story_id());
   ASSERT_EQ(3u, executor_.last_commands().size());
   EXPECT_EQ("one",
             executor_.last_commands().at(0).remove_mod().mod_name->at(0));
@@ -87,6 +84,32 @@
             executor_.last_commands().at(2).remove_mod().mod_name->at(0));
 }
 
+TEST_F(PuppetMasterTest, CommandsAreSentToExecutor_IfWeCloseStoryChannel) {
+  // We're going to call Execute(), and then immediately drop the
+  // StoryPuppetMaster connection. We won't get a callback, but we still
+  // expect that the commands are executed.
+  auto story = ControlStory("foo");
+
+  // Enqueue some commands. Do this twice and show that all the commands show
+  // up as one batch.
+  fidl::VectorPtr<fuchsia::modular::StoryCommand> commands;
+  commands.push_back(MakeRemoveModCommand("one"));
+  story->Enqueue(std::move(commands));
+
+  fuchsia::modular::ExecuteResult result;
+  bool callback_called{false};
+  // Instruct our test executor to return an OK status.
+  executor_.SetExecuteReturnResult(fuchsia::modular::ExecuteStatus::OK,
+                                   nullptr);
+  story->Execute([&](fuchsia::modular::ExecuteResult r) {
+    callback_called = true;
+  });
+  story.Close(ZX_OK);
+  RunLoopUntil([&]() { return executor_.execute_count() > 0; });
+  EXPECT_FALSE(callback_called);
+  EXPECT_EQ(1, executor_.execute_count());
+}
+
 TEST_F(PuppetMasterTest, MultipleExecuteCalls) {
   // Create a new story, and then execute some new commands on the same
   // connection. We should see that the StoryCommandExecutor receives the story
diff --git a/bin/sessionmgr/puppet_master/story_puppet_master_impl.cc b/bin/sessionmgr/puppet_master/story_puppet_master_impl.cc
index 8593aaf..588a5f4 100644
--- a/bin/sessionmgr/puppet_master/story_puppet_master_impl.cc
+++ b/bin/sessionmgr/puppet_master/story_puppet_master_impl.cc
@@ -75,11 +75,12 @@
 }  // namespace
 
 StoryPuppetMasterImpl::StoryPuppetMasterImpl(
-    fidl::StringPtr story_name, SessionStorage* const session_storage,
-    StoryCommandExecutor* const executor)
+    fidl::StringPtr story_name, OperationContainer* const operations,
+    SessionStorage* const session_storage, StoryCommandExecutor* const executor)
     : story_name_(story_name),
       session_storage_(session_storage),
-      executor_(executor) {
+      executor_(executor),
+      operations_(operations) {
   FXL_DCHECK(session_storage != nullptr);
   FXL_DCHECK(executor != nullptr);
 }
@@ -98,9 +99,9 @@
 
 void StoryPuppetMasterImpl::Execute(ExecuteCallback done) {
   // First ensure that the story is created.
-  operations_.Add(new ExecuteOperation(session_storage_, executor_, story_name_,
-                                       std::move(story_options_),
-                                       std::move(enqueued_commands_), done));
+  operations_->Add(new ExecuteOperation(session_storage_, executor_,
+                                        story_name_, std::move(story_options_),
+                                        std::move(enqueued_commands_), done));
 }
 
 void StoryPuppetMasterImpl::SetCreateOptions(
diff --git a/bin/sessionmgr/puppet_master/story_puppet_master_impl.h b/bin/sessionmgr/puppet_master/story_puppet_master_impl.h
index 49c6d93..40d3ecd 100644
--- a/bin/sessionmgr/puppet_master/story_puppet_master_impl.h
+++ b/bin/sessionmgr/puppet_master/story_puppet_master_impl.h
@@ -22,6 +22,7 @@
 class StoryPuppetMasterImpl : public fuchsia::modular::StoryPuppetMaster {
  public:
   StoryPuppetMasterImpl(fidl::StringPtr story_name,
+                        OperationContainer* operations,
                         SessionStorage* session_storage,
                         StoryCommandExecutor* executor);
   ~StoryPuppetMasterImpl() override;
@@ -43,7 +44,7 @@
 
   std::vector<fuchsia::modular::StoryCommand> enqueued_commands_;
 
-  OperationCollection operations_;
+  OperationContainer* const operations_;  // Not owned.
 
   fuchsia::modular::StoryOptions story_options_;