blob: 80ead8fbcfdbe6bc9a1fd7dbd7fb018b80d28ee0 [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 <fuchsia/device/llcpp/fidl.h>
#include <fuchsia/hardware/audio/llcpp/fidl.h>
#include <lib/fake_ddk/fake_ddk.h>
#include <lib/simple-audio-stream/simple-audio-stream.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/clock.h>
#include <threads.h>
#include <set>
#include <audio-proto-utils/format-utils.h>
#include <zxtest/zxtest.h>
namespace {
namespace audio_fidl = ::llcpp::fuchsia::hardware::audio;
audio_fidl::PcmFormat GetDefaultPcmFormat() {
audio_fidl::PcmFormat format;
format.number_of_channels = 2;
format.channels_to_use_bitmask = 0x03;
format.sample_format = audio_fidl::SampleFormat::PCM_SIGNED;
format.frame_rate = 48000;
format.bytes_per_sample = 2;
format.valid_bits_per_sample = 16;
return format;
}
} // namespace
namespace audio {
class MockSimpleAudio : public SimpleAudioStream {
public:
static constexpr uint32_t kTestFrameRate = 48000;
static constexpr uint8_t kTestNumberOfChannels = 2;
static constexpr uint32_t kTestFifoDepth = 16;
static constexpr uint32_t kTestClockDomain = audio_fidl::CLOCK_DOMAIN_EXTERNAL;
static constexpr uint32_t kTestPositionNotify = 4;
static constexpr float kTestGain = 1.2345f;
MockSimpleAudio(zx_device_t* parent) : SimpleAudioStream(parent, false /* is input */) {}
void PostSetPlugState(bool plugged, zx::duration delay) {
async::PostDelayedTask(
dispatcher(),
[this, plugged]() {
ScopedToken t(domain_token());
SetPlugState(plugged);
},
delay);
}
protected:
zx_status_t Init() __TA_REQUIRES(domain_token()) override {
fbl::AllocChecker ac;
supported_formats_.reserve(1, &ac);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
audio_stream_format_range_t range = {};
range.min_channels = kTestNumberOfChannels;
range.max_channels = kTestNumberOfChannels;
range.sample_formats = AUDIO_SAMPLE_FORMAT_16BIT;
range.min_frames_per_second = kTestFrameRate;
range.max_frames_per_second = kTestFrameRate;
range.flags = ASF_RANGE_FLAG_FPS_48000_FAMILY;
supported_formats_.push_back(range);
fifo_depth_ = kTestFifoDepth;
clock_domain_ = kTestClockDomain;
// Set our gain capabilities.
cur_gain_state_.cur_gain = 0;
cur_gain_state_.cur_mute = false;
cur_gain_state_.cur_agc = false;
cur_gain_state_.min_gain = 0;
cur_gain_state_.max_gain = 100;
cur_gain_state_.gain_step = 0;
cur_gain_state_.can_mute = true;
cur_gain_state_.can_agc = true;
SetInitialPlugState(AUDIO_PDNF_CAN_NOTIFY);
snprintf(device_name_, sizeof(device_name_), "test-audio-in");
snprintf(mfr_name_, sizeof(mfr_name_), "Bike Sheds, Inc.");
snprintf(prod_name_, sizeof(prod_name_), "testy_mctestface");
unique_id_ = AUDIO_STREAM_UNIQUE_ID_BUILTIN_MICROPHONE;
return ZX_OK;
}
zx_status_t SetGain(const audio_proto::SetGainReq& req) __TA_REQUIRES(domain_token()) override {
if (req.flags & AUDIO_SGF_GAIN_VALID) {
cur_gain_state_.cur_gain = req.gain;
}
if (req.flags & AUDIO_SGF_AGC_VALID) {
cur_gain_state_.cur_agc = req.flags & AUDIO_SGF_AGC;
}
if (req.flags & AUDIO_SGF_MUTE_VALID) {
cur_gain_state_.cur_mute = req.flags & AUDIO_SGF_MUTE;
}
return ZX_OK;
}
zx_status_t ChangeFormat(const audio_proto::StreamSetFmtReq& req)
__TA_REQUIRES(domain_token()) override {
return ZX_OK;
}
zx_status_t GetBuffer(const audio_proto::RingBufGetBufferReq& req, uint32_t* out_num_rb_frames,
zx::vmo* out_buffer) __TA_REQUIRES(domain_token()) override {
zx::vmo rb;
*out_num_rb_frames = req.min_ring_buffer_frames;
zx::vmo::create(*out_num_rb_frames * 2 * 2, 0, &rb);
us_per_notification_ = 1'000 * MockSimpleAudio::kTestFrameRate / *out_num_rb_frames * 1'000 /
req.notifications_per_ring;
constexpr uint32_t rights = ZX_RIGHT_READ | ZX_RIGHT_WRITE | ZX_RIGHT_MAP | ZX_RIGHT_TRANSFER;
return rb.duplicate(rights, out_buffer);
}
zx_status_t Start(uint64_t* out_start_time) __TA_REQUIRES(domain_token()) override {
*out_start_time = zx::clock::get_monotonic().get();
notify_timer_.PostDelayed(dispatcher(), zx::usec(us_per_notification_));
return ZX_OK;
}
zx_status_t Stop() __TA_REQUIRES(domain_token()) override {
notify_timer_.Cancel();
return ZX_OK;
}
void ProcessRingNotification() {
ScopedToken t(domain_token());
audio_proto::RingBufPositionNotify resp = {};
resp.hdr.cmd = AUDIO_RB_POSITION_NOTIFY;
resp.monotonic_time = zx::clock::get_monotonic().get();
resp.ring_buffer_pos = kTestPositionNotify;
NotifyPosition(resp);
notify_timer_.PostDelayed(dispatcher(), zx::usec(us_per_notification_));
}
void ShutdownHook() __TA_REQUIRES(domain_token()) override { Stop(); }
private:
async::TaskClosureMethod<MockSimpleAudio, &MockSimpleAudio::ProcessRingNotification> notify_timer_
TA_GUARDED(domain_token()){this};
uint32_t us_per_notification_ = 0;
};
TEST(SimpleAudioTest, DdkLifeCycleTest) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
ddk::SuspendTxn txn(server->zxdev(), 0, false, DEVICE_SUSPEND_REASON_SELECTIVE_SUSPEND);
server->DdkSuspend(std::move(txn));
EXPECT_FALSE(tester.remove_called());
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, UnbindAndAlsoShutdown) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
server->DdkAsyncRemove();
server->Shutdown();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, SetAndGetGain) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto builder = audio_fidl::GainState::UnownedBuilder();
fidl::aligned<float> target_gain = MockSimpleAudio::kTestGain;
builder.set_gain_db(fidl::unowned_ptr(&target_gain));
auto status =
audio_fidl::StreamConfig::Call::SetGain(zx::unowned_channel(ch->channel), builder.build());
ASSERT_OK(status.status());
auto gain_state =
audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch->channel));
ASSERT_OK(gain_state.status());
ASSERT_EQ(MockSimpleAudio::kTestGain, gain_state->gain_state.gain_db());
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, WatchGainAndCloseStreamBeforeReply) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto builder = audio_fidl::GainState::UnownedBuilder();
fidl::aligned<float> target_gain = MockSimpleAudio::kTestGain;
builder.set_gain_db(fidl::unowned_ptr(&target_gain));
auto status =
audio_fidl::StreamConfig::Call::SetGain(zx::unowned_channel(ch->channel), builder.build());
ASSERT_OK(status.status());
// One watch for initial reply.
auto gain_state =
audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch->channel));
ASSERT_OK(gain_state.status());
ASSERT_EQ(MockSimpleAudio::kTestGain, gain_state->gain_state.gain_db());
// A second watch with no reply since there is no change of gain.
auto f = [](void* arg) -> int {
auto ch = static_cast<audio_fidl::Device::ResultOf::GetChannel*>(arg);
audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel((*ch)->channel));
return 0;
};
thrd_t th;
ASSERT_OK(thrd_create_with_name(&th, f, &ch, "test-thread"));
// We want the watch to be started before we reset the channel triggering a deactivation.
zx::nanosleep(zx::deadline_after(zx::msec(100)));
ch->channel.reset();
int result = -1;
thrd_join(th, &result);
ASSERT_EQ(result, 0);
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, SetAndGetAgc) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto builder = audio_fidl::GainState::UnownedBuilder();
fidl::aligned<bool> target_agc1 = true;
builder.set_agc_enabled(fidl::unowned_ptr(&target_agc1));
auto status1 =
audio_fidl::StreamConfig::Call::SetGain(zx::unowned_channel(ch->channel), builder.build());
ASSERT_OK(status1.status());
auto gain_state1 =
audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch->channel));
ASSERT_OK(gain_state1.status());
ASSERT_TRUE(gain_state1->gain_state.agc_enabled());
fidl::aligned<bool> target_agc2 = false;
builder.set_agc_enabled(fidl::unowned_ptr(&target_agc2));
auto status2 =
audio_fidl::StreamConfig::Call::SetGain(zx::unowned_channel(ch->channel), builder.build());
ASSERT_OK(status2.status());
auto gain_state2 =
audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch->channel));
ASSERT_OK(gain_state2.status());
ASSERT_FALSE(gain_state2->gain_state.agc_enabled());
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, SetAndGetMute) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto builder = audio_fidl::GainState::UnownedBuilder();
fidl::aligned<bool> muted1 = true;
builder.set_muted(fidl::unowned_ptr(&muted1));
auto status1 =
audio_fidl::StreamConfig::Call::SetGain(zx::unowned_channel(ch->channel), builder.build());
ASSERT_OK(status1.status());
auto gain_state1 =
audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch->channel));
ASSERT_OK(gain_state1.status());
ASSERT_TRUE(gain_state1->gain_state.muted());
fidl::aligned<bool> muted2 = false;
builder.set_muted(fidl::unowned_ptr(&muted2));
auto status2 =
audio_fidl::StreamConfig::Call::SetGain(zx::unowned_channel(ch->channel), builder.build());
ASSERT_OK(status2.status());
auto gain_state2 =
audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch->channel));
ASSERT_OK(gain_state2.status());
ASSERT_FALSE(gain_state2->gain_state.muted());
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, SetMuteWhenDisabled) {
struct MockSimpleAudioLocal : public MockSimpleAudio {
MockSimpleAudioLocal(zx_device_t* parent) : MockSimpleAudio(parent) {}
zx_status_t Init() __TA_REQUIRES(domain_token()) override {
auto status = MockSimpleAudio::Init();
cur_gain_state_.can_mute = false;
return status;
}
};
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudioLocal>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto builder = audio_fidl::GainState::UnownedBuilder();
fidl::aligned<bool> muted1 = true;
builder.set_muted(fidl::unowned_ptr(&muted1));
auto status1 =
audio_fidl::StreamConfig::Call::SetGain(zx::unowned_channel(ch->channel), builder.build());
ASSERT_OK(status1.status());
auto gain_state1 =
audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch->channel));
ASSERT_OK(gain_state1.status());
ASSERT_FALSE(gain_state1->gain_state.has_muted());
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, Enumerate1) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto channel = zx::unowned_channel(ch->channel);
audio_fidl::StreamConfig::SyncClient client(std::move(ch->channel));
auto ret = client.GetSupportedFormats();
auto& supported_formats = ret->supported_formats;
auto& formats = supported_formats[0].pcm_supported_formats();
ASSERT_EQ(1, formats.number_of_channels.count());
ASSERT_EQ(1, formats.sample_formats.count());
ASSERT_EQ(audio_fidl::SampleFormat::PCM_SIGNED, formats.sample_formats[0]);
ASSERT_EQ(1, formats.frame_rates.count());
ASSERT_EQ(48000, formats.frame_rates[0]);
ASSERT_EQ(1, formats.bytes_per_sample.count());
ASSERT_EQ(2, formats.bytes_per_sample[0]);
ASSERT_EQ(1, formats.valid_bits_per_sample.count());
ASSERT_EQ(16, formats.valid_bits_per_sample[0]);
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, Enumerate2) {
struct MockSimpleAudioLocal : public MockSimpleAudio {
MockSimpleAudioLocal(zx_device_t* parent) : MockSimpleAudio(parent) {}
zx_status_t Init() __TA_REQUIRES(domain_token()) override {
auto status = MockSimpleAudio::Init();
audio_stream_format_range_t range1 = {};
audio_stream_format_range_t range2 = {};
range1.min_channels = 2;
range1.max_channels = 4;
range1.sample_formats = AUDIO_SAMPLE_FORMAT_24BIT_IN32;
range1.min_frames_per_second = 48'000;
range1.max_frames_per_second = 768'000;
range1.flags = ASF_RANGE_FLAG_FPS_48000_FAMILY;
range2.min_channels = 1;
range2.max_channels = 1;
range2.sample_formats = AUDIO_SAMPLE_FORMAT_32BIT_FLOAT;
range2.min_frames_per_second = 88'200;
range2.max_frames_per_second = 88'200;
range2.flags = ASF_RANGE_FLAG_FPS_CONTINUOUS; // Ok only because min and max fps are equal.
supported_formats_ = fbl::Vector<audio_stream_format_range_t>{range1, range2};
return status;
}
};
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudioLocal>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto channel = zx::unowned_channel(ch->channel);
audio_fidl::StreamConfig::SyncClient client(std::move(ch->channel));
auto ret = client.GetSupportedFormats();
auto& supported_formats = ret->supported_formats;
ASSERT_EQ(2, supported_formats.count());
auto& formats1 = supported_formats[0].pcm_supported_formats();
ASSERT_EQ(3, formats1.number_of_channels.count());
ASSERT_EQ(2, formats1.number_of_channels[0]);
ASSERT_EQ(3, formats1.number_of_channels[1]);
ASSERT_EQ(4, formats1.number_of_channels[2]);
ASSERT_EQ(1, formats1.sample_formats.count());
ASSERT_EQ(audio_fidl::SampleFormat::PCM_SIGNED, formats1.sample_formats[0]);
ASSERT_EQ(5, formats1.frame_rates.count());
std::set<uint32_t> rates1;
for (auto& i : formats1.frame_rates) {
rates1.insert(i);
}
ASSERT_EQ(rates1, std::set<uint32_t>({48'000, 96'000, 192'000, 384'000, 768'000}));
ASSERT_EQ(1, formats1.bytes_per_sample.count());
ASSERT_EQ(4, formats1.bytes_per_sample[0]);
ASSERT_EQ(1, formats1.valid_bits_per_sample.count());
ASSERT_EQ(24, formats1.valid_bits_per_sample[0]);
auto& formats2 = supported_formats[1].pcm_supported_formats();
ASSERT_EQ(1, formats2.number_of_channels.count());
ASSERT_EQ(1, formats2.number_of_channels[0]);
ASSERT_EQ(1, formats2.sample_formats.count());
ASSERT_EQ(audio_fidl::SampleFormat::PCM_FLOAT, formats2.sample_formats[0]);
ASSERT_EQ(1, formats2.frame_rates.count());
std::set<uint32_t> rates2;
for (auto& i : formats2.frame_rates) {
rates2.insert(i);
}
ASSERT_EQ(rates2, std::set<uint32_t>({88'200}));
ASSERT_EQ(1, formats2.bytes_per_sample.count());
ASSERT_EQ(4, formats2.bytes_per_sample[0]);
ASSERT_EQ(1, formats2.valid_bits_per_sample.count());
ASSERT_EQ(32, formats2.valid_bits_per_sample[0]);
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, CreateRingBuffer1) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto channel = zx::unowned_channel(ch->channel);
audio_fidl::StreamConfig::SyncClient client(std::move(ch->channel));
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
fidl::aligned<audio_fidl::PcmFormat> pcm_format = GetDefaultPcmFormat();
auto builder = audio_fidl::Format::UnownedBuilder();
builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
client.CreateRingBuffer(builder.build(), std::move(remote));
auto result = audio_fidl::RingBuffer::Call::GetProperties(zx::unowned_channel(local));
ASSERT_OK(result.status());
ASSERT_EQ(result->properties.fifo_depth(), MockSimpleAudio::kTestFifoDepth);
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, CreateRingBuffer2) {
struct MockSimpleAudioLocal : public MockSimpleAudio {
MockSimpleAudioLocal(zx_device_t* parent) : MockSimpleAudio(parent) {}
zx_status_t Init() __TA_REQUIRES(domain_token()) override {
audio_stream_format_range_t range = {};
range.min_channels = 1;
range.max_channels = 4;
range.sample_formats = AUDIO_SAMPLE_FORMAT_24BIT_IN32 | AUDIO_SAMPLE_FORMAT_FLAG_UNSIGNED;
range.min_frames_per_second = 22050;
range.max_frames_per_second = 88200;
range.flags = ASF_RANGE_FLAG_FPS_44100_FAMILY;
supported_formats_.push_back(range);
return MockSimpleAudio::Init();
}
};
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudioLocal>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto channel = zx::unowned_channel(ch->channel);
audio_fidl::StreamConfig::SyncClient client(std::move(ch->channel));
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
audio_fidl::PcmFormat pcm_format = {};
pcm_format.number_of_channels = 4;
pcm_format.channels_to_use_bitmask = 0x0f;
pcm_format.sample_format = audio_fidl::SampleFormat::PCM_UNSIGNED;
pcm_format.frame_rate = 44100;
pcm_format.bytes_per_sample = 4;
pcm_format.valid_bits_per_sample = 24;
auto builder = audio_fidl::Format::UnownedBuilder();
builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
client.CreateRingBuffer(builder.build(), std::move(remote));
auto result = audio_fidl::RingBuffer::Call::GetProperties(zx::unowned_channel(local));
ASSERT_OK(result.status());
ASSERT_EQ(result->properties.fifo_depth(), MockSimpleAudio::kTestFifoDepth);
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, SetBadFormat1) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto channel = zx::unowned_channel(ch->channel);
audio_fidl::StreamConfig::SyncClient client(std::move(ch->channel));
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
// Define a pretty bad format.
audio_fidl::PcmFormat pcm_format = {};
pcm_format.sample_format = audio_fidl::SampleFormat::PCM_SIGNED;
auto builder = audio_fidl::Format::UnownedBuilder();
builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
auto result0 = client.CreateRingBuffer(builder.build(), std::move(remote));
ASSERT_EQ(ZX_OK, result0.status()); // CreateRingBuffer is sent successfully.
auto result1 = client.GetSupportedFormats();
ASSERT_EQ(ZX_ERR_PEER_CLOSED, result1.status()); // With a bad format we get a channel close.
auto result2 = audio_fidl::RingBuffer::Call::GetProperties(zx::unowned_channel(local));
ASSERT_EQ(ZX_ERR_PEER_CLOSED, result2.status()); // With a bad format we get a channel close.
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, SetBadFormat2) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto channel = zx::unowned_channel(ch->channel);
audio_fidl::StreamConfig::SyncClient client(std::move(ch->channel));
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
// Define an almost good format.
audio_fidl::PcmFormat pcm_format = GetDefaultPcmFormat();
pcm_format.frame_rate = 48001; // Bad rate.
auto builder = audio_fidl::Format::UnownedBuilder();
builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
auto result0 = client.CreateRingBuffer(builder.build(), std::move(remote));
ASSERT_EQ(ZX_OK, result0.status()); // CreateRingBuffer is sent successfully.
auto result1 = client.GetSupportedFormats();
ASSERT_EQ(ZX_ERR_PEER_CLOSED, result1.status()); // With a bad format we get a channel close.
auto result2 = audio_fidl::RingBuffer::Call::GetProperties(zx::unowned_channel(local));
ASSERT_EQ(ZX_ERR_PEER_CLOSED, result2.status()); // With a bad format we get a channel close.
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, GetIds) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto result = audio_fidl::StreamConfig::Call::GetProperties(zx::unowned_channel(ch->channel));
ASSERT_OK(result.status());
audio_stream_unique_id_t mic = AUDIO_STREAM_UNIQUE_ID_BUILTIN_MICROPHONE;
ASSERT_BYTES_EQ(result->properties.unique_id().data(), mic.data,
strlen(reinterpret_cast<char*>(mic.data)));
ASSERT_BYTES_EQ(result->properties.manufacturer().data(), "Bike Sheds, Inc.",
strlen("Bike Sheds, Inc."));
ASSERT_EQ(result->properties.clock_domain(), MockSimpleAudio::kTestClockDomain);
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, MultipleChannelsPlugDetectState) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
// We get 2 channels from the one FIDL channel acquired via FidlClient() using GetChannel.
audio_fidl::Device::ResultOf::GetChannel ch1 = client.GetChannel();
audio_fidl::Device::ResultOf::GetChannel ch2 = client.GetChannel();
ASSERT_EQ(ch1.status(), ZX_OK);
ASSERT_EQ(ch2.status(), ZX_OK);
auto prop1 = audio_fidl::StreamConfig::Call::GetProperties(zx::unowned_channel(ch1->channel));
auto prop2 = audio_fidl::StreamConfig::Call::GetProperties(zx::unowned_channel(ch2->channel));
ASSERT_OK(prop1.status());
ASSERT_OK(prop2.status());
ASSERT_EQ(prop1->properties.plug_detect_capabilities(),
audio_fidl::PlugDetectCapabilities::CAN_ASYNC_NOTIFY);
ASSERT_EQ(prop2->properties.plug_detect_capabilities(),
audio_fidl::PlugDetectCapabilities::CAN_ASYNC_NOTIFY);
auto state1 = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch1->channel));
auto state2 = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch2->channel));
ASSERT_OK(state1.status());
ASSERT_OK(state2.status());
ASSERT_FALSE(state1->plug_state.plugged());
ASSERT_FALSE(state2->plug_state.plugged());
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, WatchPlugDetectAndCloseStreamBeforeReply) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
// We get 2 channels from the one FIDL channel acquired via FidlClient() using GetChannel.
audio_fidl::Device::ResultOf::GetChannel ch1 = client.GetChannel();
audio_fidl::Device::ResultOf::GetChannel ch2 = client.GetChannel();
ASSERT_EQ(ch1.status(), ZX_OK);
ASSERT_EQ(ch2.status(), ZX_OK);
auto prop1 = audio_fidl::StreamConfig::Call::GetProperties(zx::unowned_channel(ch1->channel));
auto prop2 = audio_fidl::StreamConfig::Call::GetProperties(zx::unowned_channel(ch2->channel));
ASSERT_OK(prop1.status());
ASSERT_OK(prop2.status());
ASSERT_EQ(prop1->properties.plug_detect_capabilities(),
audio_fidl::PlugDetectCapabilities::CAN_ASYNC_NOTIFY);
ASSERT_EQ(prop2->properties.plug_detect_capabilities(),
audio_fidl::PlugDetectCapabilities::CAN_ASYNC_NOTIFY);
// Watch each channel for initial reply.
auto state1 = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch1->channel));
auto state2 = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch2->channel));
ASSERT_OK(state1.status());
ASSERT_OK(state2.status());
ASSERT_FALSE(state1->plug_state.plugged());
ASSERT_FALSE(state2->plug_state.plugged());
// Secondary watches with no reply since there is no change of plug detect state.
auto f = [](void* arg) -> int {
audio_fidl::Device::ResultOf::GetChannel* ch =
static_cast<audio_fidl::Device::ResultOf::GetChannel*>(arg);
audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel((*ch)->channel));
return 0;
};
thrd_t th1;
ASSERT_OK(thrd_create_with_name(&th1, f, &ch1, "test-thread-1"));
thrd_t th2;
ASSERT_OK(thrd_create_with_name(&th2, f, &ch2, "test-thread-2"));
// We want the watches to be started before we reset the channels triggering deactivations.
zx::nanosleep(zx::deadline_after(zx::msec(100)));
ch1->channel.reset();
ch2->channel.reset();
int result = -1;
thrd_join(th1, &result);
ASSERT_EQ(result, 0);
result = -1;
thrd_join(th2, &result);
ASSERT_EQ(result, 0);
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, MultipleChannelsPlugDetectNotify) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
// We get 2 channels from the one FIDL channel acquired via FidlClient() using GetChannel.
audio_fidl::Device::ResultOf::GetChannel ch1 = client.GetChannel();
audio_fidl::Device::ResultOf::GetChannel ch2 = client.GetChannel();
audio_fidl::Device::ResultOf::GetChannel ch3 = client.GetChannel();
ASSERT_EQ(ch1.status(), ZX_OK);
ASSERT_EQ(ch2.status(), ZX_OK);
ASSERT_EQ(ch3.status(), ZX_OK);
auto state1a = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch1->channel));
auto state2a = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch2->channel));
auto state3a = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch3->channel));
ASSERT_OK(state1a.status());
ASSERT_OK(state2a.status());
ASSERT_OK(state3a.status());
ASSERT_FALSE(state1a->plug_state.plugged());
ASSERT_FALSE(state2a->plug_state.plugged());
ASSERT_FALSE(state3a->plug_state.plugged());
server->PostSetPlugState(true, zx::duration(zx::msec(100)));
auto state1b = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch1->channel));
auto state2b = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch2->channel));
auto state3b = audio_fidl::StreamConfig::Call::WatchPlugState(zx::unowned_channel(ch3->channel));
ASSERT_OK(state1b.status());
ASSERT_OK(state2b.status());
ASSERT_OK(state3b.status());
ASSERT_TRUE(state1b->plug_state.plugged());
ASSERT_TRUE(state2b->plug_state.plugged());
ASSERT_TRUE(state3b->plug_state.plugged());
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, MultipleChannelsGainState) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
// We get 2 channels from the one FIDL channel acquired via FidlClient() using GetChannel.
audio_fidl::Device::ResultOf::GetChannel ch1 = client.GetChannel();
audio_fidl::Device::ResultOf::GetChannel ch2 = client.GetChannel();
ASSERT_EQ(ch1.status(), ZX_OK);
ASSERT_EQ(ch2.status(), ZX_OK);
auto state1 = audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch1->channel));
auto state2 = audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch2->channel));
ASSERT_OK(state1.status());
ASSERT_OK(state2.status());
ASSERT_EQ(0.f, state1->gain_state.gain_db());
ASSERT_EQ(0.f, state2->gain_state.gain_db());
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, MultipleChannelsGainStateNotify) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
// We get 2 channels from the one FIDL channel acquired via FidlClient() using GetChannel.
audio_fidl::Device::ResultOf::GetChannel ch1 = client.GetChannel();
audio_fidl::Device::ResultOf::GetChannel ch2 = client.GetChannel();
audio_fidl::Device::ResultOf::GetChannel ch3 = client.GetChannel();
ASSERT_EQ(ch1.status(), ZX_OK);
ASSERT_EQ(ch2.status(), ZX_OK);
ASSERT_EQ(ch3.status(), ZX_OK);
auto state1a = audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch1->channel));
auto state2a = audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch2->channel));
auto state3a = audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch3->channel));
ASSERT_OK(state1a.status());
ASSERT_OK(state2a.status());
ASSERT_OK(state3a.status());
ASSERT_EQ(0.f, state1a->gain_state.gain_db());
ASSERT_EQ(0.f, state2a->gain_state.gain_db());
ASSERT_EQ(0.f, state3a->gain_state.gain_db());
auto f = [](void* arg) -> int {
zx::nanosleep(zx::deadline_after(zx::msec(100)));
audio_fidl::Device::ResultOf::GetChannel* ch1 =
static_cast<audio_fidl::Device::ResultOf::GetChannel*>(arg);
auto builder = audio_fidl::GainState::UnownedBuilder();
fidl::aligned<bool> muted = false;
fidl::aligned<bool> agc = false;
builder.set_muted(fidl::unowned_ptr(&muted));
builder.set_agc_enabled(fidl::unowned_ptr(&agc));
fidl::aligned<float> target_gain = MockSimpleAudio::kTestGain;
builder.set_gain_db(fidl::unowned_ptr(&target_gain));
audio_fidl::StreamConfig::Call::SetGain(zx::unowned_channel((*ch1)->channel), builder.build());
return 0;
};
thrd_t th;
ASSERT_OK(thrd_create_with_name(&th, f, &ch1, "test-thread"));
auto state1b = audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch1->channel));
auto state2b = audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch2->channel));
auto state3b = audio_fidl::StreamConfig::Call::WatchGainState(zx::unowned_channel(ch3->channel));
ASSERT_OK(state1b.status());
ASSERT_OK(state2b.status());
ASSERT_OK(state3b.status());
ASSERT_EQ(MockSimpleAudio::kTestGain, state1b->gain_state.gain_db());
ASSERT_EQ(MockSimpleAudio::kTestGain, state2b->gain_state.gain_db());
ASSERT_EQ(MockSimpleAudio::kTestGain, state3b->gain_state.gain_db());
int result = -1;
thrd_join(th, &result);
ASSERT_EQ(result, 0);
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, RingBufferTests) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
fidl::aligned<audio_fidl::PcmFormat> pcm_format = GetDefaultPcmFormat();
auto builder = audio_fidl::Format::UnownedBuilder();
builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
auto rb = audio_fidl::StreamConfig::Call::CreateRingBuffer(zx::unowned_channel(ch->channel),
builder.build(), std::move(remote));
ASSERT_OK(rb.status());
constexpr uint32_t kNumberOfPositionNotifications = 5;
// Buffer is set to hold at least 1 second, with kNumberOfPositionNotifications notifications
// per ring buffer (i.e. per second) we set the time waiting for the watch below to 200ms+.
auto vmo = audio_fidl::RingBuffer::Call::GetVmo(
zx::unowned_channel(local), MockSimpleAudio::kTestFrameRate, kNumberOfPositionNotifications);
ASSERT_OK(vmo.status());
auto start = audio_fidl::RingBuffer::Call::Start(zx::unowned_channel(local));
ASSERT_OK(start.status());
auto position =
audio_fidl::RingBuffer::Call::WatchClockRecoveryPositionInfo(zx::unowned_channel(local));
ASSERT_EQ(MockSimpleAudio::kTestPositionNotify, position->position_info.position);
auto stop = audio_fidl::RingBuffer::Call::Stop(zx::unowned_channel(local));
ASSERT_OK(stop.status());
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, WatchPositionAndCloseRingBufferBeforeReply) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
fidl::aligned<audio_fidl::PcmFormat> pcm_format = GetDefaultPcmFormat();
auto builder = audio_fidl::Format::UnownedBuilder();
builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
auto rb = audio_fidl::StreamConfig::Call::CreateRingBuffer(zx::unowned_channel(ch->channel),
builder.build(), std::move(remote));
ASSERT_OK(rb.status());
constexpr uint32_t kNumberOfPositionNotifications = 5;
// Buffer is set to hold at least 1 second, with kNumberOfPositionNotifications notifications
// per ring buffer (i.e. per second) the time waiting before getting a position reply is 200ms+.
auto vmo = audio_fidl::RingBuffer::Call::GetVmo(
zx::unowned_channel(local), MockSimpleAudio::kTestFrameRate, kNumberOfPositionNotifications);
ASSERT_OK(vmo.status());
auto start = audio_fidl::RingBuffer::Call::Start(zx::unowned_channel(local));
ASSERT_OK(start.status());
// Watch position notifications.
auto f = [](void* arg) -> int {
auto ch = static_cast<zx::channel*>(arg);
audio_fidl::RingBuffer::Call::WatchClockRecoveryPositionInfo(zx::unowned_channel(*ch));
return 0;
};
thrd_t th;
ASSERT_OK(thrd_create_with_name(&th, f, &local, "test-thread"));
// We want the watch to be started before we reset the channel triggering a deactivation.
zx::nanosleep(zx::deadline_after(zx::msec(100)));
local.reset();
ch->channel.reset();
int result = -1;
thrd_join(th, &result);
ASSERT_EQ(result, 0);
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, ClientCloseStreamConfigProtocol) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
ch->channel.reset();
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, ClientCloseRingBufferProtocol) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto channel = zx::unowned_channel(ch->channel);
audio_fidl::StreamConfig::SyncClient client(std::move(ch->channel));
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
fidl::aligned<audio_fidl::PcmFormat> pcm_format = GetDefaultPcmFormat();
auto builder = audio_fidl::Format::UnownedBuilder();
builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
auto ret = client.CreateRingBuffer(builder.build(), std::move(remote));
ASSERT_OK(ret.status());
local.reset();
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, ClientCloseStreamConfigProtocolWithARingBufferProtocol) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
ASSERT_EQ(ch.status(), ZX_OK);
auto channel = zx::unowned_channel(ch->channel);
audio_fidl::StreamConfig::SyncClient client(std::move(ch->channel));
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
fidl::aligned<audio_fidl::PcmFormat> pcm_format = GetDefaultPcmFormat();
auto builder = audio_fidl::Format::UnownedBuilder();
builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
auto ret = client.CreateRingBuffer(builder.build(), std::move(remote));
ASSERT_OK(ret.status());
client.mutable_channel()->reset();
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
TEST(SimpleAudioTest, NonPriviledged) {
fake_ddk::Bind tester;
auto server = audio::SimpleAudioStream::Create<MockSimpleAudio>(fake_ddk::kFakeParent);
ASSERT_NOT_NULL(server);
audio_fidl::Device::SyncClient client_wrap(std::move(tester.FidlClient()));
audio_fidl::Device::ResultOf::GetChannel ch1 = client_wrap.GetChannel();
audio_fidl::Device::ResultOf::GetChannel ch2 = client_wrap.GetChannel();
audio_fidl::Device::ResultOf::GetChannel ch3 = client_wrap.GetChannel();
ASSERT_EQ(ch1.status(), ZX_OK);
ASSERT_EQ(ch2.status(), ZX_OK);
ASSERT_EQ(ch3.status(), ZX_OK);
fidl::aligned<audio_fidl::PcmFormat> pcm_format = GetDefaultPcmFormat();
auto builder = audio_fidl::Format::UnownedBuilder();
builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
auto channel1 = zx::unowned_channel(ch1->channel);
audio_fidl::StreamConfig::SyncClient client1(std::move(ch1->channel));
zx::channel local1, remote1;
ASSERT_OK(zx::channel::create(0, &local1, &remote1));
auto ret1 = client1.CreateRingBuffer(builder.build(), std::move(remote1));
ASSERT_OK(ret1.status());
audio_fidl::RingBuffer::SyncClient ringbuffer1(std::move(local1));
auto stop1 = ringbuffer1.Stop();
ASSERT_OK(stop1.status()); // Priviledged channel.
auto channel2 = zx::unowned_channel(ch2->channel);
audio_fidl::StreamConfig::SyncClient client2(std::move(ch2->channel));
zx::channel local2, remote2;
ASSERT_OK(zx::channel::create(0, &local2, &remote2));
auto ret2 = client2.CreateRingBuffer(builder.build(), std::move(remote2));
ASSERT_OK(ret2.status());
audio_fidl::RingBuffer::SyncClient ringbuffer2(std::move(local2));
auto stop2 = ringbuffer2.Stop();
ASSERT_NOT_OK(stop2.status()); // Non-priviledged channel.
auto channel3 = zx::unowned_channel(ch3->channel);
audio_fidl::StreamConfig::SyncClient client3(std::move(ch3->channel));
zx::channel local3, remote3;
ASSERT_OK(zx::channel::create(0, &local3, &remote3));
auto ret3 = client3.CreateRingBuffer(builder.build(), std::move(remote3));
ASSERT_OK(ret3.status());
audio_fidl::RingBuffer::SyncClient ringbuffer3(std::move(local3));
auto stop3 = ringbuffer3.Stop();
ASSERT_NOT_OK(stop3.status()); // Non-priviledged channel.
server->DdkAsyncRemove();
EXPECT_TRUE(tester.Ok());
server->DdkRelease();
}
} // namespace audio