text: /*
   *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
   *
   *  Use of this source code is governed by a BSD-style license
   *  that can be found in the LICENSE file in the root of the source
   *  tree. An additional intellectual property rights grant can be found
   *  in the file PATENTS.  All contributing project authors may
   *  be found in the AUTHORS file in the root of the source tree.
   */
  
  #include "modules/audio_processing/aec3/echo_canceller3.h"
  
  #include <deque>
  #include <memory>
  #include <sstream>
  #include <string>
  #include <utility>
  #include <vector>
  
  #include "modules/audio_processing/aec3/aec3_common.h"
  #include "modules/audio_processing/aec3/block_processor.h"
  #include "modules/audio_processing/aec3/frame_blocker.h"
  #include "modules/audio_processing/aec3/mock/mock_block_processor.h"
  #include "modules/audio_processing/audio_buffer.h"
  #include "test/gmock.h"
  #include "test/gtest.h"
  
  namespace webrtc {
  namespace {
  
  using testing::StrictMock;
  using testing::_;
  
  // Populates the frame with linearly increasing sample values for each band,
  // with a band-specific offset, in order to allow simple bitexactness
  // verification for each band.
  void PopulateInputFrame(size_t frame_length,
                          size_t num_bands,
                          size_t frame_index,
                          float* const* frame,
                          int offset) {
    for (size_t k = 0; k < num_bands; ++k) {
      for (size_t i = 0; i < frame_length; ++i) {
        float value = static_cast<int>(frame_index * frame_length + i) + offset;
        frame[k][i] = (value > 0 ? 5000 * k + value : 0);
      }
    }
  }
  
  // Populates the frame with linearly increasing sample values.
  void PopulateInputFrame(size_t frame_length,
                          size_t frame_index,
                          float* frame,
                          int offset) {
    for (size_t i = 0; i < frame_length; ++i) {
      float value = static_cast<int>(frame_index * frame_length + i) + offset;
      frame[i] = std::max(value, 0.f);
    }
  }
  
  // Verifies the that samples in the output frame are identical to the samples
  // that were produced for the input frame, with an offset in order to compensate
  // for buffering delays.
  bool VerifyOutputFrameBitexactness(size_t frame_length,
                                     size_t num_bands,
                                     size_t frame_index,
                                     const float* const* frame,
                                     int offset) {
    float reference_frame_data[kMaxNumBands][2 * kSubFrameLength];
    float* reference_frame[kMaxNumBands];
    for (size_t k = 0; k < num_bands; ++k) {
      reference_frame[k] = &reference_frame_data[k][0];
    }
  
    PopulateInputFrame(frame_length, num_bands, frame_index, reference_frame,
                       offset);
    for (size_t k = 0; k < num_bands; ++k) {
      for (size_t i = 0; i < frame_length; ++i) {
        if (reference_frame[k][i] != frame[k][i]) {
          return false;
        }
      }
    }
  
    return true;
  }
  
  // Class for testing that the capture data is properly received by the block
  // processor and that the processor data is properly passed to the
  // EchoCanceller3 output.
  class CaptureTransportVerificationProcessor : public BlockProcessor {
   public:
    explicit CaptureTransportVerificationProcessor(size_t num_bands) {}
    ~CaptureTransportVerificationProcessor() override = default;
  
    void ProcessCapture(bool level_change,
                        bool saturated_microphone_signal,
                        std::vector<std::vector<float>>* capture_block) override {
    }
  
    void BufferRender(const std::vector<std::vector<float>>& block) override {}
  
    void UpdateEchoLeakageStatus(bool leakage_detected) override {}
  
    void GetMetrics(EchoControl::Metrics* metrics) const override {}
  
   private:
    RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureTransportVerificationProcessor);
  };
  
  // Class for testing that the render data is properly received by the block
  // processor.
  class RenderTransportVerificationProcessor : public BlockProcessor {
   public:
    explicit RenderTransportVerificationProcessor(size_t num_bands) {}
    ~RenderTransportVerificationProcessor() override = default;
  
    void ProcessCapture(bool level_change,
                        bool saturated_microphone_signal,
                        std::vector<std::vector<float>>* capture_block) override {
      std::vector<std::vector<float>> render_block =
          received_render_blocks_.front();
      received_render_blocks_.pop_front();
      capture_block->swap(render_block);
    }
  
    void BufferRender(const std::vector<std::vector<float>>& block) override {
      received_render_blocks_.push_back(block);
    }
  
    void UpdateEchoLeakageStatus(bool leakage_detected) override {}
  
    void GetMetrics(EchoControl::Metrics* metrics) const override {}
  
   private:
    std::deque<std::vector<std::vector<float>>> received_render_blocks_;
    RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderTransportVerificationProcessor);
  };
  
  class EchoCanceller3Tester {
   public:
    explicit EchoCanceller3Tester(int sample_rate_hz)
        : sample_rate_hz_(sample_rate_hz),
          num_bands_(NumBandsForRate(sample_rate_hz_)),
          frame_length_(sample_rate_hz_ == 8000 ? 80 : 160),
          fullband_frame_length_(rtc::CheckedDivExact(sample_rate_hz_, 100)),
          capture_buffer_(fullband_frame_length_,
                          1,
                          fullband_frame_length_,
                          1,
                          fullband_frame_length_),
          render_buffer_(fullband_frame_length_,
                         1,
                         fullband_frame_length_,
                         1,
                         fullband_frame_length_) {}
  
    // Verifies that the capture data is properly received by the block processor
    // and that the processor data is properly passed to the EchoCanceller3
    // output.
    void RunCaptureTransportVerificationTest() {
      EchoCanceller3 aec3(
          EchoCanceller3Config(), sample_rate_hz_, false,
          std::unique_ptr<BlockProcessor>(
              new CaptureTransportVerificationProcessor(num_bands_)));
  
      for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
           ++frame_index) {
        aec3.AnalyzeCapture(&capture_buffer_);
        OptionalBandSplit();
        PopulateInputFrame(frame_length_, num_bands_, frame_index,
                           &capture_buffer_.split_bands_f(0)[0], 0);
        PopulateInputFrame(frame_length_, frame_index,
                           &render_buffer_.channels_f()[0][0], 0);
  
        aec3.AnalyzeRender(&render_buffer_);
        aec3.ProcessCapture(&capture_buffer_, false);
        EXPECT_TRUE(VerifyOutputFrameBitexactness(
            frame_length_, num_bands_, frame_index,
            &capture_buffer_.split_bands_f(0)[0], -64));
      }
    }
  
    // Test method for testing that the render data is properly received by the
    // block processor.
    void RunRenderTransportVerificationTest() {
      EchoCanceller3 aec3(
          EchoCanceller3Config(), sample_rate_hz_, false,
          std::unique_ptr<BlockProcessor>(
              new RenderTransportVerificationProcessor(num_bands_)));
  
      for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
           ++frame_index) {
        aec3.AnalyzeCapture(&capture_buffer_);
        OptionalBandSplit();
        PopulateInputFrame(frame_length_, num_bands_, frame_index,
                           &capture_buffer_.split_bands_f(0)[0], 100);
        PopulateInputFrame(frame_length_, num_bands_, frame_index,
                           &render_buffer_.split_bands_f(0)[0], 0);
  
        aec3.AnalyzeRender(&render_buffer_);
        aec3.ProcessCapture(&capture_buffer_, false);
        EXPECT_TRUE(VerifyOutputFrameBitexactness(
            frame_length_, num_bands_, frame_index,
            &capture_buffer_.split_bands_f(0)[0], -64));
      }
    }
  
    // Verifies that information about echo path changes are properly propagated
    // to the block processor.
    // The cases tested are:
    // -That no set echo path change flags are received when there is no echo path
    // change.
    // -That set echo path change flags are received and continues to be received
    // as long as echo path changes are flagged.
    // -That set echo path change flags are no longer received when echo path
    // change events stop being flagged.
    enum class EchoPathChangeTestVariant { kNone, kOneSticky, kOneNonSticky };
  
    void RunEchoPathChangeVerificationTest(
        EchoPathChangeTestVariant echo_path_change_test_variant) {
      const size_t num_full_blocks_per_frame =
          rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100) / kBlockSize;
      const size_t expected_num_block_to_process =
          (kNumFramesToProcess *
           rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)) /
          kBlockSize;
      std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>>
          block_processor_mock(
              new StrictMock<webrtc::test::MockBlockProcessor>());
      EXPECT_CALL(*block_processor_mock, BufferRender(_))
          .Times(expected_num_block_to_process);
      EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(_)).Times(0);
  
      switch (echo_path_change_test_variant) {
        case EchoPathChangeTestVariant::kNone:
          EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _))
              .Times(expected_num_block_to_process);
          break;
        case EchoPathChangeTestVariant::kOneSticky:
          EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _))
              .Times(expected_num_block_to_process);
          break;
        case EchoPathChangeTestVariant::kOneNonSticky:
          EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _))
              .Times(num_full_blocks_per_frame);
          EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _))
              .Times(expected_num_block_to_process - num_full_blocks_per_frame);
          break;
      }
  
      EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, false,
                          std::move(block_processor_mock));
  
      for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
           ++frame_index) {
        bool echo_path_change = false;
        switch (echo_path_change_test_variant) {
          case EchoPathChangeTestVariant::kNone:
            break;
          case EchoPathChangeTestVariant::kOneSticky:
            echo_path_change = true;
            break;
          case EchoPathChangeTestVariant::kOneNonSticky:
            if (frame_index == 0) {
              echo_path_change = true;
            }
            break;
        }
  
        aec3.AnalyzeCapture(&capture_buffer_);
        OptionalBandSplit();
  
        PopulateInputFrame(frame_length_, num_bands_, frame_index,
                           &capture_buffer_.split_bands_f(0)[0], 0);
        PopulateInputFrame(frame_length_, frame_index,
                           &render_buffer_.channels_f()[0][0], 0);
  
        aec3.AnalyzeRender(&render_buffer_);
        aec3.ProcessCapture(&capture_buffer_, echo_path_change);
      }
    }
  
    // Test for verifying that echo leakage information is being properly passed
    // to the processor.
    // The cases tested are:
    // -That no method calls are received when they should not.
    // -That false values are received each time they are flagged.
    // -That true values are received each time they are flagged.
    // -That a false value is received when flagged after a true value has been
    // flagged.
    enum class EchoLeakageTestVariant {
      kNone,
      kFalseSticky,
      kTrueSticky,
      kTrueNonSticky
    };
  
    void RunEchoLeakageVerificationTest(
        EchoLeakageTestVariant leakage_report_variant) {
      const size_t expected_num_block_to_process =
          (kNumFramesToProcess *
           rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)) /
          kBlockSize;
      std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>>
          block_processor_mock(
              new StrictMock<webrtc::test::MockBlockProcessor>());
      EXPECT_CALL(*block_processor_mock, BufferRender(_))
          .Times(expected_num_block_to_process);
      EXPECT_CALL(*block_processor_mock, ProcessCapture(_, _, _))
          .Times(expected_num_block_to_process);
  
      switch (leakage_report_variant) {
        case EchoLeakageTestVariant::kNone:
          EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(_)).Times(0);
          break;
        case EchoLeakageTestVariant::kFalseSticky:
          EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(false))
              .Times(1);
          break;
        case EchoLeakageTestVariant::kTrueSticky:
          EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(true))
              .Times(1);
          break;
        case EchoLeakageTestVariant::kTrueNonSticky: {
          testing::InSequence s;
          EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(true))
              .Times(1);
          EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(false))
              .Times(kNumFramesToProcess - 1);
        } break;
      }
  
      EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, false,
                          std::move(block_processor_mock));
  
      for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
           ++frame_index) {
        switch (leakage_report_variant) {
          case EchoLeakageTestVariant::kNone:
            break;
          case EchoLeakageTestVariant::kFalseSticky:
            if (frame_index == 0) {
              aec3.UpdateEchoLeakageStatus(false);
            }
            break;
          case EchoLeakageTestVariant::kTrueSticky:
            if (frame_index == 0) {
              aec3.UpdateEchoLeakageStatus(true);
            }
            break;
          case EchoLeakageTestVariant::kTrueNonSticky:
            if (frame_index == 0) {
              aec3.UpdateEchoLeakageStatus(true);
            } else {
              aec3.UpdateEchoLeakageStatus(false);
            }
            break;
        }
  
        aec3.AnalyzeCapture(&capture_buffer_);
        OptionalBandSplit();
  
        PopulateInputFrame(frame_length_, num_bands_, frame_index,
                           &capture_buffer_.split_bands_f(0)[0], 0);
        PopulateInputFrame(frame_length_, frame_index,
                           &render_buffer_.channels_f()[0][0], 0);
  
        aec3.AnalyzeRender(&render_buffer_);
        aec3.ProcessCapture(&capture_buffer_, false);
      }
    }
  
    // This verifies that saturation information is properly passed to the
    // BlockProcessor.
    // The cases tested are:
    // -That no saturation event is passed to the processor if there is no
    // saturation.
    // -That one frame with one negative saturated sample value is reported to be
    // saturated and that following non-saturated frames are properly reported as
    // not being saturated.
    // -That one frame with one positive saturated sample value is reported to be
    // saturated and that following non-saturated frames are properly reported as
    // not being saturated.
    enum class SaturationTestVariant { kNone, kOneNegative, kOnePositive };
  
    void RunCaptureSaturationVerificationTest(
        SaturationTestVariant saturation_variant) {
      const size_t num_full_blocks_per_frame =
          rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100) / kBlockSize;
      const size_t expected_num_block_to_process =
          (kNumFramesToProcess *
           rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)) /
          kBlockSize;
      std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>>
          block_processor_mock(
              new StrictMock<webrtc::test::MockBlockProcessor>());
      EXPECT_CALL(*block_processor_mock, BufferRender(_))
          .Times(expected_num_block_to_process);
      EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(_)).Times(0);
  
      switch (saturation_variant) {
        case SaturationTestVariant::kNone:
          EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _))
              .Times(expected_num_block_to_process);
          break;
        case SaturationTestVariant::kOneNegative: {
          testing::InSequence s;
          EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _))
              .Times(num_full_blocks_per_frame);
          EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _))
              .Times(expected_num_block_to_process - num_full_blocks_per_frame);
        } break;
        case SaturationTestVariant::kOnePositive: {
          testing::InSequence s;
          EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _))
              .Times(num_full_blocks_per_frame);
          EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _))
              .Times(expected_num_block_to_process - num_full_blocks_per_frame);
        } break;
      }
  
      EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, false,
                          std::move(block_processor_mock));
      for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
           ++frame_index) {
        for (int k = 0; k < fullband_frame_length_; ++k) {
          capture_buffer_.channels_f()[0][k] = 0.f;
        }
        switch (saturation_variant) {
          case SaturationTestVariant::kNone:
            break;
          case SaturationTestVariant::kOneNegative:
            if (frame_index == 0) {
              capture_buffer_.channels_f()[0][10] = -32768.f;
            }
            break;
          case SaturationTestVariant::kOnePositive:
            if (frame_index == 0) {
              capture_buffer_.channels_f()[0][10] = 32767.f;
            }
            break;
        }
  
        aec3.AnalyzeCapture(&capture_buffer_);
        OptionalBandSplit();
  
        PopulateInputFrame(frame_length_, num_bands_, frame_index,
                           &capture_buffer_.split_bands_f(0)[0], 0);
        PopulateInputFrame(frame_length_, num_bands_, frame_index,
                           &render_buffer_.split_bands_f(0)[0], 0);
  
        aec3.AnalyzeRender(&render_buffer_);
        aec3.ProcessCapture(&capture_buffer_, false);
      }
    }
  
    // This test verifies that the swapqueue is able to handle jitter in the
    // capture and render API calls.
    void RunRenderSwapQueueVerificationTest() {
      const EchoCanceller3Config config;
      EchoCanceller3 aec3(
          config, sample_rate_hz_, false,
          std::unique_ptr<BlockProcessor>(
              new RenderTransportVerificationProcessor(num_bands_)));
  
      for (size_t frame_index = 0; frame_index < kRenderTransferQueueSizeFrames;
           ++frame_index) {
        if (sample_rate_hz_ > 16000) {
          render_buffer_.SplitIntoFrequencyBands();
        }
        PopulateInputFrame(frame_length_, num_bands_, frame_index,
                           &render_buffer_.split_bands_f(0)[0], 0);
  
        if (sample_rate_hz_ > 16000) {
          render_buffer_.SplitIntoFrequencyBands();
        }
  
        aec3.AnalyzeRender(&render_buffer_);
      }
  
      for (size_t frame_index = 0; frame_index < kRenderTransferQueueSizeFrames;
           ++frame_index) {
        aec3.AnalyzeCapture(&capture_buffer_);
        if (sample_rate_hz_ > 16000) {
          capture_buffer_.SplitIntoFrequencyBands();
        }
  
        PopulateInputFrame(frame_length_, num_bands_, frame_index,
                           &capture_buffer_.split_bands_f(0)[0], 0);
  
        aec3.ProcessCapture(&capture_buffer_, false);
        EXPECT_TRUE(VerifyOutputFrameBitexactness(
            frame_length_, num_bands_, frame_index,
            &capture_buffer_.split_bands_f(0)[0], -64));
      }
    }
  
    // This test verifies that a buffer overrun in the render swapqueue is
    // properly reported.
    void RunRenderPipelineSwapQueueOverrunReturnValueTest() {
      EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, false);
  
      constexpr size_t kRenderTransferQueueSize = 30;
      for (size_t k = 0; k < 2; ++k) {
        for (size_t frame_index = 0; frame_index < kRenderTransferQueueSize;
             ++frame_index) {
          if (sample_rate_hz_ > 16000) {
            render_buffer_.SplitIntoFrequencyBands();
          }
          PopulateInputFrame(frame_length_, frame_index,
                             &render_buffer_.channels_f()[0][0], 0);
  
          if (k == 0) {
            aec3.AnalyzeRender(&render_buffer_);
          } else {
            aec3.AnalyzeRender(&render_buffer_);
          }
        }
      }
    }
  
  #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
    // Verifies the that the check for the number of bands in the AnalyzeRender
    // input is correct by adjusting the sample rates of EchoCanceller3 and the
    // input AudioBuffer to have a different number of bands.
    void RunAnalyzeRenderNumBandsCheckVerification() {
      // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a
      // way that the number of bands for the rates are different.
      const int aec3_sample_rate_hz = sample_rate_hz_ == 48000 ? 32000 : 48000;
      EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz, false);
      PopulateInputFrame(frame_length_, 0, &render_buffer_.channels_f()[0][0], 0);
  
      EXPECT_DEATH(aec3.AnalyzeRender(&render_buffer_), "");
    }
  
    // Verifies the that the check for the number of bands in the ProcessCapture
    // input is correct by adjusting the sample rates of EchoCanceller3 and the
    // input AudioBuffer to have a different number of bands.
    void RunProcessCaptureNumBandsCheckVerification() {
      // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a
      // way that the number of bands for the rates are different.
      const int aec3_sample_rate_hz = sample_rate_hz_ == 48000 ? 32000 : 48000;
      EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz, false);
      PopulateInputFrame(frame_length_, num_bands_, 0,
                         &capture_buffer_.split_bands_f(0)[0], 100);
      EXPECT_DEATH(aec3.ProcessCapture(&capture_buffer_, false), "");
    }
  
    // Verifies the that the check for the frame length in the AnalyzeRender input
    // is correct by adjusting the sample rates of EchoCanceller3 and the input
    // AudioBuffer to have a different frame lengths.
    void RunAnalyzeRenderFrameLengthCheckVerification() {
      // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a
      // way that the band frame lengths are different.
      const int aec3_sample_rate_hz = sample_rate_hz_ == 8000 ? 16000 : 8000;
      EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz, false);
  
      OptionalBandSplit();
      PopulateInputFrame(frame_length_, 0, &render_buffer_.channels_f()[0][0], 0);
  
      EXPECT_DEATH(aec3.AnalyzeRender(&render_buffer_), "");
    }
  
    // Verifies the that the check for the frame length in the AnalyzeRender input
    // is correct by adjusting the sample rates of EchoCanceller3 and the input
    // AudioBuffer to have a different frame lengths.
    void RunProcessCaptureFrameLengthCheckVerification() {
      // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a
      // way that the band frame lengths are different.
      const int aec3_sample_rate_hz = sample_rate_hz_ == 8000 ? 16000 : 8000;
      EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz, false);
  
      OptionalBandSplit();
      PopulateInputFrame(frame_length_, num_bands_, 0,
                         &capture_buffer_.split_bands_f(0)[0], 100);
  
      EXPECT_DEATH(aec3.ProcessCapture(&capture_buffer_, false), "");
    }
  
  #endif
  
   private:
    void OptionalBandSplit() {
      if (sample_rate_hz_ > 16000) {
        capture_buffer_.SplitIntoFrequencyBands();
        render_buffer_.SplitIntoFrequencyBands();
      }
    }
  
    static constexpr size_t kNumFramesToProcess = 20;
    const int sample_rate_hz_;
    const size_t num_bands_;
    const size_t frame_length_;
    const int fullband_frame_length_;
    AudioBuffer capture_buffer_;
    AudioBuffer render_buffer_;
  
    RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(EchoCanceller3Tester);
  };
  
  std::string ProduceDebugText(int sample_rate_hz) {
    std::ostringstream ss;
    ss << "Sample rate: " << sample_rate_hz;
    return ss.str();
  }
  
  std::string ProduceDebugText(int sample_rate_hz, int variant) {
    std::ostringstream ss;
    ss << "Sample rate: " << sample_rate_hz << ", variant: " << variant;
    return ss.str();
  }
  
  }  // namespace
  
  TEST(EchoCanceller3Buffering, CaptureBitexactness) {
    for (auto rate : {8000, 16000, 32000, 48000}) {
      SCOPED_TRACE(ProduceDebugText(rate));
      EchoCanceller3Tester(rate).RunCaptureTransportVerificationTest();
    }
  }
  
  TEST(EchoCanceller3Buffering, RenderBitexactness) {
    for (auto rate : {8000, 16000, 32000, 48000}) {
      SCOPED_TRACE(ProduceDebugText(rate));
      EchoCanceller3Tester(rate).RunRenderTransportVerificationTest();
    }
  }
  
  TEST(EchoCanceller3Buffering, RenderSwapQueue) {
    for (auto rate : {8000, 16000}) {
      SCOPED_TRACE(ProduceDebugText(rate));
      EchoCanceller3Tester(rate).RunRenderSwapQueueVerificationTest();
    }
  }
  
  TEST(EchoCanceller3Buffering, RenderSwapQueueOverrunReturnValue) {
    for (auto rate : {8000, 16000, 32000, 48000}) {
      SCOPED_TRACE(ProduceDebugText(rate));
      EchoCanceller3Tester(rate)
          .RunRenderPipelineSwapQueueOverrunReturnValueTest();
    }
  }
  
  TEST(EchoCanceller3Messaging, CaptureSaturation) {
    auto variants = {EchoCanceller3Tester::SaturationTestVariant::kNone,
                     EchoCanceller3Tester::SaturationTestVariant::kOneNegative,
                     EchoCanceller3Tester::SaturationTestVariant::kOnePositive};
    for (auto rate : {8000, 16000, 32000, 48000}) {
      for (auto variant : variants) {
        SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant)));
        EchoCanceller3Tester(rate).RunCaptureSaturationVerificationTest(variant);
      }
    }
  }
  
  TEST(EchoCanceller3Messaging, EchoPathChange) {
    auto variants = {
        EchoCanceller3Tester::EchoPathChangeTestVariant::kNone,
        EchoCanceller3Tester::EchoPathChangeTestVariant::kOneSticky,
        EchoCanceller3Tester::EchoPathChangeTestVariant::kOneNonSticky};
    for (auto rate : {8000, 16000, 32000, 48000}) {
      for (auto variant : variants) {
        SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant)));
        EchoCanceller3Tester(rate).RunEchoPathChangeVerificationTest(variant);
      }
    }
  }
  
  TEST(EchoCanceller3Messaging, EchoLeakage) {
    auto variants = {
        EchoCanceller3Tester::EchoLeakageTestVariant::kNone,
        EchoCanceller3Tester::EchoLeakageTestVariant::kFalseSticky,
        EchoCanceller3Tester::EchoLeakageTestVariant::kTrueSticky,
        EchoCanceller3Tester::EchoLeakageTestVariant::kTrueNonSticky};
    for (auto rate : {8000, 16000, 32000, 48000}) {
      for (auto variant : variants) {
        SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant)));
        EchoCanceller3Tester(rate).RunEchoLeakageVerificationTest(variant);
      }
    }
  }
  
  #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
  
  TEST(EchoCanceller3InputCheck, WrongCaptureNumBandsCheckVerification) {
    for (auto rate : {8000, 16000, 32000, 48000}) {
      SCOPED_TRACE(ProduceDebugText(rate));
      EchoCanceller3Tester(rate).RunProcessCaptureNumBandsCheckVerification();
    }
  }
  
  // TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
  // tests on test bots has been fixed.
  TEST(EchoCanceller3InputCheck,
       DISABLED_WrongRenderFrameLengthCheckVerification) {
    for (auto rate : {8000, 16000}) {
      SCOPED_TRACE(ProduceDebugText(rate));
      EchoCanceller3Tester(rate).RunAnalyzeRenderFrameLengthCheckVerification();
    }
  }
  
  TEST(EchoCanceller3InputCheck, WrongCaptureFrameLengthCheckVerification) {
    for (auto rate : {8000, 16000}) {
      SCOPED_TRACE(ProduceDebugText(rate));
      EchoCanceller3Tester(rate).RunProcessCaptureFrameLengthCheckVerification();
    }
  }
  
  // Verifiers that the verification for null input to the render analysis api
  // call works.
  TEST(EchoCanceller3InputCheck, NullRenderAnalysisParameter) {
    EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 8000, false)
                     .AnalyzeRender(nullptr),
                 "");
  }
  
  // Verifiers that the verification for null input to the capture analysis api
  // call works.
  TEST(EchoCanceller3InputCheck, NullCaptureAnalysisParameter) {
    EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 8000, false)
                     .AnalyzeCapture(nullptr),
                 "");
  }
  
  // Verifiers that the verification for null input to the capture processing api
  // call works.
  TEST(EchoCanceller3InputCheck, NullCaptureProcessingParameter) {
    EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 8000, false)
                     .ProcessCapture(nullptr, false),
                 "");
  }
  
  // Verifies the check for correct sample rate.
  // TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
  // tests on test bots has been fixed.
  TEST(EchoCanceller3InputCheck, DISABLED_WrongSampleRate) {
    ApmDataDumper data_dumper(0);
    EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 8001, false), "");
  }
  
  #endif
  
  }  // namespace webrtc
  