Refactor disk_mount_manager_unittest by replacing gmock with a dedicated mock class of DiskMountManagerObserver.

This change is based on
https://codereview.chromium.org/2292473002/
which was once merged but reverted by
https://codereview.chromium.org/2340433002/

BUG=641943

Review-Url: https://codereview.chromium.org/2333983004
Cr-Commit-Position: refs/heads/master@{#418478}
diff --git a/chromeos/disks/disk_mount_manager_unittest.cc b/chromeos/disks/disk_mount_manager_unittest.cc
index 314f9d2..3bb583e 100644
--- a/chromeos/disks/disk_mount_manager_unittest.cc
+++ b/chromeos/disks/disk_mount_manager_unittest.cc
@@ -6,22 +6,23 @@
 #include <stdint.h>
 
 #include "base/bind.h"
+#include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_cros_disks_client.h"
 #include "chromeos/disks/disk_mount_manager.h"
-#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using base::MakeUnique;
+using base::StringPrintf;
 using chromeos::disks::DiskMountManager;
 using chromeos::CrosDisksClient;
 using chromeos::DBusThreadManager;
 using chromeos::FakeCrosDisksClient;
-using testing::_;
-using testing::Field;
-using testing::InSequence;
-using testing::InvokeWithoutArgs;
+using chromeos::MountType;
+using chromeos::disks::MountCondition;
 
 namespace {
 
@@ -152,31 +153,259 @@
   },
 };
 
-// Mocks DiskMountManager observer.
-class MockDiskMountManagerObserver : public DiskMountManager::Observer {
- public:
-  virtual ~MockDiskMountManagerObserver() {}
-
-  MOCK_METHOD2(OnDiskEvent, void(DiskMountManager::DiskEvent event,
-                                 const DiskMountManager::Disk* disk));
-  MOCK_METHOD2(OnDeviceEvent, void(DiskMountManager::DeviceEvent event,
-                                   const std::string& device_path));
-  MOCK_METHOD3(OnMountEvent,
-      void(DiskMountManager::MountEvent event,
-           chromeos::MountError error_code,
-           const DiskMountManager::MountPointInfo& mount_point));
-  MOCK_METHOD3(OnFormatEvent,
-      void(DiskMountManager::FormatEvent event,
-           chromeos::FormatError error_code,
-           const std::string& device_path));
+// Represents which function in |DiskMountManager::Observer| was invoked.
+enum ObserverEventType {
+  DEVICE_EVENT,  // OnDeviceEvent()
+  DISK_EVENT,    // OnDiskEvent()
+  FORMAT_EVENT,  // OnFormatEvent()
+  MOUNT_EVENT    // OnMountEvent()
 };
 
-// Expect |is_read_only| value of a disk object keyed by |source_path|.
-void ExpectDiskReadOnly(const DiskMountManager* manager,
-                        const std::string& source_path,
-                        bool expected) {
-  EXPECT_EQ(expected,
-            manager->disks().find(source_path)->second->is_read_only());
+// Represents every event notified to |DiskMountManager::Observer|.
+struct ObserverEvent {
+ public:
+  virtual ObserverEventType type() const = 0;
+};
+
+// Represents an invocation of |DiskMountManager::Observer::OnDeviceEvent()|.
+struct DeviceEvent : public ObserverEvent {
+  DiskMountManager::DeviceEvent event;
+  std::string device_path;
+
+  DeviceEvent() {}
+
+  DeviceEvent(DiskMountManager::DeviceEvent event,
+              const std::string& device_path)
+      : event(event), device_path(device_path) {}
+
+  ObserverEventType type() const override { return DEVICE_EVENT; }
+
+  bool operator==(const DeviceEvent& other) const {
+    return event == other.event && device_path == other.device_path;
+  }
+
+  std::string DebugString() const {
+    return StringPrintf("OnDeviceEvent(%d, %s)", event, device_path.c_str());
+  }
+};
+
+// Represents an invocation of |DiskMountManager::Observer::OnDiskEvent()|.
+struct DiskEvent : public ObserverEvent {
+  DiskMountManager::DiskEvent event;
+  std::unique_ptr<DiskMountManager::Disk> disk;
+
+  DiskEvent(DiskMountManager::DiskEvent event,
+            const DiskMountManager::Disk& disk)
+      : event(event),
+        disk(std::unique_ptr<DiskMountManager::Disk>(
+            new DiskMountManager::Disk(disk))) {}
+
+  DiskEvent(DiskEvent&& other)
+      : event(other.event), disk(std::move(other.disk)) {}
+
+  ObserverEventType type() const override { return DISK_EVENT; }
+
+  bool operator==(const DiskEvent& other) const {
+    return event == other.event && disk == other.disk;
+  }
+
+  std::string DebugString() const {
+    return StringPrintf("OnDiskEvent(event=%d, device_path=%s, mount_path=%s",
+                        event, disk->device_path().c_str(),
+                        disk->mount_path().c_str());
+  }
+};
+
+// Represents an invocation of |DiskMountManager::Observer::OnFormatEvent()|.
+struct FormatEvent : public ObserverEvent {
+  DiskMountManager::FormatEvent event;
+  chromeos::FormatError error_code;
+  std::string device_path;
+
+  FormatEvent() {}
+  FormatEvent(DiskMountManager::FormatEvent event,
+              chromeos::FormatError error_code,
+              const std::string& device_path)
+      : event(event), error_code(error_code), device_path(device_path) {}
+
+  ObserverEventType type() const override { return FORMAT_EVENT; }
+
+  bool operator==(const FormatEvent& other) const {
+    return event == other.event && error_code == other.error_code &&
+           device_path == other.device_path;
+  }
+
+  std::string DebugString() const {
+    return StringPrintf("OnFormatEvent(%d, %d, %s)", event, error_code,
+                        device_path.c_str());
+  }
+};
+
+// Represents an invocation of |DiskMountManager::Observer::OnMountEvent()|.
+struct MountEvent : public ObserverEvent {
+  DiskMountManager::MountEvent event;
+  chromeos::MountError error_code;
+  DiskMountManager::MountPointInfo mount_point;
+
+  // Not passed to callback, but read by handlers. So it's captured upon
+  // callback.
+  std::unique_ptr<DiskMountManager::Disk> disk;
+
+  MountEvent(MountEvent&& other)
+      : event(other.event),
+        error_code(other.error_code),
+        mount_point(other.mount_point),
+        disk(std::move(other.disk)) {}
+  MountEvent(DiskMountManager::MountEvent event,
+             chromeos::MountError error_code,
+             const DiskMountManager::MountPointInfo& mount_point,
+             const DiskMountManager::Disk& disk)
+      : event(event),
+        error_code(error_code),
+        mount_point(mount_point),
+        disk(new DiskMountManager::Disk(disk)) {}
+
+  ObserverEventType type() const override { return MOUNT_EVENT; }
+
+  bool operator==(const MountEvent& other) const;
+
+  std::string DebugString() const {
+    return StringPrintf("OnMountEvent(%d, %d, %s, %s, %d, %d)", event,
+                        error_code, mount_point.source_path.c_str(),
+                        mount_point.mount_path.c_str(), mount_point.mount_type,
+                        mount_point.mount_condition);
+  }
+};
+
+// A mock |Observer| class which records all invocation of the methods invoked
+// from DiskMountManager and all the arguments passed to them.
+class MockDiskMountManagerObserver : public DiskMountManager::Observer {
+ public:
+  MockDiskMountManagerObserver(const DiskMountManager* manager)
+      : manager_(manager) {}
+  ~MockDiskMountManagerObserver() override {}
+
+  // Mock notify methods.
+  void OnDeviceEvent(DiskMountManager::DeviceEvent event,
+                     const std::string& device_path) override {
+    events_.push_back(MakeUnique<DeviceEvent>(event, device_path));
+  }
+
+  void OnDiskEvent(DiskMountManager::DiskEvent event,
+                   const DiskMountManager::Disk* disk) override {
+    // Take a snapshot (copy) of the Disk object at the time of invocation for
+    // later verification.
+    events_.push_back(MakeUnique<DiskEvent>(event, *disk));
+  }
+
+  void OnFormatEvent(DiskMountManager::FormatEvent event,
+                     chromeos::FormatError error_code,
+                     const std::string& device_path) override {
+    events_.push_back(MakeUnique<FormatEvent>(event, error_code, device_path));
+  }
+
+  void OnMountEvent(
+      DiskMountManager::MountEvent event,
+      chromeos::MountError error_code,
+      const DiskMountManager::MountPointInfo& mount_point) override {
+    // Take a snapshot (copy) of a Disk object at the time of invocation.
+    // It can be verified later besides the arguments.
+    events_.push_back(MakeUnique<MountEvent>(
+        event, error_code, mount_point,
+        *manager_->disks().find(mount_point.source_path)->second));
+  }
+
+  // Gets invocation history to be verified by testcases.
+  // Verifies if the |index|th invocation is OnDeviceEvent() and returns
+  // details.
+  const DeviceEvent& GetDeviceEvent(size_t index) {
+    DCHECK_GT(events_.size(), index);
+    DCHECK_EQ(DEVICE_EVENT, events_[index]->type());
+    return static_cast<const DeviceEvent&>(*events_[index]);
+  }
+
+  // Verifies if the |index|th invocation is OnDiskEvent() and returns details.
+  const DiskEvent& GetDiskEvent(size_t index) {
+    DCHECK_GT(events_.size(), index);
+    DCHECK_EQ(DISK_EVENT, events_[index]->type());
+    return static_cast<const DiskEvent&>(*events_[index]);
+  }
+
+  // Verifies if the |index|th invocation is OnFormatEvent() and returns
+  // details.
+  const FormatEvent& GetFormatEvent(size_t index) {
+    DCHECK_GT(events_.size(), index);
+    DCHECK_EQ(FORMAT_EVENT, events_[index]->type());
+    return static_cast<const FormatEvent&>(*events_[index]);
+  }
+
+  // Verifies if the |index|th invocation is OnMountEvent() and returns details.
+  const MountEvent& GetMountEvent(size_t index) {
+    DCHECK_GT(events_.size(), index);
+    DCHECK_EQ(MOUNT_EVENT, events_[index]->type());
+    return static_cast<const MountEvent&>(*events_[index]);
+  }
+
+  // Returns number of callback invocations happened so far.
+  size_t GetEventCount() { return events_.size(); }
+
+  // Counts the number of |MountEvent| recorded so far that matches the given
+  // condition.
+  size_t CountMountEvents(DiskMountManager::MountEvent mount_event_type,
+                          chromeos::MountError error_code,
+                          const std::string& mount_path) {
+    size_t num_matched = 0;
+    for (const auto& it : events_) {
+      if (it->type() != MOUNT_EVENT)
+        continue;
+      const MountEvent& mount_event = static_cast<const MountEvent&>(*it);
+      if (mount_event.event == mount_event_type &&
+          mount_event.error_code == error_code &&
+          mount_event.mount_point.mount_path == mount_path)
+        num_matched++;
+    }
+    return num_matched;
+  }
+
+  // Counts the number of |FormatEvent| recorded so far that matches with
+  // |format_event|.
+  size_t CountFormatEvents(const FormatEvent& exptected_format_event) {
+    size_t num_matched = 0;
+    for (const auto& it : events_) {
+      if (it->type() != FORMAT_EVENT)
+        continue;
+      if (static_cast<const FormatEvent&>(*it) == exptected_format_event)
+        num_matched++;
+    }
+    return num_matched;
+  }
+
+ private:
+  // Pointer to the manager object to which this |Observer| is registered.
+  const DiskMountManager* manager_;
+
+  // Records all invocations.
+  std::vector<std::unique_ptr<ObserverEvent>> events_;
+};
+
+// Shift operators of ostream.
+// Needed to print values in case of EXPECT_* failure in gtest.
+std::ostream& operator<<(std::ostream& stream,
+                         const DeviceEvent& device_event) {
+  return stream << device_event.DebugString();
+}
+
+std::ostream& operator<<(std::ostream& stream, const DiskEvent& disk_event) {
+  return stream << disk_event.DebugString();
+}
+
+std::ostream& operator<<(std::ostream& stream,
+                         const FormatEvent& format_event) {
+  return stream << format_event.DebugString();
+}
+
+std::ostream& operator<<(std::ostream& stream, const MountEvent& mount_event) {
+  return stream << mount_event.DebugString();
 }
 
 class DiskMountManagerTest : public testing::Test {
@@ -196,12 +425,14 @@
 
     InitDisksAndMountPoints();
 
-    DiskMountManager::GetInstance()->AddObserver(&observer_);
+    observer_.reset(
+        new MockDiskMountManagerObserver(DiskMountManager::GetInstance()));
+    DiskMountManager::GetInstance()->AddObserver(observer_.get());
   }
 
   // Shuts down dbus thread manager and disk moutn manager used in the test.
   void TearDown() override {
-    DiskMountManager::GetInstance()->RemoveObserver(&observer_);
+    DiskMountManager::GetInstance()->RemoveObserver(observer_.get());
     DiskMountManager::Shutdown();
     DBusThreadManager::Shutdown();
   }
@@ -265,39 +496,38 @@
 
  protected:
   chromeos::FakeCrosDisksClient* fake_cros_disks_client_;
-  MockDiskMountManagerObserver observer_;
+  std::unique_ptr<MockDiskMountManagerObserver> observer_;
   base::MessageLoopForUI message_loop_;
 };
 
 // Tests that the observer gets notified on attempt to format non existent mount
 // point.
 TEST_F(DiskMountManagerTest, Format_NotMounted) {
-  EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
-                                       chromeos::FORMAT_ERROR_UNKNOWN,
-                                       "/mount/non_existent"))
-      .Times(1);
   DiskMountManager::GetInstance()->FormatMountedDevice("/mount/non_existent");
+  ASSERT_EQ(1U, observer_->GetEventCount());
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
+                        chromeos::FORMAT_ERROR_UNKNOWN, "/mount/non_existent"),
+            observer_->GetFormatEvent(0));
 }
 
 // Tests that the observer gets notified on attempt to format read-only mount
 // point.
 TEST_F(DiskMountManagerTest, Format_ReadOnly) {
-  EXPECT_CALL(observer_,
-              OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
-                            chromeos::FORMAT_ERROR_DEVICE_NOT_ALLOWED,
-                            kReadOnlyMountpath))
-      .Times(1);
   DiskMountManager::GetInstance()->FormatMountedDevice(kReadOnlyMountpath);
+  ASSERT_EQ(1U, observer_->GetEventCount());
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
+                        chromeos::FORMAT_ERROR_DEVICE_NOT_ALLOWED,
+                        kReadOnlyMountpath),
+            observer_->GetFormatEvent(0));
 }
 
 // Tests that it is not possible to format archive mount point.
 TEST_F(DiskMountManagerTest, Format_Archive) {
-  EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
-                                       chromeos::FORMAT_ERROR_UNKNOWN,
-                                       "/archive/source_path"))
-      .Times(1);
-
   DiskMountManager::GetInstance()->FormatMountedDevice("/archive/mount_path");
+  ASSERT_EQ(1U, observer_->GetEventCount());
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
+                        chromeos::FORMAT_ERROR_UNKNOWN, "/archive/source_path"),
+            observer_->GetFormatEvent(0));
 }
 
 // Tests that format fails if the device cannot be unmounted.
@@ -306,25 +536,6 @@
   // In this test unmount will fail, and there should be no attempt to
   // format the device.
 
-  // Set up expectations for observer mock.
-  // Observer should be notified that unmount attempt fails and format task
-  // failed to start.
-  {
-    InSequence s;
-
-    EXPECT_CALL(observer_,
-        OnMountEvent(DiskMountManager::UNMOUNTING,
-                     chromeos::MOUNT_ERROR_INTERNAL,
-                     Field(&DiskMountManager::MountPointInfo::mount_path,
-                           "/device/mount_path")))
-        .Times(1);
-
-    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
-                                         chromeos::FORMAT_ERROR_UNKNOWN,
-                                         "/device/source_path"))
-        .Times(1);
-  }
-
   fake_cros_disks_client_->MakeUnmountFail();
   // Start test.
   DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
@@ -332,6 +543,17 @@
   // Cros disks will respond asynchronoulsy, so let's drain the message loop.
   base::RunLoop().RunUntilIdle();
 
+  // Observer should be notified that unmount attempt fails and format task
+  // failed to start.
+  ASSERT_EQ(2U, observer_->GetEventCount());
+  const MountEvent& mount_event = observer_->GetMountEvent(0);
+  EXPECT_EQ(DiskMountManager::UNMOUNTING, mount_event.event);
+  EXPECT_EQ(chromeos::MOUNT_ERROR_INTERNAL, mount_event.error_code);
+  EXPECT_EQ("/device/mount_path", mount_event.mount_point.mount_path);
+
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
+                        chromeos::FORMAT_ERROR_UNKNOWN, "/device/source_path"),
+            observer_->GetFormatEvent(1));
   EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
   EXPECT_EQ("/device/mount_path",
             fake_cros_disks_client_->last_unmount_device_path());
@@ -350,25 +572,6 @@
   // In this test, unmount will succeed, but call to Format method will
   // fail.
 
-  // Set up expectations for observer mock.
-  // Observer should be notified that the device was unmounted and format task
-  // failed to start.
-  {
-    InSequence s;
-
-    EXPECT_CALL(observer_,
-        OnMountEvent(DiskMountManager::UNMOUNTING,
-                     chromeos::MOUNT_ERROR_NONE,
-                     Field(&DiskMountManager::MountPointInfo::mount_path,
-                           "/device/mount_path")))
-        .Times(1);
-
-    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
-                                         chromeos::FORMAT_ERROR_UNKNOWN,
-                                         "/device/source_path"))
-        .Times(1);
-  }
-
   fake_cros_disks_client_->MakeFormatFail();
   // Start the test.
   DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
@@ -376,6 +579,18 @@
   // Cros disks will respond asynchronoulsy, so let's drain the message loop.
   base::RunLoop().RunUntilIdle();
 
+  // Observer should be notified that the device was unmounted and format task
+  // failed to start.
+  ASSERT_EQ(2U, observer_->GetEventCount());
+  const MountEvent& mount_event = observer_->GetMountEvent(0);
+  EXPECT_EQ(DiskMountManager::UNMOUNTING, mount_event.event);
+  EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, mount_event.error_code);
+  EXPECT_EQ("/device/mount_path", mount_event.mount_point.mount_path);
+
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
+                        chromeos::FORMAT_ERROR_UNKNOWN, "/device/source_path"),
+            observer_->GetFormatEvent(1));
+
   EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
   EXPECT_EQ("/device/mount_path",
             fake_cros_disks_client_->last_unmount_device_path());
@@ -397,37 +612,6 @@
   // CrosDisksClient will report that the format process for the first request
   // is successfully started.
 
-  // Set up expectations for observer mock.
-  // The observer should get a FORMAT_STARTED event for one format request and a
-  // FORMAT_COMPLETED with an error code for the other format request. The
-  // formatting will be started only for the first request.
-  // There should be only one UNMOUNTING event. The result of the second one
-  // should not be reported as the mount point will go away after the first
-  // request.
-  //
-  // Note that in this test the format completion signal will not be simulated,
-  // so the observer should not get FORMAT_COMPLETED signal.
-  {
-    InSequence s;
-
-    EXPECT_CALL(observer_,
-        OnMountEvent(DiskMountManager::UNMOUNTING,
-                     chromeos::MOUNT_ERROR_NONE,
-                     Field(&DiskMountManager::MountPointInfo::mount_path,
-                           "/device/mount_path")))
-        .Times(1);
-
-    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
-                                         chromeos::FORMAT_ERROR_UNKNOWN,
-                                         "/device/source_path"))
-        .Times(1);
-
-    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
-                                         chromeos::FORMAT_ERROR_NONE,
-                                         "/device/source_path"))
-        .Times(1);
-  }
-
   fake_cros_disks_client_->set_unmount_listener(
       base::Bind(&FakeCrosDisksClient::MakeUnmountFail,
                  base::Unretained(fake_cros_disks_client_)));
@@ -438,6 +622,28 @@
   // Cros disks will respond asynchronoulsy, so let's drain the message loop.
   base::RunLoop().RunUntilIdle();
 
+  // The observer should get a FORMAT_STARTED event for one format request and a
+  // FORMAT_COMPLETED with an error code for the other format request. The
+  // formatting will be started only for the first request.
+  // There should be only one UNMOUNTING event. The result of the second one
+  // should not be reported as the mount point will go away after the first
+  // request.
+  //
+  // Note that in this test the format completion signal will not be simulated,
+  // so the observer should not get FORMAT_COMPLETED signal.
+
+  ASSERT_EQ(3U, observer_->GetEventCount());
+  const MountEvent& mount_event = observer_->GetMountEvent(0);
+  EXPECT_EQ(DiskMountManager::UNMOUNTING, mount_event.event);
+  EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, mount_event.error_code);
+  EXPECT_EQ("/device/mount_path", mount_event.mount_point.mount_path);
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
+                        chromeos::FORMAT_ERROR_UNKNOWN, "/device/source_path"),
+            observer_->GetFormatEvent(1));
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_STARTED,
+                        chromeos::FORMAT_ERROR_NONE, "/device/source_path"),
+            observer_->GetFormatEvent(2));
+
   EXPECT_EQ(2, fake_cros_disks_client_->unmount_call_count());
   EXPECT_EQ("/device/mount_path",
             fake_cros_disks_client_->last_unmount_device_path());
@@ -453,37 +659,22 @@
   EXPECT_FALSE(HasMountPoint("/device/mount_path"));
 }
 
+// Verifies a |MountEvent| with the given condition. This function only checks
+// the |mount_path| in |MountPointInfo| to make sure to match the event with
+// preceding mount invocations.
+void VerifyMountEvent(const MountEvent& mount_event,
+                      DiskMountManager::MountEvent mount_event_type,
+                      chromeos::MountError error_code,
+                      const std::string& mount_path) {
+  EXPECT_EQ(mount_event_type, mount_event.event);
+  EXPECT_EQ(error_code, mount_event.error_code);
+  EXPECT_EQ(mount_path, mount_event.mount_point.mount_path);
+}
+
 // Tests the case when the format process actually starts and fails.
 TEST_F(DiskMountManagerTest, Format_FormatFails) {
   // Both unmount and format device cals are successful in this test.
 
-  // Set up expectations for observer mock.
-  // The observer should get notified that the device was unmounted and that
-  // formatting has started.
-  // After the formatting starts, the test will simulate failing
-  // FORMAT_COMPLETED signal, so the observer should also be notified the
-  // formatting has failed (FORMAT_COMPLETED event).
-  {
-    InSequence s;
-
-    EXPECT_CALL(observer_,
-        OnMountEvent(DiskMountManager::UNMOUNTING,
-                     chromeos::MOUNT_ERROR_NONE,
-                     Field(&DiskMountManager::MountPointInfo::mount_path,
-                           "/device/mount_path")))
-        .Times(1);
-
-    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
-                                         chromeos::FORMAT_ERROR_NONE,
-                                         "/device/source_path"))
-        .Times(1);
-
-    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
-                                         chromeos::FORMAT_ERROR_UNKNOWN,
-                                         "/device/source_path"))
-        .Times(1);
-  }
-
   // Start the test.
   DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
 
@@ -508,6 +699,21 @@
   // soon).
   fake_cros_disks_client_->SendFormatCompletedEvent(
       chromeos::FORMAT_ERROR_UNKNOWN, "/device/source_path");
+
+  // The observer should get notified that the device was unmounted and that
+  // formatting has started.
+  // After the formatting starts, the test will simulate failing
+  // FORMAT_COMPLETED signal, so the observer should also be notified the
+  // formatting has failed (FORMAT_COMPLETED event).
+  ASSERT_EQ(3U, observer_->GetEventCount());
+  VerifyMountEvent(observer_->GetMountEvent(0), DiskMountManager::UNMOUNTING,
+                   chromeos::MOUNT_ERROR_NONE, "/device/mount_path");
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_STARTED,
+                        chromeos::FORMAT_ERROR_NONE, "/device/source_path"),
+            observer_->GetFormatEvent(1));
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
+                        chromeos::FORMAT_ERROR_UNKNOWN, "/device/source_path"),
+            observer_->GetFormatEvent(2));
 }
 
 // Tests the case when formatting completes successfully.
@@ -515,30 +721,6 @@
   // Set up cros disks client mocks.
   // Both unmount and format device cals are successful in this test.
 
-  // Set up expectations for observer mock.
-  // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED
-  // events (all of them without an error set).
-  {
-    InSequence s;
-
-    EXPECT_CALL(observer_,
-        OnMountEvent(DiskMountManager::UNMOUNTING,
-                     chromeos::MOUNT_ERROR_NONE,
-                     Field(&DiskMountManager::MountPointInfo::mount_path,
-                           "/device/mount_path")))
-        .Times(1);
-
-    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
-                                         chromeos::FORMAT_ERROR_NONE,
-                                         "/device/source_path"))
-        .Times(1);
-
-    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
-                                         chromeos::FORMAT_ERROR_NONE,
-                                         "/device/source_path"))
-        .Times(1);
-  }
-
   // Start the test.
   DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
 
@@ -561,6 +743,18 @@
   // Simulate cros_disks reporting success.
   fake_cros_disks_client_->SendFormatCompletedEvent(
       chromeos::FORMAT_ERROR_NONE, "/device/source_path");
+
+  // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED
+  // events (all of them without an error set).
+  ASSERT_EQ(3U, observer_->GetEventCount());
+  VerifyMountEvent(observer_->GetMountEvent(0), DiskMountManager::UNMOUNTING,
+                   chromeos::MOUNT_ERROR_NONE, "/device/mount_path");
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_STARTED,
+                        chromeos::FORMAT_ERROR_NONE, "/device/source_path"),
+            observer_->GetFormatEvent(1));
+  EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
+                        chromeos::FORMAT_ERROR_NONE, "/device/source_path"),
+            observer_->GetFormatEvent(2));
 }
 
 // Tests that it's possible to format the device twice in a row (this may not be
@@ -569,36 +763,6 @@
   // All unmount and format device cals are successful in this test.
   // Each of the should be made twice (once for each formatting task).
 
-  // Set up expectations for observer mock.
-  // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED
-  // events (all of them without an error set) twice (once for each formatting
-  // task).
-  // Also, there should be a MOUNTING event when the device remounting is
-  // simulated.
-  EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
-                                       chromeos::FORMAT_ERROR_NONE,
-                                       "/device/source_path"))
-      .Times(2);
-
-  EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
-                                       chromeos::FORMAT_ERROR_NONE,
-                                       "/device/source_path"))
-      .Times(2);
-
-  EXPECT_CALL(observer_,
-      OnMountEvent(DiskMountManager::UNMOUNTING,
-                   chromeos::MOUNT_ERROR_NONE,
-                   Field(&DiskMountManager::MountPointInfo::mount_path,
-                         "/device/mount_path")))
-      .Times(2);
-
-  EXPECT_CALL(observer_,
-      OnMountEvent(DiskMountManager::MOUNTING,
-                   chromeos::MOUNT_ERROR_NONE,
-                   Field(&DiskMountManager::MountPointInfo::mount_path,
-                         "/device/mount_path")))
-      .Times(1);
-
   // Start the test.
   DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
 
@@ -650,6 +814,29 @@
   // Simulate cros_disks reporting success.
   fake_cros_disks_client_->SendFormatCompletedEvent(
       chromeos::FORMAT_ERROR_NONE, "/device/source_path");
+
+  // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED
+  // events (all of them without an error set) twice (once for each formatting
+  // task).
+  // Also, there should be a MOUNTING event when the device remounting is
+  // simulated.
+  EXPECT_EQ(7U, observer_->GetEventCount());
+
+  EXPECT_EQ(2U, observer_->CountFormatEvents(FormatEvent(
+                    DiskMountManager::FORMAT_COMPLETED,
+                    chromeos::FORMAT_ERROR_NONE, "/device/source_path")));
+
+  EXPECT_EQ(2U, observer_->CountFormatEvents(FormatEvent(
+                    DiskMountManager::FORMAT_STARTED,
+                    chromeos::FORMAT_ERROR_NONE, "/device/source_path")));
+
+  EXPECT_EQ(2U, observer_->CountMountEvents(DiskMountManager::UNMOUNTING,
+                                            chromeos::MOUNT_ERROR_NONE,
+                                            "/device/mount_path"));
+
+  EXPECT_EQ(1U, observer_->CountMountEvents(DiskMountManager::MOUNTING,
+                                            chromeos::MOUNT_ERROR_NONE,
+                                            "/device/mount_path"));
 }
 
 TEST_F(DiskMountManagerTest, MountPath_RecordAccessMode) {
@@ -662,27 +849,6 @@
   const std::string kMountPath1 = "/media/foo";
   const std::string kMountPath2 = "/media/bar";
 
-  // Event handlers of observers should be called.
-  EXPECT_CALL(
-      observer_,
-      OnMountEvent(
-          DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
-          Field(&DiskMountManager::MountPointInfo::mount_path, kMountPath1)));
-  // For the 2nd source, the disk (block device) is not read-only but the
-  // test will mount it in read-only mode.
-  // Observers query |disks_| from |DiskMountManager| in its event handler for
-  // a mount completion event. Therefore |disks_| must be updated with correct
-  // |read_only| value before notifying to observers.
-  EXPECT_CALL(
-      observer_,
-      OnMountEvent(
-          DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
-          Field(&DiskMountManager::MountPointInfo::mount_path, kMountPath2)))
-      .WillOnce(InvokeWithoutArgs(
-          // Verify if the disk appears read-only at the time of notification
-          // to observers.
-          [&]() { ExpectDiskReadOnly(manager, kSourcePath2, true); }));
-
   manager->MountPath(kSourcePath1, kSourceFormat, std::string(),
                      chromeos::MOUNT_TYPE_DEVICE,
                      chromeos::MOUNT_ACCESS_MODE_READ_WRITE);
@@ -697,6 +863,24 @@
       chromeos::MOUNT_ERROR_NONE, kSourcePath2, chromeos::MOUNT_TYPE_DEVICE,
       kMountPath2);
 
+  // Event handlers of observers should be called.
+  ASSERT_EQ(2U, observer_->GetEventCount());
+  VerifyMountEvent(observer_->GetMountEvent(0), DiskMountManager::MOUNTING,
+                   chromeos::MOUNT_ERROR_NONE, kMountPath1);
+  // For the 2nd source, the disk (block device) is not read-only but the
+  // test will mount it in read-only mode.
+  // Observers query |disks_| from |DiskMountManager| in its event handler for
+  // a mount completion event. Therefore |disks_| must be updated with correct
+  // |read_only| value before notifying to observers.
+  const MountEvent& secondMountEvent = observer_->GetMountEvent(1);
+  EXPECT_EQ(DiskMountManager::MOUNTING, secondMountEvent.event);
+  EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, secondMountEvent.error_code);
+  EXPECT_EQ(kMountPath2, secondMountEvent.mount_point.mount_path);
+  // Verify if the disk appears read-only at the time of notification to
+  // observers.
+  EXPECT_TRUE(secondMountEvent.disk->is_read_only());
+
+  // Verify the final state of manager->disks.
   const DiskMountManager::DiskMap& disks = manager->disks();
   ASSERT_GT(disks.count(kSourcePath1), 0U);
   EXPECT_FALSE(disks.find(kSourcePath1)->second->is_read_only());
@@ -709,13 +893,6 @@
   const std::string kSourceFormat = std::string();
   const std::string kMountLabel = std::string();  // N/A for MOUNT_TYPE_DEVICE
 
-  // Event handlers of observers should be called.
-  EXPECT_CALL(
-      observer_,
-      OnMountEvent(DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
-                   Field(&DiskMountManager::MountPointInfo::mount_path,
-                         kReadOnlyMountpath)));
-
   // Attempt to mount a read-only device in read-write mode.
   manager->MountPath(kReadOnlyDeviceSource, kSourceFormat, std::string(),
                      chromeos::MOUNT_TYPE_DEVICE,
@@ -725,6 +902,10 @@
       chromeos::MOUNT_ERROR_NONE, kReadOnlyDeviceSource,
       chromeos::MOUNT_TYPE_DEVICE, kReadOnlyMountpath);
 
+  // Event handlers of observers should be called.
+  ASSERT_EQ(1U, observer_->GetEventCount());
+  VerifyMountEvent(observer_->GetMountEvent(0), DiskMountManager::MOUNTING,
+                   chromeos::MOUNT_ERROR_NONE, kReadOnlyMountpath);
   const DiskMountManager::DiskMap& disks = manager->disks();
   ASSERT_GT(disks.count(kReadOnlyDeviceSource), 0U);
   // The mounted disk should preserve the read-only flag of the block device.