/*
 *  Copyright (c) 2012 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 "testing/gtest/include/gtest/gtest.h"
extern "C" {
#include "webrtc/modules/audio_processing/aec/aec_core.h"
}
#include "webrtc/modules/audio_processing/aec/echo_cancellation_internal.h"
#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h"
#include "webrtc/test/testsupport/gtest_disable.h"
#include "webrtc/typedefs.h"

namespace {

class SystemDelayTest : public ::testing::Test {
 protected:
  SystemDelayTest();
  virtual void SetUp();
  virtual void TearDown();

  // Initialization of AEC handle with respect to |sample_rate_hz|. Since the
  // device sample rate is unimportant we set that value to 48000 Hz.
  void Init(int sample_rate_hz);

  // Makes one render call and one capture call in that specific order.
  void RenderAndCapture(int device_buffer_ms);

  // Fills up the far-end buffer with respect to the default device buffer size.
  size_t BufferFillUp();

  // Runs and verifies the behavior in a stable startup procedure.
  void RunStableStartup();

  // Maps buffer size in ms into samples, taking the unprocessed frame into
  // account.
  int MapBufferSizeToSamples(int size_in_ms, bool extended_filter);

  void* handle_;
  Aec* self_;
  size_t samples_per_frame_;
  // Dummy input/output speech data.
  static const int kSamplesPerChunk = 160;
  float far_[kSamplesPerChunk];
  float near_[kSamplesPerChunk];
  float out_[kSamplesPerChunk];
  const float* near_ptr_;
  float* out_ptr_;
};

SystemDelayTest::SystemDelayTest()
    : handle_(NULL), self_(NULL), samples_per_frame_(0) {
  // Dummy input data are set with more or less arbitrary non-zero values.
  for (int i = 0; i < kSamplesPerChunk; i++) {
    far_[i] = 257.0;
    near_[i] = 514.0;
  }
  memset(out_, 0, sizeof(out_));
  near_ptr_ = near_;
  out_ptr_ = out_;
}

void SystemDelayTest::SetUp() {
  handle_ = WebRtcAec_Create();
  ASSERT_TRUE(handle_);
  self_ = reinterpret_cast<Aec*>(handle_);
}

void SystemDelayTest::TearDown() {
  // Free AEC
  WebRtcAec_Free(handle_);
  handle_ = NULL;
}

// In SWB mode nothing is added to the buffer handling with respect to
// functionality compared to WB. We therefore only verify behavior in NB and WB.
static const int kSampleRateHz[] = {8000, 16000};
static const size_t kNumSampleRates =
    sizeof(kSampleRateHz) / sizeof(*kSampleRateHz);

// Default audio device buffer size used.
static const int kDeviceBufMs = 100;

// Requirement for a stable device convergence time in ms. Should converge in
// less than |kStableConvergenceMs|.
static const int kStableConvergenceMs = 100;

// Maximum convergence time in ms. This means that we should leave the startup
// phase after |kMaxConvergenceMs| independent of device buffer stability
// conditions.
static const int kMaxConvergenceMs = 500;

void SystemDelayTest::Init(int sample_rate_hz) {
  // Initialize AEC
  EXPECT_EQ(0, WebRtcAec_Init(handle_, sample_rate_hz, 48000));
  EXPECT_EQ(0, WebRtcAec_system_delay(self_->aec));

  // One frame equals 10 ms of data.
  samples_per_frame_ = static_cast<size_t>(sample_rate_hz / 100);
}

void SystemDelayTest::RenderAndCapture(int device_buffer_ms) {
  EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_));
  EXPECT_EQ(0,
            WebRtcAec_Process(handle_,
                              &near_ptr_,
                              1,
                              &out_ptr_,
                              samples_per_frame_,
                              device_buffer_ms,
                              0));
}

size_t SystemDelayTest::BufferFillUp() {
  // To make sure we have a full buffer when we verify stability we first fill
  // up the far-end buffer with the same amount as we will report in through
  // Process().
  size_t buffer_size = 0;
  for (int i = 0; i < kDeviceBufMs / 10; i++) {
    EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_));
    buffer_size += samples_per_frame_;
    EXPECT_EQ(static_cast<int>(buffer_size),
              WebRtcAec_system_delay(self_->aec));
  }
  return buffer_size;
}

void SystemDelayTest::RunStableStartup() {
  // To make sure we have a full buffer when we verify stability we first fill
  // up the far-end buffer with the same amount as we will report in through
  // Process().
  size_t buffer_size = BufferFillUp();

  if (WebRtcAec_delay_agnostic_enabled(self_->aec) == 1) {
    // In extended_filter mode we set the buffer size after the first processed
    // 10 ms chunk. Hence, we don't need to wait for the reported system delay
    // values to become stable.
    RenderAndCapture(kDeviceBufMs);
    buffer_size += samples_per_frame_;
    EXPECT_EQ(0, self_->startup_phase);
  } else {
    // A stable device should be accepted and put in a regular process mode
    // within |kStableConvergenceMs|.
    int process_time_ms = 0;
    for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) {
      RenderAndCapture(kDeviceBufMs);
      buffer_size += samples_per_frame_;
      if (self_->startup_phase == 0) {
        // We have left the startup phase.
        break;
      }
    }
    // Verify convergence time.
    EXPECT_GT(kStableConvergenceMs, process_time_ms);
  }
  // Verify that the buffer has been flushed.
  EXPECT_GE(static_cast<int>(buffer_size),
            WebRtcAec_system_delay(self_->aec));
}

  int SystemDelayTest::MapBufferSizeToSamples(int size_in_ms,
                                              bool extended_filter) {
  // If extended_filter is disabled we add an extra 10 ms for the unprocessed
  // frame. That is simply how the algorithm is constructed.
  return static_cast<int>(
      (size_in_ms + (extended_filter ? 0 : 10)) * samples_per_frame_ / 10);
}

// The tests should meet basic requirements and not be adjusted to what is
// actually implemented. If we don't get good code coverage this way we either
// lack in tests or have unnecessary code.
// General requirements:
// 1) If we add far-end data the system delay should be increased with the same
//    amount we add.
// 2) If the far-end buffer is full we should flush the oldest data to make room
//    for the new. In this case the system delay is unaffected.
// 3) There should exist a startup phase in which the buffer size is to be
//    determined. In this phase no cancellation should be performed.
// 4) Under stable conditions (small variations in device buffer sizes) the AEC
//    should determine an appropriate local buffer size within
//    |kStableConvergenceMs| ms.
// 5) Under unstable conditions the AEC should make a decision within
//    |kMaxConvergenceMs| ms.
// 6) If the local buffer runs out of data we should stuff the buffer with older
//    frames.
// 7) The system delay should within |kMaxConvergenceMs| ms heal from
//    disturbances like drift, data glitches, toggling events and outliers.
// 8) The system delay should never become negative.

TEST_F(SystemDelayTest, CorrectIncreaseWhenBufferFarend) {
  // When we add data to the AEC buffer the internal system delay should be
  // incremented with the same amount as the size of data.
  // This process should be independent of DA-AEC and extended_filter mode.
  for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) {
    WebRtcAec_enable_extended_filter(self_->aec, extended_filter);
    EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec));
    for (int da_aec = 0; da_aec <= 1; ++da_aec) {
      WebRtcAec_enable_delay_agnostic(self_->aec, da_aec);
      EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec));
      for (size_t i = 0; i < kNumSampleRates; i++) {
        Init(kSampleRateHz[i]);
        // Loop through a couple of calls to make sure the system delay
        // increments correctly.
        for (int j = 1; j <= 5; j++) {
          EXPECT_EQ(0,
                    WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_));
          EXPECT_EQ(static_cast<int>(j * samples_per_frame_),
                    WebRtcAec_system_delay(self_->aec));
        }
      }
    }
  }
}

// TODO(bjornv): Add a test to verify behavior if the far-end buffer is full
// when adding new data.

TEST_F(SystemDelayTest, CorrectDelayAfterStableStartup) {
  // We run the system in a stable startup. After that we verify that the system
  // delay meets the requirements.
  // This process should be independent of DA-AEC and extended_filter mode.
  for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) {
    WebRtcAec_enable_extended_filter(self_->aec, extended_filter);
    EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec));
    for (int da_aec = 0; da_aec <= 1; ++da_aec) {
      WebRtcAec_enable_delay_agnostic(self_->aec, da_aec);
      EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec));
      for (size_t i = 0; i < kNumSampleRates; i++) {
        Init(kSampleRateHz[i]);
        RunStableStartup();

        // Verify system delay with respect to requirements, i.e., the
        // |system_delay| is in the interval [75%, 100%] of what's reported on
        // the average.
        // In extended_filter mode we target 50% and measure after one processed
        // 10 ms chunk.
        int average_reported_delay =
            static_cast<int>(kDeviceBufMs * samples_per_frame_ / 10);
        EXPECT_GE(average_reported_delay, WebRtcAec_system_delay(self_->aec));
        int lower_bound = WebRtcAec_extended_filter_enabled(self_->aec)
                              ? average_reported_delay / 2 - samples_per_frame_
                              : average_reported_delay * 3 / 4;
        EXPECT_LE(lower_bound, WebRtcAec_system_delay(self_->aec));
      }
    }
  }
}

TEST_F(SystemDelayTest, CorrectDelayAfterUnstableStartup) {
  // This test does not apply in extended_filter mode, since we only use the
  // the first 10 ms chunk to determine a reasonable buffer size. Neither does
  // it apply if DA-AEC is on because that overrides the startup procedure.
  WebRtcAec_enable_extended_filter(self_->aec, 0);
  EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(self_->aec));
  WebRtcAec_enable_delay_agnostic(self_->aec, 0);
  EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(self_->aec));

  // In an unstable system we would start processing after |kMaxConvergenceMs|.
  // On the last frame the AEC buffer is adjusted to 60% of the last reported
  // device buffer size.
  // We construct an unstable system by altering the device buffer size between
  // two values |kDeviceBufMs| +- 25 ms.
  for (size_t i = 0; i < kNumSampleRates; i++) {
    Init(kSampleRateHz[i]);

    // To make sure we have a full buffer when we verify stability we first fill
    // up the far-end buffer with the same amount as we will report in on the
    // average through Process().
    size_t buffer_size = BufferFillUp();

    int buffer_offset_ms = 25;
    int reported_delay_ms = 0;
    int process_time_ms = 0;
    for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) {
      reported_delay_ms = kDeviceBufMs + buffer_offset_ms;
      RenderAndCapture(reported_delay_ms);
      buffer_size += samples_per_frame_;
      buffer_offset_ms = -buffer_offset_ms;
      if (self_->startup_phase == 0) {
        // We have left the startup phase.
        break;
      }
    }
    // Verify convergence time.
    EXPECT_GE(kMaxConvergenceMs, process_time_ms);
    // Verify that the buffer has been flushed.
    EXPECT_GE(static_cast<int>(buffer_size),
              WebRtcAec_system_delay(self_->aec));

    // Verify system delay with respect to requirements, i.e., the
    // |system_delay| is in the interval [60%, 100%] of what's last reported.
    EXPECT_GE(static_cast<int>(reported_delay_ms * samples_per_frame_ / 10),
              WebRtcAec_system_delay(self_->aec));
    EXPECT_LE(
        static_cast<int>(reported_delay_ms * samples_per_frame_ / 10 * 3 / 5),
        WebRtcAec_system_delay(self_->aec));
  }
}

TEST_F(SystemDelayTest, CorrectDelayAfterStableBufferBuildUp) {
  // This test does not apply in extended_filter mode, since we only use the
  // the first 10 ms chunk to determine a reasonable buffer size. Neither does
  // it apply if DA-AEC is on because that overrides the startup procedure.
  WebRtcAec_enable_extended_filter(self_->aec, 0);
  EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(self_->aec));
  WebRtcAec_enable_delay_agnostic(self_->aec, 0);
  EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(self_->aec));

  // In this test we start by establishing the device buffer size during stable
  // conditions, but with an empty internal far-end buffer. Once that is done we
  // verify that the system delay is increased correctly until we have reach an
  // internal buffer size of 75% of what's been reported.
  for (size_t i = 0; i < kNumSampleRates; i++) {
    Init(kSampleRateHz[i]);

    // We assume that running |kStableConvergenceMs| calls will put the
    // algorithm in a state where the device buffer size has been determined. We
    // can make that assumption since we have a separate stability test.
    int process_time_ms = 0;
    for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) {
      EXPECT_EQ(0,
                WebRtcAec_Process(handle_,
                                  &near_ptr_,
                                  1,
                                  &out_ptr_,
                                  samples_per_frame_,
                                  kDeviceBufMs,
                                  0));
    }
    // Verify that a buffer size has been established.
    EXPECT_EQ(0, self_->checkBuffSize);

    // We now have established the required buffer size. Let us verify that we
    // fill up before leaving the startup phase for normal processing.
    size_t buffer_size = 0;
    size_t target_buffer_size = kDeviceBufMs * samples_per_frame_ / 10 * 3 / 4;
    process_time_ms = 0;
    for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) {
      RenderAndCapture(kDeviceBufMs);
      buffer_size += samples_per_frame_;
      if (self_->startup_phase == 0) {
        // We have left the startup phase.
        break;
      }
    }
    // Verify convergence time.
    EXPECT_GT(kMaxConvergenceMs, process_time_ms);
    // Verify that the buffer has reached the desired size.
    EXPECT_LE(static_cast<int>(target_buffer_size),
              WebRtcAec_system_delay(self_->aec));

    // Verify normal behavior (system delay is kept constant) after startup by
    // running a couple of calls to BufferFarend() and Process().
    for (int j = 0; j < 6; j++) {
      int system_delay_before_calls = WebRtcAec_system_delay(self_->aec);
      RenderAndCapture(kDeviceBufMs);
      EXPECT_EQ(system_delay_before_calls, WebRtcAec_system_delay(self_->aec));
    }
  }
}

TEST_F(SystemDelayTest, CorrectDelayWhenBufferUnderrun) {
  // Here we test a buffer under run scenario. If we keep on calling
  // WebRtcAec_Process() we will finally run out of data, but should
  // automatically stuff the buffer. We verify this behavior by checking if the
  // system delay goes negative.
  // This process should be independent of DA-AEC and extended_filter mode.
  for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) {
    WebRtcAec_enable_extended_filter(self_->aec, extended_filter);
    EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec));
    for (int da_aec = 0; da_aec <= 1; ++da_aec) {
      WebRtcAec_enable_delay_agnostic(self_->aec, da_aec);
      EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec));
      for (size_t i = 0; i < kNumSampleRates; i++) {
        Init(kSampleRateHz[i]);
        RunStableStartup();

        // The AEC has now left the Startup phase. We now have at most
        // |kStableConvergenceMs| in the buffer. Keep on calling Process() until
        // we run out of data and verify that the system delay is non-negative.
        for (int j = 0; j <= kStableConvergenceMs; j += 10) {
          EXPECT_EQ(0, WebRtcAec_Process(handle_, &near_ptr_, 1, &out_ptr_,
                                         samples_per_frame_, kDeviceBufMs, 0));
          EXPECT_LE(0, WebRtcAec_system_delay(self_->aec));
        }
      }
    }
  }
}

TEST_F(SystemDelayTest, CorrectDelayDuringDrift) {
  // This drift test should verify that the system delay is never exceeding the
  // device buffer. The drift is simulated by decreasing the reported device
  // buffer size by 1 ms every 100 ms. If the device buffer size goes below 30
  // ms we jump (add) 10 ms to give a repeated pattern.

  // This process should be independent of DA-AEC and extended_filter mode.
  for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) {
    WebRtcAec_enable_extended_filter(self_->aec, extended_filter);
    EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec));
    for (int da_aec = 0; da_aec <= 1; ++da_aec) {
      WebRtcAec_enable_delay_agnostic(self_->aec, da_aec);
      EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec));
      for (size_t i = 0; i < kNumSampleRates; i++) {
        Init(kSampleRateHz[i]);
        RunStableStartup();

        // We have left the startup phase and proceed with normal processing.
        int jump = 0;
        for (int j = 0; j < 1000; j++) {
          // Drift = -1 ms per 100 ms of data.
          int device_buf_ms = kDeviceBufMs - (j / 10) + jump;
          int device_buf = MapBufferSizeToSamples(device_buf_ms,
                                                  extended_filter == 1);

          if (device_buf_ms < 30) {
            // Add 10 ms data, taking affect next frame.
            jump += 10;
          }
          RenderAndCapture(device_buf_ms);

          // Verify that the system delay does not exceed the device buffer.
          EXPECT_GE(device_buf, WebRtcAec_system_delay(self_->aec));

          // Verify that the system delay is non-negative.
          EXPECT_LE(0, WebRtcAec_system_delay(self_->aec));
        }
      }
    }
  }
}

TEST_F(SystemDelayTest, ShouldRecoverAfterGlitch) {
  // This glitch test should verify that the system delay recovers if there is
  // a glitch in data. The data glitch is constructed as 200 ms of buffering
  // after which the stable procedure continues. The glitch is never reported by
  // the device.
  // The system is said to be in a non-causal state if the difference between
  // the device buffer and system delay is less than a block (64 samples).

  // This process should be independent of DA-AEC and extended_filter mode.
  for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) {
    WebRtcAec_enable_extended_filter(self_->aec, extended_filter);
    EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec));
    for (int da_aec = 0; da_aec <= 1; ++da_aec) {
      WebRtcAec_enable_delay_agnostic(self_->aec, da_aec);
      EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec));
      for (size_t i = 0; i < kNumSampleRates; i++) {
        Init(kSampleRateHz[i]);
        RunStableStartup();
        int device_buf = MapBufferSizeToSamples(kDeviceBufMs,
                                                extended_filter == 1);
        // Glitch state.
        for (int j = 0; j < 20; j++) {
          EXPECT_EQ(0,
                    WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_));
          // No need to verify system delay, since that is done in a separate
          // test.
        }
        // Verify that we are in a non-causal state, i.e.,
        // |system_delay| > |device_buf|.
        EXPECT_LT(device_buf, WebRtcAec_system_delay(self_->aec));

        // Recover state. Should recover at least 4 ms of data per 10 ms, hence
        // a glitch of 200 ms will take at most 200 * 10 / 4 = 500 ms to recover
        // from.
        bool non_causal = true;  // We are currently in a non-causal state.
        for (int j = 0; j < 50; j++) {
          int system_delay_before = WebRtcAec_system_delay(self_->aec);
          RenderAndCapture(kDeviceBufMs);
          int system_delay_after = WebRtcAec_system_delay(self_->aec);
          // We have recovered if
          // |device_buf| - |system_delay_after| >= PART_LEN (1 block).
          // During recovery, |system_delay_after| < |system_delay_before|,
          // otherwise they are equal.
          if (non_causal) {
            EXPECT_LT(system_delay_after, system_delay_before);
            if (device_buf - system_delay_after >= PART_LEN) {
              non_causal = false;
            }
          } else {
            EXPECT_EQ(system_delay_before, system_delay_after);
          }
          // Verify that the system delay is non-negative.
          EXPECT_LE(0, WebRtcAec_system_delay(self_->aec));
        }
        // Check that we have recovered.
        EXPECT_FALSE(non_causal);
      }
    }
  }
}

TEST_F(SystemDelayTest, UnaffectedWhenSpuriousDeviceBufferValues) {
  // This test does not apply in extended_filter mode, since we only use the
  // the first 10 ms chunk to determine a reasonable buffer size.
  const int extended_filter = 0;
  WebRtcAec_enable_extended_filter(self_->aec, extended_filter);
  EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec));

  // Should be DA-AEC independent.
  for (int da_aec = 0; da_aec <= 1; ++da_aec) {
    WebRtcAec_enable_delay_agnostic(self_->aec, da_aec);
    EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec));
    // This spurious device buffer data test aims at verifying that the system
    // delay is unaffected by large outliers.
    // The system is said to be in a non-causal state if the difference between
    // the device buffer and system delay is less than a block (64 samples).
    for (size_t i = 0; i < kNumSampleRates; i++) {
      Init(kSampleRateHz[i]);
      RunStableStartup();
      int device_buf = MapBufferSizeToSamples(kDeviceBufMs,
                                              extended_filter == 1);

      // Normal state. We are currently not in a non-causal state.
      bool non_causal = false;

      // Run 1 s and replace device buffer size with 500 ms every 100 ms.
      for (int j = 0; j < 100; j++) {
        int system_delay_before_calls = WebRtcAec_system_delay(self_->aec);
        int device_buf_ms = j % 10 == 0 ? 500 : kDeviceBufMs;
        RenderAndCapture(device_buf_ms);

        // Check for non-causality.
        if (device_buf - WebRtcAec_system_delay(self_->aec) < PART_LEN) {
          non_causal = true;
        }
        EXPECT_FALSE(non_causal);
        EXPECT_EQ(system_delay_before_calls,
                  WebRtcAec_system_delay(self_->aec));

        // Verify that the system delay is non-negative.
        EXPECT_LE(0, WebRtcAec_system_delay(self_->aec));
      }
    }
  }
}

TEST_F(SystemDelayTest, CorrectImpactWhenTogglingDeviceBufferValues) {
  // This test aims at verifying that the system delay is "unaffected" by
  // toggling values reported by the device.
  // The test is constructed such that every other device buffer value is zero
  // and then 2 * |kDeviceBufMs|, hence the size is constant on the average. The
  // zero values will force us into a non-causal state and thereby lowering the
  // system delay until we basically run out of data. Once that happens the
  // buffer will be stuffed.
  // TODO(bjornv): This test will have a better impact if we verified that the
  // delay estimate goes up when the system delay goes down to meet the average
  // device buffer size.

  // This test does not apply if DA-AEC is enabled and extended_filter mode
  // disabled.
  for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) {
    WebRtcAec_enable_extended_filter(self_->aec, extended_filter);
    EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec));
    for (int da_aec = 0; da_aec <= 1; ++da_aec) {
      WebRtcAec_enable_delay_agnostic(self_->aec, da_aec);
      EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec));
      if (extended_filter == 0 && da_aec == 1) {
        continue;
      }
      for (size_t i = 0; i < kNumSampleRates; i++) {
        Init(kSampleRateHz[i]);
        RunStableStartup();
        const int device_buf = MapBufferSizeToSamples(kDeviceBufMs,
                                                      extended_filter == 1);

        // Normal state. We are currently not in a non-causal state.
        bool non_causal = false;

        // Loop through 100 frames (both render and capture), which equals 1 s
        // of data. Every odd frame we set the device buffer size to
        // 2 * |kDeviceBufMs| and even frames we set the device buffer size to
        // zero.
        for (int j = 0; j < 100; j++) {
          int system_delay_before_calls = WebRtcAec_system_delay(self_->aec);
          int device_buf_ms = 2 * (j % 2) * kDeviceBufMs;
          RenderAndCapture(device_buf_ms);

          // Check for non-causality, compared with the average device buffer
          // size.
          non_causal |= (device_buf - WebRtcAec_system_delay(self_->aec) < 64);
          EXPECT_GE(system_delay_before_calls,
                    WebRtcAec_system_delay(self_->aec));

          // Verify that the system delay is non-negative.
          EXPECT_LE(0, WebRtcAec_system_delay(self_->aec));
        }
        // Verify we are not in a non-causal state.
        EXPECT_FALSE(non_causal);
      }
    }
  }
}

}  // namespace
