Keep main loop running until FilesystemCopierAction under test has stopped.

This CL fixes FilesystemCopierActionTest.RunAsRootTerminateEarlyTest
such that it does not prematurely terminate the glib main loop before
FilesystemCopierAction::AsyncReadReadyCallback is invoked upon the
cancellation of the action.

BUG=chromium-os:34448
TEST=Run unit tests repeatedly with glib 2.30 and 2.32.

Change-Id: I26c15811ee41d16df731c4acbe24ed48c8739dde
Reviewed-on: https://gerrit.chromium.org/gerrit/34976
Commit-Ready: Ben Chan <benchan@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
Reviewed-by: Gilad Arnold <garnold@chromium.org>
diff --git a/filesystem_copier_action.cc b/filesystem_copier_action.cc
index c9be962..1561f93 100644
--- a/filesystem_copier_action.cc
+++ b/filesystem_copier_action.cc
@@ -128,6 +128,10 @@
   }
 }
 
+bool FilesystemCopierAction::IsCleanupPending() const {
+  return (src_stream_ != NULL);
+}
+
 void FilesystemCopierAction::Cleanup(ActionExitCode code) {
   for (int i = 0; i < 2; i++) {
     g_object_unref(canceller_[i]);
diff --git a/filesystem_copier_action.h b/filesystem_copier_action.h
index 9bd9b20..366ae40 100644
--- a/filesystem_copier_action.h
+++ b/filesystem_copier_action.h
@@ -46,6 +46,12 @@
   void PerformAction();
   void TerminateProcessing();
 
+  // Used for testing. Return true if Cleanup() has not yet been called due
+  // to a callback upon the completion or cancellation of the copier action.
+  // A test should wait until IsCleanupPending() returns false before
+  // terminating the glib main loop.
+  bool IsCleanupPending() const;
+
   // Used for testing, so we can copy from somewhere other than root
   void set_copy_source(const std::string& path) { copy_source_ = path; }
 
diff --git a/filesystem_copier_action_unittest.cc b/filesystem_copier_action_unittest.cc
index 25e9484..f7bb618 100644
--- a/filesystem_copier_action_unittest.cc
+++ b/filesystem_copier_action_unittest.cc
@@ -43,10 +43,18 @@
 
 class FilesystemCopierActionTestDelegate : public ActionProcessorDelegate {
  public:
-  FilesystemCopierActionTestDelegate() : ran_(false), code_(kActionCodeError) {}
+  FilesystemCopierActionTestDelegate(GMainLoop* loop,
+                                     FilesystemCopierAction* action)
+      : loop_(loop), action_(action), ran_(false), code_(kActionCodeError) {}
   void ExitMainLoop() {
-    while (g_main_context_pending(NULL)) {
-      g_main_context_iteration(NULL, false);
+    GMainContext* context = g_main_loop_get_context(loop_);
+    // We cannot use g_main_context_pending() alone to determine if it is safe
+    // to quit the main loop here becasuse g_main_context_pending() may return
+    // FALSE when g_input_stream_read_async() in FilesystemCopierAction has
+    // been cancelled but the callback has not yet been invoked.
+    while (g_main_context_pending(context) || action_->IsCleanupPending()) {
+      g_main_context_iteration(context, false);
+      g_usleep(100);
     }
     g_main_loop_quit(loop_);
   }
@@ -64,13 +72,11 @@
       code_ = code;
     }
   }
-  void set_loop(GMainLoop* loop) {
-    loop_ = loop;
-  }
-  bool ran() { return ran_; }
-  ActionExitCode code() { return code_; }
+  bool ran() const { return ran_; }
+  ActionExitCode code() const { return code_; }
  private:
   GMainLoop* loop_;
+  FilesystemCopierAction* action_;
   bool ran_;
   ActionExitCode code_;
 };
@@ -188,9 +194,6 @@
   }
 
   ActionProcessor processor;
-  FilesystemCopierActionTestDelegate delegate;
-  delegate.set_loop(loop);
-  processor.set_delegate(&delegate);
 
   ObjectFeederAction<InstallPlan> feeder_action;
   FilesystemCopierAction copier_action(use_kernel_partition,
@@ -200,6 +203,8 @@
   BondActions(&feeder_action, &copier_action);
   BondActions(&copier_action, &collector_action);
 
+  FilesystemCopierActionTestDelegate delegate(loop, &copier_action);
+  processor.set_delegate(&delegate);
   processor.EnqueueAction(&feeder_action);
   processor.EnqueueAction(&copier_action);
   processor.EnqueueAction(&collector_action);