[tests] Refactor negative audio tests (efficiency/clarity)

This CL refactors the negative test cases (those that expect
the FIDL object to fail, and the channel to disconnect) to
gather common setup code, and to 'checkerboard' our validation
of the various scenarios to reduce test running time.

Test: build, CQ, git-file-tidy

Change-Id: Ie282a81ad31f6aa3c7e1eb72ccbba8a5a05ed994
diff --git a/bin/media/audio_core/test/audio_renderer_sync_test.cc b/bin/media/audio_core/test/audio_renderer_sync_test.cc
index 20887f5..47f3982 100644
--- a/bin/media/audio_core/test/audio_renderer_sync_test.cc
+++ b/bin/media/audio_core/test/audio_renderer_sync_test.cc
@@ -41,13 +41,6 @@
 };
 
 //
-// AudioRendererSyncTest_Negative
-//
-// Separate test class, purely for test case categorization, for cases in which
-// we expect the binding to disconnect -- and the AudioRendererSyncPtr to reset.
-class AudioRendererSyncTest_Negative : public AudioRendererSyncTest {};
-
-//
 // AudioRendererSync validation
 //
 // Basic validation of GetMinLeadTime() for the synchronous AudioRenderer.
@@ -58,6 +51,8 @@
   ASSERT_EQ(ZX_OK, audio_renderer_sync_->GetMinLeadTime(&min_lead_time))
       << kConnectionErr;
   EXPECT_GE(min_lead_time, 0) << "No MinLeadTime update received";
+
+  // TODO(mpuryear): test GetMinLeadTime with nullptr
 }
 
 // Before renderers are operational, multiple SetPcmStreamTypes should succeed.
@@ -87,12 +82,9 @@
   EXPECT_GE(min_lead_time, 0);
 }
 
-//
-// AudioRendererSync negative validation
-//
 // Before setting format, PlayNoReply should cause a Disconnect.
 // GetMinLeadTime is our way of verifying whether the connection survived.
-TEST_F(AudioRendererSyncTest_Negative, PlayNoReplyNoFormatCausesDisconnect) {
+TEST_F(AudioRendererSyncTest, PlayNoReplyNoFormatCausesDisconnect) {
   int64_t min_lead_time = -1;
   // First, make sure we still have a renderer at all.
   ASSERT_EQ(ZX_OK, audio_renderer_sync_->GetMinLeadTime(&min_lead_time));
@@ -109,7 +101,7 @@
 
 // Before setting format, PauseNoReply should cause a Disconnect.
 // GetMinLeadTime is our way of verifying whether the connection survived.
-TEST_F(AudioRendererSyncTest_Negative, PauseNoReplyWithoutFormat) {
+TEST_F(AudioRendererSyncTest, PauseNoReplyWithoutFormatCausesDisconnect) {
   int64_t min_lead_time = -1;
   // First, make sure we still have a renderer at all.
   ASSERT_EQ(ZX_OK, audio_renderer_sync_->GetMinLeadTime(&min_lead_time));
diff --git a/bin/media/audio_core/test/audio_renderer_test.cc b/bin/media/audio_core/test/audio_renderer_test.cc
index c033253..9fd653d 100644
--- a/bin/media/audio_core/test/audio_renderer_test.cc
+++ b/bin/media/audio_core/test/audio_renderer_test.cc
@@ -19,6 +19,8 @@
  protected:
   void SetUp() override;
   void TearDown() override;
+  void SetNegativeExpectations();
+  void ExpectDisconnect();
 
   std::shared_ptr<component::Services> environment_services_;
   fuchsia::media::AudioPtr audio_;
@@ -31,17 +33,6 @@
 };
 
 //
-// AudioRendererTestNegative
-//
-// A specialization of AudioRendererTest to validate scenarios where we expect
-// AudioRenderer bindings to disconnect (Audio bindings should be OK).
-class AudioRendererTest_Negative : public AudioRendererTest {
- protected:
-  void SetUp() override;
-  void ExpectDisconnect();
-};
-
-//
 // AudioRendererTest implementation
 //
 void AudioRendererTest::SetUp() {
@@ -57,6 +48,11 @@
   audio_renderer_.set_error_handler(err_handler);
 }
 
+void AudioRendererTest::SetNegativeExpectations() {
+  expect_error_ = true;
+  expect_renderer_ = false;
+}
+
 void AudioRendererTest::TearDown() {
   ASSERT_TRUE(audio_.is_bound());
   EXPECT_EQ(expect_error_, error_occurred_);
@@ -65,30 +61,46 @@
   ::gtest::RealLoopFixture::TearDown();
 }
 
-//
-// AudioRendererTest_Negative implementation
-//
-void AudioRendererTest_Negative::SetUp() {
-  AudioRendererTest::SetUp();
-
-  expect_error_ = true;
-  expect_renderer_ = false;
-}
-
-void AudioRendererTest_Negative::ExpectDisconnect() {
+void AudioRendererTest::ExpectDisconnect() {
   EXPECT_TRUE(RunLoopWithTimeoutOrUntil([this]() { return error_occurred_; },
                                         kDurationResponseExpected,
                                         kDurationGranularity));
 }
 
 //
+// AudioRenderer implements the base classes StreamBufferSet and StreamSink.
+
+//
+// StreamBufferSet validation
+//
+// TODO(mpuryear): test AddPayloadBuffer(uint32 id, handle<vmo> payload_buffer);
+// Also negative testing: bad id, null or bad handle
+
+// TODO(mpuryear): test RemovePayloadBuffer(uint32 id);
+// Also negative testing: unknown or already-removed id
+
+//
+// StreamSink validation
+//
+
+// TODO(mpuryear): test SendPacket(StreamPacket packet) -> ();
+// Also negative testing: malformed packet
+
+// TODO(mpuryear): test SendPacketNoReply(StreamPacket packet);
+// Also negative testing: malformed packet
+
+// TODO(mpuryear): test EndOfStream();
+// Also proper sequence of callbacks/completions
+
+// TODO(mpuryear): test DiscardAllPackets() -> ();
+// Also when no packets, when started
+
+// TODO(mpuryear): test DiscardAllPacketsNoReply();
+// Also when no packets, when started
+
+//
 // AudioRenderer validation
 //
-//
-// TODO(mpuryear): Remaining test coverage work within AudioRenderer:
-// SetPtsUnits, SetPtsContinuityThreshold, SetReferenceClock;
-// Also, positive coverage for Play, PlayNoReply, Pause, PauseNoReply,
-//
 
 // AudioRenderer contains an internal state machine. To enter the "configured"
 // state, it must receive and successfully execute both SetPcmStreamType and
@@ -96,10 +108,6 @@
 // "operational" mode when any packets are enqueued (received and not yet played
 // and/or released).
 
-// TODO(mpuryear): add tests to validate the following --
-// **** Basic API validation for asynchronous AudioRenderer:
-// SetPayloadBuffer, SendPacket, SendPacketNoReply, Flush.
-
 // **** Before we enter Configured mode:
 // SendPacket before SetPcmStreamType must fail.
 // SendPacket before SetPayloadBuffer must fail.
@@ -143,22 +151,29 @@
 }
 
 // TODO(mpuryear): test SetPtsUnits(uint32 tick_per_sec_num,uint32 denom);
+// Also negative testing: zero values, nullptrs, huge num/small denom
 
 // TODO(mpuryear): test SetPtsContinuityThreshold(float32 threshold_sec);
+// Also negative testing: NaN, negative, very large, infinity
 
 // TODO(mpuryear): test SetReferenceClock(handle reference_clock);
+// Also negative testing: null handle, bad handle, handle to something else
 
 // TODO(mpuryear): test Play(int64 ref_time, int64 med)->(int64 ref, int64 med);
 // Verify success after setting format and submitting buffers.
+// Also: when already in Play, very positive vals, very negative vals
 
 // TODO(mpuryear): test PlayNoReply(int64 reference_time, int64 media_time);
 // Verify success after setting format and submitting buffers.
+// Also: when already in Play, very positive vals, very negative vals
 
 // TODO(mpuryear): test Pause()->(int64 reference_time, int64 media_time);
 // Verify success after setting format and submitting buffers.
+// Also: when already in Pause
 
 // TODO(mpuryear): test PauseNoReply();
 // Verify success after setting format and submitting buffers.
+// Also: when already in Pause
 
 // Validate MinLeadTime events, when enabled.
 TEST_F(AudioRendererTest, EnableMinLeadTimeEvents) {
@@ -284,13 +299,11 @@
 }
 
 //
-// AudioRendererTest_Negative
-//
-// Separate test class for cases in which we expect the AudioRenderer binding to
+// SetStreamType is not yet implemented. We expect the AudioRenderer binding to
 // disconnect, and our AudioRenderer interface ptr to be reset.
-//
-// SetStreamType is not yet implemented and expected to cause a Disconnect.
-TEST_F(AudioRendererTest_Negative, SetStreamType) {
+TEST_F(AudioRendererTest, SetStreamType) {
+  SetNegativeExpectations();
+
   fuchsia::media::AudioStreamType stream_format;
   stream_format.sample_format = fuchsia::media::AudioSampleFormat::SIGNED_16;
   stream_format.channels = 1;
@@ -306,13 +319,10 @@
   ExpectDisconnect();
 }
 
-// TODO(mpuryear): negative tests for the following:
-//    SetPtsUnits(uint32 tick_per_sec_num,uint32 denom)
-//    SetPtsContinuityThreshold(float32 threshold_sec)
-//    SetReferenceClock(handle reference_clock)
-
 // Before setting format, Play should not succeed.
-TEST_F(AudioRendererTest_Negative, PlayWithoutFormat) {
+TEST_F(AudioRendererTest, PlayWithoutFormat) {
+  SetNegativeExpectations();
+
   int64_t ref_time_received = -1;
   int64_t media_time_received = -1;
 
@@ -331,7 +341,9 @@
 }
 
 // After setting format but before submitting buffers, Play should not succeed.
-TEST_F(AudioRendererTest_Negative, PlayWithoutBuffers) {
+TEST_F(AudioRendererTest, PlayWithoutBuffers) {
+  SetNegativeExpectations();
+
   fuchsia::media::AudioStreamType format;
   format.sample_format = fuchsia::media::AudioSampleFormat::FLOAT;
   format.channels = 1;
@@ -356,7 +368,9 @@
 }
 
 // Before setting format, PlayNoReply should cause a Disconnect.
-TEST_F(AudioRendererTest_Negative, PlayNoReplyWithoutFormat) {
+TEST_F(AudioRendererTest, PlayNoReplyWithoutFormat) {
+  SetNegativeExpectations();
+
   audio_renderer_->PlayNoReply(fuchsia::media::NO_TIMESTAMP,
                                fuchsia::media::NO_TIMESTAMP);
 
@@ -365,7 +379,9 @@
 }
 
 // Before setting format, Pause should not succeed.
-TEST_F(AudioRendererTest_Negative, PauseWithoutFormat) {
+TEST_F(AudioRendererTest, PauseWithoutFormat) {
+  SetNegativeExpectations();
+
   int64_t ref_time_received = -1;
   int64_t media_time_received = -1;
 
@@ -382,7 +398,9 @@
 }
 
 // After setting format but before submitting buffers, Pause should not succeed.
-TEST_F(AudioRendererTest_Negative, PauseWithoutBuffers) {
+TEST_F(AudioRendererTest, PauseWithoutBuffers) {
+  SetNegativeExpectations();
+
   fuchsia::media::AudioStreamType format;
   format.sample_format = fuchsia::media::AudioSampleFormat::FLOAT;
   format.channels = 1;
@@ -405,7 +423,9 @@
 }
 
 // Before setting format, PauseNoReply should cause a Disconnect.
-TEST_F(AudioRendererTest_Negative, PauseNoReplyWithoutFormat) {
+TEST_F(AudioRendererTest, PauseNoReplyWithoutFormat) {
+  SetNegativeExpectations();
+
   audio_renderer_->PauseNoReply();
 
   // Disconnect callback should be received.
diff --git a/bin/media/audio_core/test/audio_sync_test.cc b/bin/media/audio_core/test/audio_sync_test.cc
index e99c74a..d32c5c0 100644
--- a/bin/media/audio_core/test/audio_sync_test.cc
+++ b/bin/media/audio_core/test/audio_sync_test.cc
@@ -37,11 +37,6 @@
 };
 
 //
-// TODO(mpuryear): AudioSyncTest_Negative class and tests, for cases where
-// we expect AudioSync binding to disconnect, and AudioSyncPtr to be reset.
-//
-
-//
 // AudioCoreSync validation
 // Tests of the synchronously-proxied Audio interface: AudioSync.
 //
diff --git a/bin/media/audio_core/test/audio_test.cc b/bin/media/audio_core/test/audio_test.cc
index 07a47a6..c144c03 100644
--- a/bin/media/audio_core/test/audio_test.cc
+++ b/bin/media/audio_core/test/audio_test.cc
@@ -197,11 +197,6 @@
 }
 
 //
-// TODO(mpuryear): AudioTest_Negative class and tests, for cases where we
-// expect Audio binding to disconnect -- and Audio interface ptr to be reset.
-//
-
-//
 // Audio validation
 // Tests of the asynchronous Audio interface.
 //
@@ -318,6 +313,10 @@
   EXPECT_TRUE(audio_.is_bound());
 }
 
+// TODO(mpuryear): Test cases where we expect Audio binding to disconnect.
+// Possibilities: null/malformed request (CreateAudioRenderer / Capturer),
+// out-of-range enum (SetOutputRoutingPolicy), NAN float (SetSystemGain).
+
 //
 // Validation of System Gain and Mute
 //
diff --git a/bin/media/audio_core/test/gain_control_test.cc b/bin/media/audio_core/test/gain_control_test.cc
index cd67c5a..0936a38 100644
--- a/bin/media/audio_core/test/gain_control_test.cc
+++ b/bin/media/audio_core/test/gain_control_test.cc
@@ -148,6 +148,13 @@
   SetUpGainControl2();
 }
 
+// For tests that cause a GainControl to disconnect, set these expectations.
+void GainControlTestBase::SetNegativeExpectations() {
+  expect_null_api_ = true;
+  expect_error_ = true;
+  expect_null_gain_control_ = true;
+}
+
 // Set Gain, asserting that state is already reset so error can be detected.
 void GainControlTestBase::SetGain(float gain_db) {
   // On initialization and every Receive...Callback(), this is set to false.
@@ -287,10 +294,12 @@
   EXPECT_TRUE(ReceiveNoGainCallback());
 }
 
-// For RenderGainControlTest_Negative/CaptureGainControlTest_Negative.
+// For negative expectations.
 //
 // Setting gain too high should cause a disconnect.
 void GainControlTestBase::TestSetGainTooHigh() {
+  SetNegativeExpectations();
+
   constexpr float expect_gain_db = kTooHighGainDb;
   SetGain(expect_gain_db);
 
@@ -300,6 +309,8 @@
 
 // Setting gain too low should cause a disconnect.
 void GainControlTestBase::TestSetGainTooLow() {
+  SetNegativeExpectations();
+
   constexpr float expect_gain_db = kTooLowGainDb;
   SetGain(expect_gain_db);
 
@@ -323,7 +334,9 @@
 // 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:
@@ -331,9 +344,17 @@
 
 // TODO(mpuryear): Validate GainChange notifications of gainramps.
 
-TEST_F(RenderGainControlTest, DuplicateSetGain) { TestDuplicateSetGain(); }
+// N.B. DuplicateSetGain behavior is tested in RendererTwoGainControlsTest.
 TEST_F(RenderGainControlTest, DuplicateSetMute) { TestDuplicateSetMute(); }
 
+TEST_F(RenderGainControlTest, SetGainTooHigh) { TestSetGainTooHigh(); }
+
+TEST_F(RenderGainControlTest, SetGainTooLow) { TestSetGainTooLow(); }
+
+// TODO(mpuryear): SetGain(NAN) - should have no effect
+
+// TODO(mpuryear): Ramp-related negative tests, across all scenarios
+
 // CaptureGainControlTest
 //
 void CaptureGainControlTest::SetUp() {
@@ -346,48 +367,33 @@
 // 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(); }
-TEST_F(CaptureGainControlTest, DuplicateSetMute) { TestDuplicateSetMute(); }
+// N.B. DuplicateSetMute behavior is tested in CapturerTwoGainControlsTest.
 
-// RenderGainControlTest_Negative
-// Specialization when we expect GainControl/AudioRenderer to disconnect.
-//
-void RenderGainControlTest_Negative::SetUp() {
-  RenderGainControlTest::SetUp();
+TEST_F(CaptureGainControlTest, SetGainTooHigh) { TestSetGainTooHigh(); }
 
-  expect_null_api_ = true;
-  expect_error_ = true;
-  expect_null_gain_control_ = true;
-}
+TEST_F(CaptureGainControlTest, SetGainTooLow) { TestSetGainTooLow(); }
 
-TEST_F(RenderGainControlTest_Negative, SetGainTooHigh) { TestSetGainTooHigh(); }
-TEST_F(RenderGainControlTest_Negative, SetGainTooLow) { TestSetGainTooLow(); }
-// TODO(mpuryear): Ramp-related negative tests, across all scenarios
-
-// CaptureGainControlTest_Negative
-// Specialization when we expect GainControl/AudioCapturer to disconnect.
-//
-void CaptureGainControlTest_Negative::SetUp() {
-  CaptureGainControlTest::SetUp();
-
-  expect_null_api_ = true;
-  expect_error_ = true;
-  expect_null_gain_control_ = true;
-}
-
-TEST_F(CaptureGainControlTest_Negative, SetGainTooHigh) {
-  TestSetGainTooHigh();
-}
-TEST_F(CaptureGainControlTest_Negative, SetGainTooLow) { TestSetGainTooLow(); }
+// TODO(mpuryear): SetGain(NAN) - should have no effect
 
 // 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();
+
+  expect_null_gain_control_2_ = true;
+  expect_error_2_ = true;
+}
+
 // Tests expect a gain callback on both gain_controls, with the provided gain_db
 // and mute values -- and no errors.
 bool SiblingGainControlsTest::ReceiveGainCallback(float gain_db, bool mute) {
@@ -492,15 +498,20 @@
 TEST_F(RendererTwoGainControlsTest, BothControlsReceiveGainNotifications) {
   TestSetGain();
 }
+
 TEST_F(RendererTwoGainControlsTest, BothControlsReceiveMuteNotifications) {
   TestSetMute();
 }
+
 TEST_F(RendererTwoGainControlsTest, DuplicateSetGain) {
   TestDuplicateSetGain();
 }
-TEST_F(RendererTwoGainControlsTest, DuplicateSetMute) {
-  TestDuplicateSetMute();
-}
+
+// N.B. DuplicateSetMute behavior is tested in RendererGainControlTest.
+
+TEST_F(RendererTwoGainControlsTest, SetGainTooHigh) { TestSetGainTooHigh(); }
+
+TEST_F(RendererTwoGainControlsTest, SetGainTooLow) { TestSetGainTooLow(); }
 
 // CapturerTwoGainControlsTest
 // Capturer with two gain controls: both should receive identical notifications.
@@ -516,55 +527,19 @@
 TEST_F(CapturerTwoGainControlsTest, BothControlsReceiveGainNotifications) {
   TestSetGain();
 }
+
 TEST_F(CapturerTwoGainControlsTest, BothControlsReceiveMuteNotifications) {
   TestSetMute();
 }
-TEST_F(CapturerTwoGainControlsTest, DuplicateSetGain) {
-  TestDuplicateSetGain();
-}
+
+// N.B. DuplicateSetGain behavior is tested in CapturerGainControlTest.
 TEST_F(CapturerTwoGainControlsTest, DuplicateSetMute) {
   TestDuplicateSetMute();
 }
 
-// RendererTwoGainControlsTest_Negative
-// Specialization when we expect GainControls and Renderer to disconnect.
-//
-void RendererTwoGainControlsTest_Negative::SetUp() {
-  RendererTwoGainControlsTest::SetUp();
+TEST_F(CapturerTwoGainControlsTest, SetGainTooHigh) { TestSetGainTooHigh(); }
 
-  expect_null_api_ = true;
-  expect_null_gain_control_ = true;
-  expect_null_gain_control_2_ = true;
-  expect_error_ = true;
-  expect_error_2_ = true;
-}
-
-TEST_F(RendererTwoGainControlsTest_Negative, SetGainTooHigh) {
-  TestSetGainTooHigh();
-}
-TEST_F(RendererTwoGainControlsTest_Negative, SetGainTooLow) {
-  TestSetGainTooLow();
-}
-
-// CapturerTwoGainControlsTest_Negative
-// Specialization when we expect GainControls and Capturer to disconnect.
-//
-void CapturerTwoGainControlsTest_Negative::SetUp() {
-  CapturerTwoGainControlsTest::SetUp();
-
-  expect_null_api_ = true;
-  expect_null_gain_control_ = true;
-  expect_null_gain_control_2_ = true;
-  expect_error_ = true;
-  expect_error_2_ = true;
-}
-
-TEST_F(CapturerTwoGainControlsTest_Negative, SetGainTooHigh) {
-  TestSetGainTooHigh();
-}
-TEST_F(CapturerTwoGainControlsTest_Negative, SetGainTooLow) {
-  TestSetGainTooLow();
-}
+TEST_F(CapturerTwoGainControlsTest, SetGainTooLow) { TestSetGainTooLow(); }
 
 // IndependentGainControlsTest
 // Verify that GainControls on different API instances are fully independent.
@@ -681,6 +656,9 @@
   TestSetMute();
 }
 
+// We expect primary GainControl/Renderer to disconnect.
+TEST_F(TwoRenderersGainControlsTest, SetGainTooLow) { TestSetGainTooLow(); }
+
 // RendererCapturerGainControlsTest
 // Renderer gain control should not affect capturer gain control.
 //
@@ -699,6 +677,11 @@
   TestSetGain();
 }
 
+// We expect primary GainControl/Renderer to disconnect.
+TEST_F(RendererCapturerGainControlsTest, SetGainTooHigh) {
+  TestSetGainTooHigh();
+}
+
 // CapturerRendererGainControlsTest
 // Capturer gain control should not affect renderer gain control.
 //
@@ -717,6 +700,11 @@
   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.
 //
@@ -734,64 +722,7 @@
   TestSetMute();
 }
 
-// TwoRenderersGainControlsTest_Negative
-// Specialization when we expect one GainControl/Renderer to disconnect.
-//
-void TwoRenderersGainControlsTest_Negative::SetUp() {
-  TwoRenderersGainControlsTest::SetUp();
-
-  expect_null_api_ = true;
-  expect_error_ = true;
-  expect_null_gain_control_ = true;
-}
-
-TEST_F(TwoRenderersGainControlsTest_Negative, SetGainTooLow) {
-  TestSetGainTooLow();
-}
-
-// RendererCapturerGainControlsTest_Negative
-// Specialization when we expect one GainControl/Renderer to disconnect.
-//
-void RendererCapturerGainControlsTest_Negative::SetUp() {
-  RendererCapturerGainControlsTest::SetUp();
-
-  expect_null_api_ = true;
-  expect_error_ = true;
-  expect_null_gain_control_ = true;
-}
-
-TEST_F(RendererCapturerGainControlsTest_Negative, SetGainTooHigh) {
-  TestSetGainTooHigh();
-}
-
-// CapturerRendererGainControlsTest_Negative
-// Specialization when we expect one GainControl/Capturer to disconnect.
-//
-void CapturerRendererGainControlsTest_Negative::SetUp() {
-  CapturerRendererGainControlsTest::SetUp();
-
-  expect_null_api_ = true;
-  expect_error_ = true;
-  expect_null_gain_control_ = true;
-}
-
-TEST_F(CapturerRendererGainControlsTest_Negative, SetGainTooHigh) {
-  TestSetGainTooHigh();
-}
-
-// TwoCapturersGainControlsTest_Negative
-// Specialization when we expect one GainControl/Capturer to disconnect.
-//
-void TwoCapturersGainControlsTest_Negative::SetUp() {
-  TwoCapturersGainControlsTest::SetUp();
-
-  expect_null_api_ = true;
-  expect_error_ = true;
-  expect_null_gain_control_ = true;
-}
-
-TEST_F(TwoCapturersGainControlsTest_Negative, SetGainTooLow) {
-  TestSetGainTooLow();
-}
+// We expect primary GainControl/Capturer to disconnect.
+TEST_F(TwoCapturersGainControlsTest, SetGainTooLow) { TestSetGainTooLow(); }
 
 }  // namespace media::audio::test
diff --git a/bin/media/audio_core/test/gain_control_test.h b/bin/media/audio_core/test/gain_control_test.h
index 1f7156b..1016cf4 100644
--- a/bin/media/audio_core/test/gain_control_test.h
+++ b/bin/media/audio_core/test/gain_control_test.h
@@ -40,6 +40,7 @@
   void SetUpGainControl2OnCapturer();
   void SetUpGainControl2OnRenderer2();
   void SetUpGainControl2OnCapturer2();
+  virtual void SetNegativeExpectations();
 
   void SetGain(float gain_db);
   void SetMute(bool mute);
@@ -110,27 +111,13 @@
   bool ApiIsNull() final { return !audio_capturer_.is_bound(); }
 };
 
-// RenderGainControlTest_Negative
-//
-// Specialization when we expect GainControl/AudioRenderer to disconnect.
-class RenderGainControlTest_Negative : public RenderGainControlTest {
- protected:
-  void SetUp() final;
-};
-
-// CaptureGainControlTest_Negative
-//
-// Specialization when we expect GainControl/AudioCapturer to disconnect.
-class CaptureGainControlTest_Negative : public CaptureGainControlTest {
- protected:
-  void SetUp() final;
-};
-
 // 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.
   bool ReceiveGainCallback(float gain_db, bool mute) final;
   bool ReceiveNoGainCallback() final;
@@ -157,24 +144,6 @@
   bool ApiIsNull() final { return !audio_capturer_.is_bound(); }
 };
 
-// RendererTwoGainControlsTest_Negative
-//
-// Specialization when we expect GainControls and Renderer to disconnect.
-class RendererTwoGainControlsTest_Negative
-    : public RendererTwoGainControlsTest {
- protected:
-  void SetUp() final;
-};
-
-// CapturerTwoGainControlsTest_Negative
-//
-// Specialization when we expect GainControls and Capturer to disconnect.
-class CapturerTwoGainControlsTest_Negative
-    : public CapturerTwoGainControlsTest {
- protected:
-  void SetUp() final;
-};
-
 // IndependentGainControlsTest
 //
 // Verify that GainControls on different API instances are fully independent.
@@ -233,42 +202,6 @@
   }
 };
 
-// TwoRenderersGainControlsTest_Negative
-//
-// Specialization when we expect one GainControl/Renderer to disconnect.
-class TwoRenderersGainControlsTest_Negative
-    : public TwoRenderersGainControlsTest {
- protected:
-  void SetUp() final;
-};
-
-// RendererCapturerGainControlsTest_Negative
-//
-// Specialization when we expect one GainControl/Renderer to disconnect.
-class RendererCapturerGainControlsTest_Negative
-    : public RendererCapturerGainControlsTest {
- protected:
-  void SetUp() final;
-};
-
-// CapturerRendererGainControlsTest_Negative
-//
-// Specialization when we expect one GainControl/Capturer to disconnect.
-class CapturerRendererGainControlsTest_Negative
-    : public CapturerRendererGainControlsTest {
- protected:
-  void SetUp() final;
-};
-
-// TwoCapturersGainControlsTest_Negative
-//
-// Specialization when we expect one GainControl/Capturer to disconnect.
-class TwoCapturersGainControlsTest_Negative
-    : public TwoCapturersGainControlsTest {
- protected:
-  void SetUp() final;
-};
-
 }  // namespace test
 }  // namespace audio
 }  // namespace media