/*
 *  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 <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 "modules/audio_processing/high_pass_filter.h"
#include "modules/audio_processing/utility/cascaded_biquad_filter.h"
#include "rtc_base/strings/string_builder.h"
#include "test/field_trial.h"
#include "test/gmock.h"
#include "test/gtest.h"

namespace webrtc {
namespace {

using ::testing::_;
using ::testing::StrictMock;

// 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;
}

bool VerifyOutputFrameBitexactness(rtc::ArrayView<const float> reference,
                                   rtc::ArrayView<const float> frame,
                                   int offset) {
  for (size_t k = 0; k < frame.size(); ++k) {
    int reference_index = static_cast<int>(k) + offset;
    if (reference_index >= 0) {
      if (reference[reference_index] != frame[k]) {
        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() = delete;
  CaptureTransportVerificationProcessor(
      const CaptureTransportVerificationProcessor&) = delete;
  CaptureTransportVerificationProcessor& operator=(
      const CaptureTransportVerificationProcessor&) = delete;

  ~CaptureTransportVerificationProcessor() override = default;

  void ProcessCapture(bool level_change,
                      bool saturated_microphone_signal,
                      Block* linear_output,
                      Block* capture_block) override {}

  void BufferRender(const Block& block) override {}

  void UpdateEchoLeakageStatus(bool leakage_detected) override {}

  void GetMetrics(EchoControl::Metrics* metrics) const override {}

  void SetAudioBufferDelay(int delay_ms) override {}

  void SetCaptureOutputUsage(bool capture_output_used) {}
};

// 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() = delete;
  RenderTransportVerificationProcessor(
      const RenderTransportVerificationProcessor&) = delete;
  RenderTransportVerificationProcessor& operator=(
      const RenderTransportVerificationProcessor&) = delete;

  ~RenderTransportVerificationProcessor() override = default;

  void ProcessCapture(bool level_change,
                      bool saturated_microphone_signal,
                      Block* linear_output,
                      Block* capture_block) override {
    Block render_block = received_render_blocks_.front();
    received_render_blocks_.pop_front();
    capture_block->Swap(render_block);
  }

  void BufferRender(const Block& block) override {
    received_render_blocks_.push_back(block);
  }

  void UpdateEchoLeakageStatus(bool leakage_detected) override {}

  void GetMetrics(EchoControl::Metrics* metrics) const override {}

  void SetAudioBufferDelay(int delay_ms) override {}

  void SetCaptureOutputUsage(bool capture_output_used) {}

 private:
  std::deque<Block> received_render_blocks_;
};

std::string ProduceDebugText(int sample_rate_hz) {
  rtc::StringBuilder ss;
  ss << "Sample rate: " << sample_rate_hz;
  return ss.Release();
}

std::string ProduceDebugText(int sample_rate_hz, int variant) {
  rtc::StringBuilder ss;
  ss << "Sample rate: " << sample_rate_hz << ", variant: " << variant;
  return ss.Release();
}

void RunAecInStereo(AudioBuffer& buffer,
                    EchoCanceller3& aec3,
                    float channel_0_value,
                    float channel_1_value) {
  rtc::ArrayView<float> data_channel_0(&buffer.channels()[0][0],
                                       buffer.num_frames());
  std::fill(data_channel_0.begin(), data_channel_0.end(), channel_0_value);
  rtc::ArrayView<float> data_channel_1(&buffer.channels()[1][0],
                                       buffer.num_frames());
  std::fill(data_channel_1.begin(), data_channel_1.end(), channel_1_value);
  aec3.AnalyzeRender(&buffer);
  aec3.AnalyzeCapture(&buffer);
  aec3.ProcessCapture(&buffer, /*level_change=*/false);
}

void RunAecInSMono(AudioBuffer& buffer,
                   EchoCanceller3& aec3,
                   float channel_0_value) {
  rtc::ArrayView<float> data_channel_0(&buffer.channels()[0][0],
                                       buffer.num_frames());
  std::fill(data_channel_0.begin(), data_channel_0.end(), channel_0_value);
  aec3.AnalyzeRender(&buffer);
  aec3.AnalyzeCapture(&buffer);
  aec3.ProcessCapture(&buffer, /*level_change=*/false);
}

}  // namespace

class EchoCanceller3Tester {
 public:
  explicit EchoCanceller3Tester(int sample_rate_hz)
      : sample_rate_hz_(sample_rate_hz),
        num_bands_(NumBandsForRate(sample_rate_hz_)),
        frame_length_(160),
        fullband_frame_length_(rtc::CheckedDivExact(sample_rate_hz_, 100)),
        capture_buffer_(fullband_frame_length_ * 100,
                        1,
                        fullband_frame_length_ * 100,
                        1,
                        fullband_frame_length_ * 100,
                        1),
        render_buffer_(fullband_frame_length_ * 100,
                       1,
                       fullband_frame_length_ * 100,
                       1,
                       fullband_frame_length_ * 100,
                       1) {}

  EchoCanceller3Tester() = delete;
  EchoCanceller3Tester(const EchoCanceller3Tester&) = delete;
  EchoCanceller3Tester& operator=(const EchoCanceller3Tester&) = delete;

  // 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(),
                        /*multichannel_config=*/absl::nullopt, sample_rate_hz_,
                        1, 1);
    aec3.SetBlockProcessorForTesting(
        std::make_unique<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(0)[0], 0);
      PopulateInputFrame(frame_length_, frame_index,
                         &render_buffer_.channels()[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(0)[0], -64));
    }
  }

  // Test method for testing that the render data is properly received by the
  // block processor.
  void RunRenderTransportVerificationTest() {
    EchoCanceller3 aec3(EchoCanceller3Config(),
                        /*multichannel_config=*/absl::nullopt, sample_rate_hz_,
                        1, 1);
    aec3.SetBlockProcessorForTesting(
        std::make_unique<RenderTransportVerificationProcessor>(num_bands_));

    std::vector<std::vector<float>> render_input(1);
    std::vector<float> capture_output;
    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(0)[0], 100);
      PopulateInputFrame(frame_length_, num_bands_, frame_index,
                         &render_buffer_.split_bands(0)[0], 0);

      for (size_t k = 0; k < frame_length_; ++k) {
        render_input[0].push_back(render_buffer_.split_bands(0)[0][k]);
      }
      aec3.AnalyzeRender(&render_buffer_);
      aec3.ProcessCapture(&capture_buffer_, false);
      for (size_t k = 0; k < frame_length_; ++k) {
        capture_output.push_back(capture_buffer_.split_bands(0)[0][k]);
      }
    }

    EXPECT_TRUE(
        VerifyOutputFrameBitexactness(render_input[0], capture_output, -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) {
    constexpr size_t kNumFullBlocksPerFrame = 160 / kBlockSize;
    constexpr size_t kExpectedNumBlocksToProcess =
        (kNumFramesToProcess * 160) / kBlockSize;
    std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>>
        block_processor_mock(
            new StrictMock<webrtc::test::MockBlockProcessor>());
    EXPECT_CALL(*block_processor_mock, BufferRender(_))
        .Times(kExpectedNumBlocksToProcess);
    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(kExpectedNumBlocksToProcess);
        break;
      case EchoPathChangeTestVariant::kOneSticky:
        EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _, _))
            .Times(kExpectedNumBlocksToProcess);
        break;
      case EchoPathChangeTestVariant::kOneNonSticky:
        EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _, _))
            .Times(kNumFullBlocksPerFrame);
        EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _, _))
            .Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
        break;
    }

    EchoCanceller3 aec3(EchoCanceller3Config(),
                        /*multichannel_config=*/absl::nullopt, sample_rate_hz_,
                        1, 1);
    aec3.SetBlockProcessorForTesting(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(0)[0], 0);
      PopulateInputFrame(frame_length_, frame_index,
                         &render_buffer_.channels()[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) {
    constexpr size_t kExpectedNumBlocksToProcess =
        (kNumFramesToProcess * 160) / kBlockSize;
    std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>>
        block_processor_mock(
            new StrictMock<webrtc::test::MockBlockProcessor>());
    EXPECT_CALL(*block_processor_mock, BufferRender(_))
        .Times(kExpectedNumBlocksToProcess);
    EXPECT_CALL(*block_processor_mock, ProcessCapture(_, _, _, _))
        .Times(kExpectedNumBlocksToProcess);

    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(),
                        /*multichannel_config=*/absl::nullopt, sample_rate_hz_,
                        1, 1);
    aec3.SetBlockProcessorForTesting(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(0)[0], 0);
      PopulateInputFrame(frame_length_, frame_index,
                         &render_buffer_.channels()[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 kNumFullBlocksPerFrame = 160 / kBlockSize;
    const size_t kExpectedNumBlocksToProcess =
        (kNumFramesToProcess * 160) / kBlockSize;
    std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>>
        block_processor_mock(
            new StrictMock<webrtc::test::MockBlockProcessor>());
    EXPECT_CALL(*block_processor_mock, BufferRender(_))
        .Times(kExpectedNumBlocksToProcess);
    EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(_)).Times(0);

    switch (saturation_variant) {
      case SaturationTestVariant::kNone:
        EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _, _))
            .Times(kExpectedNumBlocksToProcess);
        break;
      case SaturationTestVariant::kOneNegative: {
        ::testing::InSequence s;
        EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _, _))
            .Times(kNumFullBlocksPerFrame);
        EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _, _))
            .Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
      } break;
      case SaturationTestVariant::kOnePositive: {
        ::testing::InSequence s;
        EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _, _))
            .Times(kNumFullBlocksPerFrame);
        EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _, _))
            .Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
      } break;
    }

    EchoCanceller3 aec3(EchoCanceller3Config(),
                        /*multichannel_config=*/absl::nullopt, sample_rate_hz_,
                        1, 1);
    aec3.SetBlockProcessorForTesting(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()[0][k] = 0.f;
      }
      switch (saturation_variant) {
        case SaturationTestVariant::kNone:
          break;
        case SaturationTestVariant::kOneNegative:
          if (frame_index == 0) {
            capture_buffer_.channels()[0][10] = -32768.f;
          }
          break;
        case SaturationTestVariant::kOnePositive:
          if (frame_index == 0) {
            capture_buffer_.channels()[0][10] = 32767.f;
          }
          break;
      }

      aec3.AnalyzeCapture(&capture_buffer_);
      OptionalBandSplit();

      PopulateInputFrame(frame_length_, num_bands_, frame_index,
                         &capture_buffer_.split_bands(0)[0], 0);
      PopulateInputFrame(frame_length_, num_bands_, frame_index,
                         &render_buffer_.split_bands(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, /*multichannel_config=*/absl::nullopt,
                        sample_rate_hz_, 1, 1);
    aec3.SetBlockProcessorForTesting(
        std::make_unique<RenderTransportVerificationProcessor>(num_bands_));

    std::vector<std::vector<float>> render_input(1);
    std::vector<float> capture_output;

    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(0)[0], 0);

      if (sample_rate_hz_ > 16000) {
        render_buffer_.SplitIntoFrequencyBands();
      }

      for (size_t k = 0; k < frame_length_; ++k) {
        render_input[0].push_back(render_buffer_.split_bands(0)[0][k]);
      }
      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(0)[0], 0);

      aec3.ProcessCapture(&capture_buffer_, false);
      for (size_t k = 0; k < frame_length_; ++k) {
        capture_output.push_back(capture_buffer_.split_bands(0)[0][k]);
      }
    }

    EXPECT_TRUE(
        VerifyOutputFrameBitexactness(render_input[0], capture_output, -64));
  }

  // This test verifies that a buffer overrun in the render swapqueue is
  // properly reported.
  void RunRenderPipelineSwapQueueOverrunReturnValueTest() {
    EchoCanceller3 aec3(EchoCanceller3Config(),
                        /*multichannel_config=*/absl::nullopt, sample_rate_hz_,
                        1, 1);

    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()[0][0], 0);

        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(),
                        /*multichannel_config=*/absl::nullopt,
                        aec3_sample_rate_hz, 1, 1);
    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(),
                        /*multichannel_config=*/absl::nullopt,
                        aec3_sample_rate_hz, 1, 1);
    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_;
};

TEST(EchoCanceller3Buffering, CaptureBitexactness) {
  for (auto rate : {16000, 32000, 48000}) {
    SCOPED_TRACE(ProduceDebugText(rate));
    EchoCanceller3Tester(rate).RunCaptureTransportVerificationTest();
  }
}

TEST(EchoCanceller3Buffering, RenderBitexactness) {
  for (auto rate : {16000, 32000, 48000}) {
    SCOPED_TRACE(ProduceDebugText(rate));
    EchoCanceller3Tester(rate).RunRenderTransportVerificationTest();
  }
}

TEST(EchoCanceller3Buffering, RenderSwapQueue) {
  EchoCanceller3Tester(16000).RunRenderSwapQueueVerificationTest();
}

TEST(EchoCanceller3Buffering, RenderSwapQueueOverrunReturnValue) {
  for (auto rate : {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 : {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 : {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 : {16000, 32000, 48000}) {
    for (auto variant : variants) {
      SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant)));
      EchoCanceller3Tester(rate).RunEchoLeakageVerificationTest(variant);
    }
  }
}

// Tests the parameter functionality for the field trial override for the
// anti-howling gain.
TEST(EchoCanceller3FieldTrials, Aec3SuppressorAntiHowlingGainOverride) {
  EchoCanceller3Config default_config;
  EchoCanceller3Config adjusted_config = AdjustConfig(default_config);
  ASSERT_EQ(
      default_config.suppressor.high_bands_suppression.anti_howling_gain,
      adjusted_config.suppressor.high_bands_suppression.anti_howling_gain);

  webrtc::test::ScopedFieldTrials field_trials(
      "WebRTC-Aec3SuppressorAntiHowlingGainOverride/0.02/");
  adjusted_config = AdjustConfig(default_config);

  ASSERT_NE(
      default_config.suppressor.high_bands_suppression.anti_howling_gain,
      adjusted_config.suppressor.high_bands_suppression.anti_howling_gain);
  EXPECT_FLOAT_EQ(
      0.02f,
      adjusted_config.suppressor.high_bands_suppression.anti_howling_gain);
}

// Tests the field trial override for the enforcement of a low active render
// limit.
TEST(EchoCanceller3FieldTrials, Aec3EnforceLowActiveRenderLimit) {
  EchoCanceller3Config default_config;
  EchoCanceller3Config adjusted_config = AdjustConfig(default_config);
  ASSERT_EQ(default_config.render_levels.active_render_limit,
            adjusted_config.render_levels.active_render_limit);

  webrtc::test::ScopedFieldTrials field_trials(
      "WebRTC-Aec3EnforceLowActiveRenderLimit/Enabled/");
  adjusted_config = AdjustConfig(default_config);

  ASSERT_NE(default_config.render_levels.active_render_limit,
            adjusted_config.render_levels.active_render_limit);
  EXPECT_FLOAT_EQ(50.f, adjusted_config.render_levels.active_render_limit);
}

// Testing the field trial-based override of the suppressor parameters for a
// joint passing of all parameters.
TEST(EchoCanceller3FieldTrials, Aec3SuppressorTuningOverrideAllParams) {
  webrtc::test::ScopedFieldTrials field_trials(
      "WebRTC-Aec3SuppressorTuningOverride/"
      "nearend_tuning_mask_lf_enr_transparent:0.1,nearend_tuning_mask_lf_enr_"
      "suppress:0.2,nearend_tuning_mask_hf_enr_transparent:0.3,nearend_tuning_"
      "mask_hf_enr_suppress:0.4,nearend_tuning_max_inc_factor:0.5,nearend_"
      "tuning_max_dec_factor_lf:0.6,normal_tuning_mask_lf_enr_transparent:0.7,"
      "normal_tuning_mask_lf_enr_suppress:0.8,normal_tuning_mask_hf_enr_"
      "transparent:0.9,normal_tuning_mask_hf_enr_suppress:1.0,normal_tuning_"
      "max_inc_factor:1.1,normal_tuning_max_dec_factor_lf:1.2,dominant_nearend_"
      "detection_enr_threshold:1.3,dominant_nearend_detection_enr_exit_"
      "threshold:1.4,dominant_nearend_detection_snr_threshold:1.5,dominant_"
      "nearend_detection_hold_duration:10,dominant_nearend_detection_trigger_"
      "threshold:11/");

  EchoCanceller3Config default_config;
  EchoCanceller3Config adjusted_config = AdjustConfig(default_config);

  ASSERT_NE(adjusted_config.suppressor.nearend_tuning.mask_lf.enr_transparent,
            default_config.suppressor.nearend_tuning.mask_lf.enr_transparent);
  ASSERT_NE(adjusted_config.suppressor.nearend_tuning.mask_lf.enr_suppress,
            default_config.suppressor.nearend_tuning.mask_lf.enr_suppress);
  ASSERT_NE(adjusted_config.suppressor.nearend_tuning.mask_hf.enr_transparent,
            default_config.suppressor.nearend_tuning.mask_hf.enr_transparent);
  ASSERT_NE(adjusted_config.suppressor.nearend_tuning.mask_hf.enr_suppress,
            default_config.suppressor.nearend_tuning.mask_hf.enr_suppress);
  ASSERT_NE(adjusted_config.suppressor.nearend_tuning.max_inc_factor,
            default_config.suppressor.nearend_tuning.max_inc_factor);
  ASSERT_NE(adjusted_config.suppressor.nearend_tuning.max_dec_factor_lf,
            default_config.suppressor.nearend_tuning.max_dec_factor_lf);
  ASSERT_NE(adjusted_config.suppressor.normal_tuning.mask_lf.enr_transparent,
            default_config.suppressor.normal_tuning.mask_lf.enr_transparent);
  ASSERT_NE(adjusted_config.suppressor.normal_tuning.mask_lf.enr_suppress,
            default_config.suppressor.normal_tuning.mask_lf.enr_suppress);
  ASSERT_NE(adjusted_config.suppressor.normal_tuning.mask_hf.enr_transparent,
            default_config.suppressor.normal_tuning.mask_hf.enr_transparent);
  ASSERT_NE(adjusted_config.suppressor.normal_tuning.mask_hf.enr_suppress,
            default_config.suppressor.normal_tuning.mask_hf.enr_suppress);
  ASSERT_NE(adjusted_config.suppressor.normal_tuning.max_inc_factor,
            default_config.suppressor.normal_tuning.max_inc_factor);
  ASSERT_NE(adjusted_config.suppressor.normal_tuning.max_dec_factor_lf,
            default_config.suppressor.normal_tuning.max_dec_factor_lf);
  ASSERT_NE(adjusted_config.suppressor.dominant_nearend_detection.enr_threshold,
            default_config.suppressor.dominant_nearend_detection.enr_threshold);
  ASSERT_NE(
      adjusted_config.suppressor.dominant_nearend_detection.enr_exit_threshold,
      default_config.suppressor.dominant_nearend_detection.enr_exit_threshold);
  ASSERT_NE(adjusted_config.suppressor.dominant_nearend_detection.snr_threshold,
            default_config.suppressor.dominant_nearend_detection.snr_threshold);
  ASSERT_NE(adjusted_config.suppressor.dominant_nearend_detection.hold_duration,
            default_config.suppressor.dominant_nearend_detection.hold_duration);
  ASSERT_NE(
      adjusted_config.suppressor.dominant_nearend_detection.trigger_threshold,
      default_config.suppressor.dominant_nearend_detection.trigger_threshold);

  EXPECT_FLOAT_EQ(
      adjusted_config.suppressor.nearend_tuning.mask_lf.enr_transparent, 0.1);
  EXPECT_FLOAT_EQ(
      adjusted_config.suppressor.nearend_tuning.mask_lf.enr_suppress, 0.2);
  EXPECT_FLOAT_EQ(
      adjusted_config.suppressor.nearend_tuning.mask_hf.enr_transparent, 0.3);
  EXPECT_FLOAT_EQ(
      adjusted_config.suppressor.nearend_tuning.mask_hf.enr_suppress, 0.4);
  EXPECT_FLOAT_EQ(adjusted_config.suppressor.nearend_tuning.max_inc_factor,
                  0.5);
  EXPECT_FLOAT_EQ(adjusted_config.suppressor.nearend_tuning.max_dec_factor_lf,
                  0.6);
  EXPECT_FLOAT_EQ(
      adjusted_config.suppressor.normal_tuning.mask_lf.enr_transparent, 0.7);
  EXPECT_FLOAT_EQ(adjusted_config.suppressor.normal_tuning.mask_lf.enr_suppress,
                  0.8);
  EXPECT_FLOAT_EQ(
      adjusted_config.suppressor.normal_tuning.mask_hf.enr_transparent, 0.9);
  EXPECT_FLOAT_EQ(adjusted_config.suppressor.normal_tuning.mask_hf.enr_suppress,
                  1.0);
  EXPECT_FLOAT_EQ(adjusted_config.suppressor.normal_tuning.max_inc_factor, 1.1);
  EXPECT_FLOAT_EQ(adjusted_config.suppressor.normal_tuning.max_dec_factor_lf,
                  1.2);
  EXPECT_FLOAT_EQ(
      adjusted_config.suppressor.dominant_nearend_detection.enr_threshold, 1.3);
  EXPECT_FLOAT_EQ(
      adjusted_config.suppressor.dominant_nearend_detection.enr_exit_threshold,
      1.4);
  EXPECT_FLOAT_EQ(
      adjusted_config.suppressor.dominant_nearend_detection.snr_threshold, 1.5);
  EXPECT_EQ(adjusted_config.suppressor.dominant_nearend_detection.hold_duration,
            10);
  EXPECT_EQ(
      adjusted_config.suppressor.dominant_nearend_detection.trigger_threshold,
      11);
}

// Testing the field trial-based override of the suppressor parameters for
// passing one parameter.
TEST(EchoCanceller3FieldTrials, Aec3SuppressorTuningOverrideOneParam) {
  webrtc::test::ScopedFieldTrials field_trials(
      "WebRTC-Aec3SuppressorTuningOverride/nearend_tuning_max_inc_factor:0.5/");

  EchoCanceller3Config default_config;
  EchoCanceller3Config adjusted_config = AdjustConfig(default_config);

  ASSERT_EQ(adjusted_config.suppressor.nearend_tuning.mask_lf.enr_transparent,
            default_config.suppressor.nearend_tuning.mask_lf.enr_transparent);
  ASSERT_EQ(adjusted_config.suppressor.nearend_tuning.mask_lf.enr_suppress,
            default_config.suppressor.nearend_tuning.mask_lf.enr_suppress);
  ASSERT_EQ(adjusted_config.suppressor.nearend_tuning.mask_hf.enr_transparent,
            default_config.suppressor.nearend_tuning.mask_hf.enr_transparent);
  ASSERT_EQ(adjusted_config.suppressor.nearend_tuning.mask_hf.enr_suppress,
            default_config.suppressor.nearend_tuning.mask_hf.enr_suppress);
  ASSERT_EQ(adjusted_config.suppressor.nearend_tuning.max_dec_factor_lf,
            default_config.suppressor.nearend_tuning.max_dec_factor_lf);
  ASSERT_EQ(adjusted_config.suppressor.normal_tuning.mask_lf.enr_transparent,
            default_config.suppressor.normal_tuning.mask_lf.enr_transparent);
  ASSERT_EQ(adjusted_config.suppressor.normal_tuning.mask_lf.enr_suppress,
            default_config.suppressor.normal_tuning.mask_lf.enr_suppress);
  ASSERT_EQ(adjusted_config.suppressor.normal_tuning.mask_hf.enr_transparent,
            default_config.suppressor.normal_tuning.mask_hf.enr_transparent);
  ASSERT_EQ(adjusted_config.suppressor.normal_tuning.mask_hf.enr_suppress,
            default_config.suppressor.normal_tuning.mask_hf.enr_suppress);
  ASSERT_EQ(adjusted_config.suppressor.normal_tuning.max_inc_factor,
            default_config.suppressor.normal_tuning.max_inc_factor);
  ASSERT_EQ(adjusted_config.suppressor.normal_tuning.max_dec_factor_lf,
            default_config.suppressor.normal_tuning.max_dec_factor_lf);
  ASSERT_EQ(adjusted_config.suppressor.dominant_nearend_detection.enr_threshold,
            default_config.suppressor.dominant_nearend_detection.enr_threshold);
  ASSERT_EQ(
      adjusted_config.suppressor.dominant_nearend_detection.enr_exit_threshold,
      default_config.suppressor.dominant_nearend_detection.enr_exit_threshold);
  ASSERT_EQ(adjusted_config.suppressor.dominant_nearend_detection.snr_threshold,
            default_config.suppressor.dominant_nearend_detection.snr_threshold);
  ASSERT_EQ(adjusted_config.suppressor.dominant_nearend_detection.hold_duration,
            default_config.suppressor.dominant_nearend_detection.hold_duration);
  ASSERT_EQ(
      adjusted_config.suppressor.dominant_nearend_detection.trigger_threshold,
      default_config.suppressor.dominant_nearend_detection.trigger_threshold);

  ASSERT_NE(adjusted_config.suppressor.nearend_tuning.max_inc_factor,
            default_config.suppressor.nearend_tuning.max_inc_factor);

  EXPECT_FLOAT_EQ(adjusted_config.suppressor.nearend_tuning.max_inc_factor,
                  0.5);
}

// Testing the field trial-based that override the exponential decay parameters.
TEST(EchoCanceller3FieldTrials, Aec3UseNearendReverb) {
  webrtc::test::ScopedFieldTrials field_trials(
      "WebRTC-Aec3UseNearendReverbLen/default_len:0.9,nearend_len:0.8/");
  EchoCanceller3Config default_config;
  EchoCanceller3Config adjusted_config = AdjustConfig(default_config);
  EXPECT_FLOAT_EQ(adjusted_config.ep_strength.default_len, 0.9);
  EXPECT_FLOAT_EQ(adjusted_config.ep_strength.nearend_len, 0.8);
}

TEST(EchoCanceller3, DetectionOfProperStereo) {
  constexpr int kSampleRateHz = 16000;
  constexpr int kNumChannels = 2;
  AudioBuffer buffer(/*input_rate=*/kSampleRateHz,
                     /*input_num_channels=*/kNumChannels,
                     /*input_rate=*/kSampleRateHz,
                     /*buffer_num_channels=*/kNumChannels,
                     /*output_rate=*/kSampleRateHz,
                     /*output_num_channels=*/kNumChannels);

  constexpr size_t kNumBlocksForMonoConfig = 1;
  constexpr size_t kNumBlocksForSurroundConfig = 2;
  EchoCanceller3Config mono_config;
  absl::optional<EchoCanceller3Config> multichannel_config;

  mono_config.multi_channel.detect_stereo_content = true;
  mono_config.multi_channel.stereo_detection_threshold = 0.0f;
  mono_config.multi_channel.stereo_detection_hysteresis_seconds = 0.0f;
  multichannel_config = mono_config;
  mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig;
  multichannel_config->filter.coarse_initial.length_blocks =
      kNumBlocksForSurroundConfig;

  EchoCanceller3 aec3(mono_config, multichannel_config,
                      /*sample_rate_hz=*/kSampleRateHz,
                      /*num_render_channels=*/kNumChannels,
                      /*num_capture_input_channels=*/kNumChannels);

  EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting());
  EXPECT_EQ(
      aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
      kNumBlocksForMonoConfig);

  RunAecInStereo(buffer, aec3, 100.0f, 100.0f);
  EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting());
  EXPECT_EQ(
      aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
      kNumBlocksForMonoConfig);

  RunAecInStereo(buffer, aec3, 100.0f, 101.0f);
  EXPECT_TRUE(aec3.StereoRenderProcessingActiveForTesting());
  EXPECT_EQ(
      aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
      kNumBlocksForSurroundConfig);
}

TEST(EchoCanceller3, DetectionOfProperStereoUsingThreshold) {
  constexpr int kSampleRateHz = 16000;
  constexpr int kNumChannels = 2;
  AudioBuffer buffer(/*input_rate=*/kSampleRateHz,
                     /*input_num_channels=*/kNumChannels,
                     /*input_rate=*/kSampleRateHz,
                     /*buffer_num_channels=*/kNumChannels,
                     /*output_rate=*/kSampleRateHz,
                     /*output_num_channels=*/kNumChannels);

  constexpr size_t kNumBlocksForMonoConfig = 1;
  constexpr size_t kNumBlocksForSurroundConfig = 2;
  EchoCanceller3Config mono_config;
  absl::optional<EchoCanceller3Config> multichannel_config;

  constexpr float kStereoDetectionThreshold = 2.0f;
  mono_config.multi_channel.detect_stereo_content = true;
  mono_config.multi_channel.stereo_detection_threshold =
      kStereoDetectionThreshold;
  mono_config.multi_channel.stereo_detection_hysteresis_seconds = 0.0f;
  multichannel_config = mono_config;
  mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig;
  multichannel_config->filter.coarse_initial.length_blocks =
      kNumBlocksForSurroundConfig;

  EchoCanceller3 aec3(mono_config, multichannel_config,
                      /*sample_rate_hz=*/kSampleRateHz,
                      /*num_render_channels=*/kNumChannels,
                      /*num_capture_input_channels=*/kNumChannels);

  EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting());
  EXPECT_EQ(
      aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
      kNumBlocksForMonoConfig);

  RunAecInStereo(buffer, aec3, 100.0f,
                 100.0f + kStereoDetectionThreshold - 1.0f);
  EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting());
  EXPECT_EQ(
      aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
      kNumBlocksForMonoConfig);

  RunAecInStereo(buffer, aec3, 100.0f,
                 100.0f + kStereoDetectionThreshold + 10.0f);
  EXPECT_TRUE(aec3.StereoRenderProcessingActiveForTesting());
  EXPECT_EQ(
      aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
      kNumBlocksForSurroundConfig);
}

TEST(EchoCanceller3, DetectionOfProperStereoUsingHysteresis) {
  constexpr int kSampleRateHz = 16000;
  constexpr int kNumChannels = 2;
  AudioBuffer buffer(/*input_rate=*/kSampleRateHz,
                     /*input_num_channels=*/kNumChannels,
                     /*input_rate=*/kSampleRateHz,
                     /*buffer_num_channels=*/kNumChannels,
                     /*output_rate=*/kSampleRateHz,
                     /*output_num_channels=*/kNumChannels);

  constexpr size_t kNumBlocksForMonoConfig = 1;
  constexpr size_t kNumBlocksForSurroundConfig = 2;
  EchoCanceller3Config mono_config;
  absl::optional<EchoCanceller3Config> surround_config;

  mono_config.multi_channel.detect_stereo_content = true;
  mono_config.multi_channel.stereo_detection_hysteresis_seconds = 0.5f;
  surround_config = mono_config;
  mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig;
  surround_config->filter.coarse_initial.length_blocks =
      kNumBlocksForSurroundConfig;

  EchoCanceller3 aec3(mono_config, surround_config,
                      /*sample_rate_hz=*/kSampleRateHz,
                      /*num_render_channels=*/kNumChannels,
                      /*num_capture_input_channels=*/kNumChannels);

  EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting());
  EXPECT_EQ(
      aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
      kNumBlocksForMonoConfig);

  RunAecInStereo(buffer, aec3, 100.0f, 100.0f);
  EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting());
  EXPECT_EQ(
      aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
      kNumBlocksForMonoConfig);

  constexpr int kNumFramesPerSecond = 100;
  for (int k = 0;
       k < static_cast<int>(
               kNumFramesPerSecond *
               mono_config.multi_channel.stereo_detection_hysteresis_seconds);
       ++k) {
    RunAecInStereo(buffer, aec3, 100.0f, 101.0f);
    EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting());
    EXPECT_EQ(
        aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
        kNumBlocksForMonoConfig);
  }

  RunAecInStereo(buffer, aec3, 100.0f, 101.0f);
  EXPECT_TRUE(aec3.StereoRenderProcessingActiveForTesting());
  EXPECT_EQ(
      aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
      kNumBlocksForSurroundConfig);
}

TEST(EchoCanceller3, StereoContentDetectionForMonoSignals) {
  constexpr int kSampleRateHz = 16000;
  constexpr int kNumChannels = 2;
  AudioBuffer buffer(/*input_rate=*/kSampleRateHz,
                     /*input_num_channels=*/kNumChannels,
                     /*input_rate=*/kSampleRateHz,
                     /*buffer_num_channels=*/kNumChannels,
                     /*output_rate=*/kSampleRateHz,
                     /*output_num_channels=*/kNumChannels);

  constexpr size_t kNumBlocksForMonoConfig = 1;
  constexpr size_t kNumBlocksForSurroundConfig = 2;
  EchoCanceller3Config mono_config;
  absl::optional<EchoCanceller3Config> multichannel_config;

  for (bool detect_stereo_content : {false, true}) {
    mono_config.multi_channel.detect_stereo_content = detect_stereo_content;
    multichannel_config = mono_config;
    mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig;
    multichannel_config->filter.coarse_initial.length_blocks =
        kNumBlocksForSurroundConfig;

    AudioBuffer mono_buffer(/*input_rate=*/kSampleRateHz,
                            /*input_num_channels=*/1,
                            /*input_rate=*/kSampleRateHz,
                            /*buffer_num_channels=*/1,
                            /*output_rate=*/kSampleRateHz,
                            /*output_num_channels=*/1);

    EchoCanceller3 aec3(mono_config, multichannel_config,
                        /*sample_rate_hz=*/kSampleRateHz,
                        /*num_render_channels=*/1,
                        /*num_capture_input_channels=*/1);

    EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting());
    EXPECT_EQ(
        aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
        kNumBlocksForMonoConfig);

    RunAecInSMono(mono_buffer, aec3, 100.0f);
    EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting());
    EXPECT_EQ(
        aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks,
        kNumBlocksForMonoConfig);
  }
}

#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)

TEST(EchoCanceller3InputCheckDeathTest, WrongCaptureNumBandsCheckVerification) {
  for (auto rate : {16000, 32000, 48000}) {
    SCOPED_TRACE(ProduceDebugText(rate));
    EchoCanceller3Tester(rate).RunProcessCaptureNumBandsCheckVerification();
  }
}

// Verifiers that the verification for null input to the capture processing api
// call works.
TEST(EchoCanceller3InputCheckDeathTest, NullCaptureProcessingParameter) {
  EXPECT_DEATH(
      EchoCanceller3(EchoCanceller3Config(),
                     /*multichannel_config_=*/absl::nullopt, 16000, 1, 1)
          .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(EchoCanceller3InputCheckDeathTest, DISABLED_WrongSampleRate) {
  ApmDataDumper data_dumper(0);
  EXPECT_DEATH(
      EchoCanceller3(EchoCanceller3Config(),
                     /*multichannel_config_=*/absl::nullopt, 8001, 1, 1),
      "");
}

#endif

}  // namespace webrtc
