From 931eb4b68fbd926646c62c3139fd3f45f95ba54d Mon Sep 17 00:00:00 2001
From: Todd Eisenberger <teisenbe@google.com>
Date: Tue, 23 Apr 2019 20:17:03 +0000
Subject: [PATCH] [devcoordinator] Implement test for composite suspend logic

Verified that this test fails without
I4f8ae59764dab9883c9fad4d6b085298cfd40dc3 and
I2a6cdda90a3ba0b111dbba650621d2c1b71ea60a

Change-Id: I741313b247c70286973a72a81fee56f69c73f747
---
 .../devcoordinator/coordinator-test.cpp       | 215 ++++++++++++------
 1 file changed, 142 insertions(+), 73 deletions(-)

diff --git a/zircon/system/core/devmgr/devcoordinator/coordinator-test.cpp b/zircon/system/core/devmgr/devcoordinator/coordinator-test.cpp
index ecbbfd7a85b..d9ec7b8407c 100644
--- a/zircon/system/core/devmgr/devcoordinator/coordinator-test.cpp
+++ b/zircon/system/core/devmgr/devcoordinator/coordinator-test.cpp
@@ -402,6 +402,8 @@ public:
 
     bool DeviceHasPendingMessages(size_t device_index);
     bool DeviceHasPendingMessages(const zx::channel& remote);
+
+    void DoSuspend(uint32_t flags);
 protected:
     void SetUp() override {
         ASSERT_NO_FATAL_FAILURES(InitializeCoordinator(&coordinator_));
@@ -507,6 +509,45 @@ bool MultipleDeviceTestCase::DeviceHasPendingMessages(size_t device_index) {
     return DeviceHasPendingMessages(devices_[device_index].remote);
 }
 
+void MultipleDeviceTestCase::DoSuspend(uint32_t flags) {
+    const bool vfs_exit_expected = (flags != DEVICE_SUSPEND_FLAG_SUSPEND_RAM);
+    if (vfs_exit_expected) {
+        zx::unowned_event event(coordinator()->fshost_event());
+        auto thrd_func = [](void* ctx) -> int {
+            zx::unowned_event event(*static_cast<zx::unowned_event*>(ctx));
+            if (event->wait_one(FSHOST_SIGNAL_EXIT, zx::time::infinite(), nullptr) != ZX_OK) {
+                return false;
+            }
+            if (event->signal(0, FSHOST_SIGNAL_EXIT_DONE) != ZX_OK) {
+                return false;
+            }
+            return true;
+        };
+        thrd_t fshost_thrd;
+        ASSERT_EQ(thrd_create(&fshost_thrd, thrd_func, &event), thrd_success);
+
+        coordinator()->Suspend(flags);
+        loop()->RunUntilIdle();
+
+        int thread_status;
+        ASSERT_EQ(thrd_join(fshost_thrd, &thread_status), thrd_success);
+        ASSERT_TRUE(thread_status);
+
+        // Make sure that vfs_exit() happened.
+        ASSERT_OK(coordinator()->fshost_event().wait_one(FSHOST_SIGNAL_EXIT_DONE, zx::time(0),
+                                                         nullptr));
+    } else {
+        coordinator()->Suspend(flags);
+        loop()->RunUntilIdle();
+
+        // Make sure that vfs_exit() didn't happen.
+        ASSERT_EQ(
+                coordinator()->fshost_event().wait_one(FSHOST_SIGNAL_EXIT | FSHOST_SIGNAL_EXIT_DONE,
+                                                       zx::time(0), nullptr),
+                ZX_ERR_TIMED_OUT);
+    }
+}
+
 class SuspendTestCase : public MultipleDeviceTestCase {
 public:
     void SuspendTest(uint32_t flags);
@@ -562,42 +603,7 @@ void SuspendTestCase::SuspendTest(uint32_t flags) {
                                            &desc.index));
     }
 
-    const bool vfs_exit_expected = (flags != DEVICE_SUSPEND_FLAG_SUSPEND_RAM);
-    if (vfs_exit_expected) {
-        zx::unowned_event event(coordinator()->fshost_event());
-        auto thrd_func = [](void* ctx) -> int {
-            zx::unowned_event event(*static_cast<zx::unowned_event*>(ctx));
-            if (event->wait_one(FSHOST_SIGNAL_EXIT, zx::time::infinite(), nullptr) != ZX_OK) {
-                return false;
-            }
-            if (event->signal(0, FSHOST_SIGNAL_EXIT_DONE) != ZX_OK) {
-                return false;
-            }
-            return true;
-        };
-        thrd_t fshost_thrd;
-        ASSERT_EQ(thrd_create(&fshost_thrd, thrd_func, &event), thrd_success);
-
-        coordinator()->Suspend(flags);
-        loop()->RunUntilIdle();
-
-        int thread_status;
-        ASSERT_EQ(thrd_join(fshost_thrd, &thread_status), thrd_success);
-        ASSERT_TRUE(thread_status);
-
-        // Make sure that vfs_exit() happened.
-        ASSERT_OK(coordinator()->fshost_event().wait_one(FSHOST_SIGNAL_EXIT_DONE, zx::time(0),
-                                                         nullptr));
-    } else {
-        coordinator()->Suspend(flags);
-        loop()->RunUntilIdle();
-
-        // Make sure that vfs_exit() didn't happen.
-        ASSERT_EQ(
-                coordinator()->fshost_event().wait_one(FSHOST_SIGNAL_EXIT | FSHOST_SIGNAL_EXIT_DONE,
-                                                       zx::time(0), nullptr),
-                ZX_ERR_TIMED_OUT);
-    }
+    ASSERT_NO_FATAL_FAILURES(DoSuspend(flags));
 
     size_t num_to_suspend = fbl::count_of(devices);
     while (num_to_suspend > 0) {
@@ -646,6 +652,10 @@ void SuspendTestCase::SuspendTest(uint32_t flags) {
 class CompositeTestCase : public MultipleDeviceTestCase {
 public:
     ~CompositeTestCase() override = default;
+
+    void CheckCompositeCreation(const char* composite_name,
+                                const size_t* device_indexes, size_t device_indexes_count,
+                                size_t* component_indexes_out, zx::channel* composite_remote_out);
 protected:
     void SetUp() override {
         MultipleDeviceTestCase::SetUp();
@@ -653,6 +663,30 @@ protected:
     }
 };
 
+void CompositeTestCase::CheckCompositeCreation(const char* composite_name,
+                                               const size_t* device_indexes,
+                                               size_t device_indexes_count,
+                                               size_t* component_indexes_out,
+                                               zx::channel* composite_remote_out) {
+    for (size_t i = 0; i < device_indexes_count; ++i) {
+        auto device_state = device(device_indexes[i]);
+        // Check that the components got bound
+        fbl::String driver = coordinator()->component_driver()->libname;
+        ASSERT_NO_FATAL_FAILURES(CheckBindDriverReceived(device_state->remote, driver.data()));
+        loop()->RunUntilIdle();
+
+        // Synthesize the AddDevice request the component driver would send
+        char name[32];
+        snprintf(name, sizeof(name), "component-device-%zu", i);
+        ASSERT_NO_FATAL_FAILURES(AddDevice(device_state->device, name, 0,
+                                           driver, &component_indexes_out[i]));
+    }
+    // Make sure the composite comes up
+    ASSERT_NO_FATAL_FAILURES(CheckCreateCompositeDeviceReceived(devhost_remote(), composite_name,
+                                                                device_indexes_count,
+                                                                composite_remote_out));
+}
+
 class CompositeAddOrderTestCase : public CompositeTestCase {
 public:
     enum class AddLocation {
@@ -701,25 +735,11 @@ void CompositeAddOrderTestCase::ExecuteTest(AddLocation add) {
         ASSERT_NO_FATAL_FAILURES(do_add());
     }
 
-    size_t component_device_indexes[fbl::count_of(device_indexes)];
-    for (size_t i = 0; i < fbl::count_of(device_indexes); ++i) {
-        auto device_state = device(device_indexes[i]);
-        // Check that the components got bound
-        fbl::String driver = coordinator()->component_driver()->libname;
-        ASSERT_NO_FATAL_FAILURES(CheckBindDriverReceived(device_state->remote, driver.data()));
-        loop()->RunUntilIdle();
-
-        // Synthesize the AddDevice request the component driver would send
-        char name[32];
-        snprintf(name, sizeof(name), "component-device-%zu", i);
-        ASSERT_NO_FATAL_FAILURES(AddDevice(device_state->device, name, 0,
-                                           driver, &component_device_indexes[i]));
-    }
-
     zx::channel composite_remote;
-    ASSERT_NO_FATAL_FAILURES(CheckCreateCompositeDeviceReceived(devhost_remote(), kCompositeDevName,
-                                                                fbl::count_of(device_indexes),
-                                                                &composite_remote));
+    size_t component_device_indexes[fbl::count_of(device_indexes)];
+    ASSERT_NO_FATAL_FAILURES(CheckCompositeCreation(kCompositeDevName,
+                                                    device_indexes, fbl::count_of(device_indexes),
+                                                    component_device_indexes, &composite_remote));
 }
 
 TEST_F(CompositeAddOrderTestCase, DefineBeforeDevices) {
@@ -765,26 +785,11 @@ TEST_F(CompositeTestCase, ComponentUnbinds) {
         ASSERT_NO_FATAL_FAILURES(AddDevice(platform_bus(), name, protocol_id[i], "",
                                            &device_indexes[i]));
     }
-    // Make sure the component devices come up
-    size_t component_device_indexes[fbl::count_of(device_indexes)];
-    for (size_t i = 0; i < fbl::count_of(device_indexes); ++i) {
-        auto device_state = device(device_indexes[i]);
-        // Wait for the components to get bound
-        fbl::String driver = coordinator()->component_driver()->libname;
-        ASSERT_NO_FATAL_FAILURES(CheckBindDriverReceived(device_state->remote, driver.data()));
-        loop()->RunUntilIdle();
-
-        // Synthesize the AddDevice request the component driver would send
-        char name[32];
-        snprintf(name, sizeof(name), "component-device-%zu", i);
-        ASSERT_NO_FATAL_FAILURES(AddDevice(device_state->device, name, 0,
-                                           driver, &component_device_indexes[i]));
-    }
-    // Make sure the composite comes up
     zx::channel composite_remote;
-    ASSERT_NO_FATAL_FAILURES(CheckCreateCompositeDeviceReceived(devhost_remote(), kCompositeDevName,
-                                                                fbl::count_of(device_indexes),
-                                                                &composite_remote));
+    size_t component_device_indexes[fbl::count_of(device_indexes)];
+    ASSERT_NO_FATAL_FAILURES(CheckCompositeCreation(kCompositeDevName,
+                                                    device_indexes, fbl::count_of(device_indexes),
+                                                    component_device_indexes, &composite_remote));
     loop()->RunUntilIdle();
 
     {
@@ -816,6 +821,70 @@ TEST_F(CompositeTestCase, ComponentUnbinds) {
                                                                 &composite_remote));
 }
 
+TEST_F(CompositeTestCase, SuspendOrder) {
+    size_t device_indexes[2];
+    uint32_t protocol_id[] = {
+        ZX_PROTOCOL_GPIO,
+        ZX_PROTOCOL_I2C,
+    };
+    static_assert(fbl::count_of(protocol_id) == fbl::count_of(device_indexes));
+
+    const char* kCompositeDevName = "composite-dev";
+    ASSERT_NO_FATAL_FAILURES(BindCompositeDefineComposite(
+            platform_bus(), protocol_id, fbl::count_of(protocol_id), nullptr /* props */,
+            0, kCompositeDevName));
+    // Add the devices to construct the composite out of.
+    for (size_t i = 0; i < fbl::count_of(device_indexes); ++i) {
+        char name[32];
+        snprintf(name, sizeof(name), "device-%zu", i);
+        ASSERT_NO_FATAL_FAILURES(AddDevice(platform_bus(), name, protocol_id[i], "",
+                                           &device_indexes[i]));
+    }
+
+    zx::channel composite_remote;
+    size_t component_device_indexes[fbl::count_of(device_indexes)];
+    ASSERT_NO_FATAL_FAILURES(CheckCompositeCreation(kCompositeDevName,
+                                                    device_indexes, fbl::count_of(device_indexes),
+                                                    component_device_indexes, &composite_remote));
+
+    const uint32_t suspend_flags = DEVICE_SUSPEND_FLAG_POWEROFF;
+    ASSERT_NO_FATAL_FAILURES(DoSuspend(suspend_flags));
+
+    // Make sure none of the components have received their suspend requests
+    ASSERT_FALSE(DeviceHasPendingMessages(platform_bus_remote()));
+    for (auto idx : device_indexes) {
+        ASSERT_FALSE(DeviceHasPendingMessages(idx));
+    }
+    for (auto idx : component_device_indexes) {
+        ASSERT_FALSE(DeviceHasPendingMessages(idx));
+    }
+    // The composite should have been the first to get one
+    ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(composite_remote, suspend_flags, ZX_OK));
+    loop()->RunUntilIdle();
+
+    // Next, all of the internal component devices should have them, but none of the devices
+    // themselves
+    ASSERT_FALSE(DeviceHasPendingMessages(platform_bus_remote()));
+    for (auto idx : device_indexes) {
+        ASSERT_FALSE(DeviceHasPendingMessages(idx));
+    }
+    for (auto idx : component_device_indexes) {
+        ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(device(idx)->remote, suspend_flags, ZX_OK));
+    }
+    loop()->RunUntilIdle();
+
+    // Next, the devices should get them
+    ASSERT_FALSE(DeviceHasPendingMessages(platform_bus_remote()));
+    for (auto idx : device_indexes) {
+        ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(device(idx)->remote, suspend_flags, ZX_OK));
+    }
+    loop()->RunUntilIdle();
+
+    // Finally, the platform bus driver, which is the parent of all of the devices
+    ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(platform_bus_remote(), suspend_flags, ZX_OK));
+    loop()->RunUntilIdle();
+}
+
 } // namespace
 
 int main(int argc, char** argv) {
-- 
GitLab