// Copyright (c) 2012 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 <stdio.h> #include <gtest/gtest.h> extern "C" { #include "cras_shm.h" #include "cras_types.h" } namespace { class ShmTestSuite : public testing::Test{ protected: virtual void SetUp() { memset(&shm_, 0, sizeof(shm_)); shm_.area = static_cast<cras_audio_shm_area *>( calloc(1, sizeof(*shm_.area) + 2048)); cras_shm_set_frame_bytes(&shm_, 4); cras_shm_set_used_size(&shm_, 1024); memcpy(&shm_.area->config, &shm_.config, sizeof(shm_.config)); frames_ = 0; } virtual void TearDown() { free(shm_.area); } struct cras_audio_shm shm_; uint8_t *buf_; size_t frames_; }; // Test that and empty buffer returns 0 readable bytes. TEST_F(ShmTestSuite, NoneReadableWhenEmpty) { buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(0, frames_); cras_shm_buffer_read(&shm_, frames_); EXPECT_EQ(0, shm_.area->read_offset[0]); } // Buffer with 100 frames filled. TEST_F(ShmTestSuite, OneHundredFilled) { shm_.area->write_offset[0] = 100 * shm_.area->config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(100, frames_); EXPECT_EQ(shm_.area->samples, buf_); cras_shm_buffer_read(&shm_, frames_ - 9); EXPECT_EQ((frames_ - 9) * shm_.config.frame_bytes, shm_.area->read_offset[0]); cras_shm_buffer_read(&shm_, 9); EXPECT_EQ(0, shm_.area->read_offset[0]); EXPECT_EQ(1, shm_.area->read_buf_idx); } // Buffer with 100 frames filled, 50 read. TEST_F(ShmTestSuite, OneHundredFilled50Read) { shm_.area->write_offset[0] = 100 * shm_.config.frame_bytes; shm_.area->read_offset[0] = 50 * shm_.config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(50, frames_); EXPECT_EQ((shm_.area->samples + shm_.area->read_offset[0]), buf_); cras_shm_buffer_read(&shm_, frames_ - 10); EXPECT_EQ(shm_.area->write_offset[0] - 10 * shm_.config.frame_bytes, shm_.area->read_offset[0]); cras_shm_buffer_read(&shm_, 10); EXPECT_EQ(0, shm_.area->read_offset[0]); } // Buffer with 100 frames filled, 50 read, offset by 25. TEST_F(ShmTestSuite, OneHundredFilled50Read25offset) { shm_.area->write_offset[0] = 100 * shm_.config.frame_bytes; shm_.area->read_offset[0] = 50 * shm_.config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 25, &frames_); EXPECT_EQ(25, frames_); EXPECT_EQ(shm_.area->samples + shm_.area->read_offset[0] + 25 * shm_.area->config.frame_bytes, (uint8_t *)buf_); } // Test wrapping across buffers. TEST_F(ShmTestSuite, WrapToNextBuffer) { shm_.config.used_size = 480 * shm_.config.frame_bytes; shm_.area->write_offset[0] = 240 * shm_.config.frame_bytes; shm_.area->read_offset[0] = 120 * shm_.config.frame_bytes; shm_.area->write_offset[1] = 240 * shm_.config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(120, frames_); EXPECT_EQ(shm_.area->samples + shm_.area->read_offset[0], (uint8_t *)buf_); buf_ = cras_shm_get_readable_frames(&shm_, frames_, &frames_); EXPECT_EQ(240, frames_); EXPECT_EQ(shm_.area->samples + shm_.config.used_size, (uint8_t *)buf_); cras_shm_buffer_read(&shm_, 350); /* Mark all-10 as read */ EXPECT_EQ(0, shm_.area->read_offset[0]); EXPECT_EQ(230 * shm_.config.frame_bytes, shm_.area->read_offset[1]); } // Test wrapping across buffers reading all samples. TEST_F(ShmTestSuite, WrapToNextBufferReadAll) { shm_.config.used_size = 480 * shm_.config.frame_bytes; shm_.area->write_offset[0] = 240 * shm_.config.frame_bytes; shm_.area->read_offset[0] = 120 * shm_.config.frame_bytes; shm_.area->write_offset[1] = 240 * shm_.config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(120, frames_); EXPECT_EQ(shm_.area->samples + shm_.area->read_offset[0], (uint8_t *)buf_); buf_ = cras_shm_get_readable_frames(&shm_, frames_, &frames_); EXPECT_EQ(240, frames_); EXPECT_EQ(shm_.area->samples + shm_.config.used_size, (uint8_t *)buf_); cras_shm_buffer_read(&shm_, 360); /* Mark all as read */ EXPECT_EQ(0, shm_.area->read_offset[0]); EXPECT_EQ(0, shm_.area->read_offset[1]); EXPECT_EQ(0, shm_.area->read_buf_idx); } // Test wrapping last buffer. TEST_F(ShmTestSuite, WrapFromFinalBuffer) { shm_.area->read_buf_idx = CRAS_NUM_SHM_BUFFERS - 1; shm_.config.used_size = 480 * shm_.config.frame_bytes; shm_.area->write_offset[shm_.area->read_buf_idx] = 240 * shm_.config.frame_bytes; shm_.area->read_offset[shm_.area->read_buf_idx] = 120 * shm_.config.frame_bytes; shm_.area->write_offset[0] = 240 * shm_.config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(120, frames_); EXPECT_EQ((uint8_t *)buf_, shm_.area->samples + shm_.config.used_size * shm_.area->read_buf_idx + shm_.area->read_offset[shm_.area->read_buf_idx]); buf_ = cras_shm_get_readable_frames(&shm_, frames_, &frames_); EXPECT_EQ(240, frames_); EXPECT_EQ(shm_.area->samples, (uint8_t *)buf_); cras_shm_buffer_read(&shm_, 350); /* Mark all-10 as read */ EXPECT_EQ(0, shm_.area->read_offset[1]); EXPECT_EQ(230 * shm_.config.frame_bytes, shm_.area->read_offset[0]); } // Test Check available to write returns 0 if not free buffer. TEST_F(ShmTestSuite, WriteAvailNotFree) { size_t ret; shm_.area->write_buf_idx = 0; shm_.area->write_offset[0] = 100 * shm_.config.frame_bytes; shm_.area->read_offset[0] = 50 * shm_.config.frame_bytes; ret = cras_shm_get_num_writeable(&shm_); EXPECT_EQ(0, ret); } // Test Check available to write returns num_frames if free buffer. TEST_F(ShmTestSuite, WriteAvailValid) { size_t ret; shm_.area->write_buf_idx = 0; shm_.config.used_size = 480 * shm_.config.frame_bytes; shm_.area->write_offset[0] = 0; shm_.area->read_offset[0] = 0; ret = cras_shm_get_num_writeable(&shm_); EXPECT_EQ(480, ret); } // Test get frames_written returns the number of frames written. TEST_F(ShmTestSuite, GetNumWritten) { size_t ret; shm_.area->write_buf_idx = 0; shm_.config.used_size = 480 * shm_.config.frame_bytes; shm_.area->write_offset[0] = 200 * shm_.config.frame_bytes; shm_.area->read_offset[0] = 0; ret = cras_shm_frames_written(&shm_); EXPECT_EQ(200, ret); } // Test that getting the base of the write buffer returns the correct pointer. TEST_F(ShmTestSuite, GetWriteBufferBase) { uint8_t* ret; shm_.area->write_buf_idx = 0; shm_.config.used_size = 480 * shm_.config.frame_bytes; shm_.area->write_offset[0] = 200 * shm_.config.frame_bytes; shm_.area->write_offset[1] = 200 * shm_.config.frame_bytes; shm_.area->read_offset[0] = 0; shm_.area->read_offset[1] = 0; ret = cras_shm_get_write_buffer_base(&shm_); EXPECT_EQ(shm_.area->samples, ret); shm_.area->write_buf_idx = 1; ret = cras_shm_get_write_buffer_base(&shm_); EXPECT_EQ(shm_.area->samples + shm_.config.used_size, ret); } TEST_F(ShmTestSuite, SetVolume) { cras_shm_set_volume_scaler(&shm_, 1.0); EXPECT_EQ(shm_.area->volume_scaler, 1.0); cras_shm_set_volume_scaler(&shm_, 1.4); EXPECT_EQ(shm_.area->volume_scaler, 1.0); cras_shm_set_volume_scaler(&shm_, -0.5); EXPECT_EQ(shm_.area->volume_scaler, 0.0); cras_shm_set_volume_scaler(&shm_, 0.5); EXPECT_EQ(shm_.area->volume_scaler, 0.5); } // Test that invalid read/write offsets are detected. TEST_F(ShmTestSuite, InvalidWriteOffset) { shm_.area->write_offset[0] = shm_.config.used_size + 50; shm_.area->read_offset[0] = 0; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(shm_.config.used_size / 4, frames_); } TEST_F(ShmTestSuite, InvalidReadOffset) { // Should ignore read+_offset and assume 0. shm_.area->write_offset[0] = 44; shm_.area->read_offset[0] = shm_.config.used_size + 25; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(shm_.area->write_offset[0] / shm_.config.frame_bytes, frames_); EXPECT_EQ(shm_.area->samples, (uint8_t *)buf_); } TEST_F(ShmTestSuite, InvalidReadAndWriteOffset) { shm_.area->write_offset[0] = shm_.config.used_size + 50; shm_.area->read_offset[0] = shm_.config.used_size + 25; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(shm_.config.used_size / 4, frames_); } TEST_F(ShmTestSuite, InputBufferOverrun) { int rc; shm_.area->write_offset[0] = 0; shm_.area->read_offset[0] = 0; shm_.area->write_offset[1] = 0; shm_.area->read_offset[1] = 0; shm_.area->write_buf_idx = 0; shm_.area->read_buf_idx = 0; EXPECT_EQ(0, cras_shm_num_overruns(&shm_)); rc = cras_shm_check_write_overrun(&shm_); EXPECT_EQ(0, rc); cras_shm_buffer_written(&shm_, 100); cras_shm_buffer_write_complete(&shm_); rc = cras_shm_check_write_overrun(&shm_); EXPECT_EQ(0, rc); cras_shm_buffer_written(&shm_, 100); cras_shm_buffer_write_complete(&shm_); // Assert two consecutive writes causes overrun. rc = cras_shm_check_write_overrun(&shm_); EXPECT_EQ(1, rc); EXPECT_EQ(1, cras_shm_num_overruns(&shm_)); } TEST_F(ShmTestSuite, GetWritableFramesNeedToWrite) { unsigned buffer_size = 480; unsigned limit = 480; unsigned written = 200; unsigned frames; shm_.area->write_buf_idx = 0; shm_.config.used_size = buffer_size * shm_.config.frame_bytes; shm_.area->write_offset[0] = written * shm_.config.frame_bytes; buf_ = cras_shm_get_writeable_frames(&shm_, limit, &frames); EXPECT_EQ(limit - written, frames); EXPECT_EQ(shm_.area->samples + shm_.area->write_offset[0], buf_); } TEST_F(ShmTestSuite, GetWritableFramesNoNeedToWrite) { unsigned buffer_size = 480; unsigned limit = 240; unsigned written = 300; unsigned frames; shm_.area->write_buf_idx = 0; shm_.config.used_size = buffer_size * shm_.config.frame_bytes; shm_.area->write_offset[0] = written * shm_.config.frame_bytes; buf_ = cras_shm_get_writeable_frames(&shm_, limit, &frames); EXPECT_EQ(0, frames); EXPECT_EQ(shm_.area->samples + shm_.area->write_offset[0], buf_); } } // namespace int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }