blob: 5386fbd3dfc8300a1e16ff2718de33a4880fd7a6 [file] [log] [blame]
// Copyright 2022 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 <fidl/fuchsia.audio.device/cpp/natural_types.h>
#include <lib/sync/cpp/completion.h>
#include <zircon/errors.h>
#include <gtest/gtest.h>
#include "src/media/audio/services/common/testing/test_server_and_async_client.h"
#include "src/media/audio/services/device_registry/adr_server_unittest_base.h"
#include "src/media/audio/services/device_registry/registry_server.h"
namespace media_audio {
namespace {
using DriverClient = fuchsia_audio_device::DriverClient;
class RegistryServerWarningTest : public AudioDeviceRegistryServerTestBase {};
class RegistryServerCodecWarningTest : public RegistryServerWarningTest {};
class RegistryServerStreamConfigWarningTest : public RegistryServerWarningTest {};
/////////////////////
// Device-less tests
//
// A subsequent call to WatchDevicesAdded before the previous one completes should fail.
TEST_F(RegistryServerWarningTest, WatchDevicesAddedWhilePending) {
auto registry = CreateTestRegistryServer();
ASSERT_EQ(RegistryServer::count(), 1u);
bool received_callback_1 = false, received_callback_2 = false;
// The first `WatchDevicesAdded` call should pend indefinitely (even after the second one fails).
registry->client()->WatchDevicesAdded().Then(
[&received_callback_1](
fidl::Result<fuchsia_audio_device::Registry::WatchDevicesAdded>& result) mutable {
received_callback_1 = true;
ADD_FAILURE() << "Unexpected completion for initial WatchDevicesAdded call";
});
RunLoopUntilIdle();
ASSERT_FALSE(received_callback_1);
// The second `WatchDevicesAdded` call should fail immediately with domain error
// ALREADY_PENDING, since the first call has not yet completed.
registry->client()->WatchDevicesAdded().Then(
[&received_callback_2](
fidl::Result<fuchsia_audio_device::Registry::WatchDevicesAdded>& result) mutable {
received_callback_2 = true;
ASSERT_TRUE(result.is_error());
ASSERT_TRUE(result.error_value().is_domain_error()) << result.error_value();
EXPECT_EQ(result.error_value().domain_error(),
fuchsia_audio_device::RegistryWatchDevicesAddedError::kAlreadyPending)
<< result.error_value();
});
RunLoopUntilIdle();
EXPECT_FALSE(received_callback_1);
EXPECT_TRUE(received_callback_2);
EXPECT_EQ(RegistryServer::count(), 1u);
}
// A subsequent call to WatchDeviceRemoved before the previous one completes should fail.
TEST_F(RegistryServerWarningTest, WatchDeviceRemovedWhilePending) {
auto registry = CreateTestRegistryServer();
ASSERT_EQ(RegistryServer::count(), 1u);
bool received_callback_1 = false, received_callback_2 = false;
// The first `WatchDeviceRemoved` call should pend indefinitely (even after the second one fails).
registry->client()->WatchDeviceRemoved().Then(
[&received_callback_1](
fidl::Result<fuchsia_audio_device::Registry::WatchDeviceRemoved>& result) mutable {
received_callback_1 = true;
ADD_FAILURE() << "Unexpected completion for initial WatchDeviceRemoved call";
});
RunLoopUntilIdle();
ASSERT_FALSE(received_callback_1);
// The second `WatchDeviceRemoved` call should fail immediately with domain error
// ALREADY_PENDING, since the first call has not yet completed.
registry->client()->WatchDeviceRemoved().Then(
[&received_callback_2](
fidl::Result<fuchsia_audio_device::Registry::WatchDeviceRemoved>& result) mutable {
received_callback_2 = true;
ASSERT_TRUE(result.is_error());
ASSERT_TRUE(result.error_value().is_domain_error()) << result.error_value();
EXPECT_EQ(result.error_value().domain_error(),
fuchsia_audio_device::RegistryWatchDeviceRemovedError::kAlreadyPending)
<< result.error_value();
});
RunLoopUntilIdle();
EXPECT_FALSE(received_callback_1);
EXPECT_TRUE(received_callback_2);
EXPECT_EQ(RegistryServer::count(), 1u);
}
// If the required 'id' field is not set, CreateObserver should fail with kInvalidTokenId.
TEST_F(RegistryServerWarningTest, CreateObserverMissingToken) {
auto registry = CreateTestRegistryServer();
ASSERT_EQ(RegistryServer::count(), 1u);
auto [observer_client_end, observer_server_end] =
CreateNaturalAsyncClientOrDie<fuchsia_audio_device::Observer>();
auto observer_client = fidl::Client<fuchsia_audio_device::Observer>(
std::move(observer_client_end), dispatcher(), observer_fidl_handler_.get());
bool received_callback = false;
registry->client()
->CreateObserver({{
.observer_server = std::move(observer_server_end),
}})
.Then([&received_callback](
fidl::Result<fuchsia_audio_device::Registry::CreateObserver>& result) {
received_callback = true;
ASSERT_TRUE(result.is_error());
ASSERT_TRUE(result.error_value().is_domain_error()) << result.error_value();
EXPECT_EQ(result.error_value().domain_error(),
fuchsia_audio_device::RegistryCreateObserverError::kInvalidTokenId)
<< result.error_value();
});
RunLoopUntilIdle();
EXPECT_TRUE(received_callback);
}
// If 'id' doesn't identify an initialized device, CreateObserver should fail with kDeviceNotFound.
TEST_F(RegistryServerWarningTest, CreateObserverBadToken) {
auto registry = CreateTestRegistryServer();
ASSERT_EQ(RegistryServer::count(), 1u);
auto [observer_client_end, observer_server_end] =
CreateNaturalAsyncClientOrDie<fuchsia_audio_device::Observer>();
auto observer_client = fidl::Client<fuchsia_audio_device::Observer>(
std::move(observer_client_end), dispatcher(), observer_fidl_handler_.get());
bool received_callback = false;
registry->client()
->CreateObserver({{
.token_id = 0, // no device is present yet.
.observer_server = std::move(observer_server_end),
}})
.Then([&received_callback](
fidl::Result<fuchsia_audio_device::Registry::CreateObserver>& result) {
received_callback = true;
ASSERT_TRUE(result.is_error());
ASSERT_TRUE(result.error_value().is_domain_error()) << result.error_value();
EXPECT_EQ(result.error_value().domain_error(),
fuchsia_audio_device::RegistryCreateObserverError::kDeviceNotFound)
<< result.error_value();
});
RunLoopUntilIdle();
EXPECT_TRUE(received_callback);
}
/////////////////////
// Codec tests
//
// If required 'observer_server' is not set, CreateObserver should fail with kInvalidObserver.
TEST_F(RegistryServerCodecWarningTest, CreateObserverMissingObserver) {
auto fake_driver = CreateFakeCodecOutput();
adr_service_->AddDevice(Device::Create(adr_service_, dispatcher(), "Test codec name",
fuchsia_audio_device::DeviceType::kCodec,
DriverClient::WithCodec(fake_driver->Enable())));
RunLoopUntilIdle();
ASSERT_EQ(adr_service_->devices().size(), 1u);
auto registry = CreateTestRegistryServer();
ASSERT_EQ(RegistryServer::count(), 1u);
auto received_callback = false;
std::optional<TokenId> added_id;
registry->client()->WatchDevicesAdded().Then(
[&received_callback,
&added_id](fidl::Result<fuchsia_audio_device::Registry::WatchDevicesAdded>& result) mutable {
received_callback = true;
ASSERT_TRUE(result.is_ok() && result->devices() && result->devices()->size() == 1u);
ASSERT_TRUE(result->devices()->at(0).token_id());
added_id = *result->devices()->at(0).token_id();
});
RunLoopUntilIdle();
ASSERT_TRUE(received_callback);
ASSERT_TRUE(added_id);
auto [observer_client_end, observer_server_end] =
CreateNaturalAsyncClientOrDie<fuchsia_audio_device::Observer>();
auto observer_client = fidl::Client<fuchsia_audio_device::Observer>(
std::move(observer_client_end), dispatcher(), observer_fidl_handler_.get());
received_callback = false;
registry->client()
->CreateObserver({{
.token_id = *added_id,
}})
.Then([&received_callback](
fidl::Result<fuchsia_audio_device::Registry::CreateObserver>& result) {
received_callback = true;
ASSERT_TRUE(result.is_error());
ASSERT_TRUE(result.error_value().is_domain_error()) << result.error_value();
EXPECT_EQ(result.error_value().domain_error(),
fuchsia_audio_device::RegistryCreateObserverError::kInvalidObserver)
<< result.error_value();
});
RunLoopUntilIdle();
EXPECT_TRUE(received_callback);
}
// If 'observer_server' is not set to a valid handle, we should fail with ZX_ERR_INVALID_ARGS.
TEST_F(RegistryServerCodecWarningTest, CreateObserverBadObserver) {
auto fake_driver = CreateFakeCodecInput();
adr_service_->AddDevice(Device::Create(adr_service_, dispatcher(), "Test codec name",
fuchsia_audio_device::DeviceType::kCodec,
DriverClient::WithCodec(fake_driver->Enable())));
RunLoopUntilIdle();
ASSERT_EQ(adr_service_->devices().size(), 1u);
auto registry = CreateTestRegistryServer();
ASSERT_EQ(RegistryServer::count(), 1u);
auto received_callback = false;
std::optional<TokenId> added_id;
registry->client()->WatchDevicesAdded().Then(
[&received_callback,
&added_id](fidl::Result<fuchsia_audio_device::Registry::WatchDevicesAdded>& result) mutable {
received_callback = true;
ASSERT_TRUE(result.is_ok() && result->devices() && result->devices()->size() == 1u);
ASSERT_TRUE(result->devices()->at(0).token_id());
added_id = *result->devices()->at(0).token_id();
});
RunLoopUntilIdle();
ASSERT_TRUE(received_callback);
ASSERT_TRUE(added_id);
auto [observer_client_end, observer_server_end] =
CreateNaturalAsyncClientOrDie<fuchsia_audio_device::Observer>();
auto observer_client = fidl::Client<fuchsia_audio_device::Observer>(
std::move(observer_client_end), dispatcher(), observer_fidl_handler_.get());
received_callback = false;
registry->client()
->CreateObserver({{
.token_id = *added_id,
.observer_server = fidl::ServerEnd<fuchsia_audio_device::Observer>(),
}})
.Then([&received_callback](
fidl::Result<fuchsia_audio_device::Registry::CreateObserver>& result) {
received_callback = true;
ASSERT_TRUE(result.is_error());
ASSERT_TRUE(result.error_value().is_framework_error()) << result.error_value();
EXPECT_EQ(result.error_value().framework_error().status(), ZX_ERR_INVALID_ARGS)
<< result.error_value();
});
RunLoopUntilIdle();
EXPECT_TRUE(received_callback);
}
// TODO(https://fxbug.dev/42068381): If Health can change post-initialization, add a test case for:
// CreateObserver with token of Codec that was ready & Added, but then became unhealthy.
/////////////////////
// StreamConfig tests
//
// If the required 'observer_server' field is not set, we should fail.
TEST_F(RegistryServerStreamConfigWarningTest, CreateObserverMissingObserver) {
auto fake_driver = CreateFakeStreamConfigOutput();
adr_service_->AddDevice(Device::Create(adr_service_, dispatcher(), "Test output name",
fuchsia_audio_device::DeviceType::kOutput,
DriverClient::WithStreamConfig(fake_driver->Enable())));
RunLoopUntilIdle();
ASSERT_EQ(adr_service_->devices().size(), 1u);
auto registry = CreateTestRegistryServer();
ASSERT_EQ(RegistryServer::count(), 1u);
auto received_callback = false;
std::optional<TokenId> added_id;
registry->client()->WatchDevicesAdded().Then(
[&received_callback,
&added_id](fidl::Result<fuchsia_audio_device::Registry::WatchDevicesAdded>& result) mutable {
received_callback = true;
ASSERT_TRUE(result.is_ok() && result->devices() && result->devices()->size() == 1u);
ASSERT_TRUE(result->devices()->at(0).token_id());
added_id = *result->devices()->at(0).token_id();
});
RunLoopUntilIdle();
ASSERT_TRUE(received_callback);
ASSERT_TRUE(added_id);
auto [observer_client_end, observer_server_end] =
CreateNaturalAsyncClientOrDie<fuchsia_audio_device::Observer>();
auto observer_client = fidl::Client<fuchsia_audio_device::Observer>(
std::move(observer_client_end), dispatcher(), observer_fidl_handler_.get());
received_callback = false;
registry->client()
->CreateObserver({{
.token_id = *added_id,
}})
.Then([&received_callback](
fidl::Result<fuchsia_audio_device::Registry::CreateObserver>& result) {
received_callback = true;
ASSERT_TRUE(result.is_error());
ASSERT_TRUE(result.error_value().is_domain_error()) << result.error_value();
EXPECT_EQ(result.error_value().domain_error(),
fuchsia_audio_device::RegistryCreateObserverError::kInvalidObserver)
<< result.error_value();
});
RunLoopUntilIdle();
EXPECT_TRUE(received_callback);
}
// If 'observer_server' is not set to a valid handle, we should fail.
TEST_F(RegistryServerStreamConfigWarningTest, CreateObserverBadObserver) {
auto fake_driver = CreateFakeStreamConfigInput();
adr_service_->AddDevice(Device::Create(adr_service_, dispatcher(), "Test input name",
fuchsia_audio_device::DeviceType::kInput,
DriverClient::WithStreamConfig(fake_driver->Enable())));
RunLoopUntilIdle();
ASSERT_EQ(adr_service_->devices().size(), 1u);
auto registry = CreateTestRegistryServer();
ASSERT_EQ(RegistryServer::count(), 1u);
auto received_callback = false;
std::optional<TokenId> added_id;
registry->client()->WatchDevicesAdded().Then(
[&received_callback,
&added_id](fidl::Result<fuchsia_audio_device::Registry::WatchDevicesAdded>& result) mutable {
received_callback = true;
ASSERT_TRUE(result.is_ok() && result->devices() && result->devices()->size() == 1u);
ASSERT_TRUE(result->devices()->at(0).token_id());
added_id = *result->devices()->at(0).token_id();
});
RunLoopUntilIdle();
ASSERT_TRUE(received_callback);
ASSERT_TRUE(added_id);
auto [observer_client_end, observer_server_end] =
CreateNaturalAsyncClientOrDie<fuchsia_audio_device::Observer>();
auto observer_client = fidl::Client<fuchsia_audio_device::Observer>(
std::move(observer_client_end), dispatcher(), observer_fidl_handler_.get());
received_callback = false;
registry->client()
->CreateObserver({{
.token_id = *added_id,
.observer_server = fidl::ServerEnd<fuchsia_audio_device::Observer>(),
}})
.Then([&received_callback](
fidl::Result<fuchsia_audio_device::Registry::CreateObserver>& result) {
received_callback = true;
ASSERT_TRUE(result.is_error());
ASSERT_TRUE(result.error_value().is_framework_error()) << result.error_value();
EXPECT_EQ(result.error_value().framework_error().status(), ZX_ERR_INVALID_ARGS)
<< result.error_value();
});
RunLoopUntilIdle();
EXPECT_TRUE(received_callback);
}
// TODO(https://fxbug.dev/42068381): If Health can change post-initialization, add a test case for:
// CreateObserver with token of StreamConfig that was ready & Added, but then became unhealthy.
} // namespace
} // namespace media_audio