blob: f4aa5ec5c8450c8ab3b245634a3d0e9eae778b0f [file] [log] [blame] [edit]
// Copyright 2020 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 <lib/simple-codec/simple-codec-client.h>
#include <lib/simple-codec/simple-codec-server.h>
#include <lib/sync/completion.h>
#include <sdk/lib/inspect/testing/cpp/zxtest/inspect.h>
#include <zxtest/zxtest.h>
#include "src/devices/testing/mock-ddk/mock-device.h"
namespace {
static const char* kTestId = "test id";
static const char* kTestManufacturer = "test man";
static const char* kTestProduct = "test prod";
static const uint32_t kTestInstanceCount = 123;
} // namespace
namespace audio {
namespace audio_fidl = ::fuchsia::hardware::audio;
namespace signal_fidl = ::fuchsia::hardware::audio::signalprocessing;
class SimpleCodecTest : public inspect::InspectTestHelper, public zxtest::Test {};
// Server tests.
class TestCodec : public SimpleCodecServer, public signal_fidl::SignalProcessing {
public:
explicit TestCodec(zx_device_t* parent) : SimpleCodecServer(parent) {}
codec_protocol_t GetProto() { return {&this->codec_protocol_ops_, this}; }
zx_status_t Shutdown() override { return ZX_OK; }
zx::status<DriverIds> Initialize() override {
return zx::ok(DriverIds{.vendor_id = 0, .device_id = 0, .instance_count = kTestInstanceCount});
}
zx_status_t Reset() override { return ZX_ERR_NOT_SUPPORTED; }
Info GetInfo() override {
return {.unique_id = kTestId, .manufacturer = kTestManufacturer, .product_name = kTestProduct};
}
zx_status_t Stop() override { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t Start() override { return ZX_OK; }
bool IsBridgeable() override { return false; }
void SetBridgedMode(bool enable_bridged_mode) override {}
void SignalProcessingConnect(
fidl::InterfaceRequest<signal_fidl::SignalProcessing> signal_processing) override {
signal_processing_binding_.emplace(this, std::move(signal_processing), dispatcher());
}
void GetElements(GetElementsCallback callback) override {
signal_fidl::Element pe;
pe.set_id(kAglPeId);
pe.set_type(signal_fidl::ElementType::AUTOMATIC_GAIN_LIMITER);
std::vector<signal_fidl::Element> pes;
pes.emplace_back(std::move(pe));
signal_fidl::Reader_GetElements_Response response(std::move(pes));
signal_fidl::Reader_GetElements_Result result;
result.set_response(std::move(response));
callback(std::move(result));
}
void SetElementState(uint64_t processing_element_id, signal_fidl::ElementState state,
SetElementStateCallback callback) override {
ASSERT_EQ(processing_element_id, kAglPeId);
ASSERT_TRUE(state.has_enabled());
agl_mode_ = state.enabled();
callback(signal_fidl::SignalProcessing_SetElementState_Result::WithResponse(
signal_fidl::SignalProcessing_SetElementState_Response()));
}
void WatchElementState(uint64_t processing_element_id,
WatchElementStateCallback callback) override {}
void GetTopologies(GetTopologiesCallback callback) override {
signal_fidl::EdgePair edge;
edge.processing_element_id_from = kAglPeId;
edge.processing_element_id_to = kAglPeId;
std::vector<signal_fidl::EdgePair> edges;
edges.emplace_back(edge);
signal_fidl::Topology topology;
topology.set_id(kTopologyId);
topology.set_processing_elements_edge_pairs(edges);
std::vector<signal_fidl::Topology> topologies;
topologies.emplace_back(std::move(topology));
signal_fidl::Reader_GetTopologies_Response response(std::move(topologies));
signal_fidl::Reader_GetTopologies_Result result;
result.set_response(std::move(response));
callback(std::move(result));
}
void SetTopology(uint64_t topology_id, SetTopologyCallback callback) override {
if (topology_id != kTopologyId) {
callback(signal_fidl::SignalProcessing_SetTopology_Result::WithErr(ZX_ERR_INVALID_ARGS));
return;
}
callback(signal_fidl::SignalProcessing_SetTopology_Result::WithResponse(
signal_fidl::SignalProcessing_SetTopology_Response()));
}
DaiSupportedFormats GetDaiFormats() override { return {}; }
zx::status<CodecFormatInfo> SetDaiFormat(const DaiFormat& format) override {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
GainFormat GetGainFormat() override { return {}; }
GainState GetGainState() override { return gain_state_; }
void SetGainState(GainState state) override { gain_state_ = state; }
bool agl_mode() { return agl_mode_; }
inspect::Inspector& inspect() { return SimpleCodecServer::inspect(); }
private:
static constexpr uint64_t kAglPeId = 1;
static constexpr uint64_t kTopologyId = 1;
GainState gain_state_ = {};
bool agl_mode_ = false;
std::optional<fidl::Binding<signal_fidl::SignalProcessing>> signal_processing_binding_;
};
TEST_F(SimpleCodecTest, ChannelConnection) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
SimpleCodecClient client;
ASSERT_OK(client.SetProtocol(&codec_proto));
auto info = client.GetInfo();
ASSERT_TRUE(info.is_ok());
ASSERT_EQ(info->unique_id.compare(kTestId), 0);
ASSERT_EQ(info->manufacturer.compare(kTestManufacturer), 0);
ASSERT_EQ(info->product_name.compare(kTestProduct), 0);
}
TEST_F(SimpleCodecTest, GainState) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
SimpleCodecClient client;
ASSERT_OK(client.SetProtocol(&codec_proto));
// Defaults to false/0db.
{
auto state = client.GetGainState();
ASSERT_TRUE(state.is_ok());
ASSERT_EQ(state->muted, false);
ASSERT_EQ(state->agc_enabled, false);
ASSERT_EQ(state->gain, 0.f);
}
// Still all set to false/0db.
{
auto state = client.GetGainState();
ASSERT_TRUE(state.is_ok());
ASSERT_EQ(state->muted, false);
ASSERT_EQ(state->agc_enabled, false);
ASSERT_EQ(state->gain, 0.f);
}
// Set gain now.
client.SetGainState({.gain = 1.23f, .muted = true, .agc_enabled = true});
// Values updated now.
{
auto state = client.GetGainState();
ASSERT_TRUE(state.is_ok());
ASSERT_EQ(state->muted, true);
ASSERT_EQ(state->agc_enabled, true);
ASSERT_EQ(state->gain, 1.23f);
}
}
TEST_F(SimpleCodecTest, SetDaiFormat) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
SimpleCodecClient client;
client.SetProtocol(&codec_proto);
DaiFormat format = {.sample_format = audio_fidl::DaiSampleFormat::PCM_SIGNED,
.frame_format = FrameFormat::I2S};
zx::status<CodecFormatInfo> codec_format_info = client.SetDaiFormat(std::move(format));
ASSERT_EQ(codec_format_info.status_value(), ZX_ERR_NOT_SUPPORTED);
}
TEST_F(SimpleCodecTest, PlugStateHardwired) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
zx::channel channel_remote, channel_local;
ASSERT_OK(zx::channel::create(0, &channel_local, &channel_remote));
ddk::CodecProtocolClient proto_client;
ASSERT_OK(codec_proto2.Connect(std::move(channel_remote)));
audio_fidl::CodecSyncPtr codec_client;
codec_client.Bind(std::move(channel_local));
audio_fidl::PlugDetectCapabilities out_plug_detect_capabilities;
ASSERT_OK(codec_client->GetPlugDetectCapabilities(&out_plug_detect_capabilities));
ASSERT_EQ(out_plug_detect_capabilities, audio_fidl::PlugDetectCapabilities::HARDWIRED);
audio_fidl::PlugState out_plug_state;
ASSERT_OK(codec_client->WatchPlugState(&out_plug_state));
ASSERT_EQ(out_plug_state.plugged(), true);
ASSERT_GT(out_plug_state.plug_state_time(), 0);
}
TEST_F(SimpleCodecTest, PlugStateCanAsyncNotify) {
constexpr int64_t kTestPlugStateTime = 123;
class TestCodecCanNotify : public TestCodec {
public:
explicit TestCodecCanNotify(zx_device_t* parent) : TestCodec(parent) {}
bool SupportsAsyncPlugState() override { return true; }
void WatchPlugState(fuchsia::hardware::audio::Codec::WatchPlugStateCallback callback) override {
fuchsia::hardware::audio::PlugState plug_state;
plug_state.set_plugged(true);
plug_state.set_plug_state_time(kTestPlugStateTime);
watch_state_called_ = true;
callback(std::move(plug_state));
}
bool watch_state_called() { return watch_state_called_; }
private:
bool watch_state_called_ = false;
};
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodecCanNotify>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodecCanNotify>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
zx::channel channel_remote, channel_local;
ASSERT_OK(zx::channel::create(0, &channel_local, &channel_remote));
ddk::CodecProtocolClient proto_client;
ASSERT_OK(codec_proto2.Connect(std::move(channel_remote)));
audio_fidl::CodecSyncPtr codec_client;
codec_client.Bind(std::move(channel_local));
audio_fidl::PlugDetectCapabilities out_plug_detect_capabilities;
ASSERT_OK(codec_client->GetPlugDetectCapabilities(&out_plug_detect_capabilities));
ASSERT_EQ(out_plug_detect_capabilities, audio_fidl::PlugDetectCapabilities::CAN_ASYNC_NOTIFY);
audio_fidl::PlugState out_plug_state;
ASSERT_OK(codec_client->WatchPlugState(&out_plug_state));
ASSERT_EQ(out_plug_state.plugged(), true);
ASSERT_EQ(out_plug_state.plug_state_time(), kTestPlugStateTime);
ASSERT_TRUE(codec->watch_state_called());
}
TEST_F(SimpleCodecTest, AglStateServerWithClientViaSignalProcessingApi) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
zx::channel channel_remote, channel_local;
ASSERT_OK(zx::channel::create(0, &channel_local, &channel_remote));
ddk::CodecProtocolClient proto_client;
ASSERT_OK(codec_proto2.Connect(std::move(channel_remote)));
audio_fidl::CodecSyncPtr codec_client;
codec_client.Bind(std::move(channel_local));
fidl::InterfaceHandle<signal_fidl::SignalProcessing> signal_processing_handle;
fidl::InterfaceRequest<signal_fidl::SignalProcessing> signal_processing_request =
signal_processing_handle.NewRequest();
ASSERT_OK(codec_client->SignalProcessingConnect(std::move(signal_processing_request)));
fidl::SynchronousInterfacePtr signal_processing_client = signal_processing_handle.BindSync();
// We should get one PE with AGL support.
signal_fidl::Reader_GetElements_Result result;
ASSERT_OK(signal_processing_client->GetElements(&result));
ASSERT_FALSE(result.is_err());
ASSERT_EQ(result.response().processing_elements.size(), 1);
ASSERT_EQ(result.response().processing_elements[0].type(),
signal_fidl::ElementType::AUTOMATIC_GAIN_LIMITER);
ASSERT_FALSE(codec->agl_mode());
// Control with enabled = true.
signal_fidl::SignalProcessing_SetElementState_Result result_enable;
signal_fidl::ElementState state_enable;
state_enable.set_enabled(true);
ASSERT_OK(signal_processing_client->SetElementState(result.response().processing_elements[0].id(),
std::move(state_enable), &result_enable));
ASSERT_FALSE(result_enable.is_err());
ASSERT_TRUE(codec->agl_mode());
}
TEST_F(SimpleCodecTest, AglStateServerViaSimpleCodecClient) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
SimpleCodecClient client;
ASSERT_OK(client.SetProtocol(&codec_proto));
ASSERT_OK(client.SetAgl(true));
ASSERT_TRUE(codec->agl_mode());
ASSERT_OK(client.SetAgl(false));
ASSERT_FALSE(codec->agl_mode());
}
TEST_F(SimpleCodecTest, AglStateServerViaSimpleCodecClientNoSupport) {
auto fake_parent = MockDevice::FakeRootParent();
struct TestCodecNoAgl : public TestCodec {
explicit TestCodecNoAgl(zx_device_t* parent) : TestCodec(parent) {}
void GetElements(GetElementsCallback callback) override {
callback(signal_fidl::Reader_GetElements_Result::WithErr(ZX_ERR_NOT_SUPPORTED));
}
};
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodecNoAgl>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
SimpleCodecClient client;
ASSERT_OK(client.SetProtocol(&codec_proto));
ASSERT_EQ(client.SetAgl(true), ZX_ERR_NOT_SUPPORTED);
}
TEST_F(SimpleCodecTest, Inspect) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
zx::channel channel_remote, channel_local;
ASSERT_OK(zx::channel::create(0, &channel_local, &channel_remote));
ddk::CodecProtocolClient proto_client;
ASSERT_OK(codec_proto2.Connect(std::move(channel_remote)));
audio_fidl::CodecSyncPtr codec_client;
codec_client.Bind(std::move(channel_local));
// Check inspect state.
ASSERT_NO_FATAL_FAILURE(ReadInspect(codec->inspect().DuplicateVmo()));
auto* simple_codec = hierarchy().GetByPath({"simple_codec"});
ASSERT_TRUE(simple_codec);
ASSERT_NO_FATAL_FAILURE(
CheckProperty(simple_codec->node(), "state", inspect::StringPropertyValue("created")));
ASSERT_NO_FATAL_FAILURE(
CheckProperty(simple_codec->node(), "start_time", inspect::IntPropertyValue(0)));
ASSERT_NO_FATAL_FAILURE(
CheckProperty(simple_codec->node(), "unique_id", inspect::StringPropertyValue("test id")));
}
TEST_F(SimpleCodecTest, InspectNoUniqueId) {
struct TestCodecNoUniqueId : public TestCodec {
explicit TestCodecNoUniqueId(zx_device_t* parent) : TestCodec(parent) {}
zx::status<DriverIds> Initialize() override {
return zx::ok(
DriverIds{.vendor_id = 0, .device_id = 0, .instance_count = kTestInstanceCount});
}
Info GetInfo() override { return {}; }
};
auto fake_parent = MockDevice::FakeRootParent();
SimpleCodecServer::CreateAndAddToDdk<TestCodecNoUniqueId>(fake_parent.get());
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
zx::channel channel_remote, channel_local;
ASSERT_OK(zx::channel::create(0, &channel_local, &channel_remote));
ddk::CodecProtocolClient proto_client;
ASSERT_OK(codec_proto2.Connect(std::move(channel_remote)));
audio_fidl::CodecSyncPtr codec_client;
codec_client.Bind(std::move(channel_local));
// Check inspect state.
ASSERT_NO_FATAL_FAILURE(ReadInspect(codec->inspect().DuplicateVmo()));
auto* simple_codec = hierarchy().GetByPath({"simple_codec"});
ASSERT_TRUE(simple_codec);
ASSERT_NO_FATAL_FAILURE(
CheckProperty(simple_codec->node(), "state", inspect::StringPropertyValue("created")));
ASSERT_NO_FATAL_FAILURE(
CheckProperty(simple_codec->node(), "start_time", inspect::IntPropertyValue(0)));
ASSERT_NO_FATAL_FAILURE(
CheckProperty(simple_codec->node(), "unique_id",
inspect::StringPropertyValue(std::to_string(kTestInstanceCount))));
}
TEST_F(SimpleCodecTest, MultipleClients) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
SimpleCodecClient codec_clients[3];
for (auto& codec_client : codec_clients) {
ASSERT_OK(codec_client.SetProtocol(codec_proto2));
}
{
auto state = codec_clients[0].GetGainState();
ASSERT_TRUE(state.is_ok());
EXPECT_EQ(state->muted, false);
EXPECT_EQ(state->agc_enabled, false);
EXPECT_EQ(state->gain, 0.f);
}
codec_clients[1].SetGainState({.gain = 1.23f, .muted = true, .agc_enabled = false});
// Wait for client 0 to be notified of the new gain state.
for (;;) {
auto state = codec_clients[0].GetGainState();
ASSERT_TRUE(state.is_ok());
if (state->muted) {
break;
}
}
{
auto state = codec_clients[0].GetGainState();
ASSERT_TRUE(state.is_ok());
EXPECT_EQ(state->muted, true);
EXPECT_EQ(state->agc_enabled, false);
EXPECT_EQ(state->gain, 1.23f);
}
codec_clients[0].SetGainState({.gain = 5.67f, .muted = true, .agc_enabled = true});
for (;;) {
auto state = codec_clients[2].GetGainState();
ASSERT_TRUE(state.is_ok());
if (state->agc_enabled) {
break;
}
}
{
auto state = codec_clients[2].GetGainState();
ASSERT_TRUE(state.is_ok());
EXPECT_EQ(state->muted, true);
EXPECT_EQ(state->agc_enabled, true);
EXPECT_EQ(state->gain, 5.67f);
}
}
TEST_F(SimpleCodecTest, MoveClient) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
SimpleCodecClient codec_client1;
ASSERT_OK(codec_client1.SetProtocol(codec_proto2));
codec_client1.SetGainState({.gain = 1.23f, .muted = true, .agc_enabled = false});
EXPECT_OK(codec_client1.Start());
SimpleCodecClient codec_client2(std::move(codec_client1));
EXPECT_NOT_OK(codec_client1.Start()); // The client was unbound, this should return an error.
{
auto state = codec_client2.GetGainState();
ASSERT_TRUE(state.is_ok());
EXPECT_EQ(state->muted, true);
EXPECT_EQ(state->agc_enabled, false);
EXPECT_EQ(state->gain, 1.23f);
}
}
TEST_F(SimpleCodecTest, CloseChannel) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
SimpleCodecClient codec_client;
ASSERT_OK(codec_client.SetProtocol(codec_proto2));
codec_client.SetGainState({.gain = 1.23f, .muted = true, .agc_enabled = false});
{
auto state = codec_client.GetGainState();
ASSERT_TRUE(state.is_ok());
EXPECT_EQ(state->muted, true);
EXPECT_EQ(state->agc_enabled, false);
EXPECT_EQ(state->gain, 1.23f);
}
EXPECT_OK(codec_client.Start());
// TestCodec doesn't support this, so calling it should cause the server to unbind.
EXPECT_NOT_OK(codec_client.Stop());
// This should fail now that our channel has been closed.
EXPECT_NOT_OK(codec_client.Start());
}
TEST_F(SimpleCodecTest, RebindClient) {
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
SimpleCodecClient codec_client;
ASSERT_OK(codec_client.SetProtocol(codec_proto2));
codec_client.SetGainState({.gain = 1.23f, .muted = true, .agc_enabled = false});
{
auto state = codec_client.GetGainState();
ASSERT_TRUE(state.is_ok());
EXPECT_EQ(state->muted, true);
EXPECT_EQ(state->agc_enabled, false);
EXPECT_EQ(state->gain, 1.23f);
}
// Do a synchronous FIDL call to flush messages on the channel and force the server to update the
// gain state.
EXPECT_OK(codec_client.Start());
ASSERT_OK(codec_client.SetProtocol(codec_proto2));
{
auto state = codec_client.GetGainState();
ASSERT_TRUE(state.is_ok());
EXPECT_EQ(state->muted, true);
EXPECT_EQ(state->agc_enabled, false);
EXPECT_EQ(state->gain, 1.23f);
}
}
TEST_F(SimpleCodecTest, MoveClientWithDispatcherProvided) {
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
ASSERT_OK(loop.StartThread("SimpleCodecClient test thread"));
auto fake_parent = MockDevice::FakeRootParent();
ASSERT_OK(SimpleCodecServer::CreateAndAddToDdk<TestCodec>(fake_parent.get()));
auto* child_dev = fake_parent->GetLatestChild();
ASSERT_NOT_NULL(child_dev);
auto codec = child_dev->GetDeviceContext<TestCodec>();
auto codec_proto = codec->GetProto();
ddk::CodecProtocolClient codec_proto2(&codec_proto);
SimpleCodecClient codec_client1(loop.dispatcher());
ASSERT_OK(codec_client1.SetProtocol(codec_proto2));
codec_client1.SetGainState({.gain = 1.23f, .muted = true, .agc_enabled = false});
EXPECT_OK(codec_client1.Start());
SimpleCodecClient codec_client2(std::move(codec_client1));
EXPECT_NOT_OK(codec_client1.Start()); // The client was unbound, this should return an error.
{
auto state = codec_client2.GetGainState();
ASSERT_TRUE(state.is_ok());
EXPECT_EQ(state->muted, true);
EXPECT_EQ(state->agc_enabled, false);
EXPECT_EQ(state->gain, 1.23f);
}
}
} // namespace audio