blob: 61794ca4bd0a757f0f2786af7b7b1009b24404f2 [file] [log] [blame]
// Copyright 2019 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 "multiple-device-test.h"
TEST_F(MultipleDeviceTestCase, UnbindThenSuspend) {
size_t parent_index;
ASSERT_NO_FATAL_FAILURES(
AddDevice(platform_bus(), "parent-device", 0 /* protocol id */, "", &parent_index));
size_t child_index;
ASSERT_NO_FATAL_FAILURES(AddDevice(device(parent_index)->device, "child-device",
0 /* protocol id */, "", &child_index));
ASSERT_NO_FATAL_FAILURES(coordinator_.ScheduleRemove(device(parent_index)->device));
coordinator_loop()->RunUntilIdle();
// The child should be unbound first.
ASSERT_NO_FATAL_FAILURES(CheckUnbindReceived(device(child_index)->remote));
const uint32_t flags = DEVICE_SUSPEND_FLAG_POWEROFF;
ASSERT_NO_FATAL_FAILURES(DoSuspend(flags));
ASSERT_NO_FATAL_FAILURES(SendUnbindReply(device(child_index)->remote));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckRemoveReceivedAndReply(device(child_index)->remote));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckRemoveReceivedAndReply(device(parent_index)->remote));
coordinator_loop()->RunUntilIdle();
// The suspend task should complete but not send a suspend message.
ASSERT_FALSE(DeviceHasPendingMessages(device(parent_index)->remote));
ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(platform_bus_remote(), flags, ZX_OK));
}
TEST_F(MultipleDeviceTestCase, SuspendThenUnbind) {
size_t parent_index;
ASSERT_NO_FATAL_FAILURES(
AddDevice(platform_bus(), "parent-device", 0 /* protocol id */, "", &parent_index));
size_t child_index;
ASSERT_NO_FATAL_FAILURES(AddDevice(device(parent_index)->device, "child-device",
0 /* protocol id */, "", &child_index));
const uint32_t flags = DEVICE_SUSPEND_FLAG_POWEROFF;
ASSERT_NO_FATAL_FAILURES(DoSuspend(flags));
// Don't reply to the suspend yet.
ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(device(child_index)->remote, flags));
ASSERT_NO_FATAL_FAILURES(coordinator_.ScheduleRemove(device(parent_index)->device));
coordinator_loop()->RunUntilIdle();
// Check that the child device has not yet started unbinding.
ASSERT_FALSE(DeviceHasPendingMessages(device(child_index)->remote));
ASSERT_NO_FATAL_FAILURES(SendSuspendReply(device(child_index)->remote, ZX_OK));
coordinator_loop()->RunUntilIdle();
// The parent should have started suspending. Don't reply yet.
ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(device(parent_index)->remote, flags));
// Finish unbinding the child.
ASSERT_NO_FATAL_FAILURES(CheckUnbindReceivedAndReply(device(child_index)->remote));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckRemoveReceivedAndReply(device(child_index)->remote));
coordinator_loop()->RunUntilIdle();
// Finish suspending the parent.
ASSERT_NO_FATAL_FAILURES(SendSuspendReply(device(parent_index)->remote, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(platform_bus_remote(), flags, ZX_OK));
// The parent should now be removed.
ASSERT_NO_FATAL_FAILURES(CheckRemoveReceivedAndReply(device(parent_index)->remote));
coordinator_loop()->RunUntilIdle();
}
TEST_F(MultipleDeviceTestCase, SuspendFidlMexec) {
ASSERT_OK(coordinator_loop()->StartThread("DevCoordLoop"));
set_coordinator_loop_thread_running(true);
async::Loop devhost_loop{&kAsyncLoopConfigNoAttachToCurrentThread};
ASSERT_OK(devhost_loop.StartThread("DevHostLoop"));
async::Wait suspend_task_pbus(
platform_bus_remote().get(), ZX_CHANNEL_READABLE, 0,
[this](async_dispatcher_t*, async::Wait*, zx_status_t, const zx_packet_signal_t*) {
CheckSuspendReceived(platform_bus_remote(), DEVICE_SUSPEND_FLAG_MEXEC, ZX_OK);
});
ASSERT_OK(suspend_task_pbus.Begin(devhost_loop.dispatcher()));
async::Wait suspend_task_sys(
sys_proxy_remote_.get(), ZX_CHANNEL_READABLE, 0,
[this](async_dispatcher_t*, async::Wait*, zx_status_t, const zx_packet_signal_t*) {
CheckSuspendReceived(sys_proxy_remote_, DEVICE_SUSPEND_FLAG_MEXEC, ZX_OK);
});
ASSERT_OK(suspend_task_sys.Begin(devhost_loop.dispatcher()));
zx::channel services, services_remote;
ASSERT_OK(zx::channel::create(0, &services, &services_remote));
ASSERT_OK(coordinator()->BindOutgoingServices(std::move(services_remote)));
zx::channel channel, channel_remote;
ASSERT_OK(zx::channel::create(0, &channel, &channel_remote));
const char* service = "svc/" fuchsia_device_manager_Administrator_Name;
ASSERT_OK(fdio_service_connect_at(services.get(), service, channel_remote.release()));
bool callback_executed = false;
DoSuspend(DEVICE_SUSPEND_FLAG_MEXEC, [&](uint32_t flags) {
zx_status_t call_status = ZX_OK;
ASSERT_OK(fuchsia_device_manager_AdministratorSuspend(channel.get(), flags, &call_status));
ASSERT_OK(call_status);
callback_executed = true;
});
ASSERT_TRUE(callback_executed);
ASSERT_FALSE(suspend_task_pbus.is_pending());
ASSERT_FALSE(suspend_task_sys.is_pending());
}
// Disable the test as it is flaking fxb/37462. Lets repro with multiply.
TEST_F(MultipleDeviceTestCase, SuspendFidlMexecFail) {
ASSERT_OK(coordinator_loop()->StartThread("DevCoordLoop"));
set_coordinator_loop_thread_running(true);
async::Loop devhost_loop{&kAsyncLoopConfigNoAttachToCurrentThread};
ASSERT_OK(devhost_loop.StartThread("DevHostLoop"));
async::Wait suspend_task_pbus(
platform_bus_remote().get(), ZX_CHANNEL_READABLE, 0,
[this](async_dispatcher_t*, async::Wait*, zx_status_t, const zx_packet_signal_t*) {
CheckSuspendReceived(platform_bus_remote(), DEVICE_SUSPEND_FLAG_MEXEC);
});
ASSERT_OK(suspend_task_pbus.Begin(devhost_loop.dispatcher()));
async::Wait suspend_task_sys(
sys_proxy_remote_.get(), ZX_CHANNEL_READABLE, 0,
[this](async_dispatcher_t*, async::Wait*, zx_status_t, const zx_packet_signal_t*) {
CheckSuspendReceived(sys_proxy_remote_, DEVICE_SUSPEND_FLAG_MEXEC, ZX_OK);
});
ASSERT_OK(suspend_task_sys.Begin(devhost_loop.dispatcher()));
zx::channel services, services_remote;
ASSERT_OK(zx::channel::create(0, &services, &services_remote));
ASSERT_OK(coordinator()->BindOutgoingServices(std::move(services_remote)));
zx::channel channel, channel_remote;
ASSERT_OK(zx::channel::create(0, &channel, &channel_remote));
const char* service = "svc/" fuchsia_device_manager_Administrator_Name;
ASSERT_OK(fdio_service_connect_at(services.get(), service, channel_remote.release()));
bool callback_executed = false;
DoSuspend(DEVICE_SUSPEND_FLAG_MEXEC, [&](uint32_t flags) {
zx_status_t call_status = ZX_OK;
ASSERT_OK(fuchsia_device_manager_AdministratorSuspend(channel.get(), flags, &call_status));
ASSERT_EQ(call_status, ZX_ERR_TIMED_OUT);
callback_executed = true;
});
ASSERT_TRUE(callback_executed);
ASSERT_FALSE(suspend_task_pbus.is_pending());
ASSERT_TRUE(suspend_task_sys.is_pending());
}
TEST_F(MultipleDeviceTestCase, UnbindThenResume) {
size_t parent_index;
ASSERT_NO_FATAL_FAILURES(
AddDevice(platform_bus(), "parent-device", 0 /* protocol id */, "", &parent_index));
size_t child_index;
ASSERT_NO_FATAL_FAILURES(AddDevice(device(parent_index)->device, "child-device",
0 /* protocol id */, "", &child_index));
coordinator_.sys_device()->set_state(devmgr::Device::State::kSuspended);
coordinator_.sys_device()->proxy()->set_state(devmgr::Device::State::kSuspended);
platform_bus()->set_state(devmgr::Device::State::kSuspended);
device(parent_index)->device->set_state(devmgr::Device::State::kSuspended);
device(child_index)->device->set_state(devmgr::Device::State::kSuspended);
ASSERT_NO_FATAL_FAILURES(coordinator_.ScheduleRemove(device(parent_index)->device));
coordinator_loop()->RunUntilIdle();
// The child should be unbound first.
ASSERT_NO_FATAL_FAILURES(CheckUnbindReceived(device(child_index)->remote));
ASSERT_NO_FATAL_FAILURES(DoResume(SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON));
ASSERT_NO_FATAL_FAILURES(
CheckResumeReceived(sys_proxy_remote_, SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckResumeReceived(
platform_bus_remote(), SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckResumeReceived(
device(parent_index)->remote, SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(SendUnbindReply(device(child_index)->remote));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckRemoveReceivedAndReply(device(child_index)->remote));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckRemoveReceivedAndReply(device(parent_index)->remote));
coordinator_loop()->RunUntilIdle();
// The resume task should complete but not send a resume message.
ASSERT_FALSE(DeviceHasPendingMessages(device(parent_index)->remote));
ASSERT_FALSE(DeviceHasPendingMessages(device(child_index)->remote));
}
TEST_F(MultipleDeviceTestCase, ResumeThenUnbind) {
size_t parent_index;
ASSERT_NO_FATAL_FAILURES(
AddDevice(platform_bus(), "parent-device", 0 /* protocol id */, "", &parent_index));
size_t child_index;
ASSERT_NO_FATAL_FAILURES(AddDevice(device(parent_index)->device, "child-device",
0 /* protocol id */, "", &child_index));
coordinator_.sys_device()->set_state(devmgr::Device::State::kSuspended);
coordinator_.sys_device()->proxy()->set_state(devmgr::Device::State::kSuspended);
platform_bus()->set_state(devmgr::Device::State::kSuspended);
device(parent_index)->device->set_state(devmgr::Device::State::kSuspended);
device(child_index)->device->set_state(devmgr::Device::State::kSuspended);
ASSERT_NO_FATAL_FAILURES(DoResume(SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON));
ASSERT_NO_FATAL_FAILURES(
CheckResumeReceived(sys_proxy_remote_, SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckResumeReceived(
platform_bus_remote(), SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON, ZX_OK));
coordinator_loop()->RunUntilIdle();
// Don't reply to the resume yet.
ASSERT_NO_FATAL_FAILURES(CheckResumeReceived(device(parent_index)->remote,
SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON));
ASSERT_NO_FATAL_FAILURES(coordinator_.ScheduleRemove(device(parent_index)->device));
coordinator_loop()->RunUntilIdle();
// Check that the child device has not yet started unbinding.
ASSERT_FALSE(DeviceHasPendingMessages(device(child_index)->remote));
ASSERT_NO_FATAL_FAILURES(SendResumeReply(device(parent_index)->remote, ZX_OK));
coordinator_loop()->RunUntilIdle();
// The Child should have started resuming now. Complete resume of child.
ASSERT_NO_FATAL_FAILURES(CheckResumeReceived(
device(child_index)->remote, SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON, ZX_OK));
coordinator_loop()->RunUntilIdle();
// Since the resume is complete, unbinding the child should start now.
ASSERT_NO_FATAL_FAILURES(CheckUnbindReceivedAndReply(device(child_index)->remote));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckRemoveReceivedAndReply(device(child_index)->remote));
coordinator_loop()->RunUntilIdle();
// The parent should now be removed.
ASSERT_NO_FATAL_FAILURES(CheckRemoveReceivedAndReply(device(parent_index)->remote));
coordinator_loop()->RunUntilIdle();
}
TEST_F(MultipleDeviceTestCase, SuspendThenResume) {
size_t parent_index;
ASSERT_NO_FATAL_FAILURES(
AddDevice(platform_bus(), "parent-device", 0 /* protocol id */, "", &parent_index));
size_t child_index;
ASSERT_NO_FATAL_FAILURES(AddDevice(device(parent_index)->device, "child-device",
0 /* protocol id */, "", &child_index));
const uint32_t flags = DEVICE_SUSPEND_FLAG_POWEROFF;
ASSERT_NO_FATAL_FAILURES(DoSuspend(flags));
// Don't reply to the suspend yet.
ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(device(child_index)->remote, flags));
// This should return without scheduling resume tasks since suspend is in
// progress.
ASSERT_NO_FATAL_FAILURES(DoResume(SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(SendSuspendReply(device(child_index)->remote, ZX_OK));
coordinator_loop()->RunUntilIdle();
// The parent should have started suspending.
ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(device(parent_index)->remote, flags, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckSuspendReceived(platform_bus_remote(), flags, ZX_OK));
ASSERT_FALSE(DeviceHasPendingMessages(device(parent_index)->remote));
ASSERT_FALSE(DeviceHasPendingMessages(device(child_index)->remote));
ASSERT_EQ(device(parent_index)->device->state(), devmgr::Device::State::kSuspended);
ASSERT_EQ(device(child_index)->device->state(), devmgr::Device::State::kSuspended);
}
TEST_F(MultipleDeviceTestCase, ResumeThenSuspend) {
size_t parent_index;
ASSERT_NO_FATAL_FAILURES(
AddDevice(platform_bus(), "parent-device", 0 /* protocol id */, "", &parent_index));
size_t child_index;
ASSERT_NO_FATAL_FAILURES(AddDevice(device(parent_index)->device, "child-device",
0 /* protocol id */, "", &child_index));
coordinator_.sys_device()->set_state(devmgr::Device::State::kSuspended);
coordinator_.sys_device()->proxy()->set_state(devmgr::Device::State::kSuspended);
platform_bus()->set_state(devmgr::Device::State::kSuspended);
device(parent_index)->device->set_state(devmgr::Device::State::kSuspended);
device(child_index)->device->set_state(devmgr::Device::State::kSuspended);
ASSERT_NO_FATAL_FAILURES(DoResume(SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(
CheckResumeReceived(sys_proxy_remote_, SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckResumeReceived(
platform_bus_remote(), SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON, ZX_OK));
coordinator_loop()->RunUntilIdle();
// Dont reply yet for the resume
ASSERT_NO_FATAL_FAILURES(CheckResumeReceived(device(parent_index)->remote,
SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON));
coordinator_loop()->RunUntilIdle();
const uint32_t flags = DEVICE_SUSPEND_FLAG_SUSPEND_RAM;
// Should be a no-op because resume is in progress.
ASSERT_NO_FATAL_FAILURES(DoSuspend(flags));
ASSERT_NO_FATAL_FAILURES(SendResumeReply(device(parent_index)->remote, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURES(CheckResumeReceived(
device(child_index)->remote, SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_FALSE(DeviceHasPendingMessages(device(parent_index)->remote));
ASSERT_FALSE(DeviceHasPendingMessages(device(child_index)->remote));
ASSERT_EQ(device(parent_index)->device->state(), devmgr::Device::State::kActive);
ASSERT_EQ(device(child_index)->device->state(), devmgr::Device::State::kActive);
}