| // Copyright 2015 The Chromium 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 "services/audio/input_sync_writer.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/compiler_specific.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/read_only_shared_memory_region.h" |
| #include "base/sync_socket.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "media/base/audio_bus.h" |
| #include "media/base/audio_parameters.h" |
| #include "media/base/channel_layout.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::_; |
| |
| namespace audio { |
| |
| namespace { |
| |
| // Number of audio buffers in the faked ring buffer. |
| const int kSegments = 10; |
| |
| } // namespace |
| |
| // Mocked out sockets used for Send/ReceiveWithTimeout. Counts the number of |
| // outstanding reads, i.e. the diff between send and receive calls. |
| class MockCancelableSyncSocket : public base::CancelableSyncSocket { |
| public: |
| explicit MockCancelableSyncSocket(int buffer_size) |
| : in_failure_mode_(false), |
| writes_(0), |
| reads_(0), |
| receives_(0), |
| buffer_size_(buffer_size), |
| read_buffer_index_(0) {} |
| |
| size_t Send(const void* buffer, size_t length) override { |
| EXPECT_EQ(length, sizeof(uint32_t)); |
| |
| ++writes_; |
| EXPECT_LE(NumberOfBuffersFilled(), buffer_size_); |
| return length; |
| } |
| |
| size_t Receive(void* buffer, size_t length) override { |
| EXPECT_EQ(0u, length % sizeof(uint32_t)); |
| |
| if (in_failure_mode_) |
| return 0; |
| if (receives_ == reads_) |
| return 0; |
| |
| uint32_t* ptr = static_cast<uint32_t*>(buffer); |
| size_t received = 0; |
| for (; received < length / sizeof(uint32_t) && receives_ < reads_; |
| ++received, ++ptr) { |
| ++receives_; |
| EXPECT_LE(receives_, reads_); |
| *ptr = ++read_buffer_index_; |
| } |
| return received * sizeof(uint32_t); |
| } |
| |
| size_t Peek() override { return (reads_ - receives_) * sizeof(uint32_t); } |
| |
| // Simluates reading |buffers| number of buffers from the ring buffer. |
| void Read(int buffers) { |
| reads_ += buffers; |
| EXPECT_LE(reads_, writes_); |
| } |
| |
| // When |in_failure_mode_| == true, the socket fails to receive. |
| void SetFailureMode(bool in_failure_mode) { |
| in_failure_mode_ = in_failure_mode; |
| } |
| |
| int NumberOfBuffersFilled() { return writes_ - reads_; } |
| |
| private: |
| bool in_failure_mode_; |
| int writes_; |
| int reads_; |
| int receives_; |
| int buffer_size_; |
| uint32_t read_buffer_index_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockCancelableSyncSocket); |
| }; |
| |
| class InputSyncWriterTest : public testing::Test { |
| public: |
| InputSyncWriterTest() { |
| const int sampling_frequency_hz = 16000; |
| const int frames = sampling_frequency_hz / 100; // 10 ms |
| const media::AudioParameters audio_params( |
| media::AudioParameters::AUDIO_FAKE, media::CHANNEL_LAYOUT_MONO, |
| sampling_frequency_hz, frames); |
| const uint32_t data_size = |
| ComputeAudioInputBufferSize(audio_params, kSegments); |
| |
| auto shared_memory = base::ReadOnlySharedMemoryRegion::Create(data_size); |
| EXPECT_TRUE(shared_memory.IsValid()); |
| |
| auto socket = std::make_unique<MockCancelableSyncSocket>(kSegments); |
| socket_ = socket.get(); |
| writer_ = std::make_unique<InputSyncWriter>( |
| mock_logger_.Get(), std::move(shared_memory), std::move(socket), |
| kSegments, audio_params); |
| audio_bus_ = media::AudioBus::Create(audio_params); |
| } |
| |
| ~InputSyncWriterTest() override {} |
| |
| // Get total number of expected log calls. On non-Android we expect one log |
| // call at first Write() call, zero on Android. We also expect all call in the |
| // with a glitch summary from the destructor. Besides that only for errors |
| // and fifo info. |
| int GetTotalNumberOfExpectedLogCalls(int expected_calls_due_to_error) { |
| #if defined(OS_ANDROID) |
| return expected_calls_due_to_error + 1; |
| #else |
| return expected_calls_due_to_error + 2; |
| #endif |
| } |
| |
| // Tests expected numbers which are given as arguments. |
| bool TestSocketAndFifoExpectations(int number_of_buffers_in_socket, |
| size_t number_of_verifications_in_socket, |
| size_t number_of_buffers_in_fifo) { |
| EXPECT_EQ(number_of_buffers_in_socket, socket_->NumberOfBuffersFilled()); |
| EXPECT_EQ(number_of_verifications_in_socket, socket_->Peek()); |
| EXPECT_EQ(number_of_buffers_in_fifo, writer_->overflow_data_.size()); |
| |
| return number_of_buffers_in_socket == socket_->NumberOfBuffersFilled() && |
| number_of_verifications_in_socket == socket_->Peek() && |
| number_of_buffers_in_fifo == writer_->overflow_data_.size(); |
| } |
| |
| protected: |
| using MockLogger = |
| base::MockCallback<base::RepeatingCallback<void(const std::string&)>>; |
| |
| base::test::ScopedTaskEnvironment env_; |
| MockLogger mock_logger_; |
| std::unique_ptr<InputSyncWriter> writer_; |
| MockCancelableSyncSocket* socket_; |
| std::unique_ptr<media::AudioBus> audio_bus_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(InputSyncWriterTest); |
| }; |
| |
| TEST_F(InputSyncWriterTest, SingleWriteAndRead) { |
| EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(0)); |
| |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(1, 0, 0)); |
| |
| socket_->Read(1); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(0, 1 * sizeof(uint32_t), 0)); |
| } |
| |
| TEST_F(InputSyncWriterTest, MultipleWritesAndReads) { |
| EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(0)); |
| |
| for (int i = 1; i <= 2 * kSegments; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(1, 0, 0)); |
| socket_->Read(1); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(0, 1 * sizeof(uint32_t), 0)); |
| } |
| } |
| |
| TEST_F(InputSyncWriterTest, MultipleWritesNoReads) { |
| EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(1)); |
| |
| // Fill the ring buffer. |
| for (int i = 1; i <= kSegments; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(i, 0, 0)); |
| } |
| |
| // Now the ring buffer is full, do more writes. We should start filling the |
| // fifo and should get one extra log call for that. |
| for (size_t i = 1; i <= kSegments; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, i)); |
| } |
| } |
| |
| TEST_F(InputSyncWriterTest, FillAndEmptyRingBuffer) { |
| EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(2)); |
| |
| // Fill the ring buffer. |
| for (int i = 1; i <= kSegments; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| } |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 0)); |
| |
| // Empty half of the ring buffer. |
| const int buffers_to_read = kSegments / 2; |
| socket_->Read(buffers_to_read); |
| EXPECT_TRUE(TestSocketAndFifoExpectations( |
| kSegments - buffers_to_read, buffers_to_read * sizeof(uint32_t), 0)); |
| |
| // Fill up again. The first write should do receive until that queue is |
| // empty. |
| for (int i = kSegments - buffers_to_read + 1; i <= kSegments; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(i, 0, 0)); |
| } |
| |
| // Another write, should put the data in the fifo, and render an extra log |
| // call. |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 1)); |
| |
| // Empty the ring buffer. |
| socket_->Read(kSegments); |
| EXPECT_TRUE( |
| TestSocketAndFifoExpectations(0, kSegments * sizeof(uint32_t), 1)); |
| |
| // Another write, should do receive until that queue is empty and write both |
| // the data in the fifo and the new data, and render a log call. |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(2, 0, 0)); |
| |
| // Read the two data blocks. |
| socket_->Read(2); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(0, 2 * sizeof(uint32_t), 0)); |
| } |
| |
| TEST_F(InputSyncWriterTest, FillRingBufferAndFifo) { |
| EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(2)); |
| |
| // Fill the ring buffer. |
| for (int i = 1; i <= kSegments; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| } |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 0)); |
| |
| // Fill the fifo. Should render one log call for starting filling it. |
| const size_t max_fifo_size = InputSyncWriter::kMaxOverflowBusesSize; |
| for (size_t i = 1; i <= max_fifo_size; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| } |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, max_fifo_size)); |
| |
| // Another write, data should be dropped and render one log call. |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, max_fifo_size)); |
| } |
| |
| TEST_F(InputSyncWriterTest, MultipleFillAndEmptyRingBufferAndPartOfFifo) { |
| EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(4)); |
| |
| // Fill the ring buffer. |
| for (int i = 1; i <= kSegments; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| } |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 0)); |
| |
| // Write more data, should be put in the fifo and render one log call for |
| // starting filling it. |
| for (size_t i = 1; i <= 2 * kSegments; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| } |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 2 * kSegments)); |
| |
| // Empty the ring buffer. |
| socket_->Read(kSegments); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(0, kSegments * sizeof(uint32_t), |
| 2 * kSegments)); |
| |
| // Another write should fill up the ring buffer with data from the fifo and |
| // put this data into the fifo. |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, kSegments + 1)); |
| |
| // Empty the ring buffer again. |
| socket_->Read(kSegments); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(0, kSegments * sizeof(uint32_t), |
| kSegments + 1)); |
| |
| // Another write should fill up the ring buffer with data from the fifo and |
| // put this data into the fifo. |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 2)); |
| |
| // Empty the ring buffer again. |
| socket_->Read(kSegments); |
| EXPECT_TRUE( |
| TestSocketAndFifoExpectations(0, kSegments * sizeof(uint32_t), 2)); |
| |
| // Another write should put the remaining data in the fifo in the ring buffer |
| // together with this data. Should render a log call for emptying the fifo. |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(3, 0, 0)); |
| |
| // Read the remaining data. |
| socket_->Read(3); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(0, 3 * sizeof(uint32_t), 0)); |
| |
| // Fill the ring buffer and part of the fifo. Should render one log call for |
| // starting filling it. |
| for (int i = 1; i <= kSegments + 2; ++i) { |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| } |
| EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 2)); |
| |
| // Empty both. Should render a log call for emptying the fifo. |
| socket_->Read(kSegments); |
| writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now()); |
| socket_->Read(3); |
| EXPECT_TRUE(TestSocketAndFifoExpectations(0, 3 * sizeof(uint32_t), 0)); |
| } |
| |
| } // namespace audio |