[audio_core][test] Refactor audio_core integration tests, step 2
The existing TestFixture methods ExpectCallback and ExpectError are
useful shorthands but can produce difficult-to-diagnose error messages
because they don't contain any context about the error. To address this
difficulty, I've replaced the ErrorHandler() and CompletionCallback()
methods with:
* AddErrorHandler, which ties each error handler to a specific FIDL
protocol object to help diagnose which protocol object failed
* AddCallback, which is like the old CompletionCallback except that
each callback is named. Additionally, multiple callbacks can be queued
to test an expected sequence of calls (this simplifies a few tests).
Most of the changes in this CL follow mechanically from the above
changes. The exception is api/gain_control_test.cc, which was also
dramatically simplified using a TYPED_TEST_SUITE.
Bug: 50645
Multiply: audio-core-api-tests
Change-Id: I8bd8d89000f6667ee8e9c5a77e7ab2911a9fabc5
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/401610
Commit-Queue: Tom Bergan <tombergan@google.com>
Reviewed-by: Tim Detwiler <tjdetwiler@google.com>
Testability-Review: Tim Detwiler <tjdetwiler@google.com>
diff --git a/src/media/audio/audio_core/test/api/BUILD.gn b/src/media/audio/audio_core/test/api/BUILD.gn
index 3679212..2791d26 100644
--- a/src/media/audio/audio_core/test/api/BUILD.gn
+++ b/src/media/audio/audio_core/test/api/BUILD.gn
@@ -39,7 +39,6 @@
"audio_renderer_test.cc",
"audio_tuner_test.cc",
"gain_control_test.cc",
- "gain_control_test.h",
"usage_gain_reporter_test.cc",
"usage_reporter_test.cc",
"volume_control.cc",
diff --git a/src/media/audio/audio_core/test/api/activity_reporter_test.cc b/src/media/audio/audio_core/test/api/activity_reporter_test.cc
index 5c8ded7..d6f20b3 100644
--- a/src/media/audio/audio_core/test/api/activity_reporter_test.cc
+++ b/src/media/audio/audio_core/test/api/activity_reporter_test.cc
@@ -30,11 +30,9 @@
TEST_F(ActivityReporterTest, ConnectToActivityReporter) {
fuchsia::media::ActivityReporterPtr activity_reporter;
environment()->ConnectToService(activity_reporter.NewRequest());
- activity_reporter.set_error_handler(ErrorHandler());
+ AddErrorHandler(activity_reporter, "ActivityReporter");
- activity_reporter->WatchRenderActivity(
- CompletionCallback([&](const std::vector<fuchsia::media::AudioRenderUsage>& activity) {}));
-
+ activity_reporter->WatchRenderActivity(AddCallback("WatchRenderActivity"));
ExpectCallback();
}
diff --git a/src/media/audio/audio_core/test/api/audio_capturer_test.cc b/src/media/audio/audio_core/test/api/audio_capturer_test.cc
index 1fc551c..2a03c26 100644
--- a/src/media/audio/audio_core/test/api/audio_capturer_test.cc
+++ b/src/media/audio/audio_core/test/api/audio_capturer_test.cc
@@ -4,6 +4,7 @@
#include <fuchsia/media/cpp/fidl.h>
#include <lib/zx/clock.h>
+#include <zircon/device/audio.h>
#include "lib/media/audio/cpp/types.h"
#include "src/media/audio/lib/clock/clone_mono.h"
@@ -23,23 +24,16 @@
HermeticAudioTest::SetUp();
audio_core_->CreateAudioCapturer(false, audio_capturer_.NewRequest());
- audio_capturer_.set_error_handler(ErrorHandler());
+ AddErrorHandler(audio_capturer_, "AudioCapturer");
}
void TearDown() override {
gain_control_.Unbind();
-
- EXPECT_EQ(bound_capturer_expected_, audio_capturer_.is_bound());
audio_capturer_.Unbind();
HermeticAudioTest::TearDown();
}
- void SetNegativeExpectations() override {
- HermeticAudioTest::SetNegativeExpectations();
- bound_capturer_expected_ = false;
- }
-
void SetFormat() {
audio_capturer_->SetPcmStreamType(
media::CreateAudioStreamType(fuchsia::media::AudioSampleFormat::SIGNED_16, 1, 16000));
@@ -56,8 +50,6 @@
fuchsia::media::AudioCapturerPtr audio_capturer_;
fuchsia::media::audio::GainControlPtr gain_control_;
-
- bool bound_capturer_expected_ = true;
};
class AudioCapturerClockTest : public AudioCapturerTest {
@@ -68,8 +60,9 @@
zx::clock GetAndValidateReferenceClock() {
zx::clock clock;
- audio_capturer_->GetReferenceClock(CompletionCallback(
- [&clock](zx::clock received_clock) { clock = std::move(received_clock); }));
+ audio_capturer_->GetReferenceClock(
+ AddCallback("GetReferenceClock",
+ [&clock](zx::clock received_clock) { clock = std::move(received_clock); }));
ExpectCallback();
EXPECT_TRUE(clock.is_valid());
@@ -112,27 +105,21 @@
SetFormat();
SetUpPayloadBuffer();
- auto callbacks = 0u;
- audio_capturer_->CaptureAt(
- 0, 0, 4000, [&callbacks](fuchsia::media::StreamPacket) { EXPECT_EQ(0u, callbacks++); });
- audio_capturer_->CaptureAt(
- 0, 4000, 4000, [&callbacks](fuchsia::media::StreamPacket) { EXPECT_EQ(1u, callbacks++); });
- audio_capturer_->CaptureAt(
- 0, 8000, 4000, [&callbacks](fuchsia::media::StreamPacket) { EXPECT_EQ(2u, callbacks++); });
- audio_capturer_->CaptureAt(
- 0, 12000, 4000, [&callbacks](fuchsia::media::StreamPacket) { EXPECT_EQ(3u, callbacks++); });
+ audio_capturer_->CaptureAt(0, 0, 4000, AddCallback("CaptureAt 0"));
+ audio_capturer_->CaptureAt(0, 4000, 4000, AddCallback("CaptureAt 4000"));
+ audio_capturer_->CaptureAt(0, 8000, 4000, AddCallback("CaptureAt 8000"));
+ audio_capturer_->CaptureAt(0, 12000, 4000, AddCallback("CaptureAt 12000"));
// Packets should complete in strict order, with DiscardAllPackets' completion afterward.
- audio_capturer_->DiscardAllPackets(
- CompletionCallback([&callbacks]() { EXPECT_EQ(4u, callbacks); }));
+ audio_capturer_->DiscardAllPackets(AddCallback("DiscardAllPackets"));
ExpectCallback();
}
TEST_F(AudioCapturerTest, DiscardAll_WithNoVmoShouldDisconnect) {
SetFormat();
- audio_capturer_->DiscardAllPackets(CompletionCallback());
- ExpectDisconnect();
+ audio_capturer_->DiscardAllPackets(AddUnexpectedCallback("DiscardAllPackets"));
+ ExpectDisconnect(audio_capturer_);
}
// DiscardAllPackets should fail, if async capture is active
@@ -140,13 +127,12 @@
SetFormat();
SetUpPayloadBuffer();
- audio_capturer_.events().OnPacketProduced =
- CompletionCallback([](fuchsia::media::StreamPacket) {});
+ audio_capturer_.events().OnPacketProduced = AddCallback("OnPacketProduced");
audio_capturer_->StartAsyncCapture(1600);
ExpectCallback();
- audio_capturer_->DiscardAllPackets(CompletionCallback());
- ExpectDisconnect();
+ audio_capturer_->DiscardAllPackets(AddUnexpectedCallback("DiscardAllPackets"));
+ ExpectDisconnect(audio_capturer_);
}
// DiscardAllPackets should fail, if async capture is in the process of stopping
@@ -154,14 +140,13 @@
SetFormat();
SetUpPayloadBuffer();
- audio_capturer_.events().OnPacketProduced =
- CompletionCallback([](fuchsia::media::StreamPacket) {});
+ audio_capturer_.events().OnPacketProduced = AddCallback("OnPacketProduced");
audio_capturer_->StartAsyncCapture(1600);
ExpectCallback();
audio_capturer_->StopAsyncCaptureNoReply();
- audio_capturer_->DiscardAllPackets(CompletionCallback());
- ExpectDisconnect();
+ audio_capturer_->DiscardAllPackets(AddUnexpectedCallback("DiscardAllPackets"));
+ ExpectDisconnect(audio_capturer_);
}
// DiscardAllPackets should succeed, if async capture is completely stopped
@@ -169,15 +154,14 @@
SetFormat();
SetUpPayloadBuffer();
- audio_capturer_.events().OnPacketProduced =
- CompletionCallback([](fuchsia::media::StreamPacket) {});
+ audio_capturer_.events().OnPacketProduced = AddCallback("OnPacketProduced");
audio_capturer_->StartAsyncCapture(1600);
ExpectCallback();
- audio_capturer_->StopAsyncCapture(CompletionCallback());
+ audio_capturer_->StopAsyncCapture(AddCallback("StopAsyncCapture"));
ExpectCallback();
- audio_capturer_->DiscardAllPackets(CompletionCallback());
+ audio_capturer_->DiscardAllPackets(AddCallback("DiscardAllPackets"));
ExpectCallback();
}
@@ -186,7 +170,7 @@
SetFormat();
audio_capturer_->DiscardAllPacketsNoReply();
- ExpectDisconnect();
+ ExpectDisconnect(audio_capturer_);
}
// DiscardAllPacketsNoReply should fail, if async capture is active
@@ -194,13 +178,12 @@
SetFormat();
SetUpPayloadBuffer();
- audio_capturer_.events().OnPacketProduced =
- CompletionCallback([](fuchsia::media::StreamPacket) {});
+ audio_capturer_.events().OnPacketProduced = AddCallback("OnPacketProduced");
audio_capturer_->StartAsyncCapture(1600);
ExpectCallback();
audio_capturer_->DiscardAllPacketsNoReply();
- ExpectDisconnect();
+ ExpectDisconnect(audio_capturer_);
}
// DiscardAllPacketsNoReply should fail, if async capture is in the process of stopping
@@ -208,14 +191,13 @@
SetFormat();
SetUpPayloadBuffer();
- audio_capturer_.events().OnPacketProduced =
- CompletionCallback([](fuchsia::media::StreamPacket) {});
+ audio_capturer_.events().OnPacketProduced = AddCallback("OnPacketProduced");
audio_capturer_->StartAsyncCapture(1600);
ExpectCallback();
audio_capturer_->StopAsyncCaptureNoReply();
audio_capturer_->DiscardAllPacketsNoReply();
- ExpectDisconnect();
+ ExpectDisconnect(audio_capturer_);
}
// DiscardAllPacketsNoReply should succeed, if async capture is completely stopped
@@ -223,12 +205,11 @@
SetFormat();
SetUpPayloadBuffer();
- audio_capturer_.events().OnPacketProduced =
- CompletionCallback([](fuchsia::media::StreamPacket) {});
+ audio_capturer_.events().OnPacketProduced = AddCallback("OnPacketProduced");
audio_capturer_->StartAsyncCapture(1600);
ExpectCallback();
- audio_capturer_->StopAsyncCapture(CompletionCallback());
+ audio_capturer_->StopAsyncCapture(AddCallback("StopAsyncCapture"));
ExpectCallback();
audio_capturer_->DiscardAllPacketsNoReply();
@@ -252,14 +233,14 @@
// Also negative testing: 0/tiny/huge num frames (bigger than packet)
TEST_F(AudioCapturerTest, Stop_WhenStoppedShouldDisconnect) {
- audio_capturer_->StopAsyncCapture(CompletionCallback());
- ExpectDisconnect();
+ audio_capturer_->StopAsyncCapture(AddUnexpectedCallback("StopAsyncCapture"));
+ ExpectDisconnect(audio_capturer_);
}
// Also test before format set, before packets submitted
TEST_F(AudioCapturerTest, StopNoReply_WhenStoppedShouldDisconnect) {
audio_capturer_->StopAsyncCaptureNoReply();
- ExpectDisconnect();
+ ExpectDisconnect(audio_capturer_);
}
// Also before format set, before packets submitted
@@ -268,27 +249,16 @@
// or GainControl binding a chance to disconnect, if an error occurred.
TEST_F(AudioCapturerTest, BindGainControl) {
// Validate AudioCapturers can create GainControl interfaces.
- bool capturer_error_occurred = false;
- bool capturer_error_occurred_2 = false;
- bool gain_error_occurred = false;
- bool gain_error_occurred_2 = false;
-
- audio_capturer_.set_error_handler(
- ErrorHandler([&capturer_error_occurred](zx_status_t) { capturer_error_occurred = true; }));
-
audio_capturer_->BindGainControl(gain_control_.NewRequest());
- gain_control_.set_error_handler(
- ErrorHandler([&gain_error_occurred](zx_status_t) { gain_error_occurred = true; }));
+ AddErrorHandler(gain_control_, "AudioCapturer::GainControl");
fuchsia::media::AudioCapturerPtr audio_capturer_2;
audio_core_->CreateAudioCapturer(true, audio_capturer_2.NewRequest());
- audio_capturer_2.set_error_handler(ErrorHandler(
- [&capturer_error_occurred_2](zx_status_t) { capturer_error_occurred_2 = true; }));
+ AddErrorHandler(audio_capturer_2, "AudioCapturer2");
fuchsia::media::audio::GainControlPtr gain_control_2;
audio_capturer_2->BindGainControl(gain_control_2.NewRequest());
- gain_control_2.set_error_handler(
- ErrorHandler([&gain_error_occurred_2](zx_status_t) { gain_error_occurred_2 = true; }));
+ AddErrorHandler(gain_control_2, "AudioCapturer::GainControl2");
// What happens to a child gain_control, when a capturer is unbound?
audio_capturer_.Unbind();
@@ -297,27 +267,11 @@
gain_control_2.Unbind();
// Give audio_capturer_ a chance to disconnect gain_control_
- ExpectDisconnect();
-
- // If gain_control_ disconnected as expected, reset errors for the next step.
- if (gain_error_occurred) {
- error_expected_ = false;
- error_occurred_ = false;
- }
+ ExpectDisconnect(gain_control_);
// Give time for other Disconnects to occur, if they must.
- audio_capturer_2->GetStreamType(CompletionCallback([](fuchsia::media::StreamType) {}));
+ audio_capturer_2->GetStreamType(AddCallback("GetStreamType"));
ExpectCallback();
-
- // Explicitly unbinding audio_capturer_ should disconnect gain_control_.
- EXPECT_FALSE(capturer_error_occurred);
- EXPECT_TRUE(gain_error_occurred);
- EXPECT_FALSE(gain_control_.is_bound());
-
- // gain_2's parent should NOT disconnect, nor a gain_2 disconnect callback.
- EXPECT_FALSE(capturer_error_occurred_2);
- EXPECT_FALSE(gain_error_occurred_2);
- EXPECT_TRUE(audio_capturer_2.is_bound());
}
// Null requests to BindGainControl should have no effect.
@@ -325,7 +279,7 @@
audio_capturer_->BindGainControl(nullptr);
// Give time for Disconnect to occur, if it must.
- audio_capturer_->GetStreamType(CompletionCallback([](fuchsia::media::StreamType) {}));
+ audio_capturer_->GetStreamType(AddCallback("GetStreamType"));
ExpectCallback();
}
@@ -415,7 +369,7 @@
audio_capturer_->SetReferenceClock(std::move(dupe_clock));
- ExpectDisconnect();
+ ExpectDisconnect(audio_capturer_);
}
// inadequate ZX_RIGHTS -- no READ should cause GetReferenceClock to fail.
@@ -425,7 +379,7 @@
audio_capturer_->SetReferenceClock(std::move(dupe_clock));
- ExpectDisconnect();
+ ExpectDisconnect(audio_capturer_);
}
// If client-submitted clock has ZX_RIGHT_WRITE, this should be removed upon GetReferenceClock
@@ -455,7 +409,7 @@
audio_capturer_->CaptureAt(0, 0, 8000, [](fuchsia::media::StreamPacket) { FAIL(); });
audio_capturer_->SetReferenceClock(clock::CloneOfMonotonic());
- ExpectDisconnect();
+ ExpectDisconnect(audio_capturer_);
}
// Setting the reference clock should succeed, after all active capture packets have returned
@@ -463,7 +417,7 @@
SetFormat();
SetUpPayloadBuffer();
- audio_capturer_->CaptureAt(0, 0, 8000, CompletionCallback([](fuchsia::media::StreamPacket) {}));
+ audio_capturer_->CaptureAt(0, 0, 8000, AddCallback("CaptureAt"));
ExpectCallback();
audio_capturer_->SetReferenceClock(clock::AdjustableCloneOfMonotonic());
@@ -476,7 +430,7 @@
SetUpPayloadBuffer();
audio_capturer_->CaptureAt(0, 0, 8000, [](fuchsia::media::StreamPacket) {});
- audio_capturer_->DiscardAllPackets(CompletionCallback());
+ audio_capturer_->DiscardAllPackets(AddCallback("DiscardAllPackets"));
ExpectCallback();
audio_capturer_->SetReferenceClock(clock::AdjustableCloneOfMonotonic());
@@ -488,13 +442,12 @@
SetFormat();
SetUpPayloadBuffer();
- audio_capturer_.events().OnPacketProduced =
- CompletionCallback([](fuchsia::media::StreamPacket) {});
+ audio_capturer_.events().OnPacketProduced = AddCallback("OnPacketProduced");
audio_capturer_->StartAsyncCapture(1600);
ExpectCallback();
audio_capturer_->SetReferenceClock(clock::CloneOfMonotonic());
- ExpectDisconnect();
+ ExpectDisconnect(audio_capturer_);
}
// Setting the reference clock should succeed, after all active capture packets have returned
@@ -502,12 +455,11 @@
SetFormat();
SetUpPayloadBuffer();
- audio_capturer_.events().OnPacketProduced =
- CompletionCallback([](fuchsia::media::StreamPacket) {});
+ audio_capturer_.events().OnPacketProduced = AddCallback("OnPacketProduced");
audio_capturer_->StartAsyncCapture(1600);
ExpectCallback();
- audio_capturer_->StopAsyncCapture(CompletionCallback());
+ audio_capturer_->StopAsyncCapture(AddCallback("StopAsyncCapture"));
ExpectCallback();
audio_capturer_->SetReferenceClock(clock::AdjustableCloneOfMonotonic());
diff --git a/src/media/audio/audio_core/test/api/audio_renderer_test.cc b/src/media/audio/audio_core/test/api/audio_renderer_test.cc
index da015ce..0953f3a 100644
--- a/src/media/audio/audio_core/test/api/audio_renderer_test.cc
+++ b/src/media/audio/audio_core/test/api/audio_renderer_test.cc
@@ -33,7 +33,6 @@
protected:
void SetUp() override;
void TearDown() override;
- void SetNegativeExpectations() override;
// Discards all in-flight packets and waits for the response from the audio
// renderer. This can be used as a simple round-trip through the audio
@@ -51,8 +50,6 @@
fuchsia::media::AudioRendererPtr audio_renderer_;
fuchsia::media::audio::GainControlPtr gain_control_;
-
- bool bound_renderer_expected_ = true;
};
// AudioRendererClockTest - thin wrapper around AudioRendererTest
@@ -68,25 +65,18 @@
HermeticAudioTest::SetUp();
audio_core_->CreateAudioRenderer(audio_renderer_.NewRequest());
- audio_renderer_.set_error_handler(ErrorHandler());
+ AddErrorHandler(audio_renderer_, "AudioRenderer");
}
void AudioRendererTest::TearDown() {
gain_control_.Unbind();
-
- EXPECT_EQ(bound_renderer_expected_, audio_renderer_.is_bound());
audio_renderer_.Unbind();
HermeticAudioTest::TearDown();
}
-void AudioRendererTest::SetNegativeExpectations() {
- HermeticAudioTest::SetNegativeExpectations();
- bound_renderer_expected_ = false;
-}
-
void AudioRendererTest::AssertConnectedAndDiscardAllPackets() {
- audio_renderer_->DiscardAllPackets(CompletionCallback());
+ audio_renderer_->DiscardAllPackets(AddCallback("DiscardAllPackets"));
ExpectCallback();
}
@@ -141,7 +131,7 @@
// should fail.
CreateAndAddPayloadBuffer(0);
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
// Test removing payload buffers.
@@ -161,7 +151,7 @@
TEST_F(AudioRendererTest, RemovePayloadBuffer_InvalidBufferIdShouldDisconnect) {
audio_renderer_->RemovePayloadBuffer(0);
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
// It is invalid to remove a payload buffer while there are queued packets.
@@ -182,7 +172,7 @@
// should fail.
audio_renderer_->RemovePayloadBuffer(0);
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
//
@@ -202,14 +192,11 @@
packet.payload_buffer_id = 0;
packet.payload_offset = 0;
packet.payload_size = kValidPayloadSize;
- bool callback_received = false;
- audio_renderer_->SendPacket(std::move(packet),
- [&callback_received] { callback_received = true; });
+ audio_renderer_->SendPacket(std::move(packet), AddCallback("SendPacket"));
audio_renderer_->Play(fuchsia::media::NO_TIMESTAMP, fuchsia::media::NO_TIMESTAMP,
[](int64_t, int64_t) {});
- RunLoopUntil([this, &callback_received]() { return error_occurred_ || callback_received; });
- EXPECT_TRUE(callback_received);
+ ExpectCallback();
}
TEST_F(AudioRendererTest, SendPacket_InvokesCallbacksInOrder) {
@@ -222,22 +209,15 @@
packet.payload_buffer_id = 0;
packet.payload_offset = 0;
packet.payload_size = kValidPayloadSize;
- uint32_t callback_count = 0;
- audio_renderer_->SendPacket(fidl::Clone(packet),
- [&callback_count] { EXPECT_EQ(0u, callback_count++); });
- audio_renderer_->SendPacket(fidl::Clone(packet),
- [&callback_count] { EXPECT_EQ(1u, callback_count++); });
- audio_renderer_->SendPacket(fidl::Clone(packet),
- [&callback_count] { EXPECT_EQ(2u, callback_count++); });
- audio_renderer_->SendPacket(fidl::Clone(packet),
- [&callback_count] { EXPECT_EQ(3u, callback_count++); });
+ audio_renderer_->SendPacket(fidl::Clone(packet), AddCallback("SendPacket1"));
+ audio_renderer_->SendPacket(fidl::Clone(packet), AddCallback("SendPacket2"));
+ audio_renderer_->SendPacket(fidl::Clone(packet), AddCallback("SendPacket3"));
+ audio_renderer_->SendPacket(fidl::Clone(packet), AddCallback("SendPacket4"));
// Play and expect the callbacks in order.
audio_renderer_->Play(fuchsia::media::NO_TIMESTAMP, fuchsia::media::NO_TIMESTAMP,
[](int64_t, int64_t) {});
-
- RunLoopUntil([this, &callback_count]() { return error_occurred_ || (callback_count == 4u); });
- EXPECT_EQ(4u, callback_count);
+ ExpectCallback();
}
TEST_F(AudioRendererTest, SendPackets_TooManyShouldDisconnect) {
@@ -261,8 +241,7 @@
for (int i = 0; i < 600; ++i) {
audio_renderer_->SendPacketNoReply(std::move(packet));
}
- SetNegativeExpectations();
- RunLoopUntil([this]() { return error_occurred_; });
+ ExpectDisconnect(audio_renderer_);
}
//
@@ -296,7 +275,7 @@
packet.payload_size = kValidPayloadSize;
audio_renderer_->SendPacketNoReply(std::move(packet));
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
// It is invalid to SendPacket before the stream type has been configured
@@ -313,7 +292,7 @@
packet.payload_size = kValidPayloadSize;
audio_renderer_->SendPacketNoReply(std::move(packet));
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
// SendPacket with a |payload_size| that is invalid.
@@ -329,7 +308,7 @@
packet.payload_size = kInvalidPayloadSize;
audio_renderer_->SendPacketNoReply(std::move(packet));
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
TEST_F(AudioRendererTest, SendPacketNoReply_BufferOutOfBoundsShouldDisconnect) {
@@ -345,7 +324,7 @@
packet.payload_size = kValidPayloadSize;
audio_renderer_->SendPacketNoReply(std::move(packet));
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
TEST_F(AudioRendererTest, SendPacketNoReply_BufferOverrunShouldDisconnect) {
@@ -361,7 +340,7 @@
packet.payload_offset = kDefaultPayloadBufferSize - kValidPayloadSize;
audio_renderer_->SendPacketNoReply(std::move(packet));
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
// TODO(mpuryear): test EndOfStream();
@@ -378,7 +357,7 @@
audio_renderer_->Play(
fuchsia::media::NO_TIMESTAMP, 0,
- CompletionCallback([&play_ref_time, &play_media_time](auto ref_time, auto media_time) {
+ AddCallback("Play", [&play_ref_time, &play_media_time](auto ref_time, auto media_time) {
play_ref_time = ref_time;
play_media_time = media_time;
}));
@@ -395,7 +374,7 @@
zx_nanosleep(play_ref_time);
audio_renderer_->Pause(
- CompletionCallback([&pause_ref_time, &pause_media_time](auto ref_time, auto media_time) {
+ AddCallback("Pause", [&pause_ref_time, &pause_media_time](auto ref_time, auto media_time) {
pause_ref_time = ref_time;
pause_media_time = media_time;
}));
@@ -426,15 +405,13 @@
packet1.payload_offset = packet2.payload_offset = packet3.payload_offset = 0;
packet1.payload_size = packet2.payload_size = packet3.payload_size = kDefaultPayloadBufferSize;
- auto callbacks = 0u;
- audio_renderer_->SendPacket(std::move(packet1), [&callbacks]() { EXPECT_EQ(0u, callbacks++); });
- audio_renderer_->SendPacket(std::move(packet2), [&callbacks]() { EXPECT_EQ(1u, callbacks++); });
- audio_renderer_->SendPacket(std::move(packet3), [&callbacks]() { EXPECT_EQ(2u, callbacks++); });
+ audio_renderer_->SendPacket(std::move(packet1), AddCallback("SendPacket1"));
+ audio_renderer_->SendPacket(std::move(packet2), AddCallback("SendPacket2"));
+ audio_renderer_->SendPacket(std::move(packet3), AddCallback("SendPacket3"));
audio_renderer_->PlayNoReply(fuchsia::media::NO_TIMESTAMP, fuchsia::media::NO_TIMESTAMP);
// Packets must complete in order, with the DiscardAllPackets completion afterward.
- audio_renderer_->DiscardAllPackets(
- CompletionCallback([&callbacks]() { EXPECT_EQ(3u, callbacks); }));
+ audio_renderer_->DiscardAllPackets(AddCallback("DiscardAllPackets"));
ExpectCallback();
}
@@ -488,7 +465,7 @@
audio_renderer_->SetPcmStreamType(format2);
// Allow an error Disconnect callback, but we expect a timeout instead.
- audio_renderer_->GetMinLeadTime(CompletionCallback([](int64_t x) {}));
+ audio_renderer_->GetMinLeadTime(AddCallback("GetMinLeadTime"));
ExpectCallback();
}
@@ -508,7 +485,7 @@
packet.payload_buffer_id = 0;
packet.payload_offset = 0;
packet.payload_size = kValidPayloadSize;
- audio_renderer_->SendPacket(std::move(packet), CompletionCallback());
+ audio_renderer_->SendPacket(std::move(packet), AddCallback("SendPacket"));
int64_t ref_time_received = -1;
int64_t media_time_received = -1;
@@ -536,7 +513,7 @@
packet.payload_buffer_id = 0;
packet.payload_offset = 0;
packet.payload_size = kValidPayloadSize;
- audio_renderer_->SendPacket(std::move(packet), CompletionCallback());
+ audio_renderer_->SendPacket(std::move(packet), AddCallback("SendPacket"));
audio_renderer_->PlayNoReply(fuchsia::media::NO_TIMESTAMP, fuchsia::media::NO_TIMESTAMP);
ExpectCallback();
@@ -553,15 +530,15 @@
// Validate MinLeadTime events, when enabled.
TEST_F(AudioRendererTest, EnableMinLeadTimeEvents) {
int64_t min_lead_time = -1;
- audio_renderer_.events().OnMinLeadTimeChanged = [&min_lead_time](int64_t min_lead_time_nsec) {
- min_lead_time = min_lead_time_nsec;
- };
+ audio_renderer_.events().OnMinLeadTimeChanged = AddCallback(
+ "OnMinLeadTimeChanged",
+ [&min_lead_time](int64_t min_lead_time_nsec) { min_lead_time = min_lead_time_nsec; });
audio_renderer_->EnableMinLeadTimeEvents(true);
// After enabling MinLeadTime events, we expect an initial notification.
// Because we have not yet set the format, we expect MinLeadTime to be 0.
- RunLoopUntil([this, &min_lead_time]() { return error_occurred_ || (min_lead_time >= 0); });
+ ExpectCallback();
EXPECT_EQ(min_lead_time, 0);
// FYI: after setting format, MinLeadTime should change to be greater than 0
@@ -571,14 +548,15 @@
// Validate MinLeadTime events, when disabled.
TEST_F(AudioRendererTest, DisableMinLeadTimeEvents) {
- audio_renderer_.events().OnMinLeadTimeChanged =
- CompletionCallback([](int64_t x) { EXPECT_FALSE(true) << kCallbackErr; });
+ audio_renderer_.events().OnMinLeadTimeChanged = [](int64_t x) {
+ ADD_FAILURE() << "Unexpected call to OnMinLeadTimeChanged";
+ };
audio_renderer_->EnableMinLeadTimeEvents(false);
// We should not receive a OnMinLeadTimeChanged callback (or Disconnect)
// before receiving this direct GetMinLeadTime callback.
- audio_renderer_->GetMinLeadTime(CompletionCallback([](int64_t x) {}));
+ audio_renderer_->GetMinLeadTime(AddCallback("GetMinLeadTime"));
ExpectCallback();
}
@@ -587,11 +565,12 @@
// Before SetPcmStreamType is called, MinLeadTime should equal zero.
TEST_F(AudioRendererTest, GetMinLeadTime) {
int64_t min_lead_time = -1;
- audio_renderer_->GetMinLeadTime(
- [&min_lead_time](int64_t min_lead_time_nsec) { min_lead_time = min_lead_time_nsec; });
+ audio_renderer_->GetMinLeadTime(AddCallback(
+ "GetMinLeadTime",
+ [&min_lead_time](int64_t min_lead_time_nsec) { min_lead_time = min_lead_time_nsec; }));
// Wait to receive Lead time callback (will loop timeout? EXPECT_FALSE)
- RunLoopUntil([this, &min_lead_time]() { return error_occurred_ || (min_lead_time >= 0); });
+ ExpectCallback();
EXPECT_EQ(min_lead_time, 0);
}
@@ -601,21 +580,15 @@
TEST_F(AudioRendererTest, BindGainControl) {
// Validate AudioRenderers can create GainControl interfaces.
audio_renderer_->BindGainControl(gain_control_.NewRequest());
- bool gc_error_occurred = false;
- auto gc_err_handler = [&gc_error_occurred](zx_status_t error) { gc_error_occurred = true; };
- gain_control_.set_error_handler(gc_err_handler);
+ AddErrorHandler(gain_control_, "AudioRenderer::GainControl");
fuchsia::media::AudioRendererPtr audio_renderer_2;
audio_core_->CreateAudioRenderer(audio_renderer_2.NewRequest());
- bool ar2_error_occurred = false;
- auto ar2_err_handler = [&ar2_error_occurred](zx_status_t error) { ar2_error_occurred = true; };
- audio_renderer_2.set_error_handler(ar2_err_handler);
+ AddErrorHandler(audio_renderer_2, "AudioRenderer2");
fuchsia::media::audio::GainControlPtr gain_control_2;
audio_renderer_2->BindGainControl(gain_control_2.NewRequest());
- bool gc2_error_occurred = false;
- auto gc2_err_handler = [&gc2_error_occurred](zx_status_t error) { gc2_error_occurred = true; };
- gain_control_2.set_error_handler(gc2_err_handler);
+ AddErrorHandler(gain_control_2, "AudioRenderer::GainControl2");
// Validate GainControl2 does NOT persist after audio_renderer_2 is unbound
audio_renderer_2.Unbind();
@@ -624,24 +597,11 @@
gain_control_.Unbind();
// Give audio_renderer_2 a chance to disconnect gain_control_2
- RunLoopUntil([this, &ar2_error_occurred, &gc_error_occurred, &gc2_error_occurred]() {
- return (error_occurred_ || ar2_error_occurred || gc_error_occurred || gc2_error_occurred);
- });
+ ExpectDisconnect(gain_control_2);
// Let audio_renderer_ show it is still alive (and allow other disconnects)
- audio_renderer_->GetMinLeadTime(CompletionCallback([](int64_t x) {}));
+ audio_renderer_->GetMinLeadTime(AddCallback("GetMinLeadTime"));
ExpectCallback();
-
- // Explicitly unbinding audio_renderer_2 should not trigger its disconnect
- // (ar2_error_occurred), but should trigger gain_control_2's disconnect.
- EXPECT_FALSE(ar2_error_occurred);
- EXPECT_TRUE(gc2_error_occurred);
- EXPECT_FALSE(gain_control_2.is_bound());
-
- // Explicitly unbinding gain_control_ should not trigger its disconnect, nor
- // its parent audio_renderer_'s.
- EXPECT_FALSE(gc_error_occurred);
- EXPECT_TRUE(audio_renderer_.is_bound());
}
// Before setting format, Play should not succeed.
@@ -657,7 +617,7 @@
});
// Disconnect callback should be received
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
EXPECT_EQ(ref_time_received, -1);
EXPECT_EQ(media_time_received, -1);
}
@@ -681,7 +641,7 @@
});
// Disconnect callback should be received
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
EXPECT_EQ(ref_time_received, -1);
EXPECT_EQ(media_time_received, -1);
}
@@ -691,7 +651,7 @@
audio_renderer_->PlayNoReply(fuchsia::media::NO_TIMESTAMP, fuchsia::media::NO_TIMESTAMP);
// Disconnect callback should be received.
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
// Before setting format, Pause should not succeed.
@@ -706,7 +666,7 @@
});
// Disconnect callback should be received
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
EXPECT_EQ(ref_time_received, -1);
EXPECT_EQ(media_time_received, -1);
}
@@ -729,7 +689,7 @@
});
// Disconnect callback should be received
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
EXPECT_EQ(ref_time_received, -1);
EXPECT_EQ(media_time_received, -1);
}
@@ -739,7 +699,7 @@
audio_renderer_->PauseNoReply();
// Disconnect callback should be received.
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
TEST_F(AudioRendererTest, SetUsage_AfterSetPcmStreamTypeShouldDisconnect) {
@@ -748,18 +708,18 @@
AssertConnectedAndDiscardAllPackets();
audio_renderer_->SetUsage(fuchsia::media::AudioRenderUsage::COMMUNICATION);
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
zx::clock AudioRendererClockTest::GetAndValidateReferenceClock() {
zx::clock clock;
- audio_renderer_->GetReferenceClock(CompletionCallback(
- [&clock](zx::clock received_clock) { clock = std::move(received_clock); }));
+ audio_renderer_->GetReferenceClock(
+ AddCallback("GetReferenceClock",
+ [&clock](zx::clock received_clock) { clock = std::move(received_clock); }));
ExpectCallback();
EXPECT_TRUE(clock.is_valid());
- EXPECT_FALSE(error_occurred());
return clock;
}
@@ -849,7 +809,7 @@
ASSERT_EQ(orig_clock.duplicate(kClockRights & ~ZX_RIGHT_DUPLICATE, &dupe_clock), ZX_OK);
audio_renderer_->SetReferenceClock(std::move(dupe_clock));
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
// inadequate ZX_RIGHTS -- no READ should cause GetReferenceClock to fail.
@@ -858,7 +818,7 @@
ASSERT_EQ(orig_clock.duplicate(kClockRights & ~ZX_RIGHT_READ, &dupe_clock), ZX_OK);
audio_renderer_->SetReferenceClock(std::move(dupe_clock));
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
// If client-submitted clock has ZX_RIGHT_WRITE, this should be removed upon GetReferenceClock
@@ -881,7 +841,7 @@
GetAndValidateReferenceClock();
audio_renderer_->Play(fuchsia::media::NO_TIMESTAMP, fuchsia::media::NO_TIMESTAMP,
- CompletionCallback([](int64_t, int64_t) {}));
+ AddCallback("Play"));
ExpectCallback();
// We are now playing, but there are no active packets.
@@ -904,7 +864,7 @@
audio_renderer_->SendPacketNoReply(std::move(packet2));
audio_renderer_->SetReferenceClock(clock::CloneOfMonotonic());
- ExpectDisconnect();
+ ExpectDisconnect(audio_renderer_);
}
// Setting the reference clock should succeed, after all active render packets have returned
@@ -917,7 +877,7 @@
packet.payload_buffer_id = 0;
packet.payload_offset = 0;
packet.payload_size = kValidPayloadSize;
- audio_renderer_->SendPacket(std::move(packet), CompletionCallback());
+ audio_renderer_->SendPacket(std::move(packet), AddCallback("SendPacket"));
audio_renderer_->PlayNoReply(fuchsia::media::NO_TIMESTAMP, fuchsia::media::NO_TIMESTAMP);
// Wait for the packet completion; now there are no active packets.
diff --git a/src/media/audio/audio_core/test/api/audio_tuner_test.cc b/src/media/audio/audio_core/test/api/audio_tuner_test.cc
index 612e4ff..5567896 100644
--- a/src/media/audio/audio_core/test/api/audio_tuner_test.cc
+++ b/src/media/audio/audio_core/test/api/audio_tuner_test.cc
@@ -30,9 +30,8 @@
TEST_F(AudioTunerTest, ConnectToAudioTuner) {
fuchsia::media::tuning::AudioTunerPtr audio_tuner;
environment()->ConnectToService(audio_tuner.NewRequest());
- audio_tuner.set_error_handler(ErrorHandler());
- audio_tuner->GetAvailableAudioEffects(
- CompletionCallback([](std::vector<fuchsia::media::tuning::AudioEffectType>) {}));
+ AddErrorHandler(audio_tuner, "AudioTuner");
+ audio_tuner->GetAvailableAudioEffects(AddCallback("GetAvailableAudioEffects"));
ExpectCallback();
}
diff --git a/src/media/audio/audio_core/test/api/gain_control_test.cc b/src/media/audio/audio_core/test/api/gain_control_test.cc
index be696e1..b763fd6 100644
--- a/src/media/audio/audio_core/test/api/gain_control_test.cc
+++ b/src/media/audio/audio_core/test/api/gain_control_test.cc
@@ -2,541 +2,232 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "src/media/audio/audio_core/test/api/gain_control_test.h"
+#include <fuchsia/media/cpp/fidl.h>
#include <cmath>
#include <gtest/gtest.h>
+#include "src/media/audio/lib/test/hermetic_audio_test.h"
+
+// TYPED_TEST_SUITE uses RTTI to print type names, but RTTI is disabled in our build, so
+// specialize this function to get nicer test failure messages.
+namespace testing::internal {
+template <>
+std::string GetTypeName<fuchsia::media::AudioRendererPtr>() {
+ return "AudioRenderer";
+}
+template <>
+std::string GetTypeName<fuchsia::media::AudioCapturerPtr>() {
+ return "AudioCapturer";
+}
+} // namespace testing::internal
+
namespace media::audio::test {
-// GainControlTestBase
-//
-void GainControlTestBase::TearDown() {
- EXPECT_EQ(!gain_control_.is_bound(), null_gain_control_expected_);
- gain_control_.Unbind();
+namespace {
+template <typename RendererOrCapturerT>
+struct RendererOrCapturerTraits {};
- EXPECT_EQ(error_occurred_2_, error_expected_2_);
- EXPECT_EQ(!gain_control_2_.is_bound(), null_gain_control_expected_2_);
- gain_control_2_.Unbind();
+template <>
+struct RendererOrCapturerTraits<fuchsia::media::AudioRendererPtr> {
+ static std::string Name() { return "AudioRenderer"; }
+ static void Create(fuchsia::media::AudioCorePtr& audio_core,
+ fuchsia::media::AudioRendererPtr& p) {
+ audio_core->CreateAudioRenderer(p.NewRequest());
+ }
+};
- // These expect_ vars indicate negative cases where we expect failure.
- EXPECT_EQ(ApiIsNull(), null_api_expected_);
- audio_renderer_.Unbind();
- audio_capturer_.Unbind();
- audio_renderer_2_.Unbind();
- audio_capturer_2_.Unbind();
+template <>
+struct RendererOrCapturerTraits<fuchsia::media::AudioCapturerPtr> {
+ static std::string Name() { return "AudioCapturer"; }
+ static void Create(fuchsia::media::AudioCorePtr& audio_core,
+ fuchsia::media::AudioCapturerPtr& p) {
+ audio_core->CreateAudioCapturer(false, p.NewRequest());
+ }
+};
+} // namespace
- HermeticAudioTest::TearDown();
-}
+template <typename RendererOrCapturerT>
+class GainControlTest : public HermeticAudioTest {
+ protected:
+ void SetUp() override {
+ HermeticAudioTest::SetUp();
-void GainControlTestBase::SetUpRenderer() {
- audio_core_->CreateAudioRenderer(audio_renderer_.NewRequest());
- audio_renderer_.set_error_handler(ErrorHandler());
-}
+ // Create two gain controllers tied to the same parent object. We will manipulate
+ // gain_control_1_ while expecting events on both gain controllers.
+ RendererOrCapturerTraits<RendererOrCapturerT>::Create(audio_core_, parent_);
+ AddErrorHandler(parent_, RendererOrCapturerTraits<RendererOrCapturerT>::Name());
-void GainControlTestBase::SetUpCapturer() {
- audio_core_->CreateAudioCapturer(false, audio_capturer_.NewRequest());
- audio_capturer_.set_error_handler(ErrorHandler());
-}
+ // Bind gc2 first. If we do this in the opposite order, then commands sent to gc1
+ // might happen concurrently with the binding of gc2, meaning gc2 will miss updates.
+ parent_->BindGainControl(gain_control_2_.NewRequest());
+ parent_->BindGainControl(gain_control_1_.NewRequest());
+ AddErrorHandler(gain_control_1_,
+ RendererOrCapturerTraits<RendererOrCapturerT>::Name() + "::GainControl1");
+ AddErrorHandler(gain_control_2_,
+ RendererOrCapturerTraits<RendererOrCapturerT>::Name() + "::GainControl2");
-void GainControlTestBase::SetUpRenderer2() {
- audio_core_->CreateAudioRenderer(audio_renderer_2_.NewRequest());
- audio_renderer_2_.set_error_handler(
- ErrorHandler([this](zx_status_t) { error_occurred_2_ = true; }));
-}
+ // To ensure there is no crosstalk, we create a dummy renderer and capturer
+ // and a gain control for each, and verify those gain controls are not called.
+ audio_core_->CreateAudioRenderer(unused_renderer_.NewRequest());
+ audio_core_->CreateAudioCapturer(false, unused_capturer_.NewRequest());
+ AddErrorHandler(unused_renderer_, "AudioRenderer (unused)");
+ AddErrorHandler(unused_capturer_, "AudioCapturer (unused)");
-void GainControlTestBase::SetUpCapturer2() {
- audio_core_->CreateAudioCapturer(false, audio_capturer_2_.NewRequest());
- audio_capturer_2_.set_error_handler(
- ErrorHandler([this](zx_status_t) { error_occurred_2_ = true; }));
-}
-
-void GainControlTestBase::SetUpGainControl() {
- gain_control_.set_error_handler(ErrorHandler());
-
- gain_control_.events().OnGainMuteChanged = CompletionCallback([this](float gain_db, bool muted) {
- received_gain_db_ = gain_db;
- received_mute_ = muted;
- });
-
- null_gain_control_expected_ = false;
-}
-
-void GainControlTestBase::SetUpGainControlOnRenderer() {
- audio_renderer_->BindGainControl(gain_control_.NewRequest());
- SetUpGainControl();
-}
-
-void GainControlTestBase::SetUpGainControlOnCapturer() {
- audio_capturer_->BindGainControl(gain_control_.NewRequest());
- SetUpGainControl();
-}
-
-void GainControlTestBase::SetUpGainControl2() {
- gain_control_2_.set_error_handler(
- ErrorHandler([this](zx_status_t) { error_occurred_2_ = true; }));
-
- gain_control_2_.events().OnGainMuteChanged =
- CompletionCallback([this](float gain_db, bool muted) {
- received_gain_db_2_ = gain_db;
- received_mute_2_ = muted;
- });
-
- null_gain_control_expected_2_ = false;
-}
-
-void GainControlTestBase::SetUpGainControl2OnRenderer() {
- audio_renderer_->BindGainControl(gain_control_2_.NewRequest());
- SetUpGainControl2();
-}
-
-void GainControlTestBase::SetUpGainControl2OnCapturer() {
- audio_capturer_->BindGainControl(gain_control_2_.NewRequest());
- SetUpGainControl2();
-}
-
-void GainControlTestBase::SetUpGainControl2OnRenderer2() {
- audio_renderer_2_->BindGainControl(gain_control_2_.NewRequest());
- SetUpGainControl2();
-}
-
-void GainControlTestBase::SetUpGainControl2OnCapturer2() {
- audio_capturer_2_->BindGainControl(gain_control_2_.NewRequest());
- SetUpGainControl2();
-}
-
-// For tests that cause a GainControl to disconnect, set these expectations.
-void GainControlTestBase::SetNegativeExpectations() {
- HermeticAudioTest::SetNegativeExpectations();
-
- null_api_expected_ = true;
- null_gain_control_expected_ = true;
-}
-
-// Set Gain, asserting that state is already reset so error can be detected.
-void GainControlTestBase::SetGain(float gain_db) { gain_control_->SetGain(gain_db); }
-
-// Set Mute, asserting that state is already reset so error can be detected.
-void GainControlTestBase::SetMute(bool mute) { gain_control_->SetMute(mute); }
-
-// Expect and absorb a single gain callback; perform related error checking.
-void GainControlTestBase::ExpectGainCallback(float gain_db, bool mute) {
- received_gain_db_ = kTooLowGainDb;
-
- RunLoopUntil([this, &gain_db, &mute]() {
- return (received_gain_db_ == gain_db) && (received_mute_ == mute);
- });
- EXPECT_EQ(received_gain_db_, gain_db);
- EXPECT_EQ(received_mute_, mute);
- EXPECT_FALSE(error_occurred_);
-}
-
-// Tests expect to receive a disconnect callback for API binding, then for
-// GainControl binding. Treat any regular gain callback received as error.
-void GainControlTestBase::ExpectDisconnect() {
- // Need to wait for both renderer/capturer AND gain_control to disconnect.
- HermeticAudioTest::ExpectDisconnect();
-
- if (gain_control_.is_bound() || !ApiIsNull()) {
- // Reset our error detector before listening again.
- error_occurred_ = false;
- HermeticAudioTest::ExpectDisconnect();
+ SetUpUnusedGainControl(unused_renderer_gain_control_, unused_renderer_);
+ SetUpUnusedGainControl(unused_capturer_gain_control_, unused_capturer_);
}
- EXPECT_TRUE(ApiIsNull());
- EXPECT_FALSE(gain_control_.is_bound());
-}
+ template <typename ParentT>
+ void SetUpUnusedGainControl(fuchsia::media::audio::GainControlPtr& gc, ParentT& parent) {
+ parent->BindGainControl(gc.NewRequest());
+ AddErrorHandler(gc, RendererOrCapturerTraits<ParentT>::Name() + "::GainControl (unused)");
-// Test implementations, called by various objects across the class hierarchy
-void GainControlTestBase::TestSetGain() {
+ gc.events().OnGainMuteChanged = [](float gain_db, bool muted) {
+ ADD_FAILURE() << "Unexpected call to unused " << RendererOrCapturerTraits<ParentT>::Name()
+ << "'s GainControl: OnGainMuteChanged(" << gain_db << ", " << muted << ")";
+ };
+ }
+
+ void ExpectGainCallback(float expected_gain_db, bool expected_mute) {
+ float received_gain_db_1 = NAN;
+ float received_gain_db_2 = NAN;
+ bool received_mute_1 = false;
+ bool received_mute_2 = false;
+
+ // We bound gc2 first, so it gets the event first.
+ gain_control_2_.events().OnGainMuteChanged =
+ AddCallback("GainControl2::OnGainMuteChanged",
+ [&received_gain_db_2, &received_mute_2](float gain_db, bool muted) {
+ received_gain_db_2 = gain_db;
+ received_mute_2 = muted;
+ });
+ gain_control_1_.events().OnGainMuteChanged =
+ AddCallback("GainControl1::OnGainMuteChanged",
+ [&received_gain_db_1, &received_mute_1](float gain_db, bool muted) {
+ received_gain_db_1 = gain_db;
+ received_mute_1 = muted;
+ });
+
+ ExpectCallback();
+ EXPECT_EQ(received_gain_db_1, expected_gain_db);
+ EXPECT_EQ(received_gain_db_2, expected_gain_db);
+ EXPECT_EQ(received_mute_1, expected_mute);
+ EXPECT_EQ(received_mute_2, expected_mute);
+ }
+
+ void ExpectParentDisconnect() {
+ // Disconnecting the parent should also disconnnect the GainControls.
+ ExpectDisconnects({ErrorHandlerFor(parent_), ErrorHandlerFor(gain_control_1_),
+ ErrorHandlerFor(gain_control_2_)});
+ }
+
+ void SetGain(float gain_db) { gain_control_1_->SetGain(gain_db); }
+ void SetMute(bool mute) { gain_control_1_->SetMute(mute); }
+
+ RendererOrCapturerT parent_;
+ fuchsia::media::audio::GainControlPtr gain_control_1_;
+ fuchsia::media::audio::GainControlPtr gain_control_2_;
+
+ fuchsia::media::AudioRendererPtr unused_renderer_;
+ fuchsia::media::AudioCapturerPtr unused_capturer_;
+ fuchsia::media::audio::GainControlPtr unused_renderer_gain_control_;
+ fuchsia::media::audio::GainControlPtr unused_capturer_gain_control_;
+};
+
+using GainControlTestTypes =
+ ::testing::Types<fuchsia::media::AudioRendererPtr, fuchsia::media::AudioCapturerPtr>;
+TYPED_TEST_SUITE(GainControlTest, GainControlTestTypes);
+
+TYPED_TEST(GainControlTest, SetGain) {
constexpr float expect_gain_db = 20.0f;
- SetGain(expect_gain_db);
- ExpectGainCallback(expect_gain_db, false);
+ this->SetGain(expect_gain_db);
+ this->ExpectGainCallback(expect_gain_db, false);
- SetGain(kUnityGainDb);
- ExpectGainCallback(kUnityGainDb, false);
+ this->SetGain(kUnityGainDb);
+ this->ExpectGainCallback(kUnityGainDb, false);
}
-void GainControlTestBase::TestSetMute() {
+TYPED_TEST(GainControlTest, SetMute) {
bool expect_mute = true;
- SetMute(expect_mute);
- ExpectGainCallback(kUnityGainDb, expect_mute);
+ this->SetMute(expect_mute);
+ this->ExpectGainCallback(kUnityGainDb, expect_mute);
expect_mute = false;
- SetMute(expect_mute);
- ExpectGainCallback(kUnityGainDb, expect_mute);
+ this->SetMute(expect_mute);
+ this->ExpectGainCallback(kUnityGainDb, expect_mute);
}
-void GainControlTestBase::TestSetGainMute() {
+TYPED_TEST(GainControlTest, SetGainMute) {
constexpr float expect_gain_db = -5.5f;
- SetGain(expect_gain_db);
- SetMute(true);
+ this->SetGain(expect_gain_db);
+ this->SetMute(true);
- ExpectGainCallback(expect_gain_db, true);
+ this->ExpectGainCallback(expect_gain_db, true);
}
-void GainControlTestBase::TestDuplicateSetGain() {
+TYPED_TEST(GainControlTest, DuplicateSetGain) {
constexpr float expect_gain_db = 20.0f;
- SetGain(expect_gain_db);
- ExpectGainCallback(expect_gain_db, false);
+ this->SetGain(expect_gain_db);
+ this->ExpectGainCallback(expect_gain_db, false);
- SetGain(expect_gain_db);
- SetMute(true);
+ this->SetGain(expect_gain_db);
+ this->SetMute(true);
// Rather than waiting for "no gain callback", we set an (independent) mute
// value and expect only a single callback that includes the more recent mute.
- ExpectGainCallback(expect_gain_db, true);
+ this->ExpectGainCallback(expect_gain_db, true);
}
-void GainControlTestBase::TestDuplicateSetMute() {
+TYPED_TEST(GainControlTest, DuplicateSetMute) {
constexpr float expect_gain_db = -42.0f;
- SetMute(true);
- ExpectGainCallback(kUnityGainDb, true);
+ this->SetMute(true);
+ this->ExpectGainCallback(kUnityGainDb, true);
- SetMute(true);
- SetGain(expect_gain_db);
+ this->SetMute(true);
+ this->SetGain(expect_gain_db);
// Rather than waiting for "no mute callback", we set an (independent) gain
// value and expect only a single callback that includes the more recent gain.
- ExpectGainCallback(expect_gain_db, true);
+ this->ExpectGainCallback(expect_gain_db, true);
}
-// For negative expectations.
-//
// Setting gain too high should cause a disconnect.
-void GainControlTestBase::TestSetGainTooHigh() {
- SetNegativeExpectations();
+TYPED_TEST(GainControlTest, SetGainTooHigh) {
+ this->SetGain(kTooHighGainDb);
- constexpr float expect_gain_db = kTooHighGainDb;
- SetGain(expect_gain_db);
-
- ExpectDisconnect();
- EXPECT_FALSE(gain_control_.is_bound());
+ this->ExpectParentDisconnect();
+ EXPECT_FALSE(this->gain_control_1_.is_bound());
+ EXPECT_FALSE(this->gain_control_2_.is_bound());
}
// Setting gain too low should cause a disconnect.
-void GainControlTestBase::TestSetGainTooLow() {
- SetNegativeExpectations();
+TYPED_TEST(GainControlTest, SetGainTooLow) {
+ this->SetGain(kTooLowGainDb);
- constexpr float expect_gain_db = kTooLowGainDb;
- SetGain(expect_gain_db);
-
- ExpectDisconnect();
- EXPECT_FALSE(gain_control_.is_bound());
+ this->ExpectParentDisconnect();
+ EXPECT_FALSE(this->gain_control_1_.is_bound());
+ EXPECT_FALSE(this->gain_control_2_.is_bound());
}
-// Setting stream-specific gain to NAN should cause both FIDL channels
-// (renderer/capturer and gain_control) to disconnect.
-void GainControlTestBase::TestSetGainNaN() {
- SetNegativeExpectations();
+// Setting gain to NAN should cause a disconnect.
+TYPED_TEST(GainControlTest, SetGainNaN) {
+ this->SetGain(NAN);
- constexpr float expect_gain_db = NAN;
- SetGain(expect_gain_db);
-
- ExpectDisconnect();
- EXPECT_FALSE(gain_control_.is_bound());
+ this->ExpectParentDisconnect();
+ EXPECT_FALSE(this->gain_control_1_.is_bound());
+ EXPECT_FALSE(this->gain_control_2_.is_bound());
}
-//
-// Basic GainControl validation with single instance.
-//
-
-// RenderGainControlTest
-//
-void RenderGainControlTest::SetUp() {
- GainControlTestBase::SetUp();
-
- SetUpRenderer();
- SetUpGainControlOnRenderer();
-}
-
-// Single renderer with one gain control: Gain, Mute and GainMute combo.
-//
-TEST_F(RenderGainControlTest, SetGain) { TestSetGain(); }
-
-TEST_F(RenderGainControlTest, SetMute) { TestSetMute(); }
-
-TEST_F(RenderGainControlTest, SetGainMute) { TestSetGainMute(); }
-
// TODO(mpuryear): Ramp-related tests (render). Relevant FIDL signature is:
// SetGainWithRamp(float32 gain_db, int64 duration_ns, RampType ramp_type);
// TODO(mpuryear): Validate GainChange notifications of gainramps.
-TEST_F(RenderGainControlTest, DuplicateSetGain) { TestDuplicateSetGain(); }
-
-TEST_F(RenderGainControlTest, DuplicateSetMute) { TestDuplicateSetMute(); }
-
-TEST_F(RenderGainControlTest, SetGainTooHigh) { TestSetGainTooHigh(); }
-
-TEST_F(RenderGainControlTest, SetGainTooLow) { TestSetGainTooLow(); }
-
-TEST_F(RenderGainControlTest, SetGainNaN) { TestSetGainNaN(); }
-
// TODO(mpuryear): Ramp-related negative tests, across all scenarios
-// CaptureGainControlTest
-//
-void CaptureGainControlTest::SetUp() {
- GainControlTestBase::SetUp();
-
- SetUpCapturer();
- SetUpGainControlOnCapturer();
-}
-
-// Single capturer with one gain control
-//
-TEST_F(CaptureGainControlTest, SetGain) { TestSetGain(); }
-
-TEST_F(CaptureGainControlTest, SetMute) { TestSetMute(); }
-
-TEST_F(CaptureGainControlTest, SetGainMute) { TestSetGainMute(); }
-
-// TODO(mpuryear): Ramp-related tests (capture)
-
-TEST_F(CaptureGainControlTest, DuplicateSetGain) { TestDuplicateSetGain(); }
-// N.B. DuplicateSetMute behavior is tested in CapturerTwoGainControlsTest.
-
-TEST_F(CaptureGainControlTest, SetGainTooHigh) { TestSetGainTooHigh(); }
-
-TEST_F(CaptureGainControlTest, SetGainTooLow) { TestSetGainTooLow(); }
-
-TEST_F(CaptureGainControlTest, SetGainNaN) { TestSetGainNaN(); }
-
-// SiblingGainControlsTest
-// On a renderer/capturer, sibling GainControls receive identical notifications.
-//
-// For tests that cause a GainControl to disconnect, set these expectations.
-void SiblingGainControlsTest::SetNegativeExpectations() {
- GainControlTestBase::SetNegativeExpectations();
-
- null_gain_control_expected_2_ = true;
- error_expected_2_ = true;
-}
-
-// Tests expect a gain callback on both gain_controls, with the provided gain_db
-// and mute values -- and no errors.
-void SiblingGainControlsTest::ExpectGainCallback(float gain_db, bool mute) {
- received_gain_db_ = kTooLowGainDb;
- received_gain_db_2_ = kTooLowGainDb;
-
- RunLoopUntil([this, gain_db, mute]() {
- return error_occurred_ || (received_gain_db_ == gain_db && received_gain_db_2_ == gain_db &&
- received_mute_ == mute && received_mute_2_ == mute);
- });
-
- EXPECT_FALSE(error_occurred_) << kDisconnectErr;
- EXPECT_FALSE(ApiIsNull());
- EXPECT_TRUE(gain_control_.is_bound());
- EXPECT_TRUE(gain_control_2_.is_bound());
-
- EXPECT_EQ(received_gain_db_, gain_db);
- EXPECT_EQ(received_gain_db_2_, gain_db);
- EXPECT_EQ(received_mute_, mute);
- EXPECT_EQ(received_mute_2_, mute);
-}
-
-// Tests expect to receive a disconnect callback for the API binding, then
-// one for each of the two GainControl bindings. In our loop, we wait until all
-// three of these have occurred. Also, if any normal gain callback is received
-// during this time, it is unexpected and treated as an error.
-void SiblingGainControlsTest::ExpectDisconnect() {
- SetNegativeExpectations();
- received_gain_db_2_ = kTooLowGainDb;
-
- // Wait Renderer/Capturer and BOTH GainControls to disconnect. Because
- // multiple disconnect callbacks could arrive between our polling interval, we
- // wait a maximum of three times, checking between them for completion.
- HermeticAudioTest::ExpectDisconnect();
- if (!ApiIsNull() || gain_control_.is_bound() || gain_control_2_.is_bound()) {
- // Reset our error detector before listening again.
- error_occurred_ = false;
- HermeticAudioTest::ExpectDisconnect();
- }
- if (!ApiIsNull() || gain_control_.is_bound() || gain_control_2_.is_bound()) {
- // Reset our error detector before listening again.
- error_occurred_ = false;
- HermeticAudioTest::ExpectDisconnect();
- }
-
- EXPECT_TRUE(error_occurred_2_);
- EXPECT_EQ(received_gain_db_2_, kTooLowGainDb);
-}
-
-// RendererTwoGainControlsTest
-// Renderer with two gain controls: both should receive identical notifications.
-//
-void RendererTwoGainControlsTest::SetUp() {
- SiblingGainControlsTest::SetUp();
-
- SetUpRenderer();
- SetUpGainControl2OnRenderer();
- SetUpGainControlOnRenderer();
-}
-
-TEST_F(RendererTwoGainControlsTest, BothControlsReceiveGainNotifications) { TestSetGain(); }
-
-TEST_F(RendererTwoGainControlsTest, BothControlsReceiveMuteNotifications) { TestSetMute(); }
-
-TEST_F(RendererTwoGainControlsTest, DuplicateSetGain) { TestDuplicateSetGain(); }
-
-// N.B. DuplicateSetMute behavior is tested in RendererGainControlTest.
-
-TEST_F(RendererTwoGainControlsTest, SetGainTooHigh) { TestSetGainTooHigh(); }
-
-TEST_F(RendererTwoGainControlsTest, SetGainTooLow) { TestSetGainTooLow(); }
-
-TEST_F(RendererTwoGainControlsTest, SetGainNaN) { TestSetGainNaN(); }
-
-// CapturerTwoGainControlsTest
-// Capturer with two gain controls: both should receive identical notifications.
-//
-void CapturerTwoGainControlsTest::SetUp() {
- SiblingGainControlsTest::SetUp();
-
- SetUpCapturer();
- SetUpGainControl2OnCapturer();
- SetUpGainControlOnCapturer();
-}
-
-TEST_F(CapturerTwoGainControlsTest, BothControlsReceiveGainNotifications) { TestSetGain(); }
-
-TEST_F(CapturerTwoGainControlsTest, BothControlsReceiveMuteNotifications) { TestSetMute(); }
-
-// N.B. DuplicateSetGain behavior is tested in CapturerGainControlTest.
-TEST_F(CapturerTwoGainControlsTest, DuplicateSetMute) { TestDuplicateSetMute(); }
-
-TEST_F(CapturerTwoGainControlsTest, SetGainTooHigh) { TestSetGainTooHigh(); }
-
-TEST_F(CapturerTwoGainControlsTest, SetGainTooLow) { TestSetGainTooLow(); }
-
-TEST_F(CapturerTwoGainControlsTest, SetGainNaN) { TestSetGainNaN(); }
-
-// IndependentGainControlsTest
-// Verify that GainControls on different API instances are fully independent.
-//
-
-// Tests expect a gain callback and no error, and neither on the independent
-// API binding and gain_control (thus we check for subsequent callback below).
-void IndependentGainControlsTest::ExpectGainCallback(float gain_db, bool mute) {
- received_gain_db_2_ = kTooLowGainDb;
-
- GainControlTestBase::ExpectGainCallback(gain_db, mute);
-
- // Not only must we not have disconnected or received unexpected gain2
- // callback, also gain1 must have received the expected callback.
- EXPECT_EQ(received_gain_db_2_, kTooLowGainDb);
-
- // Even if we did get the gain callback we wanted, now we check for other
- // gain callbacks -- or a disconnect. If any of these occur, then we fail.
- if (!error_occurred_ && received_gain_db_ == gain_db && received_gain_db_2_ == kTooLowGainDb) {
- received_gain_db_ = kTooLowGainDb;
-
- RunLoopUntilIdle();
-
- EXPECT_FALSE(error_occurred_) << kDisconnectErr;
- EXPECT_EQ(received_gain_db_, kTooLowGainDb);
- EXPECT_EQ(received_gain_db_2_, kTooLowGainDb);
- }
-}
-
-// Tests expect to receive a disconnect callback for the API binding, then
-// another for the GainControl binding. If before unbinding, that GainControl
-// generates a gain callback, this is unexpected and treated as an error. We
-// still expect nothing from the independent API binding and its gain_control
-// (thus we wait for timeout).
-void IndependentGainControlsTest::ExpectDisconnect() {
- received_gain_db_2_ = kTooLowGainDb;
-
- // We expect Renderer/Capturer AND GainControl to disconnect. Wait for both.
- // We do NOT expect second renderer/capturer to disconnect nor other callback.
- GainControlTestBase::ExpectDisconnect();
-
- // Even if we did get the disconnect callbacks we wanted, now wait for other
- // unexpected callbacks. If none occur, then we pass.
- RunLoopUntilIdle();
-
- // After these disconnects, both Gain and API should be gone, but not Gain2.
- EXPECT_FALSE(error_occurred_2_) << "Unexpected disconnect: independent gain";
- EXPECT_TRUE(gain_control_2_.is_bound());
-
- EXPECT_EQ(received_gain_db_2_, kTooLowGainDb);
-}
-
-// TwoRenderersGainControlsTest
-// Two renderers, each with a gain control: we expect no cross-impact.
-//
-void TwoRenderersGainControlsTest::SetUp() {
- IndependentGainControlsTest::SetUp();
-
- SetUpRenderer2();
- SetUpGainControl2OnRenderer2();
-
- SetUpRenderer();
- SetUpGainControlOnRenderer();
-}
-
-TEST_F(TwoRenderersGainControlsTest, OtherInstanceReceivesNoMuteNotification) { TestSetMute(); }
-
-// We expect primary GainControl/Renderer to disconnect.
-TEST_F(TwoRenderersGainControlsTest, SetGainTooLow) { TestSetGainTooLow(); }
-
-// RendererCapturerGainControlsTest
-// Renderer gain control should not affect capturer gain control.
-//
-void RendererCapturerGainControlsTest::SetUp() {
- IndependentGainControlsTest::SetUp();
-
- SetUpCapturer();
- SetUpGainControl2OnCapturer();
-
- SetUpRenderer();
- SetUpGainControlOnRenderer();
-}
-
-TEST_F(RendererCapturerGainControlsTest, OtherInstanceReceivesNoGainNotification) { TestSetGain(); }
-
-// We expect primary GainControl/Renderer to disconnect.
-TEST_F(RendererCapturerGainControlsTest, SetGainTooHigh) { TestSetGainTooHigh(); }
-
-// CapturerRendererGainControlsTest
-// Capturer gain control should not affect renderer gain control.
-//
-void CapturerRendererGainControlsTest::SetUp() {
- IndependentGainControlsTest::SetUp();
-
- SetUpRenderer();
- SetUpGainControl2OnRenderer();
-
- SetUpCapturer();
- SetUpGainControlOnCapturer();
-}
-
-TEST_F(CapturerRendererGainControlsTest, OtherInstanceReceivesNoGainNotification) { TestSetGain(); }
-
-// We expect primary GainControl/Capturer to disconnect.
-TEST_F(CapturerRendererGainControlsTest, SetGainTooHigh) { TestSetGainTooHigh(); }
-
-// TwoCapturersGainControlsTest
-// Two capturers, each with a gain control: we expect no cross-impact.
-//
-void TwoCapturersGainControlsTest::SetUp() {
- IndependentGainControlsTest::SetUp();
-
- SetUpCapturer2();
- SetUpGainControl2OnCapturer2();
-
- SetUpCapturer();
- SetUpGainControlOnCapturer();
-}
-
-TEST_F(TwoCapturersGainControlsTest, OtherInstanceReceivesNoMuteNotification) { TestSetMute(); }
-
-// We expect primary GainControl/Capturer to disconnect.
-TEST_F(TwoCapturersGainControlsTest, SetGainTooLow) { TestSetGainTooLow(); }
-
} // namespace media::audio::test
diff --git a/src/media/audio/audio_core/test/api/gain_control_test.h b/src/media/audio/audio_core/test/api/gain_control_test.h
deleted file mode 100644
index 5c8ad78..0000000
--- a/src/media/audio/audio_core/test/api/gain_control_test.h
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_TEST_FIDL_GAIN_CONTROL_TEST_H_
-#define SRC_MEDIA_AUDIO_AUDIO_CORE_TEST_FIDL_GAIN_CONTROL_TEST_H_
-
-#include <fuchsia/media/cpp/fidl.h>
-
-#include "src/media/audio/lib/test/hermetic_audio_test.h"
-
-namespace media::audio::test {
-
-// GainControlTestBase
-//
-// This set of tests verifies asynchronous usage of GainControl.
-class GainControlTestBase : public HermeticAudioTest {
- protected:
- void TearDown() final;
-
- void SetNegativeExpectations() override;
- void SetUpRenderer();
- void SetUpCapturer();
- void SetUpRenderer2();
- void SetUpCapturer2();
- void SetUpGainControl();
- void SetUpGainControlOnRenderer();
- void SetUpGainControlOnCapturer();
- void SetUpGainControl2();
- void SetUpGainControl2OnRenderer();
- void SetUpGainControl2OnCapturer();
- void SetUpGainControl2OnRenderer2();
- void SetUpGainControl2OnCapturer2();
-
- // Always augmented by child implementations that set up the API interface.
- virtual bool ApiIsNull() = 0;
-
- void SetGain(float gain_db);
- void SetMute(bool mute);
-
- // Tests expect a gain callback. Absorb this; perform related error checking.
- virtual void ExpectGainCallback(float gain_db, bool mute);
-
- // Tests expect the API binding to disconnect, then the GainControl binding as
- // well. After the first disconnect, assert that GainControl is still bound.
- void ExpectDisconnect() override;
-
- // Core test cases that are validated across various scenarios
- void TestSetGain();
- void TestSetMute();
- void TestSetGainMute();
- void TestDuplicateSetGain();
- void TestDuplicateSetMute();
- void TestSetGainTooHigh();
- void TestSetGainTooLow();
- void TestSetGainNaN();
-
- fuchsia::media::AudioRendererPtr audio_renderer_;
- fuchsia::media::AudioCapturerPtr audio_capturer_;
- fuchsia::media::audio::GainControlPtr gain_control_;
-
- float received_gain_db_ = kTooLowGainDb;
- bool received_mute_ = false;
-
- // Member variables for tests that use multiple interface bindings
- bool error_occurred_2_ = false;
- fuchsia::media::AudioRendererPtr audio_renderer_2_;
- fuchsia::media::AudioCapturerPtr audio_capturer_2_;
- fuchsia::media::audio::GainControlPtr gain_control_2_;
-
- float received_gain_db_2_ = kTooLowGainDb;
- bool received_mute_2_ = false;
-
- // Member variables to manage our expectations
- bool null_api_expected_ = false;
- bool null_gain_control_expected_ = false;
- bool null_gain_control_expected_2_ = true;
- bool error_expected_2_ = false;
-};
-
-// RenderGainControlTest
-//
-class RenderGainControlTest : public GainControlTestBase {
- protected:
- void SetUp() override;
- bool ApiIsNull() final { return !audio_renderer_.is_bound(); }
-};
-
-// CaptureGainControlTest
-//
-class CaptureGainControlTest : public GainControlTestBase {
- protected:
- void SetUp() override;
- bool ApiIsNull() final { return !audio_capturer_.is_bound(); }
-};
-
-// SiblingGainControlsTest
-//
-// On a renderer/capturer, sibling GainControls receive identical notifications.
-class SiblingGainControlsTest : public GainControlTestBase {
- protected:
- void SetNegativeExpectations() override;
-
- // Absorb a gain callback from the sibling GainControl as well.
- void ExpectGainCallback(float gain_db, bool mute) final;
-
- // Absorb the second GainControl's disconnect, once the first disconnects.
- void ExpectDisconnect() final;
-};
-
-// RendererTwoGainControlsTest
-//
-// Verify that Renderer's second GainControl receives the same notifications.
-class RendererTwoGainControlsTest : public SiblingGainControlsTest {
- protected:
- void SetUp() override;
- bool ApiIsNull() final { return !audio_renderer_.is_bound(); }
-};
-
-// CapturerTwoGainControlsTest
-//
-// Verify that Capturer's second GainControl receives the same notifications.
-class CapturerTwoGainControlsTest : public SiblingGainControlsTest {
- protected:
- void SetUp() override;
- bool ApiIsNull() final { return !audio_capturer_.is_bound(); }
-};
-
-// IndependentGainControlsTest
-//
-// Verify that GainControls on different API instances are fully independent.
-class IndependentGainControlsTest : public GainControlTestBase {
- protected:
- // Expect nothing from the independent gain control.
- void ExpectGainCallback(float gain_db, bool mute) final;
-
- // Expect NO disconnect from our independent gain control -- after the first
- // gain control disconnect has already occurred.
- void ExpectDisconnect() final;
-};
-
-// TwoRenderersGainControlsTest
-//
-// Verify that Renderers' GainControls are fully independent.
-class TwoRenderersGainControlsTest : public IndependentGainControlsTest {
- protected:
- void SetUp() override;
- bool ApiIsNull() final { return !audio_renderer_.is_bound() && audio_renderer_2_.is_bound(); }
-};
-
-// RendererCapturerGainControlsTest
-//
-// Verify that Renderer GainControl does not affect Capturer GainControl.
-class RendererCapturerGainControlsTest : public IndependentGainControlsTest {
- protected:
- void SetUp() override;
- bool ApiIsNull() final { return !audio_renderer_.is_bound() && audio_capturer_.is_bound(); }
-};
-
-// CapturerRendererGainControlsTest
-//
-// Verify that Capturer GainControl does not affect Renderer GainControl.
-class CapturerRendererGainControlsTest : public IndependentGainControlsTest {
- protected:
- void SetUp() override;
- bool ApiIsNull() final { return !audio_capturer_.is_bound() && audio_renderer_.is_bound(); }
-};
-
-// TwoCapturersGainControlsTest
-//
-// Verify that Capturers' GainControls are fully independent.
-class TwoCapturersGainControlsTest : public IndependentGainControlsTest {
- protected:
- void SetUp() override;
- bool ApiIsNull() final { return !audio_capturer_.is_bound() && audio_capturer_2_.is_bound(); }
-};
-
-} // namespace media::audio::test
-
-#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_TEST_FIDL_GAIN_CONTROL_TEST_H_
diff --git a/src/media/audio/audio_core/test/api/usage_gain_reporter_test.cc b/src/media/audio/audio_core/test/api/usage_gain_reporter_test.cc
index a7573c3..58bbd1c 100644
--- a/src/media/audio/audio_core/test/api/usage_gain_reporter_test.cc
+++ b/src/media/audio/audio_core/test/api/usage_gain_reporter_test.cc
@@ -74,7 +74,7 @@
// Test that the user is connected to the usage gain reporter.
// TODO(50645): Also test muted
TEST_F(UsageGainReporterTest, ConnectToUsageGainReporter) {
- fit::closure completer = CompletionCallback([] {});
+ fit::closure completer = AddCallback("OnGainMuteChanged", [] {});
// The specific choice of format doesn't matter here, any output device will do.
constexpr auto kSampleFormat = fuchsia::media::AudioSampleFormat::SIGNED_16;
@@ -90,7 +90,7 @@
fuchsia::media::UsageGainReporterPtr gain_reporter;
environment()->ConnectToService(gain_reporter.NewRequest());
- gain_reporter.set_error_handler(ErrorHandler());
+ AddErrorHandler(gain_reporter, "GainReporter");
auto fake_listener = std::make_unique<FakeGainListener>(std::move(completer));
gain_reporter->RegisterListener(device_id_string_, fidl::Clone(usage),
diff --git a/src/media/audio/audio_core/test/api/usage_reporter_test.cc b/src/media/audio/audio_core/test/api/usage_reporter_test.cc
index b335b42..e0eb632 100644
--- a/src/media/audio/audio_core/test/api/usage_reporter_test.cc
+++ b/src/media/audio/audio_core/test/api/usage_reporter_test.cc
@@ -35,11 +35,11 @@
// TODO(50645): More complete testing of the integration with renderers
TEST_F(UsageReporterTest, ConnectToUsageReporter) {
- fit::closure completer = CompletionCallback([] {});
+ fit::closure completer = AddCallback("OnStateChanged", [] {});
fuchsia::media::UsageReporterPtr audio_core;
environment()->ConnectToService(audio_core.NewRequest());
- audio_core.set_error_handler(ErrorHandler());
+ AddErrorHandler(audio_core, "AudioCore");
fuchsia::media::Usage usage;
usage.set_render_usage(fuchsia::media::AudioRenderUsage::MEDIA);
diff --git a/src/media/audio/audio_core/test/api/volume_control.cc b/src/media/audio/audio_core/test/api/volume_control.cc
index aeaadeb..788fb0c 100644
--- a/src/media/audio/audio_core/test/api/volume_control.cc
+++ b/src/media/audio/audio_core/test/api/volume_control.cc
@@ -18,7 +18,7 @@
TEST_F(VolumeControlTest, ConnectToRenderUsageVolume) {
fuchsia::media::AudioCorePtr audio_core;
environment()->ConnectToService(audio_core.NewRequest());
- audio_core.set_error_handler(ErrorHandler());
+ AddErrorHandler(audio_core, "AudioCore");
fuchsia::media::audio::VolumeControlPtr client1;
fuchsia::media::audio::VolumeControlPtr client2;
@@ -31,20 +31,25 @@
float volume = 0.0;
bool muted = false;
- client2.events().OnVolumeMuteChanged =
- CompletionCallback([&volume, &muted](float new_volume, bool new_muted) {
- volume = new_volume;
- muted = new_muted;
- });
+ auto add_callback = [this, &client2, &volume, &muted]() {
+ client2.events().OnVolumeMuteChanged =
+ AddCallback("OnVolumeMuteChanged", [&volume, &muted](float new_volume, bool new_muted) {
+ volume = new_volume;
+ muted = new_muted;
+ });
+ };
+ add_callback();
ExpectCallback();
EXPECT_FLOAT_EQ(volume, 1.0);
+ add_callback();
client1->SetVolume(0.5);
ExpectCallback();
EXPECT_FLOAT_EQ(volume, 0.5);
EXPECT_EQ(muted, false);
+ add_callback();
client1->SetMute(true);
ExpectCallback();
EXPECT_EQ(muted, true);
diff --git a/src/media/audio/audio_core/test/audio_admin/audio_admin_test.cc b/src/media/audio/audio_core/test/audio_admin/audio_admin_test.cc
index 6c36b0e..65f584b 100644
--- a/src/media/audio/audio_core/test/audio_admin/audio_admin_test.cc
+++ b/src/media/audio/audio_core/test/audio_admin/audio_admin_test.cc
@@ -201,8 +201,8 @@
// we should have mixed audio available for capture. Our playback is sized
// to be much much larger than our capture to prevent test flakes.
renderer->renderer()->Play(zx::clock::get_monotonic().get(), 0,
- CompletionCallback([&ref_time_received, &media_time_received](
- int64_t ref_time, int64_t media_time) {
+ AddCallback("Play", [&ref_time_received, &media_time_received](
+ int64_t ref_time, int64_t media_time) {
ref_time_received = ref_time;
media_time_received = media_time;
}));
@@ -217,13 +217,11 @@
zx_nanosleep(zx_deadline_after(sleep_duration));
// Add a callback for when we get our captured packet.
- bool produced_packet = false;
capturer->capturer().events().OnPacketProduced =
- CompletionCallback([&captured, &produced_packet](fuchsia::media::StreamPacket packet) {
+ AddCallback("OnPacketProduced", [&captured](fuchsia::media::StreamPacket packet) {
// We only care about the first set of captured samples
if (captured.payload_size == 0) {
captured = packet;
- produced_packet = true;
}
});
@@ -257,17 +255,6 @@
auto renderer = SetUpRenderer(fuchsia::media::AudioRenderUsage::SYSTEM_AGENT, kPlaybackData1);
auto capturer = SetUpCapturer(fuchsia::media::AudioCaptureUsage::BACKGROUND);
- // Add a callback for when we get our captured packet.
- bool produced_packet = false;
- capturer->capturer().events().OnPacketProduced =
- CompletionCallback([&captured, &produced_packet](fuchsia::media::StreamPacket packet) {
- // We only care about the first set of captured samples
- if (captured.payload_size == 0) {
- captured = packet;
- produced_packet = true;
- }
- });
-
// Get the minimum duration after submitting a packet to when we can start
// capturing what we sent on the loopback interface
zx_duration_t sleep_duration = GetMinLeadTime({renderer});
@@ -285,8 +272,8 @@
// we should have mixed audio available for capture. Our playback is sized
// to be much much larger than our capture to prevent test flakes.
renderer->renderer()->Play(zx::clock::get_monotonic().get(), 0,
- CompletionCallback([&ref_time_received, &media_time_received](
- int64_t ref_time, int64_t media_time) {
+ AddCallback("Play", [&ref_time_received, &media_time_received](
+ int64_t ref_time, int64_t media_time) {
ref_time_received = ref_time;
media_time_received = media_time;
}));
@@ -301,6 +288,13 @@
zx_nanosleep(zx_deadline_after(sleep_duration));
// Capture 10 samples of audio.
+ capturer->capturer().events().OnPacketProduced =
+ AddCallback("OnPacketProduced", [&captured](fuchsia::media::StreamPacket packet) {
+ // We only care about the first set of captured samples
+ if (captured.payload_size == 0) {
+ captured = packet;
+ }
+ });
capturer->capturer()->StartAsyncCapture(10);
ExpectCallback();
@@ -352,8 +346,8 @@
// we should have mixed audio available for capture. Our playback is sized
// to be much much larger than our capture to prevent test flakes.
renderer->renderer()->Play(zx::clock::get_monotonic().get(), 0,
- CompletionCallback([&ref_time_received, &media_time_received](
- int64_t ref_time, int64_t media_time) {
+ AddCallback("Play", [&ref_time_received, &media_time_received](
+ int64_t ref_time, int64_t media_time) {
ref_time_received = ref_time;
media_time_received = media_time;
}));
@@ -371,8 +365,8 @@
fuchsia::media::StreamPacket loopback_captured;
bool produced_loopback_packet = false;
loopback_capturer->capturer().events().OnPacketProduced =
- CompletionCallback([&loopback_captured, &produced_loopback_packet,
- ref_time_received](fuchsia::media::StreamPacket packet) {
+ AddCallback("OnPacketProduced", [&loopback_captured, &produced_loopback_packet,
+ ref_time_received](fuchsia::media::StreamPacket packet) {
// We only care about the first set of captured samples
if (packet.pts > ref_time_received && loopback_captured.payload_size == 0) {
loopback_captured = packet;
@@ -411,18 +405,6 @@
// SetUp loopback capture
auto capturer = SetUpLoopbackCapturer();
- // Add a callback for when we get our captured packet.
- bool produced_packet = false;
- fuchsia::media::StreamPacket captured;
- capturer->capturer().events().OnPacketProduced =
- CompletionCallback([&captured, &produced_packet](fuchsia::media::StreamPacket packet) {
- // We only care about the first set of captured samples
- if (captured.payload_size == 0) {
- captured = packet;
- produced_packet = true;
- }
- });
-
// Get the minimum duration after submitting a packet to when we can start
// capturing what we sent on the loopback interface.
zx_duration_t sleep_duration = GetMinLeadTime({renderer1, renderer2});
@@ -445,8 +427,8 @@
renderer1->renderer()->PlayNoReply(playat, 0);
// Only get the callback for the second renderer.
renderer2->renderer()->Play(playat, 0,
- CompletionCallback([&ref_time_received, &media_time_received](
- int64_t ref_time, int64_t media_time) {
+ AddCallback("Play", [&ref_time_received, &media_time_received](
+ int64_t ref_time, int64_t media_time) {
ref_time_received = ref_time;
media_time_received = media_time;
}));
@@ -461,6 +443,14 @@
zx_nanosleep(zx_deadline_after(sleep_duration));
// Capture 10 samples of audio.
+ fuchsia::media::StreamPacket captured;
+ capturer->capturer().events().OnPacketProduced =
+ AddCallback("OnPacketProduced", [&captured](fuchsia::media::StreamPacket packet) {
+ // We only care about the first set of captured samples
+ if (captured.payload_size == 0) {
+ captured = packet;
+ }
+ });
capturer->capturer()->StartAsyncCapture(10);
ExpectCallback();
@@ -504,18 +494,6 @@
// SetUp loopback capture
auto capturer = SetUpLoopbackCapturer();
- // Add a callback for when we get our captured packet.
- bool produced_packet = false;
- fuchsia::media::StreamPacket captured;
- capturer->capturer().events().OnPacketProduced =
- CompletionCallback([&captured, &produced_packet](fuchsia::media::StreamPacket packet) {
- // We only care about the first set of captured samples
- if (captured.payload_size == 0) {
- captured = packet;
- produced_packet = true;
- }
- });
-
// Get the minimum duration after submitting a packet to when we can start
// capturing what we sent on the loopback interface.
zx_duration_t sleep_duration = GetMinLeadTime({renderer1, renderer2});
@@ -538,8 +516,8 @@
renderer1->renderer()->PlayNoReply(playat, 0);
// Only get the callback for the second renderer.
renderer2->renderer()->Play(playat, 0,
- CompletionCallback([&ref_time_received, &media_time_received](
- int64_t ref_time, int64_t media_time) {
+ AddCallback("Play", [&ref_time_received, &media_time_received](
+ int64_t ref_time, int64_t media_time) {
ref_time_received = ref_time;
media_time_received = media_time;
}));
@@ -554,6 +532,14 @@
zx_nanosleep(zx_deadline_after(sleep_duration));
// Capture 10 samples of audio.
+ fuchsia::media::StreamPacket captured;
+ capturer->capturer().events().OnPacketProduced =
+ AddCallback("OnPacketProduced", [&captured](fuchsia::media::StreamPacket packet) {
+ // We only care about the first set of captured samples
+ if (captured.payload_size == 0) {
+ captured = packet;
+ }
+ });
capturer->capturer()->StartAsyncCapture(10);
ExpectCallback();
@@ -584,18 +570,6 @@
// SetUp loopback capture
auto capturer = SetUpLoopbackCapturer();
- // Add a callback for when we get our captured packet.
- bool produced_packet = false;
- fuchsia::media::StreamPacket captured;
- capturer->capturer().events().OnPacketProduced =
- CompletionCallback([&captured, &produced_packet](fuchsia::media::StreamPacket packet) {
- // We only care about the first set of captured samples
- if (captured.payload_size == 0) {
- captured = packet;
- produced_packet = true;
- }
- });
-
// Get the minimum duration after submitting a packet to when we can start
// capturing what we sent on the loopback interface.
zx_duration_t sleep_duration = GetMinLeadTime({renderer1});
@@ -618,8 +592,8 @@
renderer1->renderer()->PlayNoReply(playat, 0);
// Only get the callback for the second renderer.
renderer2->renderer()->Play(playat, 0,
- CompletionCallback([&ref_time_received, &media_time_received](
- int64_t ref_time, int64_t media_time) {
+ AddCallback("Play", [&ref_time_received, &media_time_received](
+ int64_t ref_time, int64_t media_time) {
ref_time_received = ref_time;
media_time_received = media_time;
}));
@@ -634,6 +608,14 @@
zx_nanosleep(zx_deadline_after(sleep_duration));
// Capture 10 samples of audio.
+ fuchsia::media::StreamPacket captured;
+ capturer->capturer().events().OnPacketProduced =
+ AddCallback("OnPacketProduced", [&captured](fuchsia::media::StreamPacket packet) {
+ // We only care about the first set of captured samples
+ if (captured.payload_size == 0) {
+ captured = packet;
+ }
+ });
capturer->capturer()->StartAsyncCapture(10);
ExpectCallback();
@@ -663,29 +645,6 @@
auto capturer1 = SetUpCapturer(fuchsia::media::AudioCaptureUsage::BACKGROUND);
auto capturer2 = SetUpCapturer(fuchsia::media::AudioCaptureUsage::BACKGROUND);
- // Add a callback for when we get our captured packet.
- fuchsia::media::StreamPacket captured1;
- bool produced_packet1 = false;
- capturer1->capturer().events().OnPacketProduced =
- CompletionCallback([&captured1, &produced_packet1](fuchsia::media::StreamPacket packet) {
- // We only care about the first set of captured samples
- if (captured1.payload_size == 0) {
- captured1 = packet;
- produced_packet1 = true;
- }
- });
-
- fuchsia::media::StreamPacket captured2;
- bool produced_packet2 = false;
- capturer2->capturer().events().OnPacketProduced =
- CompletionCallback([&captured2, &produced_packet2](fuchsia::media::StreamPacket packet) {
- // We only care about the first set of captured samples
- if (captured2.payload_size == 0) {
- captured2 = packet;
- produced_packet2 = true;
- }
- });
-
// Get the minimum duration after submitting a packet to when we can start
// capturing what we sent on the loopback interface.
zx_duration_t sleep_duration = GetMinLeadTime({renderer});
@@ -706,8 +665,8 @@
// to be much much larger than our capture to prevent test flakes.
auto playat = zx::clock::get_monotonic().get();
renderer->renderer()->Play(playat, 0,
- CompletionCallback([&ref_time_received, &media_time_received](
- int64_t ref_time, int64_t media_time) {
+ AddCallback("Play", [&ref_time_received, &media_time_received](
+ int64_t ref_time, int64_t media_time) {
ref_time_received = ref_time;
media_time_received = media_time;
}));
@@ -722,10 +681,27 @@
zx_nanosleep(zx_deadline_after(sleep_duration));
// Capture 10 samples of audio.
+ fuchsia::media::StreamPacket captured1;
+ capturer1->capturer().events().OnPacketProduced =
+ AddCallbackUnordered("OnPacketProduced", [&captured1](fuchsia::media::StreamPacket packet) {
+ // We only care about the first set of captured samples
+ if (captured1.payload_size == 0) {
+ captured1 = packet;
+ }
+ });
+
+ fuchsia::media::StreamPacket captured2;
+ capturer2->capturer().events().OnPacketProduced =
+ AddCallbackUnordered("OnPacketProduced", [&captured2](fuchsia::media::StreamPacket packet) {
+ // We only care about the first set of captured samples
+ if (captured2.payload_size == 0) {
+ captured2 = packet;
+ }
+ });
+
capturer1->capturer()->StartAsyncCapture(10);
capturer2->capturer()->StartAsyncCapture(10);
- RunLoopUntil(
- [&produced_packet1, &produced_packet2]() { return produced_packet1 && produced_packet2; });
+ ExpectCallback();
// Check that all of the samples contain the expected data.
ExpectPacketContains("captured1", captured1, capturer1->SnapshotPayload(), 10,
@@ -759,8 +735,8 @@
// Add a callback for when we get our captured packet.
fuchsia::media::StreamPacket captured1;
bool produced_packet1 = false;
- capturer1->capturer().events().OnPacketProduced =
- CompletionCallback([&captured1, &produced_packet1](fuchsia::media::StreamPacket packet) {
+ capturer1->capturer().events().OnPacketProduced = AddCallback(
+ "OnPacketProduced", [&captured1, &produced_packet1](fuchsia::media::StreamPacket packet) {
// We only care about the first set of captured samples
if (captured1.payload_size == 0) {
captured1 = packet;
@@ -770,8 +746,8 @@
fuchsia::media::StreamPacket captured2;
bool produced_packet2 = false;
- capturer2->capturer().events().OnPacketProduced =
- CompletionCallback([&captured2, &produced_packet2](fuchsia::media::StreamPacket packet) {
+ capturer2->capturer().events().OnPacketProduced = AddCallback(
+ "OnPacketProduced", [&captured2, &produced_packet2](fuchsia::media::StreamPacket packet) {
// We only care about the first set of captured samples
if (captured2.payload_size == 0) {
captured2 = packet;
@@ -799,8 +775,8 @@
// to be much much larger than our capture to prevent test flakes.
auto playat = zx::clock::get_monotonic().get();
renderer->renderer()->Play(playat, 0,
- CompletionCallback([&ref_time_received, &media_time_received](
- int64_t ref_time, int64_t media_time) {
+ AddCallback("Play", [&ref_time_received, &media_time_received](
+ int64_t ref_time, int64_t media_time) {
ref_time_received = ref_time;
media_time_received = media_time;
}));
diff --git a/src/media/audio/audio_core/test/hardware/audio_core_hardware_test.cc b/src/media/audio/audio_core/test/hardware/audio_core_hardware_test.cc
index e14ff3b..f73200a 100644
--- a/src/media/audio/audio_core/test/hardware/audio_core_hardware_test.cc
+++ b/src/media/audio/audio_core/test/hardware/audio_core_hardware_test.cc
@@ -12,6 +12,14 @@
namespace media::audio::test {
+// For operations expected to generate a response, wait __1 minute__. We do this to avoid flaky
+// results when testing on high-load (high-latency) environments. For reference, in mid-2018 when
+// observing highly-loaded local QEMU instances running code that generated correct completion
+// responses, we observed timeouts if waiting 20 ms, but not if waiting 50 ms. This value is 3000x
+// that (!) -- WELL beyond the limit of human acceptability. Thus, intermittent failures (rather
+// than being a "potentially flaky test") mean that the system is, intermittently, UNACCEPTABLE.
+constexpr zx::duration kDurationResponseExpected = zx::sec(60);
+
void AudioCoreHardwareTest::SetUp() {
TestFixture::SetUp();
@@ -29,16 +37,11 @@
RunLoopUntilIdle();
}
-void AudioCoreHardwareTest::TearDown() { ASSERT_FALSE(error_occurred()); }
-
bool AudioCoreHardwareTest::WaitForCaptureDevice() {
audio_device_enumerator_ = sys::ServiceDirectory::CreateFromNamespace()
->Connect<fuchsia::media::AudioDeviceEnumerator>();
- audio_device_enumerator_.set_error_handler(ErrorHandler([](zx_status_t status) {
- FAIL() << "Client connection to fuchsia.media.AudioDeviceEnumerator: "
- << zx_status_get_string(status) << " (" << status << ")";
- }));
+ AddErrorHandler(audio_device_enumerator_, "AudioDeviceEnumerator");
audio_device_enumerator_.events().OnDeviceAdded =
([this](fuchsia::media::AudioDeviceInfo device) {
@@ -78,18 +81,14 @@
}
});
- RunLoopWithTimeoutOrUntil([this]() { return error_occurred_ || capture_device_is_default_; },
+ RunLoopWithTimeoutOrUntil([this]() { return ErrorOccurred() || capture_device_is_default_; },
kDurationResponseExpected);
return capture_device_is_default_;
}
void AudioCoreHardwareTest::ConnectToAudioCore() {
audio_core_ = sys::ServiceDirectory::CreateFromNamespace()->Connect<fuchsia::media::AudioCore>();
-
- audio_core_.set_error_handler(ErrorHandler([](zx_status_t status) {
- FAIL() << "Client connection to fuchsia.media.AudioCore: " << zx_status_get_string(status)
- << " (" << status << ")";
- }));
+ AddErrorHandler(audio_core_, "AudioCore");
}
void AudioCoreHardwareTest::ConnectToAudioCapturer() {
@@ -97,11 +96,7 @@
constexpr bool kNotLoopback = false;
audio_core_->CreateAudioCapturer(kNotLoopback, audio_capturer_.NewRequest());
-
- audio_capturer_.set_error_handler(ErrorHandler([](zx_status_t status) {
- FAIL() << "Client connection to fuchsia.media.AudioCapturer: " << zx_status_get_string(status)
- << " (" << status << ")";
- }));
+ AddErrorHandler(audio_capturer_, "AudioCapturer");
audio_capturer_->SetUsage(kUsage);
}
@@ -110,11 +105,7 @@
ASSERT_TRUE(audio_capturer_.is_bound());
audio_capturer_->BindGainControl(stream_gain_control_.NewRequest());
-
- stream_gain_control_.set_error_handler(ErrorHandler([](zx_status_t status) {
- FAIL() << "Client connection to (capture stream) fuchsia.media.audio.GainControl: "
- << zx_status_get_string(status) << " (" << status << ")";
- }));
+ AddErrorHandler(stream_gain_control_, "AudioCapturer::GainControl");
}
// Set gain for this capturer gain control, capture usage and all capture devices.
@@ -133,13 +124,14 @@
// Fetch the initial media type and adjust channel_count_ and frames_per_second_ if needed.
void AudioCoreHardwareTest::GetDefaultCaptureFormat() {
- audio_capturer_->GetStreamType(CompletionCallback([this](fuchsia::media::StreamType stream_type) {
- ASSERT_TRUE(stream_type.medium_specific.is_audio()) << "Default format is not audio!";
- const auto& format = stream_type.medium_specific.audio();
+ audio_capturer_->GetStreamType(
+ AddCallback("GetStreamType", [this](fuchsia::media::StreamType stream_type) {
+ ASSERT_TRUE(stream_type.medium_specific.is_audio()) << "Default format is not audio!";
+ const auto& format = stream_type.medium_specific.audio();
- channel_count_ = format.channels;
- frames_per_second_ = format.frames_per_second;
- }));
+ channel_count_ = format.channels;
+ frames_per_second_ = format.frames_per_second;
+ }));
ExpectCallback();
@@ -213,7 +205,7 @@
const uint32_t payload_offset = 0u;
audio_capturer_->CaptureAt(kPayloadBufferId, payload_offset, vmo_buffer_frame_count_,
- CompletionCallback([this](fuchsia::media::StreamPacket packet) {
+ AddCallback("CaptureAt", [this](fuchsia::media::StreamPacket packet) {
OnPacketProduced(packet);
}));
// Wait for the capture buffer to be returned.
diff --git a/src/media/audio/audio_core/test/hardware/audio_core_hardware_test.h b/src/media/audio/audio_core/test/hardware/audio_core_hardware_test.h
index b7b0d52..c625325 100644
--- a/src/media/audio/audio_core/test/hardware/audio_core_hardware_test.h
+++ b/src/media/audio/audio_core/test/hardware/audio_core_hardware_test.h
@@ -36,7 +36,6 @@
static constexpr uint32_t kBytesPerSample = 4;
void SetUp() override;
- void TearDown() override;
bool WaitForCaptureDevice();
diff --git a/src/media/audio/audio_core/test/pipeline/audio_pipeline_test.cc b/src/media/audio/audio_core/test/pipeline/audio_pipeline_test.cc
index 4d8f3cc..680addd 100644
--- a/src/media/audio/audio_core/test/pipeline/audio_pipeline_test.cc
+++ b/src/media/audio/audio_core/test/pipeline/audio_pipeline_test.cc
@@ -122,14 +122,9 @@
renderer_->Play(this, first_time, 0);
renderer_->WaitForPackets(this, first_time, {first_packets[0], first_packets[1]});
- auto received_discard_all_callback = false;
- renderer_->renderer()->DiscardAllPackets(CompletionCallback([&received_discard_all_callback]() {
- received_discard_all_callback = true;
- AUDIO_LOG(DEBUG) << "DiscardAllPackets #1 complete";
- }));
- RunLoopUntil([this, &received_discard_all_callback]() {
- return (error_occurred_ || received_discard_all_callback);
- });
+ renderer_->renderer()->DiscardAllPackets(AddCallback(
+ "DiscardAllPackets", []() { AUDIO_LOG(DEBUG) << "DiscardAllPackets #1 complete"; }));
+ ExpectCallback();
// The entire first two packets must have been written. Subsequent packets may have been partially
// written, depending on exactly when the DiscardAllPackets command is received. The remaining
diff --git a/src/media/audio/lib/test/BUILD.gn b/src/media/audio/lib/test/BUILD.gn
index 17020f0..15fad1a 100644
--- a/src/media/audio/lib/test/BUILD.gn
+++ b/src/media/audio/lib/test/BUILD.gn
@@ -27,6 +27,7 @@
public_deps = [
"//garnet/public/lib/gtest",
+ "//sdk/lib/fidl/cpp",
"//zircon/public/lib/fit",
]
}
diff --git a/src/media/audio/lib/test/capturer_shim.h b/src/media/audio/lib/test/capturer_shim.h
index 21e6a25..c18aebc 100644
--- a/src/media/audio/lib/test/capturer_shim.h
+++ b/src/media/audio/lib/test/capturer_shim.h
@@ -76,7 +76,7 @@
: CapturerShimImpl(format, payload_frame_count) {
audio_core->CreateAudioCapturerWithConfiguration(format.stream_type(), std::move(config),
capturer_.NewRequest());
- capturer_.set_error_handler(fixture->ErrorHandler());
+ fixture->AddErrorHandler(capturer_, "AudioCapturer");
capturer_->SetPcmStreamType({.sample_format = format_.sample_format(),
.channels = format_.channels(),
@@ -114,11 +114,11 @@
// TODO(55243): Enable AddPayloadBuffer before the capturer is created.
capturer_->AddPayloadBuffer(0, std::move(vmo));
});
- capturer_.set_error_handler(fixture->ErrorHandler());
+ fixture->AddErrorHandler(capturer_, "UltrasoundCapturer");
}
void WaitForDevice() {
- fixture_->RunLoopUntil([this] { return created_ || fixture_->error_occurred(); });
+ fixture_->RunLoopUntil([this] { return created_ || fixture_->ErrorOccurred(); });
}
bool created() const { return created_; }
diff --git a/src/media/audio/lib/test/hermetic_audio_test.cc b/src/media/audio/lib/test/hermetic_audio_test.cc
index 495aeef..e1524fc 100644
--- a/src/media/audio/lib/test/hermetic_audio_test.cc
+++ b/src/media/audio/lib/test/hermetic_audio_test.cc
@@ -50,13 +50,13 @@
TestFixture::SetUp();
environment_->ConnectToService(audio_core_.NewRequest());
- audio_core_.set_error_handler(ErrorHandler());
+ AddErrorHandler(audio_core_, "AudioCore");
environment_->ConnectToService(ultrasound_factory_.NewRequest());
- ultrasound_factory_.set_error_handler(ErrorHandler());
+ AddErrorHandler(ultrasound_factory_, "UltrasoundFactory");
environment_->ConnectToService(audio_dev_enum_.NewRequest());
- audio_dev_enum_.set_error_handler(ErrorHandler());
+ AddErrorHandler(audio_dev_enum_, "AudioDeviceEnumerator");
WatchForDeviceArrivals();
}
@@ -118,18 +118,6 @@
TestFixture::TearDown();
}
-void HermeticAudioTest::ExpectCallback() {
- TestFixture::ExpectCallback();
-
- EXPECT_TRUE(audio_core_.is_bound());
-}
-
-void HermeticAudioTest::ExpectDisconnect() {
- TestFixture::ExpectDisconnect();
-
- EXPECT_TRUE(audio_core_.is_bound());
-}
-
template <fuchsia::media::AudioSampleFormat SampleFormat>
VirtualOutput<SampleFormat>* HermeticAudioTest::CreateOutput(
const audio_stream_unique_id_t& device_id, TypedFormat<SampleFormat> format,
@@ -165,7 +153,7 @@
// Wait for device to become the default.
RunLoopUntil([this, id]() { return devices_[id].is_default; });
- EXPECT_FALSE(error_occurred_);
+ ExpectNoUnexpectedErrors("during CreateOutput");
return out;
}
@@ -197,7 +185,7 @@
renderers_.push_back(std::move(ptr));
// Wait until the renderer is connected.
- RunLoopUntil([this, out]() { return error_occurred_ || (out->GetMinLeadTime() > 0); });
+ RunLoopUntil([this, out]() { return ErrorOccurred() || (out->GetMinLeadTime() > 0); });
return out;
}
diff --git a/src/media/audio/lib/test/hermetic_audio_test.h b/src/media/audio/lib/test/hermetic_audio_test.h
index adf27a4..df20c9e 100644
--- a/src/media/audio/lib/test/hermetic_audio_test.h
+++ b/src/media/audio/lib/test/hermetic_audio_test.h
@@ -44,9 +44,6 @@
void SetUp() override;
void TearDown() override;
- void ExpectCallback() override;
- void ExpectDisconnect() override;
-
// Register that the test expects no audio underflows. This expectation will be checked by
// TearDown().
void FailUponUnderflows() { disallow_underflows_ = true; }
diff --git a/src/media/audio/lib/test/renderer_shim.cc b/src/media/audio/lib/test/renderer_shim.cc
index 0058650..aaf5b9e 100644
--- a/src/media/audio/lib/test/renderer_shim.cc
+++ b/src/media/audio/lib/test/renderer_shim.cc
@@ -39,12 +39,8 @@
}
void RendererShimImpl::Play(TestFixture* fixture, int64_t reference_time, int64_t media_time) {
- bool played = false;
- renderer_->Play(reference_time, media_time,
- [&played](int64_t reference_time, int64_t media_time) { played = true; });
-
- fixture->RunLoopUntil([&played]() { return played; });
- ASSERT_FALSE(fixture->error_occurred());
+ renderer_->Play(reference_time, media_time, fixture->AddCallback("Play"));
+ fixture->ExpectCallback();
}
template <fuchsia::media::AudioSampleFormat SampleFormat>
@@ -114,7 +110,7 @@
}
return true;
});
- ASSERT_FALSE(fixture->error_occurred());
+ fixture->ExpectNoUnexpectedErrors("during WaitForPackets");
}
// Explicitly instantiate all possible implementations.
diff --git a/src/media/audio/lib/test/renderer_shim.h b/src/media/audio/lib/test/renderer_shim.h
index 3ce682a..02c83f1 100644
--- a/src/media/audio/lib/test/renderer_shim.h
+++ b/src/media/audio/lib/test/renderer_shim.h
@@ -11,6 +11,7 @@
#include <memory>
#include <vector>
+#include "src/lib/fxl/strings/string_printf.h"
#include "src/lib/testing/loop_fixture/real_loop_fixture.h"
#include "src/media/audio/lib/format/audio_buffer.h"
#include "src/media/audio/lib/format/format.h"
@@ -125,7 +126,7 @@
size_t payload_frame_count, fuchsia::media::AudioRenderUsage usage)
: RendererShimImpl(format, payload_frame_count) {
audio_core->CreateAudioRenderer(renderer_.NewRequest());
- renderer_.set_error_handler(fixture->ErrorHandler());
+ fixture->AddErrorHandler(renderer_, "AudioRenderer");
WatchEvents();
renderer_->SetUsage(usage);
@@ -163,7 +164,7 @@
EXPECT_EQ(stream_type.channels, format_.channels());
EXPECT_EQ(stream_type.frames_per_second, format_.frames_per_second());
});
- renderer_.set_error_handler(fixture->ErrorHandler());
+ fixture->AddErrorHandler(renderer_, "UltrasoundRenderer");
WatchEvents();
SetPtsUnits(format_.frames_per_second(), 1);
@@ -171,7 +172,7 @@
}
void WaitForDevice() {
- fixture_->RunLoopUntil([this] { return created_ || fixture_->error_occurred(); });
+ fixture_->RunLoopUntil([this] { return created_ || fixture_->ErrorOccurred(); });
}
bool created() const { return created_; }
diff --git a/src/media/audio/lib/test/test_fixture.cc b/src/media/audio/lib/test/test_fixture.cc
index eac4be8..cf8b61f 100644
--- a/src/media/audio/lib/test/test_fixture.cc
+++ b/src/media/audio/lib/test/test_fixture.cc
@@ -4,36 +4,107 @@
#include "src/media/audio/lib/test/test_fixture.h"
+#include <zircon/status.h>
+
+#include "src/lib/fxl/strings/string_printf.h"
+
namespace media::audio::test {
-//
-// TestFixture implementation
-//
-void TestFixture::SetUp() { ::gtest::RealLoopFixture::SetUp(); }
+
+constexpr zx::duration kLoopTimeout = zx::sec(10);
void TestFixture::TearDown() {
- EXPECT_EQ(error_expected_, error_occurred_);
-
+ ExpectNoUnexpectedErrors("during TearDown");
::gtest::RealLoopFixture::TearDown();
}
void TestFixture::ExpectCallback() {
- callback_received_ = false;
+ int64_t last_seqno = 0;
+ while (!pending_callbacks_.empty()) {
+ auto pcb = pending_callbacks_.front();
+ pending_callbacks_.pop_front();
- RunLoopUntil([this]() { return (error_occurred_ || callback_received_); });
+ RunLoopWithTimeoutOrUntil([this, pcb]() { return new_error_ || pcb->seqno > 0; }, kLoopTimeout);
- EXPECT_FALSE(error_occurred_) << kDisconnectErr;
- EXPECT_TRUE(callback_received_);
+ if (new_error_) {
+ new_error_ = false;
+ ADD_FAILURE() << "Unexpected error while waiting for " << pcb->name;
+ ExpectNoUnexpectedErrors(fxl::StringPrintf("while waiting for %s", pcb->name.c_str()));
+ pending_callbacks_.clear();
+ return;
+ }
+ if (pcb->seqno == 0) {
+ ADD_FAILURE() << "Did not get a " << pcb->name << " callback within "
+ << kLoopTimeout.to_msecs() << "ms";
+ pending_callbacks_.clear();
+ return;
+ }
+
+ if (pcb->ordered) {
+ EXPECT_GT(pcb->seqno, last_seqno) << pcb->name << " called out-of-order";
+ last_seqno = pcb->seqno;
+ }
+ }
}
-void TestFixture::ExpectError(zx_status_t expected_error) {
- SetNegativeExpectations();
- callback_received_ = false;
+void TestFixture::ExpectErrors(const std::vector<std::shared_ptr<ErrorHandler>>& errors) {
+ std::string names = "{";
+ std::string sep;
+ for (auto& eh : errors) {
+ names += eh->name;
+ sep = ", ";
+ }
+ names += "}";
- RunLoopUntil([this]() { return (error_occurred_ || callback_received_); });
+ RunLoopWithTimeoutOrUntil(
+ [errors]() {
+ for (auto& eh : errors) {
+ if (eh->error_code != eh->expected_error_code) {
+ return false;
+ }
+ }
+ return true;
+ },
+ kLoopTimeout);
- EXPECT_TRUE(error_occurred_);
- EXPECT_EQ(error_code_, expected_error);
- EXPECT_FALSE(callback_received_) << kCallbackErr;
+ new_error_ = false;
+ ExpectNoUnexpectedErrors(fxl::StringPrintf("when waiting error in %s", names.c_str()));
+}
+
+void TestFixture::ExpectNoUnexpectedErrors(const std::string& msg_for_failure) {
+ for (auto& [_, eh] : error_handlers_) {
+ EXPECT_EQ(eh->error_code, eh->expected_error_code)
+ << msg_for_failure << ": " << eh->name << " had an unexpected error\nExpected error is "
+ << zx_status_get_string(eh->expected_error_code) << "\nActual error is "
+ << zx_status_get_string(eh->error_code);
+ }
+}
+
+std::pair<std::shared_ptr<TestFixture::ErrorHandler>, fit::function<void(zx_status_t)>>
+TestFixture::NewErrorHandler(const std::string& name) {
+ auto eh = std::make_shared<ErrorHandler>();
+ eh->name = name;
+ return std::make_pair(eh, [this, eh](zx_status_t status) {
+ eh->error_code = status;
+ new_error_ = true;
+ });
+}
+
+std::shared_ptr<TestFixture::PendingCallback> TestFixture::NewPendingCallback(
+ const std::string& name, bool ordered) {
+ auto pcb = std::make_shared<PendingCallback>();
+ pcb->name = name;
+ pcb->ordered = ordered;
+ pending_callbacks_.push_back(pcb);
+ return pcb;
+}
+
+bool TestFixture::ErrorOccurred() {
+ for (auto& [_, eh] : error_handlers_) {
+ if (eh->error_code != ZX_OK) {
+ return true;
+ }
+ }
+ return false;
}
} // namespace media::audio::test
diff --git a/src/media/audio/lib/test/test_fixture.h b/src/media/audio/lib/test/test_fixture.h
index 04877de..495bbac 100644
--- a/src/media/audio/lib/test/test_fixture.h
+++ b/src/media/audio/lib/test/test_fixture.h
@@ -4,80 +4,126 @@
#ifndef SRC_MEDIA_AUDIO_LIB_TEST_TEST_FIXTURE_H_
#define SRC_MEDIA_AUDIO_LIB_TEST_TEST_FIXTURE_H_
+#include <lib/fidl/cpp/interface_ptr.h>
+#include <lib/fidl/cpp/synchronous_interface_ptr.h>
#include <lib/fit/function.h>
#include <lib/gtest/real_loop_fixture.h>
+#include <zircon/errors.h>
+#include <deque>
+#include <initializer_list>
#include <optional>
+#include <unordered_map>
namespace media::audio::test {
-// For operations expected to generate a response, wait __1 minute__. We do this to avoid flaky
-// results when testing on high-load (high-latency) environments. For reference, in mid-2018 when
-// observing highly-loaded local QEMU instances running code that generated correct completion
-// responses, we observed timeouts if waiting 20 ms, but not if waiting 50 ms. This value is 3000x
-// that (!) -- WELL beyond the limit of human acceptability. Thus, intermittent failures (rather
-// than being a "potentially flaky test") mean that the system is, intermittently, UNACCEPTABLE.
+// TestFixture wraps a RealLoopFixture with methods to check for FIDL errors and callbacks.
+// For example, to check for disconnection:
//
-// Also, when expecting a response we can save time by checking more frequently. Restated,
-// kDurationResponseExpected should ALWAYS use kDurationGranularity.
+// SomeInterfacePtr ptr;
+// environment->ConnectToService(ptr.NewRequest());
+// AddErrorHandler(ptr, "SomeInterface");
//
-// These two values codify the following ordered priorities:
-// 1) False-positive test failures are expensive and must be eliminated.
-// 2) Having done that, streamline test run-time (time=resources=cost);
-constexpr zx::duration kDurationResponseExpected = zx::sec(60);
-constexpr zx::duration kDurationGranularity = zx::duration::infinite();
-
-constexpr char kDisconnectErr[] = "Connection to fuchsia.media FIDL interface was lost!\n";
-constexpr char kTimeoutErr[] = "Timeout -- no callback received!\n";
-constexpr char kCallbackErr[] = "Unexpected callback received!\n";
-
+// ... do something that should disconnect ptr ...
//
-// TestFixture
+// ExpectDisconnect(ptr);
+//
+// Or, to check that a sequence of callbacks are executed as expected:
+//
+// SomeInterfacePtr ptr;
+// environment->ConnectToService(ptr.NewRequest());
+// AddErrorHandler(ptr, "SomeInterface");
+//
+// int b;
+// ptr.events().OnA = AddCallback("A")
+// ptr.events().OnB = AddCallback("B", [&b](int x) { b = x; });
+//
+// // This verifies that callbacks A and B are executed, in that order, that B
+// // is called with the correct argument, and that the ErrorHandler is not called.
+// ExpectCallback();
+// EXPECT_EQ(b, 42);
//
class TestFixture : public ::gtest::RealLoopFixture {
public:
- bool error_occurred() const { return error_occurred_; }
+ struct ErrorHandler {
+ std::string name;
+ zx_status_t error_code = ZX_OK; // set after the ErrorHandler is triggered
+ zx_status_t expected_error_code = ZX_OK; // expected error for ExpectErrors
+ };
- // Simple handler, when the only required response is to record the error.
- auto ErrorHandler() {
- return [this](zx_status_t error) {
- error_occurred_ = true;
- error_code_ = error;
- };
+ // Add a new ErrorHandler for the given protocol. If this ErrorHandler triggers unexpectedly,
+ // the given name will be included in the test failure message. The InterfacePtr must
+ // live for the duration of this TestFixture.
+ template <class T>
+ void AddErrorHandler(fidl::InterfacePtr<T>& ptr, std::string name) {
+ auto [h, cb] = NewErrorHandler(name);
+ ptr.set_error_handler(std::move(cb));
+ error_handlers_[ptr.channel().get()] = h;
}
- // Accept (and call) a custom handler, for more nuanced error responses.
+ // Retrieves a previously-added error handler.
+ // Useful for direct calls to ExpectErrors or ExpectDisconnects. Tests that
+ // use ExpectError or ExpectDisconnect won't need this.
+ template <class T>
+ std::shared_ptr<ErrorHandler> ErrorHandlerFor(fidl::InterfacePtr<T>& ptr) {
+ auto eh = error_handlers_[ptr.channel().get()];
+ FX_CHECK(eh);
+ return eh;
+ }
+
+ // Add an expected callback to the pending set.
+ // Callbacks are expected to occur in the order in which they are added.
+ // Optionally, providew a custom function to invoke when the expected callback is triggered.
+ auto AddCallback(const std::string& name);
template <typename Callable>
- auto ErrorHandler(Callable err_handler) {
- return [this, err_handler = std::move(err_handler)](zx_status_t error) {
- error_occurred_ = true;
- error_code_ = error;
- err_handler(error);
- };
- }
+ auto AddCallback(const std::string& name, Callable callback);
- // Simple callback, when the only requirement is to record the callback.
- auto CompletionCallback() {
- return [this]() { callback_received_ = true; };
- }
-
- // Accept (and call) a custom callback, for more nuanced behavior.
+ // Like AddCallback, but allow the callback to happen in any order.
+ auto AddCallbackUnordered(const std::string& name);
template <typename Callable>
- auto CompletionCallback(Callable callback) {
- return [this, callback = std::move(callback)](auto&&... args) {
- callback_received_ = true;
- callback(std::forward<decltype(args)>(args)...);
- };
+ auto AddCallbackUnordered(const std::string& name, Callable callback);
+
+ // Add an unexpected callback. The test will fail if this callback is triggered.
+ auto AddUnexpectedCallback(const std::string& name) {
+ return [name](auto&&...) { ADD_FAILURE() << "Got unexpected callback " << name; };
}
- // The below methods contain gtest EXPECT checks that verify basic outcomes.
- //
- // Wait for CompletionCallback or ErrorHandler, expecting callback.
- virtual void ExpectCallback();
+ // Wait until all pending callbacks are drained. Fails if an error is encountered.
+ // Callbacks are expected to occur in the order they are added. After this method
+ // returns, the pending callback set is emptied and new callbacks may be added for
+ // a future call to ExpectCallback.
+ void ExpectCallback();
- // Wait for CompletionCallback or ErrorHandler, expecting the specified error.
- virtual void ExpectDisconnect() { ExpectError(ZX_ERR_PEER_CLOSED); }
- void ExpectError(zx_status_t expect_error);
+ // Wait for the given ErrorHandlers to trigger with their expected errors. Fails if
+ // different errors are found or if errors are triggered in different ErrorHandlers.
+ void ExpectErrors(const std::vector<std::shared_ptr<ErrorHandler>>& errors);
+
+ // Shorthand to expect many disconnect errors.
+ void ExpectDisconnects(const std::vector<std::shared_ptr<ErrorHandler>>& errors) {
+ std::vector<std::shared_ptr<ErrorHandler>> handlers;
+ for (auto eh : errors) {
+ eh->expected_error_code = ZX_ERR_PEER_CLOSED;
+ }
+ ExpectErrors(errors);
+ }
+
+ // Shorthand to expect a single error.
+ template <class T>
+ void ExpectError(fidl::InterfacePtr<T>& ptr, zx_status_t expected_error) {
+ auto eh = ErrorHandlerFor(ptr);
+ eh->expected_error_code = expected_error;
+ ExpectErrors({eh});
+ }
+ template <class T>
+ void ExpectDisconnect(fidl::InterfacePtr<T>& ptr) {
+ ExpectError(ptr, ZX_ERR_PEER_CLOSED);
+ }
+
+ // Verifies that no unexpected errors have occurred so far.
+ void ExpectNoUnexpectedErrors(const std::string& msg_for_failure);
+
+ // Reports whether any ErrorHandlers have triggered.
+ bool ErrorOccurred();
// Promote to public so that non-subclasses can advance through time.
using ::gtest::RealLoopFixture::RunLoop;
@@ -87,20 +133,61 @@
using ::gtest::RealLoopFixture::RunLoopWithTimeoutOrUntil;
protected:
- void SetUp() override;
void TearDown() override;
- // Set expectations for negative test cases. Called by ExpectError/Disconnect.
- virtual void SetNegativeExpectations() { error_expected_ = true; }
-
- bool error_expected_ = false;
- bool error_occurred_ = false;
- zx_status_t error_code_ = ZX_OK;
-
private:
- bool callback_received_ = false;
+ struct PendingCallback {
+ std::string name;
+ int64_t seqno = 0;
+ bool ordered;
+ };
+
+ auto AddCallbackInternal(const std::string& name, bool ordered) {
+ auto pb = NewPendingCallback(name, ordered);
+ return [this, pb](auto&&...) { pb->seqno = next_seqno_++; };
+ }
+
+ template <typename Callable>
+ auto AddCallbackInternal(const std::string& name, Callable callback, bool ordered) {
+ auto pb = NewPendingCallback(name, ordered);
+ return [this, pb, callback = std::move(callback)](auto&&... args) {
+ pb->seqno = next_seqno_++;
+ callback(std::forward<decltype(args)>(args)...);
+ };
+ }
+
+ std::pair<std::shared_ptr<ErrorHandler>, fit::function<void(zx_status_t)>> NewErrorHandler(
+ const std::string& name);
+ std::shared_ptr<PendingCallback> NewPendingCallback(const std::string& name, bool ordered);
+
+ void ExpectErrorsInternal(const std::vector<std::shared_ptr<ErrorHandler>>& errors);
+
+ std::unordered_map<zx_handle_t, std::shared_ptr<ErrorHandler>> error_handlers_;
+ std::deque<std::shared_ptr<PendingCallback>> pending_callbacks_;
+ int64_t next_seqno_ = 1;
+ bool new_error_ = false;
};
+// These must be defined in the header file, because of the auto return type, but
+// also must be defined after AddCallbackInternal.
+inline auto TestFixture::AddCallback(const std::string& name) {
+ return AddCallbackInternal(name, true);
+}
+
+template <typename Callable>
+inline auto TestFixture::AddCallback(const std::string& name, Callable callback) {
+ return AddCallbackInternal(name, callback, true);
+}
+
+inline auto TestFixture::AddCallbackUnordered(const std::string& name) {
+ return AddCallbackInternal(name, false);
+}
+
+template <typename Callable>
+inline auto TestFixture::AddCallbackUnordered(const std::string& name, Callable callback) {
+ return AddCallbackInternal(name, callback, false);
+}
+
} // namespace media::audio::test
#endif // SRC_MEDIA_AUDIO_LIB_TEST_TEST_FIXTURE_H_
diff --git a/src/media/audio/lib/test/virtual_device.cc b/src/media/audio/lib/test/virtual_device.cc
index 0afa900..85c807e 100644
--- a/src/media/audio/lib/test/virtual_device.cc
+++ b/src/media/audio/lib/test/virtual_device.cc
@@ -25,7 +25,7 @@
inspect_id_(inspect_id),
rb_(format, frame_count) {
environment->ConnectToService(device_.NewRequest());
- device_.set_error_handler(fixture->ErrorHandler());
+ fixture->AddErrorHandler(device_, "VirtualAudioDevice");
WatchEvents();
std::array<uint8_t, 16> device_id_array;