普通文本  |  1218行  |  35.48 KB

// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

extern "C" {
#include "audio_thread.c"
#include "cras_audio_area.h"
}

#include <gtest/gtest.h>
#include <map>

#define MAX_CALLS 10
#define BUFFER_SIZE 8192
#define FIRST_CB_LEVEL 480

static int cras_audio_thread_busyloop_called;
static unsigned int cras_rstream_dev_offset_called;
static unsigned int cras_rstream_dev_offset_ret[MAX_CALLS];
static const struct cras_rstream *cras_rstream_dev_offset_rstream_val[MAX_CALLS];
static unsigned int cras_rstream_dev_offset_dev_id_val[MAX_CALLS];
static unsigned int cras_rstream_dev_offset_update_called;
static const struct cras_rstream *cras_rstream_dev_offset_update_rstream_val[MAX_CALLS];
static unsigned int cras_rstream_dev_offset_update_frames_val[MAX_CALLS];
static unsigned int cras_rstream_dev_offset_update_dev_id_val[MAX_CALLS];
static int cras_iodev_all_streams_written_ret;
static struct cras_audio_area *cras_iodev_get_output_buffer_area;
static int cras_iodev_put_output_buffer_called;
static unsigned int cras_iodev_put_output_buffer_nframes;
static unsigned int cras_iodev_fill_odev_zeros_frames;
static int dev_stream_playback_frames_ret;
static unsigned int cras_iodev_prepare_output_before_write_samples_called;
static enum CRAS_IODEV_STATE cras_iodev_prepare_output_before_write_samples_state;
static unsigned int cras_iodev_get_output_buffer_called;
static unsigned int cras_iodev_frames_to_play_in_sleep_called;
static int cras_iodev_prepare_output_before_write_samples_ret;
static int cras_iodev_reset_request_called;
static struct cras_iodev *cras_iodev_reset_request_iodev;
static int cras_iodev_output_underrun_called;
static int cras_device_monitor_reset_device_called;
static struct cras_iodev *cras_device_monitor_reset_device_iodev;
static struct cras_iodev *cras_iodev_start_ramp_odev;
static enum CRAS_IODEV_RAMP_REQUEST cras_iodev_start_ramp_request;
static std::map<const struct dev_stream*, struct timespec> dev_stream_wake_time_val;

void ResetGlobalStubData() {
  cras_rstream_dev_offset_called = 0;
  cras_rstream_dev_offset_update_called = 0;
  for (int i = 0; i < MAX_CALLS; i++) {
    cras_rstream_dev_offset_ret[i] = 0;
    cras_rstream_dev_offset_rstream_val[i] = NULL;
    cras_rstream_dev_offset_dev_id_val[i] = 0;
    cras_rstream_dev_offset_update_rstream_val[i] = NULL;
    cras_rstream_dev_offset_update_frames_val[i] = 0;
    cras_rstream_dev_offset_update_dev_id_val[i] = 0;
  }
  cras_iodev_all_streams_written_ret = 0;
  if (cras_iodev_get_output_buffer_area) {
    free(cras_iodev_get_output_buffer_area->channels[0].buf);
    free(cras_iodev_get_output_buffer_area);
    cras_iodev_get_output_buffer_area = NULL;
  }
  cras_iodev_put_output_buffer_called = 0;
  cras_iodev_put_output_buffer_nframes = 0;
  cras_iodev_fill_odev_zeros_frames = 0;
  cras_iodev_frames_to_play_in_sleep_called = 0;
  dev_stream_playback_frames_ret = 0;
  cras_iodev_prepare_output_before_write_samples_called = 0;
  cras_iodev_prepare_output_before_write_samples_state = CRAS_IODEV_STATE_OPEN;
  cras_iodev_get_output_buffer_called = 0;
  cras_iodev_prepare_output_before_write_samples_ret = 0;
  cras_iodev_reset_request_called = 0;
  cras_iodev_reset_request_iodev = NULL;
  cras_iodev_output_underrun_called = 0;
  cras_device_monitor_reset_device_called = 0;
  cras_device_monitor_reset_device_iodev = NULL;
  cras_iodev_start_ramp_odev = NULL;
  cras_iodev_start_ramp_request = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK;
  dev_stream_wake_time_val.clear();
}

// Test streams and devices manipulation.
class StreamDeviceSuite : public testing::Test {
  protected:
    virtual void SetUp() {
      thread_ = audio_thread_create();
      ResetStubData();
    }

    virtual void TearDown() {
      audio_thread_destroy(thread_);
      ResetGlobalStubData();
    }

    virtual void SetupDevice(cras_iodev *iodev,
                             enum CRAS_STREAM_DIRECTION direction) {
      memset(iodev, 0, sizeof(*iodev));
      iodev->info.idx = ++device_id_;
      iodev->direction = direction;
      iodev->configure_dev = configure_dev;
      iodev->close_dev = close_dev;
      iodev->frames_queued = frames_queued;
      iodev->delay_frames = delay_frames;
      iodev->get_buffer = get_buffer;
      iodev->put_buffer = put_buffer;
      iodev->flush_buffer = flush_buffer;
      iodev->ext_format = &format_;
      iodev->buffer_size = BUFFER_SIZE;
      iodev->min_cb_level = FIRST_CB_LEVEL;
    }

    void ResetStubData() {
      device_id_ = 0;
      open_dev_called_ = 0;
      close_dev_called_ = 0;
      frames_queued_ = 0;
      delay_frames_ = 0;
      audio_buffer_size_ = 0;
    }

    void SetupRstream(struct cras_rstream *rstream,
                      enum CRAS_STREAM_DIRECTION direction) {
      memset(rstream, 0, sizeof(*rstream));
      rstream->direction = direction;
      rstream->cb_threshold = 480;
      rstream->shm.area = static_cast<cras_audio_shm_area*>(
          calloc(1, sizeof(*rstream->shm.area)));
    }

    void TearDownRstream(struct cras_rstream *rstream) {
      free(rstream->shm.area);
    }

    void SetupPinnedStream(struct cras_rstream *rstream,
                           enum CRAS_STREAM_DIRECTION direction,
                           cras_iodev* pin_to_dev) {
      SetupRstream(rstream, direction);
      rstream->is_pinned = 1;
      rstream->pinned_dev_idx = pin_to_dev->info.idx;
    }

    static int configure_dev(cras_iodev* iodev) {
      open_dev_called_++;
      return 0;
    }

    static int close_dev(cras_iodev* iodev) {
      close_dev_called_++;
      return 0;
    }

    static int frames_queued(const cras_iodev* iodev, struct timespec* tstamp) {
      clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
      return frames_queued_;
    }

    static int delay_frames(const cras_iodev* iodev) {
      return delay_frames_;
    }

    static int get_buffer(cras_iodev* iodev,
                          struct cras_audio_area** area,
                          unsigned int* num) {
      size_t sz = sizeof(*area_) + sizeof(struct cras_channel_area) * 2;

      if (audio_buffer_size_ < *num)
        *num = audio_buffer_size_;

      area_ = (cras_audio_area*)calloc(1, sz);
      area_->frames = *num;
      area_->num_channels = 2;
      area_->channels[0].buf = audio_buffer_;
      channel_area_set_channel(&area_->channels[0], CRAS_CH_FL);
      area_->channels[0].step_bytes = 4;
      area_->channels[1].buf = audio_buffer_ + 2;
      channel_area_set_channel(&area_->channels[1], CRAS_CH_FR);
      area_->channels[1].step_bytes = 4;

      *area = area_;
      return 0;
    }

    static int put_buffer(cras_iodev* iodev, unsigned int num) {
      free(area_);
      return 0;
    }

    static int flush_buffer(cras_iodev *iodev) {
      return 0;
    }

    int device_id_;
    struct audio_thread *thread_;

    static int open_dev_called_;
    static int close_dev_called_;
    static int frames_queued_;
    static int delay_frames_;
    static struct cras_audio_format format_;
    static struct cras_audio_area *area_;
    static uint8_t audio_buffer_[BUFFER_SIZE];
    static unsigned int audio_buffer_size_;
};

int StreamDeviceSuite::open_dev_called_;
int StreamDeviceSuite::close_dev_called_;
int StreamDeviceSuite::frames_queued_;
int StreamDeviceSuite::delay_frames_;
struct cras_audio_format StreamDeviceSuite::format_;
struct cras_audio_area *StreamDeviceSuite::area_;
uint8_t StreamDeviceSuite::audio_buffer_[8192];
unsigned int StreamDeviceSuite::audio_buffer_size_;

TEST_F(StreamDeviceSuite, AddRemoveOpenOutputDevice) {
  struct cras_iodev iodev;
  struct open_dev *adev;

  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);

  // Check the newly added device is open.
  thread_add_open_dev(thread_, &iodev);
  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
  EXPECT_EQ(adev->dev, &iodev);

  thread_rm_open_dev(thread_, &iodev);
  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
  EXPECT_EQ(NULL, adev);
}

TEST_F(StreamDeviceSuite, StartRamp) {
  struct cras_iodev iodev;
  struct open_dev *adev;
  int rc;
  enum CRAS_IODEV_RAMP_REQUEST req;

  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);

  // Check the newly added device is open.
  thread_add_open_dev(thread_, &iodev);
  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
  EXPECT_EQ(adev->dev, &iodev);

  // Ramp up for unmute.
  req = CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE;
  rc = thread_dev_start_ramp(thread_, &iodev, req);

  EXPECT_EQ(0, rc);
  EXPECT_EQ(&iodev, cras_iodev_start_ramp_odev);
  EXPECT_EQ(req, cras_iodev_start_ramp_request);

  // Ramp down for mute.
  ResetStubData();
  req = CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE;

  rc = thread_dev_start_ramp(thread_, &iodev, req);

  EXPECT_EQ(0, rc);
  EXPECT_EQ(&iodev, cras_iodev_start_ramp_odev);
  EXPECT_EQ(req, cras_iodev_start_ramp_request);
  thread_rm_open_dev(thread_, &iodev);
}

TEST_F(StreamDeviceSuite, AddRemoveOpenInputDevice) {
  struct cras_iodev iodev;
  struct open_dev *adev;

  SetupDevice(&iodev, CRAS_STREAM_INPUT);

  // Check the newly added device is open.
  thread_add_open_dev(thread_, &iodev);
  adev = thread_->open_devs[CRAS_STREAM_INPUT];
  EXPECT_EQ(adev->dev, &iodev);

  thread_rm_open_dev(thread_, &iodev);
  adev = thread_->open_devs[CRAS_STREAM_INPUT];
  EXPECT_EQ(NULL, adev);
}

TEST_F(StreamDeviceSuite, AddRemoveMultipleOpenDevices) {
  struct cras_iodev odev;
  struct cras_iodev odev2;
  struct cras_iodev odev3;
  struct cras_iodev idev;
  struct cras_iodev idev2;
  struct cras_iodev idev3;
  struct open_dev *adev;

  SetupDevice(&odev, CRAS_STREAM_OUTPUT);
  SetupDevice(&odev2, CRAS_STREAM_OUTPUT);
  SetupDevice(&odev3, CRAS_STREAM_OUTPUT);
  SetupDevice(&idev, CRAS_STREAM_INPUT);
  SetupDevice(&idev2, CRAS_STREAM_INPUT);
  SetupDevice(&idev3, CRAS_STREAM_INPUT);

  // Add 2 open devices and check both are open.
  thread_add_open_dev(thread_, &odev);
  thread_add_open_dev(thread_, &odev2);
  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
  EXPECT_EQ(adev->dev, &odev);
  EXPECT_EQ(adev->next->dev, &odev2);

  // Remove first open device and check the second one is still open.
  thread_rm_open_dev(thread_, &odev);
  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
  EXPECT_EQ(adev->dev, &odev2);

  // Add another open device and check both are open.
  thread_add_open_dev(thread_, &odev3);
  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
  EXPECT_EQ(adev->dev, &odev2);
  EXPECT_EQ(adev->next->dev, &odev3);

  // Add 2 open devices and check both are open.
  thread_add_open_dev(thread_, &idev);
  thread_add_open_dev(thread_, &idev2);
  adev = thread_->open_devs[CRAS_STREAM_INPUT];
  EXPECT_EQ(adev->dev, &idev);
  EXPECT_EQ(adev->next->dev, &idev2);

  // Remove first open device and check the second one is still open.
  thread_rm_open_dev(thread_, &idev);
  adev = thread_->open_devs[CRAS_STREAM_INPUT];
  EXPECT_EQ(adev->dev, &idev2);

  // Add and remove another open device and check still open.
  thread_add_open_dev(thread_, &idev3);
  thread_rm_open_dev(thread_, &idev3);
  adev = thread_->open_devs[CRAS_STREAM_INPUT];
  EXPECT_EQ(adev->dev, &idev2);
  thread_rm_open_dev(thread_, &idev2);
  thread_rm_open_dev(thread_, &odev2);
  thread_rm_open_dev(thread_, &odev3);
}

TEST_F(StreamDeviceSuite, MultipleInputStreamsCopyFirstStreamOffset) {
  struct cras_iodev iodev;
  struct cras_iodev iodev2;
  struct cras_iodev *iodevs[] = {&iodev, &iodev2};
  struct cras_rstream rstream;
  struct cras_rstream rstream2;
  struct cras_rstream rstream3;

  SetupDevice(&iodev, CRAS_STREAM_INPUT);
  SetupDevice(&iodev2, CRAS_STREAM_INPUT);
  SetupRstream(&rstream, CRAS_STREAM_INPUT);
  SetupRstream(&rstream2, CRAS_STREAM_INPUT);
  SetupRstream(&rstream3, CRAS_STREAM_INPUT);

  thread_add_open_dev(thread_, &iodev);
  thread_add_open_dev(thread_, &iodev2);

  thread_add_stream(thread_, &rstream, iodevs, 2);
  EXPECT_NE((void *)NULL, iodev.streams);
  EXPECT_NE((void *)NULL, iodev2.streams);

  EXPECT_EQ(0, cras_rstream_dev_offset_called);
  EXPECT_EQ(0, cras_rstream_dev_offset_update_called);

  // Fake offset for rstream
  cras_rstream_dev_offset_ret[0] = 30;
  cras_rstream_dev_offset_ret[1] = 0;

  thread_add_stream(thread_, &rstream2, iodevs, 2);
  EXPECT_EQ(2, cras_rstream_dev_offset_called);
  EXPECT_EQ(&rstream, cras_rstream_dev_offset_rstream_val[0]);
  EXPECT_EQ(iodev.info.idx, cras_rstream_dev_offset_dev_id_val[0]);
  EXPECT_EQ(&rstream, cras_rstream_dev_offset_rstream_val[1]);
  EXPECT_EQ(iodev2.info.idx, cras_rstream_dev_offset_dev_id_val[1]);

  EXPECT_EQ(2, cras_rstream_dev_offset_update_called);
  EXPECT_EQ(&rstream2, cras_rstream_dev_offset_update_rstream_val[0]);
  EXPECT_EQ(30, cras_rstream_dev_offset_update_frames_val[0]);
  EXPECT_EQ(&rstream2, cras_rstream_dev_offset_update_rstream_val[1]);
  EXPECT_EQ(0, cras_rstream_dev_offset_update_frames_val[1]);

  thread_rm_open_dev(thread_, &iodev);
  thread_rm_open_dev(thread_, &iodev2);
  TearDownRstream(&rstream);
  TearDownRstream(&rstream2);
  TearDownRstream(&rstream3);
}

TEST_F(StreamDeviceSuite, InputStreamsSetInputDeviceWakeTime) {
  struct cras_iodev iodev;
  struct cras_iodev *iodevs[] = {&iodev};
  struct cras_rstream rstream1, rstream2;
  struct timespec ts_wake_1 = {.tv_sec = 1, .tv_nsec = 500};
  struct timespec ts_wake_2 = {.tv_sec = 1, .tv_nsec = 1000};
  struct open_dev *adev;

  SetupDevice(&iodev, CRAS_STREAM_INPUT);
  SetupRstream(&rstream1, CRAS_STREAM_INPUT);
  SetupRstream(&rstream2, CRAS_STREAM_INPUT);

  thread_add_open_dev(thread_, &iodev);
  thread_add_stream(thread_, &rstream1, iodevs, 1);
  thread_add_stream(thread_, &rstream2, iodevs, 1);
  EXPECT_NE((void *)NULL, iodev.streams);

  // Assume device is running.
  iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;

  // Set stub data for dev_stream_wake_time.
  dev_stream_wake_time_val[iodev.streams] = ts_wake_1;
  dev_stream_wake_time_val[iodev.streams->next] = ts_wake_2;

  // Send captured samples to client.
  // This will also update wake time for this device based on
  // dev_stream_wake_time of each stream of this device.
  dev_io_send_captured_samples(thread_->open_devs[CRAS_STREAM_INPUT]);

  // wake_ts is maintained in open_dev.
  adev = thread_->open_devs[CRAS_STREAM_INPUT];

  // The wake up time for this device is the minimum of
  // ts_wake_1 and ts_wake_2.
  EXPECT_EQ(ts_wake_1.tv_sec, adev->wake_ts.tv_sec);
  EXPECT_EQ(ts_wake_1.tv_nsec, adev->wake_ts.tv_nsec);

  thread_rm_open_dev(thread_, &iodev);
  TearDownRstream(&rstream1);
  TearDownRstream(&rstream2);
}

TEST_F(StreamDeviceSuite, AddRemoveMultipleStreamsOnMultipleDevices) {
  struct cras_iodev iodev, *piodev = &iodev;
  struct cras_iodev iodev2, *piodev2 = &iodev2;
  struct cras_rstream rstream;
  struct cras_rstream rstream2;
  struct cras_rstream rstream3;
  struct dev_stream *dev_stream;

  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
  SetupDevice(&iodev2, CRAS_STREAM_OUTPUT);
  SetupRstream(&rstream, CRAS_STREAM_OUTPUT);
  SetupRstream(&rstream2, CRAS_STREAM_OUTPUT);
  SetupRstream(&rstream3, CRAS_STREAM_OUTPUT);

  // Add first device as open and check 2 streams can be added.
  thread_add_open_dev(thread_, &iodev);
  thread_add_stream(thread_, &rstream, &piodev, 1);
  dev_stream = iodev.streams;
  EXPECT_EQ(dev_stream->stream, &rstream);
  thread_add_stream(thread_, &rstream2, &piodev, 1);
  EXPECT_EQ(dev_stream->next->stream, &rstream2);

  // Add second device as open and check no streams are copied over.
  thread_add_open_dev(thread_, &iodev2);
  dev_stream = iodev2.streams;
  EXPECT_EQ(NULL, dev_stream);
  // Also check the 2 streams on first device remain intact.
  dev_stream = iodev.streams;
  EXPECT_EQ(dev_stream->stream, &rstream);
  EXPECT_EQ(dev_stream->next->stream, &rstream2);

  // Add a stream to the second dev and check it isn't also added to the first.
  thread_add_stream(thread_, &rstream3, &piodev2, 1);
  dev_stream = iodev.streams;
  EXPECT_EQ(dev_stream->stream, &rstream);
  EXPECT_EQ(dev_stream->next->stream, &rstream2);
  EXPECT_EQ(NULL, dev_stream->next->next);
  dev_stream = iodev2.streams;
  EXPECT_EQ(&rstream3, dev_stream->stream);
  EXPECT_EQ(NULL, dev_stream->next);

  // Remove first device from open and streams on second device remain
  // intact.
  thread_rm_open_dev(thread_, &iodev);
  dev_stream = iodev2.streams;
  EXPECT_EQ(&rstream3, dev_stream->stream);
  EXPECT_EQ(NULL, dev_stream->next);

  // Remove 2 streams, check the streams are removed from both open devices.
  dev_io_remove_stream(&thread_->open_devs[rstream.direction],
		       &rstream, &iodev);
  dev_io_remove_stream(&thread_->open_devs[rstream3.direction],
		       &rstream3, &iodev2);
  dev_stream = iodev2.streams;
  EXPECT_EQ(NULL, dev_stream);

  // Remove open devices and check stream is on fallback device.
  thread_rm_open_dev(thread_, &iodev2);

  // Add open device, again check it is empty of streams.
  thread_add_open_dev(thread_, &iodev);
  dev_stream = iodev.streams;
  EXPECT_EQ(NULL, dev_stream);

  thread_rm_open_dev(thread_, &iodev);
  TearDownRstream(&rstream);
  TearDownRstream(&rstream2);
  TearDownRstream(&rstream3);
}

TEST_F(StreamDeviceSuite, WriteOutputSamplesPrepareOutputFailed) {
  struct cras_iodev iodev;
  struct open_dev *adev;

  ResetGlobalStubData();

  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);

  // Add the device.
  thread_add_open_dev(thread_, &iodev);
  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];

  // Assume device is started.
  iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
  // Assume device remains in no stream state;
  cras_iodev_prepare_output_before_write_samples_state = \
      CRAS_IODEV_STATE_NO_STREAM_RUN;

  // Assume there is an error in prepare_output.
  cras_iodev_prepare_output_before_write_samples_ret = -EINVAL;

  // cras_iodev should handle no stream playback.
  EXPECT_EQ(-EINVAL,
	    write_output_samples(&thread_->open_devs[CRAS_STREAM_OUTPUT],
				 adev, nullptr));

  // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
  // called.
  EXPECT_EQ(0, cras_iodev_get_output_buffer_called);

  thread_rm_open_dev(thread_, &iodev);
}

TEST_F(StreamDeviceSuite, WriteOutputSamplesNoStream) {
  struct cras_iodev iodev;
  struct open_dev *adev;

  ResetGlobalStubData();

  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);

  // Add the device.
  thread_add_open_dev(thread_, &iodev);
  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];

  // Assume device is started.
  iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
  // Assume device remains in no stream state;
  cras_iodev_prepare_output_before_write_samples_state = \
      CRAS_IODEV_STATE_NO_STREAM_RUN;

  // cras_iodev should handle no stream playback.
  write_output_samples(&thread_->open_devs[CRAS_STREAM_OUTPUT], adev, nullptr);
  EXPECT_EQ(1, cras_iodev_prepare_output_before_write_samples_called);
  // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
  // called.
  EXPECT_EQ(0, cras_iodev_get_output_buffer_called);

  thread_rm_open_dev(thread_, &iodev);
}

TEST_F(StreamDeviceSuite, WriteOutputSamplesLeaveNoStream) {
  struct cras_iodev iodev;
  struct open_dev *adev;

  ResetGlobalStubData();

  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);

  // Setup the output buffer for device.
  cras_iodev_get_output_buffer_area = cras_audio_area_create(2);

  // Add the device.
  thread_add_open_dev(thread_, &iodev);
  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];

  // Assume device in no stream state.
  iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;

  // Assume device remains in no stream state;
  cras_iodev_prepare_output_before_write_samples_state = \
      CRAS_IODEV_STATE_NO_STREAM_RUN;

  // cras_iodev should NOT leave no stream state;
  write_output_samples(&thread_->open_devs[CRAS_STREAM_OUTPUT], adev, nullptr);
  EXPECT_EQ(1, cras_iodev_prepare_output_before_write_samples_called);
  // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
  // called.
  EXPECT_EQ(0, cras_iodev_get_output_buffer_called);

  // Assume device leaves no stream state;
  cras_iodev_prepare_output_before_write_samples_state = \
      CRAS_IODEV_STATE_NORMAL_RUN;

  // cras_iodev should write samples from streams.
  write_output_samples(&thread_->open_devs[CRAS_STREAM_OUTPUT], adev, nullptr);
  EXPECT_EQ(2, cras_iodev_prepare_output_before_write_samples_called);
  EXPECT_EQ(1, cras_iodev_get_output_buffer_called);

  thread_rm_open_dev(thread_, &iodev);
}

TEST_F(StreamDeviceSuite, DoPlaybackNoStream) {
  struct cras_iodev iodev;

  ResetGlobalStubData();

  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);

  // Add the device.
  thread_add_open_dev(thread_, &iodev);

  // Assume device is started.
  iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
  // Assume device remains in no stream state;
  cras_iodev_prepare_output_before_write_samples_state = \
      CRAS_IODEV_STATE_NO_STREAM_RUN;
  // Add 10 frames in queue to prevent underrun
  frames_queued_ = 10;

  // cras_iodev should handle no stream playback.
  dev_io_playback_write(&thread_->open_devs[CRAS_STREAM_OUTPUT], nullptr);
  EXPECT_EQ(1, cras_iodev_prepare_output_before_write_samples_called);
  // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
  // called.
  EXPECT_EQ(0, cras_iodev_get_output_buffer_called);

  EXPECT_EQ(0, cras_iodev_output_underrun_called);
  // cras_iodev_frames_to_play_in_sleep should be called from
  // update_dev_wakeup_time.
  EXPECT_EQ(1, cras_iodev_frames_to_play_in_sleep_called);

  thread_rm_open_dev(thread_, &iodev);
}

TEST_F(StreamDeviceSuite, DoPlaybackUnderrun) {
  struct cras_iodev iodev, *piodev = &iodev;
  struct cras_rstream rstream;

  ResetGlobalStubData();

  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
  SetupRstream(&rstream, CRAS_STREAM_OUTPUT);

  // Setup the output buffer for device.
  cras_iodev_get_output_buffer_area = cras_audio_area_create(2);

  // Add the device and add the stream.
  thread_add_open_dev(thread_, &iodev);
  thread_add_stream(thread_, &rstream, &piodev, 1);

  // Assume device is running and there is an underrun.
  // It wrote 11 frames into device but new hw_level is only 10.
  // It means underrun may happened because 10 - 11 < 0.
  // Audio thread should ask iodev to handle output underrun.
  iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
  frames_queued_ = 10;
  cras_iodev_all_streams_written_ret = 11;

  // Assume device in normal run stream state;
  cras_iodev_prepare_output_before_write_samples_state = \
      CRAS_IODEV_STATE_NORMAL_RUN;

  EXPECT_EQ(0, cras_iodev_output_underrun_called);
  dev_io_playback_write(&thread_->open_devs[CRAS_STREAM_OUTPUT], nullptr);
  EXPECT_EQ(1, cras_iodev_output_underrun_called);

  thread_rm_open_dev(thread_, &iodev);
  TearDownRstream(&rstream);
}

TEST_F(StreamDeviceSuite, DoPlaybackSevereUnderrun) {
  struct cras_iodev iodev, *piodev = &iodev;
  struct cras_rstream rstream;

  ResetGlobalStubData();

  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
  SetupRstream(&rstream, CRAS_STREAM_OUTPUT);

  // Setup the output buffer for device.
  cras_iodev_get_output_buffer_area = cras_audio_area_create(2);

  // Add the device and add the stream.
  thread_add_open_dev(thread_, &iodev);
  thread_add_stream(thread_, &rstream, &piodev, 1);

  // Assume device is running and there is a severe underrun.
  iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
  frames_queued_ = -EPIPE;

  // Assume device in normal run stream state;
  cras_iodev_prepare_output_before_write_samples_state = \
      CRAS_IODEV_STATE_NORMAL_RUN;

  dev_io_playback_write(&thread_->open_devs[CRAS_STREAM_OUTPUT], nullptr);

  // Audio thread should ask main thread to reset device.
  EXPECT_EQ(1, cras_iodev_reset_request_called);
  EXPECT_EQ(&iodev, cras_iodev_reset_request_iodev);

  thread_rm_open_dev(thread_, &iodev);
  TearDownRstream(&rstream);
}

TEST(AUdioThreadStreams, DrainStream) {
  struct cras_rstream rstream;
  struct cras_audio_shm_area shm_area;
  struct audio_thread thread;

  memset(&rstream, 0, sizeof(rstream));
  memset(&shm_area, 0, sizeof(shm_area));
  rstream.shm.config.frame_bytes = 4;
  shm_area.config.frame_bytes = 4;
  shm_area.config.used_size = 4096 * 4;
  rstream.shm.config.used_size = 4096 * 4;
  rstream.shm.area = &shm_area;
  rstream.format.frame_rate = 48000;
  rstream.direction = CRAS_STREAM_OUTPUT;

  shm_area.write_offset[0] = 1 * 4;
  EXPECT_EQ(1, thread_drain_stream_ms_remaining(&thread, &rstream));

  shm_area.write_offset[0] = 479 * 4;
  EXPECT_EQ(10, thread_drain_stream_ms_remaining(&thread, &rstream));

  shm_area.write_offset[0] = 0;
  EXPECT_EQ(0, thread_drain_stream_ms_remaining(&thread, &rstream));

  rstream.direction = CRAS_STREAM_INPUT;
  shm_area.write_offset[0] = 479 * 4;
  EXPECT_EQ(0, thread_drain_stream_ms_remaining(&thread, &rstream));
}

TEST(BusyloopDetectSuite, CheckerTest) {
  continuous_zero_sleep_count = 0;
  cras_audio_thread_busyloop_called = 0;
  timespec wait_ts;
  wait_ts.tv_sec = 0;
  wait_ts.tv_nsec = 0;

  check_busyloop(&wait_ts);
  EXPECT_EQ(continuous_zero_sleep_count, 1);
  EXPECT_EQ(cras_audio_thread_busyloop_called, 0);
  check_busyloop(&wait_ts);
  EXPECT_EQ(continuous_zero_sleep_count, 2);
  EXPECT_EQ(cras_audio_thread_busyloop_called, 1);
  check_busyloop(&wait_ts);
  EXPECT_EQ(continuous_zero_sleep_count, 3);
  EXPECT_EQ(cras_audio_thread_busyloop_called, 1);

  wait_ts.tv_sec = 1;
  check_busyloop(&wait_ts);
  EXPECT_EQ(continuous_zero_sleep_count, 0);
  EXPECT_EQ(cras_audio_thread_busyloop_called, 1);
}

extern "C" {

int cras_iodev_add_stream(struct cras_iodev *iodev, struct dev_stream *stream)
{
  DL_APPEND(iodev->streams, stream);
  return 0;
}

unsigned int cras_iodev_all_streams_written(struct cras_iodev *iodev)
{
  return cras_iodev_all_streams_written_ret;
}

int cras_iodev_close(struct cras_iodev *iodev)
{
  return 0;
}

void cras_iodev_free_format(struct cras_iodev *iodev)
{
  return;
}

double cras_iodev_get_est_rate_ratio(const struct cras_iodev *iodev)
{
  return 1.0;
}

unsigned int cras_iodev_max_stream_offset(const struct cras_iodev *iodev)
{
  return 0;
}

int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level,
                    const struct cras_audio_format *fmt)
{
  return 0;
}

int cras_iodev_put_buffer(struct cras_iodev *iodev, unsigned int nframes)
{
  return 0;
}

struct dev_stream *cras_iodev_rm_stream(struct cras_iodev *iodev,
                                        const struct cras_rstream *stream)
{
  struct dev_stream *out;
  DL_FOREACH(iodev->streams, out) {
    if (out->stream == stream) {
      DL_DELETE(iodev->streams, out);
      return out;
    }
  }
  return NULL;
}

int cras_iodev_set_format(struct cras_iodev *iodev,
                          const struct cras_audio_format *fmt)
{
  return 0;
}

unsigned int cras_iodev_stream_offset(struct cras_iodev *iodev,
                                      struct dev_stream *stream)
{
  return 0;
}

int dev_stream_attached_devs(const struct dev_stream *dev_stream)
{
  return 1;
}

void cras_iodev_stream_written(struct cras_iodev *iodev,
                               struct dev_stream *stream,
                               unsigned int nwritten)
{
}

int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level,
                           struct timespec *level_tstamp)
{
  return 0;
}

int cras_iodev_put_input_buffer(struct cras_iodev *iodev)
{
  return 0;
}

int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
                                 unsigned int nframes, int* non_empty,
                                 struct cras_fmt_conv *output_converter) {
  cras_iodev_put_output_buffer_called++;
  cras_iodev_put_output_buffer_nframes = nframes;
  return 0;
}

int cras_iodev_get_input_buffer(struct cras_iodev *iodev,
				unsigned *frames)
{
  return 0;
}

int cras_iodev_get_output_buffer(struct cras_iodev *iodev,
				 struct cras_audio_area **area,
				 unsigned *frames)
{
  cras_iodev_get_output_buffer_called++;
  *area = cras_iodev_get_output_buffer_area;
  return 0;
}

int cras_iodev_get_dsp_delay(const struct cras_iodev *iodev)
{
  return 0;
}

void cras_fmt_conv_destroy(struct cras_fmt_conv **conv)
{
}

struct cras_fmt_conv *cras_channel_remix_conv_create(
    unsigned int num_channels,
    const float *coefficient)
{
  return NULL;
}

void cras_rstream_dev_attach(struct cras_rstream *rstream,
                             unsigned int dev_id,
                             void *dev_ptr)
{
}

void cras_rstream_dev_detach(struct cras_rstream *rstream, unsigned int dev_id)
{
}

void cras_rstream_destroy(struct cras_rstream *stream)
{
}

void cras_rstream_dev_offset_update(struct cras_rstream *rstream,
				    unsigned int frames,
				    unsigned int dev_id)
{
  int i = cras_rstream_dev_offset_update_called;
  if (i < MAX_CALLS)  {
    cras_rstream_dev_offset_update_rstream_val[i] = rstream;
    cras_rstream_dev_offset_update_frames_val[i] = frames;
    cras_rstream_dev_offset_update_dev_id_val[i] = dev_id;
    cras_rstream_dev_offset_update_called++;
  }
}

unsigned int cras_rstream_dev_offset(const struct cras_rstream *rstream,
				     unsigned int dev_id)
{
  int i = cras_rstream_dev_offset_called;
  if (i < MAX_CALLS) {
    cras_rstream_dev_offset_rstream_val[i] = rstream;
    cras_rstream_dev_offset_dev_id_val[i] = dev_id;
    cras_rstream_dev_offset_called++;
    return cras_rstream_dev_offset_ret[i];
  }
  return 0;
}

void cras_rstream_record_fetch_interval(struct cras_rstream *rstream,
    const struct timespec *now)
{
}

int cras_set_rt_scheduling(int rt_lim)
{
  return 0;
}

int cras_set_thread_priority(int priority)
{
  return 0;
}

void cras_system_rm_select_fd(int fd)
{
}

unsigned int dev_stream_capture(struct dev_stream *dev_stream,
                                const struct cras_audio_area *area,
                                unsigned int area_offset,
                                float software_gain_scaler)
{
  return 0;
}

unsigned int dev_stream_capture_avail(const struct dev_stream *dev_stream)
{
  return 0;

}
unsigned int dev_stream_cb_threshold(const struct dev_stream *dev_stream)
{
  return 0;
}

int dev_stream_capture_update_rstream(struct dev_stream *dev_stream)
{
  return 0;
}

struct dev_stream *dev_stream_create(struct cras_rstream *stream,
                                     unsigned int dev_id,
                                     const struct cras_audio_format *dev_fmt,
                                     void *dev_ptr, struct timespec *cb_ts)
{
  struct dev_stream *out = static_cast<dev_stream*>(calloc(1, sizeof(*out)));
  out->stream = stream;
  return out;
}

void dev_stream_destroy(struct dev_stream *dev_stream)
{
  free(dev_stream);
}

int dev_stream_mix(struct dev_stream *dev_stream,
		   const struct cras_audio_format *fmt,
                   uint8_t *dst,
                   unsigned int num_to_write)
{
  return num_to_write;
}

int dev_stream_playback_frames(const struct dev_stream *dev_stream)
{
  return dev_stream_playback_frames_ret;
}

int dev_stream_playback_update_rstream(struct dev_stream *dev_stream)
{
  return 0;
}

int dev_stream_poll_stream_fd(const struct dev_stream *dev_stream)
{
  return dev_stream->stream->fd;
}

int dev_stream_can_fetch(struct dev_stream *dev_stream)
{
  return 1;
}

int dev_stream_request_playback_samples(struct dev_stream *dev_stream,
                                        const struct timespec *now)
{
  return 0;
}

void dev_stream_set_delay(const struct dev_stream *dev_stream,
                          unsigned int delay_frames)
{
}

void dev_stream_set_dev_rate(struct dev_stream *dev_stream,
                             unsigned int dev_rate,
                             double dev_rate_ratio,
                             double master_rate_ratio,
                             int coarse_rate_adjust)
{
}

void dev_stream_update_frames(const struct dev_stream *dev_stream)
{
}

int dev_stream_wake_time(struct dev_stream *dev_stream,
                         unsigned int curr_level,
                         struct timespec *level_tstamp,
                         unsigned int cap_limit,
                         int is_cap_limit_stream,
                         struct timespec *wake_time)
{
  if (dev_stream_wake_time_val.find(dev_stream) !=
      dev_stream_wake_time_val.end()) {
    wake_time->tv_sec = dev_stream_wake_time_val[dev_stream].tv_sec;
    wake_time->tv_nsec = dev_stream_wake_time_val[dev_stream].tv_nsec;
  }
  return 0;
}

int dev_stream_is_pending_reply(const struct dev_stream *dev_stream)
{
  return 0;
}

int dev_stream_flush_old_audio_messages(struct dev_stream *dev_stream)
{
  return 0;
}

int cras_iodev_frames_queued(struct cras_iodev *iodev, struct timespec *tstamp)
{
	return iodev->frames_queued(iodev, tstamp);
}

int cras_iodev_buffer_avail(struct cras_iodev *iodev, unsigned hw_level)
{
  struct timespec tstamp;
  return iodev->buffer_size - iodev->frames_queued(iodev, &tstamp);
}

int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames)
{
  cras_iodev_fill_odev_zeros_frames = frames;
  return 0;
}

int cras_iodev_output_underrun(struct cras_iodev *odev)
{
  cras_iodev_output_underrun_called++;
  return 0;
}

int cras_iodev_prepare_output_before_write_samples(struct cras_iodev *odev)
{
  cras_iodev_prepare_output_before_write_samples_called++;
  odev->state = cras_iodev_prepare_output_before_write_samples_state;
  return cras_iodev_prepare_output_before_write_samples_ret;
}

int cras_server_metrics_highest_hw_level(unsigned hw_level,
		enum CRAS_STREAM_DIRECTION direction)
{
  return 0;
}

int cras_server_metrics_longest_fetch_delay(int delay_msec)
{
  return 0;
}

int cras_server_metrics_num_underruns(unsigned num_underruns)
{
  return 0;
}

float cras_iodev_get_software_gain_scaler(const struct cras_iodev *iodev)
{
  return 1.0f;
}

unsigned int cras_iodev_frames_to_play_in_sleep(struct cras_iodev *odev,
                                                unsigned int *hw_level,
                                                struct timespec *hw_tstamp)
{
  *hw_level = cras_iodev_frames_queued(odev, hw_tstamp);
  cras_iodev_frames_to_play_in_sleep_called++;
  return 0;
}

int cras_iodev_odev_should_wake(const struct cras_iodev *odev)
{
  return 1;
}

struct cras_audio_area *cras_audio_area_create(int num_channels)
{
  struct cras_audio_area *area;
  size_t sz;

  sz = sizeof(*area) + num_channels * sizeof(struct cras_channel_area);
  area = (cras_audio_area *)calloc(1, sz);
  area->num_channels = num_channels;
  area->channels[0].buf = (uint8_t*)calloc(1, BUFFER_SIZE * 2 * num_channels);

  return area;
}

enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev)
{
  return iodev->state;
}

unsigned int cras_iodev_get_num_underruns(const struct cras_iodev *iodev)
{
  return 0;
}

int cras_iodev_reset_request(struct cras_iodev *iodev)
{
  cras_iodev_reset_request_called++;
  cras_iodev_reset_request_iodev = iodev;
  return 0;
}

unsigned int cras_iodev_get_num_severe_underruns(const struct cras_iodev *iodev)
{
  return 0;
}

void cras_iodev_update_highest_hw_level(struct cras_iodev *iodev,
		unsigned int hw_level)
{
}

int cras_iodev_start_ramp(struct cras_iodev *odev,
                          enum CRAS_IODEV_RAMP_REQUEST request)
{
  cras_iodev_start_ramp_odev = odev;
  cras_iodev_start_ramp_request = request;
  return 0;
}

int input_data_get_for_stream(
		struct input_data *data,
		struct cras_rstream *stream,
		struct buffer_share *offsets,
		struct cras_audio_area **area,
		unsigned int *offset)
{
  return 0;
}

int input_data_put_for_stream(struct input_data *data,
			   struct cras_rstream *stream,
			   struct buffer_share *offsets,
			   unsigned int frames)
{
  return 0;
}

#ifdef HAVE_WEBRTC_APM

uint64_t cras_apm_list_get_effects(struct cras_apm_list *list)
{
  return 0;
}

void cras_apm_list_set_debug_recording(struct cras_apm *apm,
    unsigned int stream_id, int start, const char *file_name_base)
{
}
void cras_apm_list_set_aec_dump(struct cras_apm_list *list,
				void *dev_ptr,
			        int start,
			        int fd)
{
}

#endif

int cras_audio_thread_busyloop()
{
  cras_audio_thread_busyloop_called ++;
  return 0;
}

}  // extern "C"

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}