blob: dcfc1d54ba81e1f1872f78d1fc39e75ced362516 [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"
class InitTestCase : public MultipleDeviceTestCase {};
TEST_F(InitTestCase, Init) {
size_t index;
ASSERT_NO_FATAL_FAILURE(AddDevice(platform_bus()->device, "device", 0 /* protocol id */, "",
true /* has_init */, false /* reply_to_init */,
true /* always_init */, zx::vmo() /* inspect */, &index));
ASSERT_FALSE(device(index)->device->is_visible());
ASSERT_NO_FATAL_FAILURE(device(index)->CheckInitReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_TRUE(device(index)->device->is_visible());
ASSERT_EQ(Device::State::kActive, device(index)->device->state());
}
// Tests that a device will not be unbound until init completes.
TEST_F(InitTestCase, InitThenUnbind) {
size_t index;
ASSERT_NO_FATAL_FAILURE(AddDevice(platform_bus()->device, "device", 0 /* protocol id */, "",
true /* has_init */, false /* reply_to_init */,
true /* always_init */, zx::vmo() /* inspect */, &index));
ASSERT_FALSE(device(index)->device->is_visible());
ASSERT_NO_FATAL_FAILURE(device(index)->CheckInitReceived());
ASSERT_NO_FATAL_FAILURE(coordinator().device_manager()->ScheduleDriverHostRequestedRemove(
device(index)->device, true /* do_unbind */));
coordinator_loop()->RunUntilIdle();
// We should not get the unbind request until we reply to the init.
ASSERT_FALSE(device(index)->HasPendingMessages());
ASSERT_NO_FATAL_FAILURE(device(index)->SendInitReply());
coordinator_loop()->RunUntilIdle();
ASSERT_TRUE(device(index)->device->is_visible());
ASSERT_NO_FATAL_FAILURE(device(index)->CheckUnbindReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(device(index)->CheckRemoveReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_EQ(Device::State::kDead, device(index)->device->state());
}
TEST_F(InitTestCase, InitThenSuspend) {
size_t index;
ASSERT_NO_FATAL_FAILURE(AddDevice(platform_bus()->device, "device", 0 /* protocol id */, "",
true /* has_init */, false /* reply_to_init */,
true /* always_init */, zx::vmo() /* inspect */, &index));
ASSERT_FALSE(device(index)->device->is_visible());
ASSERT_NO_FATAL_FAILURE(device(index)->CheckInitReceived());
const uint32_t flags = DEVICE_SUSPEND_FLAG_POWEROFF;
ASSERT_NO_FATAL_FAILURE(DoSuspend(flags));
coordinator_loop()->RunUntilIdle();
// We should not get the suspend request until we reply to the init.
ASSERT_FALSE(device(index)->HasPendingMessages());
ASSERT_NO_FATAL_FAILURE(device(index)->SendInitReply());
coordinator_loop()->RunUntilIdle();
ASSERT_TRUE(device(index)->device->is_visible());
ASSERT_NO_FATAL_FAILURE(device(index)->CheckSuspendReceivedAndReply(flags, ZX_OK));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(platform_bus()->CheckSuspendReceivedAndReply(flags, ZX_OK));
ASSERT_EQ(Device::State::kSuspended, device(index)->device->state());
}
TEST_F(InitTestCase, ForcedRemovalDuringInit) {
size_t index;
ASSERT_NO_FATAL_FAILURE(AddDevice(platform_bus()->device, "device", 0 /* protocol id */, "",
true /* has_init */, false /* reply_to_init */,
true /* always_init */, zx::vmo() /* inspect */, &index));
auto* test_device = device(index);
// Don't reply to the init request.
ASSERT_NO_FATAL_FAILURE(test_device->CheckInitReceived());
// Close the device's channel to trigger a forced removal.
test_device->controller_server.reset();
test_device->coordinator_client.reset();
coordinator_loop()->RunUntilIdle();
// Check the device is dead and has no pending init task.
ASSERT_EQ(Device::State::kDead, test_device->device->state());
ASSERT_NULL(test_device->device->GetActiveInit());
ASSERT_NO_FATAL_FAILURE(test_device->SendInitReply());
}
// Tests that a device is unbound if init fails.
TEST_F(InitTestCase, FailedInit) {
size_t index;
ASSERT_NO_FATAL_FAILURE(AddDevice(platform_bus()->device, "device", 0 /* protocol id */, "",
true /* has_init */, false /* reply_to_init */,
true /* always_init */, zx::vmo() /* inspect */, &index));
ASSERT_FALSE(device(index)->device->is_visible());
ASSERT_NO_FATAL_FAILURE(device(index)->CheckInitReceivedAndReply(ZX_ERR_NO_MEMORY));
coordinator_loop()->RunUntilIdle();
// Init failed, so device should not be visible.
ASSERT_FALSE(device(index)->device->is_visible());
// Unbind should be scheduled.
ASSERT_NO_FATAL_FAILURE(device(index)->CheckUnbindReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(device(index)->CheckRemoveReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_EQ(Device::State::kDead, device(index)->device->state());
}
// Tests that a child init task will not run until the parent's init task completes.
TEST_F(InitTestCase, InitParentThenChild) {
size_t parent_index;
ASSERT_NO_FATAL_FAILURE(AddDevice(
platform_bus()->device, "parent-device", 0 /* protocol id */, "", true /* has_init */,
false /* reply_to_init */, true /* always_init */, zx::vmo() /* inspect */, &parent_index));
// Don't reply to init yet.
ASSERT_NO_FATAL_FAILURE(device(parent_index)->CheckInitReceived());
coordinator_loop()->RunUntilIdle();
size_t child_index;
ASSERT_NO_FATAL_FAILURE(AddDevice(
device(parent_index)->device, "child-device", 0 /* protocol id */, "", true /* has_init */,
false /* reply_to_init */, true /* always_init */, zx::vmo() /* inspect */, &child_index));
// Child init should not run until parent init task completes.
ASSERT_FALSE(device(child_index)->HasPendingMessages());
ASSERT_NO_FATAL_FAILURE(device(parent_index)->SendInitReply());
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(device(child_index)->CheckInitReceivedAndReply());
coordinator_loop()->RunUntilIdle();
}
TEST_F(InitTestCase, InitParentFail) {
size_t parent_index;
ASSERT_NO_FATAL_FAILURE(AddDevice(
platform_bus()->device, "parent-device", 0 /* protocol id */, "", true /* has_init */,
false /* reply_to_init */, true /* always_init */, zx::vmo() /* inspect */, &parent_index));
// Don't reply to init yet.
ASSERT_NO_FATAL_FAILURE(device(parent_index)->CheckInitReceived());
coordinator_loop()->RunUntilIdle();
size_t child_index;
ASSERT_NO_FATAL_FAILURE(AddDevice(
device(parent_index)->device, "child-device", 0 /* protocol id */, "", true /* has_init */,
false /* reply_to_init */, true /* always_init */, zx::vmo() /* inspect */, &child_index));
ASSERT_FALSE(device(child_index)->HasPendingMessages());
ASSERT_NO_FATAL_FAILURE(device(parent_index)->SendInitReply(ZX_ERR_NO_MEMORY));
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(device(child_index)->CheckInitReceivedAndReply());
coordinator_loop()->RunUntilIdle();
// The parent and child devices should be removed after a failed init.
ASSERT_NO_FATAL_FAILURE(device(parent_index)->CheckUnbindReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(device(child_index)->CheckUnbindReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(device(child_index)->CheckRemoveReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(device(parent_index)->CheckRemoveReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_EQ(Device::State::kDead, device(parent_index)->device->state());
ASSERT_EQ(Device::State::kDead, device(child_index)->device->state());
}
// TODO(fxbug.dev/43370): these tests can be removed once init tasks can be enabled for all devices.
TEST_F(InitTestCase, LegacyNoInit) {
size_t index;
ASSERT_NO_FATAL_FAILURE(AddDevice(platform_bus()->device, "device", 0 /* protocol id */, "",
false /* has_init */, false /* reply_to_init */,
false /* always_init */, zx::vmo() /* inspect */, &index));
ASSERT_TRUE(device(index)->device->is_visible());
ASSERT_EQ(Device::State::kActive, device(index)->device->state());
}
TEST_F(InitTestCase, LegacyInit) {
size_t index;
ASSERT_NO_FATAL_FAILURE(AddDevice(platform_bus()->device, "device", 0 /* protocol id */, "",
true /* has_init */, false /* reply_to_init */,
false /* always_init */, zx::vmo() /* inspect */, &index));
ASSERT_FALSE(device(index)->device->is_visible());
ASSERT_NO_FATAL_FAILURE(device(index)->CheckInitReceivedAndReply());
coordinator_loop()->RunUntilIdle();
ASSERT_TRUE(device(index)->device->is_visible());
ASSERT_EQ(Device::State::kActive, device(index)->device->state());
}