// 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 #include 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_.header = static_cast(calloc(1, sizeof(*shm_.header))); shm_.samples = static_cast(calloc(1, 2048)); shm_.samples_info.length = 2048; cras_shm_set_frame_bytes(&shm_, 4); cras_shm_set_used_size(&shm_, 1024); memcpy(&shm_.header->config, &shm_.config, sizeof(shm_.config)); frames_ = 0; } virtual void TearDown() { free(shm_.header); free(shm_.samples); } 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_.header->read_offset[0]); } // Buffer with 100 frames filled. TEST_F(ShmTestSuite, OneHundredFilled) { shm_.header->write_offset[0] = 100 * shm_.header->config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(100, frames_); EXPECT_EQ(shm_.samples, buf_); cras_shm_buffer_read(&shm_, frames_ - 9); EXPECT_EQ((frames_ - 9) * shm_.config.frame_bytes, shm_.header->read_offset[0]); cras_shm_buffer_read(&shm_, 9); EXPECT_EQ(0, shm_.header->read_offset[0]); EXPECT_EQ(1, shm_.header->read_buf_idx); } // Buffer with 100 frames filled, 50 read. TEST_F(ShmTestSuite, OneHundredFilled50Read) { shm_.header->write_offset[0] = 100 * shm_.config.frame_bytes; shm_.header->read_offset[0] = 50 * shm_.config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(50, frames_); EXPECT_EQ((shm_.samples + shm_.header->read_offset[0]), buf_); cras_shm_buffer_read(&shm_, frames_ - 10); EXPECT_EQ(shm_.header->write_offset[0] - 10 * shm_.config.frame_bytes, shm_.header->read_offset[0]); cras_shm_buffer_read(&shm_, 10); EXPECT_EQ(0, shm_.header->read_offset[0]); } // Buffer with 100 frames filled, 50 read, offset by 25. TEST_F(ShmTestSuite, OneHundredFilled50Read25offset) { shm_.header->write_offset[0] = 100 * shm_.config.frame_bytes; shm_.header->read_offset[0] = 50 * shm_.config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 25, &frames_); EXPECT_EQ(25, frames_); EXPECT_EQ(shm_.samples + shm_.header->read_offset[0] + 25 * shm_.header->config.frame_bytes, (uint8_t*)buf_); } // Test wrapping across buffers. TEST_F(ShmTestSuite, WrapToNextBuffer) { uint32_t used_size = cras_shm_used_size(&shm_); uint32_t used_frames = used_size / cras_shm_frame_bytes(&shm_); shm_.header->write_offset[0] = (used_size / 2); shm_.header->read_offset[0] = (used_size / 4); shm_.header->write_offset[1] = (used_size / 2); buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(used_frames / 4, frames_); EXPECT_EQ(cras_shm_buff_for_idx(&shm_, 0) + (used_size / 4), (uint8_t*)buf_); buf_ = cras_shm_get_readable_frames(&shm_, frames_, &frames_); EXPECT_EQ(used_frames / 2, frames_); EXPECT_EQ(cras_shm_buff_for_idx(&shm_, 1), (uint8_t*)buf_); /* Mark all but 10 frames as read */ cras_shm_buffer_read(&shm_, (used_frames / 2) + (used_frames / 4) - 10); EXPECT_EQ(0, shm_.header->read_offset[0]); EXPECT_EQ(((used_frames / 2) - 10) * shm_.config.frame_bytes, shm_.header->read_offset[1]); EXPECT_EQ(1, shm_.header->read_buf_idx); } // Test wrapping across buffers reading all samples. TEST_F(ShmTestSuite, WrapToNextBufferReadAll) { uint32_t used_size = cras_shm_used_size(&shm_); uint32_t used_frames = used_size / cras_shm_frame_bytes(&shm_); shm_.header->write_offset[0] = (used_size / 2); shm_.header->read_offset[0] = (used_size / 4); shm_.header->write_offset[1] = (used_size / 2); buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(used_frames / 4, frames_); EXPECT_EQ(cras_shm_buff_for_idx(&shm_, 0) + (used_size / 4), (uint8_t*)buf_); buf_ = cras_shm_get_readable_frames(&shm_, frames_, &frames_); EXPECT_EQ(used_frames / 2, frames_); EXPECT_EQ(cras_shm_buff_for_idx(&shm_, 1), (uint8_t*)buf_); /* Mark all frames as read */ cras_shm_buffer_read(&shm_, (used_frames / 2) + (used_frames / 4)); EXPECT_EQ(0, shm_.header->read_offset[0]); EXPECT_EQ(0, shm_.header->read_offset[1]); EXPECT_EQ(0, shm_.header->read_buf_idx); } // Test wrapping last buffer. TEST_F(ShmTestSuite, WrapFromFinalBuffer) { uint32_t used_size = cras_shm_used_size(&shm_); uint32_t used_frames = used_size / cras_shm_frame_bytes(&shm_); uint32_t buf_idx = CRAS_NUM_SHM_BUFFERS - 1; shm_.header->read_buf_idx = buf_idx; shm_.header->write_offset[buf_idx] = (used_size / 2); shm_.header->read_offset[buf_idx] = (used_size / 4); shm_.header->write_offset[0] = (used_size / 2); buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(used_frames / 4, frames_); EXPECT_EQ(cras_shm_buff_for_idx(&shm_, buf_idx) + (used_size / 4), (uint8_t*)buf_); buf_ = cras_shm_get_readable_frames(&shm_, frames_, &frames_); EXPECT_EQ(used_frames / 2, frames_); EXPECT_EQ(cras_shm_buff_for_idx(&shm_, 0), (uint8_t*)buf_); /* Mark all but 10 frames as read */ cras_shm_buffer_read(&shm_, (used_frames / 2) + (used_frames / 4) - 10); EXPECT_EQ(0, shm_.header->read_offset[buf_idx]); EXPECT_EQ(((used_frames / 2) - 10) * shm_.config.frame_bytes, shm_.header->read_offset[0]); EXPECT_EQ(0, shm_.header->read_buf_idx); } // Test Check available to write returns 0 if not free buffer. TEST_F(ShmTestSuite, WriteAvailNotFree) { size_t ret; shm_.header->write_buf_idx = 0; shm_.header->write_offset[0] = 100 * shm_.config.frame_bytes; shm_.header->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_.header->write_buf_idx = 0; shm_.config.used_size = 480 * shm_.config.frame_bytes; shm_.header->write_offset[0] = 0; shm_.header->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_.header->write_buf_idx = 0; shm_.config.used_size = 480 * shm_.config.frame_bytes; shm_.header->write_offset[0] = 200 * shm_.config.frame_bytes; shm_.header->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_.header->write_buf_idx = 0; shm_.header->write_offset[0] = 128 * shm_.config.frame_bytes; shm_.header->write_offset[1] = 128 * shm_.config.frame_bytes; shm_.header->read_offset[0] = 0; shm_.header->read_offset[1] = 0; ret = cras_shm_get_write_buffer_base(&shm_); EXPECT_EQ(shm_.samples, ret); shm_.header->write_buf_idx = 1; ret = cras_shm_get_write_buffer_base(&shm_); EXPECT_EQ(shm_.samples + shm_.config.used_size, ret); } TEST_F(ShmTestSuite, SetVolume) { cras_shm_set_volume_scaler(&shm_, 1.0); EXPECT_EQ(shm_.header->volume_scaler, 1.0); cras_shm_set_volume_scaler(&shm_, 1.4); EXPECT_EQ(shm_.header->volume_scaler, 1.0); cras_shm_set_volume_scaler(&shm_, -0.5); EXPECT_EQ(shm_.header->volume_scaler, 0.0); cras_shm_set_volume_scaler(&shm_, 0.5); EXPECT_EQ(shm_.header->volume_scaler, 0.5); } // Test that invalid read/write offsets are detected. TEST_F(ShmTestSuite, InvalidWriteOffset) { shm_.header->write_offset[0] = shm_.config.used_size + 50; shm_.header->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_.header->write_offset[0] = 44; shm_.header->read_offset[0] = shm_.config.used_size + 25; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(shm_.header->write_offset[0] / shm_.config.frame_bytes, frames_); EXPECT_EQ(shm_.samples, (uint8_t*)buf_); } TEST_F(ShmTestSuite, InvalidReadAndWriteOffset) { shm_.header->write_offset[0] = shm_.config.used_size + 50; shm_.header->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_.header->write_offset[0] = 0; shm_.header->read_offset[0] = 0; shm_.header->write_offset[1] = 0; shm_.header->read_offset[1] = 0; shm_.header->write_buf_idx = 0; shm_.header->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_.header->write_buf_idx = 0; shm_.config.used_size = buffer_size * shm_.config.frame_bytes; shm_.header->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_.samples + shm_.header->write_offset[0], buf_); } TEST_F(ShmTestSuite, GetWritableFramesNoNeedToWrite) { unsigned buffer_size = 480; unsigned limit = 240; unsigned written = 300; unsigned frames; shm_.header->write_buf_idx = 0; shm_.config.used_size = buffer_size * shm_.config.frame_bytes; shm_.header->write_offset[0] = written * shm_.config.frame_bytes; buf_ = cras_shm_get_writeable_frames(&shm_, limit, &frames); EXPECT_EQ(0, frames); EXPECT_EQ(shm_.samples + shm_.header->write_offset[0], buf_); } // Test wrapping of buffers that don't start at normal offsets. TEST_F(ShmTestSuite, WrapWithNonstandardBufferLocations) { uint32_t frame_bytes = cras_shm_frame_bytes(&shm_); uint32_t used_frames = 24; uint32_t used_size = used_frames * frame_bytes; cras_shm_set_used_size(&shm_, used_size); cras_shm_set_buffer_offset(&shm_, 0, 15); cras_shm_set_buffer_offset(&shm_, 1, 479); shm_.header->read_offset[0] = (used_frames / 4) * shm_.config.frame_bytes; shm_.header->write_offset[0] = (used_frames / 2) * shm_.config.frame_bytes; shm_.header->read_offset[1] = 0; shm_.header->write_offset[1] = (used_frames / 3) * shm_.config.frame_bytes; buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_); EXPECT_EQ(used_frames / 4, frames_); EXPECT_EQ(cras_shm_buff_for_idx(&shm_, 0) + shm_.header->read_offset[0], (uint8_t*)buf_); buf_ = cras_shm_get_readable_frames(&shm_, frames_, &frames_); EXPECT_EQ(used_frames / 3, frames_); EXPECT_EQ(cras_shm_buff_for_idx(&shm_, 1), (uint8_t*)buf_); /* Mark all but 5 frames as read */ cras_shm_buffer_read(&shm_, (used_frames / 4) + (used_frames / 3) - 5); EXPECT_EQ(0, shm_.header->read_offset[0]); EXPECT_EQ(((used_frames / 3) - 5) * shm_.config.frame_bytes, shm_.header->read_offset[1]); } TEST_F(ShmTestSuite, PlaybackWithDifferentSequentialBufferLocations) { uint32_t frame_bytes = cras_shm_frame_bytes(&shm_); uint32_t used_frames = 24; uint32_t used_size = used_frames * frame_bytes; cras_shm_set_used_size(&shm_, used_size); uint32_t first_offset = 2.7 * used_size; /* Make samples area long enough to hold all of the buffers starting from * first_offset, with an extra 'used_size' bytes of free space at the end. */ uint32_t samples_length = first_offset + used_size * (CRAS_NUM_SHM_BUFFERS + 1); free(shm_.samples); shm_.samples = static_cast(calloc(1, samples_length)); shm_.samples_info.length = samples_length; uint32_t total_frames_written = 0; // Fill all of the buffers. for (unsigned int i = 0; i < CRAS_NUM_SHM_BUFFERS; i++) { cras_shm_set_buffer_offset(&shm_, i, first_offset + i * used_size); shm_.header->write_in_progress[i] = 1; shm_.header->write_offset[i] = 0; frames_ = MIN(10 + i, used_frames); cras_shm_buffer_written(&shm_, frames_); total_frames_written += frames_; cras_shm_buffer_write_complete(&shm_); } uint32_t total_frames_available = 0; // Consume all available frames. while ((buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_))) { total_frames_available += frames_; EXPECT_GE(buf_, shm_.samples); EXPECT_LE(buf_ + frames_, shm_.samples + samples_length); cras_shm_buffer_read(&shm_, frames_); } EXPECT_EQ(total_frames_written, total_frames_available); uint32_t second_offset = 1.2 * used_size; // Fill half of the buffers. for (unsigned int i = 0; i < CRAS_NUM_SHM_BUFFERS / 2; i++) { cras_shm_set_buffer_offset(&shm_, i, second_offset + i * used_size); shm_.header->write_in_progress[i] = 1; shm_.header->write_offset[i] = 0; frames_ = MIN(3 + 2 * i, used_frames); cras_shm_buffer_written(&shm_, frames_); total_frames_written += frames_; cras_shm_buffer_write_complete(&shm_); } // Consume all available frames. while ((buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_))) { total_frames_available += frames_; EXPECT_GE(buf_, shm_.samples); EXPECT_LE(buf_ + frames_, shm_.samples + samples_length); cras_shm_buffer_read(&shm_, frames_); } EXPECT_EQ(total_frames_written, total_frames_available); // Fill rest of the buffers. for (unsigned int i = CRAS_NUM_SHM_BUFFERS / 2; i < CRAS_NUM_SHM_BUFFERS; i++) { cras_shm_set_buffer_offset(&shm_, i, second_offset + i * used_size); shm_.header->write_in_progress[i] = 1; shm_.header->write_offset[i] = 0; frames_ = MIN(3 + 2 * i, used_frames); cras_shm_buffer_written(&shm_, frames_); total_frames_written += frames_; cras_shm_buffer_write_complete(&shm_); } // Consume all available frames. while ((buf_ = cras_shm_get_readable_frames(&shm_, 0, &frames_))) { total_frames_available += frames_; EXPECT_GE(buf_, shm_.samples); EXPECT_LE(buf_ + frames_, shm_.samples + samples_length); cras_shm_buffer_read(&shm_, frames_); } EXPECT_EQ(total_frames_written, total_frames_available); } TEST_F(ShmTestSuite, GetCheckedBufferOffset) { uint32_t used_size = cras_shm_used_size(&shm_); uint32_t samples_length = used_size * 8; shm_.samples_info.length = samples_length; uint32_t offset; for (unsigned int i = 0; i < CRAS_NUM_SHM_BUFFERS; i++) { shm_.header->buffer_offset[i] = 0; offset = cras_shm_get_checked_buffer_offset(&shm_, i); EXPECT_EQ(0, offset) << "Expected valid buffer offset for buffer " << i; shm_.header->buffer_offset[i] = used_size; offset = cras_shm_get_checked_buffer_offset(&shm_, i); EXPECT_EQ(used_size, offset) << "Expected valid buffer offset for buffer " << i; shm_.header->buffer_offset[i] = samples_length - 1; offset = cras_shm_get_checked_buffer_offset(&shm_, i); EXPECT_EQ(samples_length - 1, offset) << "Expected valid buffer offset for buffer " << i; shm_.header->buffer_offset[i] = samples_length; offset = cras_shm_get_checked_buffer_offset(&shm_, i); EXPECT_EQ(samples_length, offset) << "Expected valid buffer offset for buffer " << i; shm_.header->buffer_offset[i] = samples_length + 1; offset = cras_shm_get_checked_buffer_offset(&shm_, i); EXPECT_EQ(samples_length, offset) << "Expected invalid buffer offset for buffer " << i; shm_.header->buffer_offset[i] = samples_length + used_size; offset = cras_shm_get_checked_buffer_offset(&shm_, i); EXPECT_EQ(samples_length, offset) << "Expected invalid buffer offset for buffer " << i; } } TEST_F(ShmTestSuite, GetCheckedReadOffset) { uint32_t used_size = cras_shm_used_size(&shm_); uint32_t samples_length = used_size * 8; shm_.samples_info.length = samples_length; uint32_t offset; for (unsigned int i = 0; i < CRAS_NUM_SHM_BUFFERS; i++) { shm_.header->read_offset[i] = 0; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(0, offset) << "Expected valid read offset for buffer " << i; shm_.header->read_offset[i] = used_size / 2; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(used_size / 2, offset) << "Expected valid read offset for buffer " << i; shm_.header->read_offset[i] = used_size; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(used_size, offset) << "Expected valid read offset for buffer " << i; // Read offsets should not be greater than used_size. shm_.header->read_offset[i] = used_size + 1; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(0, offset) << "Expected invalid read offset for buffer " << i; shm_.header->buffer_offset[i] = samples_length - used_size / 2; shm_.header->read_offset[i] = 0; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(0, offset) << "Expected valid read offset for buffer " << i; shm_.header->read_offset[i] = used_size / 4; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(used_size / 4, offset) << "Expected valid read offset for buffer " << i; shm_.header->read_offset[i] = used_size / 2; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(used_size / 2, offset) << "Expected valid read offset for buffer " << i; shm_.header->read_offset[i] = used_size / 2 + 1; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(0, offset) << "Expected invalid read offset for buffer " << i; shm_.header->read_offset[i] = used_size; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(0, offset) << "Expected invalid read offset for buffer " << i; shm_.header->read_offset[i] = used_size + 1; offset = cras_shm_get_checked_read_offset(&shm_, i); EXPECT_EQ(0, offset) << "Expected invalid read offset for buffer " << i; } } TEST_F(ShmTestSuite, GetCheckedWriteOffset) { uint32_t used_size = cras_shm_used_size(&shm_); uint32_t samples_length = used_size * 8; shm_.samples_info.length = samples_length; uint32_t offset; for (unsigned int i = 0; i < CRAS_NUM_SHM_BUFFERS; i++) { shm_.header->write_offset[i] = 0; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(0, offset) << "Expected valid write offset for buffer " << i; shm_.header->write_offset[i] = used_size / 2; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(used_size / 2, offset) << "Expected valid write offset for buffer " << i; shm_.header->write_offset[i] = used_size; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(used_size, offset) << "Expected valid write offset for buffer " << i; // Write offsets should not be greater than used_size. shm_.header->write_offset[i] = used_size + 1; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(used_size, offset) << "Expected invalid write offset for buffer " << i; uint32_t buffer_offset = samples_length - used_size / 2; shm_.header->buffer_offset[i] = buffer_offset; shm_.header->write_offset[i] = 0; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(0, offset) << "Expected valid write offset for buffer " << i; shm_.header->write_offset[i] = used_size / 4; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(used_size / 4, offset) << "Expected valid write offset for buffer " << i; shm_.header->write_offset[i] = used_size / 2; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(used_size / 2, offset) << "Expected valid write offset for buffer " << i; // Write offsets should not be longer than the samples area. shm_.header->write_offset[i] = used_size / 2 + 1; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(samples_length - buffer_offset, offset) << "Expected invalid write offset for buffer " << i; shm_.header->write_offset[i] = used_size; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(samples_length - buffer_offset, offset) << "Expected invalid write offset for buffer " << i; shm_.header->write_offset[i] = used_size + 1; offset = cras_shm_get_checked_write_offset(&shm_, i); EXPECT_EQ(samples_length - buffer_offset, offset) << "Expected invalid write offset for buffer " << i; } } } // namespace int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }