text: /* Copyright 2013 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.
   */
  
  #include <gtest/gtest.h>
  #include <stdint.h>
  #include <time.h>
  
  extern "C" {
    #include "cras_hfp_info.c"
  }
  
  static struct hfp_info *info;
  static struct cras_iodev dev;
  static cras_audio_format format;
  
  static thread_callback thread_cb;
  static void *cb_data;
  static timespec ts;
  
  void ResetStubData() {
    format.format = SND_PCM_FORMAT_S16_LE;
    format.num_channels = 1;
    format.frame_rate = 8000;
    dev.format = &format;
  }
  
  namespace {
  
  TEST(HfpInfo, AddRmDev) {
    info = hfp_info_create();
    ASSERT_NE(info, (void *)NULL);
    dev.direction = CRAS_STREAM_OUTPUT;
  
    /* Test add dev */
    ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
    ASSERT_TRUE(hfp_info_has_iodev(info));
  
    /* Test remove dev */
    ASSERT_EQ(0, hfp_info_rm_iodev(info, &dev));
    ASSERT_FALSE(hfp_info_has_iodev(info));
  
    hfp_info_destroy(info);
  }
  
  TEST(HfpInfo, AddRmDevInvalid) {
    info = hfp_info_create();
    ASSERT_NE(info, (void *)NULL);
  
    dev.direction = CRAS_STREAM_OUTPUT;
  
    /* Remove an iodev which doesn't exist */
    ASSERT_NE(0, hfp_info_rm_iodev(info, &dev));
  
    /* Adding an iodev twice returns error code */
    ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
    ASSERT_NE(0, hfp_info_add_iodev(info, &dev));
  
    hfp_info_destroy(info);
  }
  
  TEST(HfpInfo, AcquirePlaybackBuffer) {
    unsigned buffer_frames, buffer_frames2, queued;
    uint8_t *samples;
  
    ResetStubData();
  
    info = hfp_info_create();
    ASSERT_NE(info, (void *)NULL);
  
    hfp_info_start(1, 48, info);
    dev.direction = CRAS_STREAM_OUTPUT;
    ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
  
    buffer_frames = 500;
    hfp_buf_acquire(info, &dev, &samples, &buffer_frames);
    ASSERT_EQ(500, buffer_frames);
  
    hfp_buf_release(info, &dev, 500);
    ASSERT_EQ(500, hfp_buf_queued(info, &dev));
  
    /* Assert the amount of frames of available buffer + queued buf is
     * greater than or equal to the buffer size, 2 bytes per frame
     */
    queued = hfp_buf_queued(info, &dev);
    buffer_frames = 500;
    hfp_buf_acquire(info, &dev, &samples, &buffer_frames);
    ASSERT_GE(info->playback_buf->used_size / 2, buffer_frames + queued);
  
    /* Consume all queued data from read buffer */
    buf_increment_read(info->playback_buf, queued * 2);
  
    queued = hfp_buf_queued(info, &dev);
    ASSERT_EQ(0, queued);
  
    /* Assert consecutive acquire buffer will acquire full used size of buffer */
    buffer_frames = 500;
    hfp_buf_acquire(info, &dev, &samples, &buffer_frames);
    hfp_buf_release(info, &dev, buffer_frames);
  
    buffer_frames2 = 500;
    hfp_buf_acquire(info, &dev, &samples, &buffer_frames2);
    hfp_buf_release(info, &dev, buffer_frames2);
  
    ASSERT_GE(info->playback_buf->used_size / 2, buffer_frames + buffer_frames2);
  
    hfp_info_destroy(info);
  }
  
  TEST(HfpInfo, AcquireCaptureBuffer) {
    unsigned buffer_frames, buffer_frames2;
    uint8_t *samples;
  
    ResetStubData();
  
    info = hfp_info_create();
    ASSERT_NE(info, (void *)NULL);
  
    hfp_info_start(1, 48, info);
    dev.direction = CRAS_STREAM_INPUT;
    ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
  
    /* Put fake data 100 bytes(50 frames) in capture buf for test */
    buf_increment_write(info->capture_buf, 100);
  
    /* Assert successfully acquire and release 100 bytes of data */
    buffer_frames = 50;
    hfp_buf_acquire(info, &dev, &samples, &buffer_frames);
    ASSERT_EQ(50, buffer_frames);
  
    hfp_buf_release(info, &dev, buffer_frames);
    ASSERT_EQ(0, hfp_buf_queued(info, &dev));
  
    /* Push fake data to capture buffer */
    buf_increment_write(info->capture_buf, info->capture_buf->used_size - 100);
    buf_increment_write(info->capture_buf, 100);
  
    /* Assert consecutive acquire call will consume the whole buffer */
    buffer_frames = 1000;
    hfp_buf_acquire(info, &dev, &samples, &buffer_frames);
    hfp_buf_release(info, &dev, buffer_frames);
    ASSERT_GE(1000, buffer_frames);
  
    buffer_frames2 = 1000;
    hfp_buf_acquire(info, &dev, &samples, &buffer_frames2);
    hfp_buf_release(info, &dev, buffer_frames2);
  
    ASSERT_GE(info->capture_buf->used_size / 2, buffer_frames + buffer_frames2);
  
    hfp_info_destroy(info);
  }
  
  TEST(HfpInfo, HfpReadWriteFD) {
    int rc;
    int sock[2];
    uint8_t sample[480];
    uint8_t *buf;
    unsigned buffer_count;
  
    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
  
    info = hfp_info_create();
    ASSERT_NE(info, (void *)NULL);
  
    dev.direction = CRAS_STREAM_INPUT;
    hfp_info_start(sock[1], 48, info);
    ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
  
    /* Mock the sco fd and send some fake data */
    send(sock[0], sample, 48, 0);
  
    rc = hfp_read(info);
    ASSERT_EQ(48, rc);
  
    rc = hfp_buf_queued(info, &dev);
    ASSERT_EQ(48 / 2, rc);
  
    /* Fill the write buffer*/
    buffer_count = info->capture_buf->used_size;
    buf = buf_write_pointer_size(info->capture_buf, &buffer_count);
    buf_increment_write(info->capture_buf, buffer_count);
    ASSERT_NE((void *)NULL, buf);
  
    rc = hfp_read(info);
    ASSERT_EQ(0, rc);
  
    ASSERT_EQ(0, hfp_info_rm_iodev(info, &dev));
    dev.direction = CRAS_STREAM_OUTPUT;
    ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
  
    /* Initial buffer is empty */
    rc = hfp_write(info);
    ASSERT_EQ(0, rc);
  
    buffer_count = 1024;
    buf = buf_write_pointer_size(info->playback_buf, &buffer_count);
    buf_increment_write(info->playback_buf, buffer_count);
  
    rc = hfp_write(info);
    ASSERT_EQ(48, rc);
  
    rc = recv(sock[0], sample, 48, 0);
    ASSERT_EQ(48, rc);
  
    hfp_info_destroy(info);
  }
  
  TEST(HfpInfo, StartHfpInfo) {
    int sock[2];
  
    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
  
    info = hfp_info_create();
    ASSERT_NE(info, (void *)NULL);
  
    hfp_info_start(sock[0], 48, info);
    ASSERT_EQ(1, hfp_info_running(info));
    ASSERT_EQ(cb_data, (void *)info);
  
    hfp_info_stop(info);
    ASSERT_EQ(0, hfp_info_running(info));
    ASSERT_EQ(NULL, cb_data);
  
    hfp_info_destroy(info);
  }
  
  TEST(HfpInfo, StartHfpInfoAndRead) {
    int rc;
    int sock[2];
    uint8_t sample[480];
  
    ResetStubData();
  
    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
  
    info = hfp_info_create();
    ASSERT_NE(info, (void *)NULL);
  
    /* Start and send two chunk of fake data */
    hfp_info_start(sock[1], 48, info);
    send(sock[0], sample ,48, 0);
    send(sock[0], sample ,48, 0);
  
    /* Trigger thread callback */
    thread_cb((struct hfp_info *)cb_data);
  
    dev.direction = CRAS_STREAM_INPUT;
    ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
  
    /* Expect no data read, since no idev present at previous thread callback */
    rc = hfp_buf_queued(info, &dev);
    ASSERT_EQ(0, rc);
  
    /* Trigger thread callback after idev added. */
    ts.tv_sec = 0;
    ts.tv_nsec = 5000000;
    thread_cb((struct hfp_info *)cb_data);
  
    rc = hfp_buf_queued(info, &dev);
    ASSERT_EQ(48 / 2, rc);
  
    /* Assert wait time is unchanged. */
    ASSERT_EQ(0, ts.tv_sec);
    ASSERT_EQ(5000000, ts.tv_nsec);
  
    hfp_info_stop(info);
    ASSERT_EQ(0, hfp_info_running(info));
  
    hfp_info_destroy(info);
  }
  
  TEST(HfpInfo, StartHfpInfoAndWrite) {
    int rc;
    int sock[2];
    uint8_t sample[480];
  
    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
  
    info = hfp_info_create();
    ASSERT_NE(info, (void *)NULL);
  
    hfp_info_start(sock[1], 48, info);
    send(sock[0], sample ,48, 0);
    send(sock[0], sample ,48, 0);
  
    /* Trigger thread callback */
    thread_cb((struct hfp_info *)cb_data);
  
    dev.direction = CRAS_STREAM_OUTPUT;
    ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
  
    /* Assert queued samples unchanged before output device added */
    ASSERT_EQ(0, hfp_buf_queued(info, &dev));
  
    /* Put some fake data and trigger thread callback again */
    buf_increment_write(info->playback_buf, 1008);
    thread_cb((struct hfp_info *)cb_data);
  
    /* Assert some samples written */
    rc = recv(sock[0], sample ,48, 0);
    ASSERT_EQ(48, rc);
    ASSERT_EQ(480, hfp_buf_queued(info, &dev));
  
    hfp_info_stop(info);
    hfp_info_destroy(info);
  }
  
  } // namespace
  
  extern "C" {
  
  struct audio_thread *cras_iodev_list_get_audio_thread()
  {
    return NULL;
  }
  
  void audio_thread_add_callback(int fd, thread_callback cb,
                                 void *data)
  {
    thread_cb = cb;
    cb_data = data;
    return;
  }
  
  int audio_thread_rm_callback_sync(struct audio_thread *thread, int fd)
  {
    thread_cb = NULL;
    cb_data = NULL;
    return 0;
  }
  }
  
  int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
  }
  