/* 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(); }