Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3884)

Unified Diff: media/audio/linux/alsa_output_unittest.cc

Issue 160497: Reimplement the AlsaPcmOutputStream and fix the threading issues. (Closed)
Patch Set: Address Andrew's comments. Diff aginst Patch 8 please. Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/audio/linux/alsa_output.cc ('k') | media/audio/linux/alsa_wrapper.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/audio/linux/alsa_output_unittest.cc
diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b215b3ad9050f033a833de8fed4bb3eebbd949cc
--- /dev/null
+++ b/media/audio/linux/alsa_output_unittest.cc
@@ -0,0 +1,379 @@
+// Copyright (c) 2009 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 "base/logging.h"
+#include "media/audio/linux/alsa_output.h"
+#include "media/audio/linux/alsa_wrapper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Eq;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::StrictMock;
+using testing::StrEq;
+
+class MockAlsaWrapper : public AlsaWrapper {
+ public:
+ MOCK_METHOD4(PcmOpen, int(snd_pcm_t** handle, const char* name,
+ snd_pcm_stream_t stream, int mode));
+ MOCK_METHOD1(PcmClose, int(snd_pcm_t* handle));
+ MOCK_METHOD1(PcmPrepare, int(snd_pcm_t* handle));
+ MOCK_METHOD1(PcmDrop, int(snd_pcm_t* handle));
+ MOCK_METHOD3(PcmWritei, snd_pcm_sframes_t(snd_pcm_t* handle,
+ const void* buffer,
+ snd_pcm_uframes_t size));
+ MOCK_METHOD3(PcmRecover, int(snd_pcm_t* handle, int err, int silent));
+ MOCK_METHOD7(PcmSetParams, int(snd_pcm_t* handle, snd_pcm_format_t format,
+ snd_pcm_access_t access, unsigned int channels,
+ unsigned int rate, int soft_resample,
+ unsigned int latency));
+ MOCK_METHOD1(PcmName, const char*(snd_pcm_t* handle));
+ MOCK_METHOD1(PcmAvailUpdate, snd_pcm_sframes_t (snd_pcm_t* handle));
+
+ MOCK_METHOD1(StrError, const char*(int errnum));
+};
+
+class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
+ public:
+ MOCK_METHOD3(OnMoreData, size_t(AudioOutputStream* stream,
+ void* dest, size_t max_size));
+ MOCK_METHOD1(OnClose, void(AudioOutputStream* stream));
+ MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code));
+};
+
+class AlsaPcmOutputStreamTest : public testing::Test {
+ protected:
+ AlsaPcmOutputStreamTest()
+ : packet_(kTestPacketSize + 1) {
+ test_stream_ = new AlsaPcmOutputStream(kTestDeviceName,
+ kTestFormat,
+ kTestChannels,
+ kTestSampleRate,
+ kTestBitsPerSample,
+ &mock_alsa_wrapper_,
+ &message_loop_);
+
+ packet_.size = kTestPacketSize;
+ }
+
+ virtual ~AlsaPcmOutputStreamTest() {
+ test_stream_ = NULL;
+ }
+
+ static const int kTestChannels;
+ static const int kTestSampleRate;
+ static const int kTestBitsPerSample;
+ static const int kTestBytesPerFrame;
+ static const AudioManager::Format kTestFormat;
+ static const char kTestDeviceName[];
+ static const char kDummyMessage[];
+ static const int kTestFramesPerPacket;
+ static const size_t kTestPacketSize;
+ static const int kTestFailedErrno;
+ static snd_pcm_t* const kFakeHandle;
+
+ StrictMock<MockAlsaWrapper> mock_alsa_wrapper_;
+ MessageLoop message_loop_;
+ scoped_refptr<AlsaPcmOutputStream> test_stream_;
+ AlsaPcmOutputStream::Packet packet_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStreamTest);
+};
+
+const int AlsaPcmOutputStreamTest::kTestChannels = 2;
+const int AlsaPcmOutputStreamTest::kTestSampleRate =
+ AudioManager::kAudioCDSampleRate;
+const int AlsaPcmOutputStreamTest::kTestBitsPerSample = 8;
+const int AlsaPcmOutputStreamTest::kTestBytesPerFrame =
+ AlsaPcmOutputStreamTest::kTestBitsPerSample / 8 *
+ AlsaPcmOutputStreamTest::kTestChannels;
+const AudioManager::Format AlsaPcmOutputStreamTest::kTestFormat =
+ AudioManager::AUDIO_PCM_LINEAR;
+const char AlsaPcmOutputStreamTest::kTestDeviceName[] = "TestDevice";
+const char AlsaPcmOutputStreamTest::kDummyMessage[] = "dummy";
+const int AlsaPcmOutputStreamTest::kTestFramesPerPacket = 100;
+const size_t AlsaPcmOutputStreamTest::kTestPacketSize =
+ AlsaPcmOutputStreamTest::kTestFramesPerPacket *
+ AlsaPcmOutputStreamTest::kTestBytesPerFrame;
+const int AlsaPcmOutputStreamTest::kTestFailedErrno = -EACCES;
+snd_pcm_t* const AlsaPcmOutputStreamTest::kFakeHandle =
+ reinterpret_cast<snd_pcm_t*>(1);
+
+TEST_F(AlsaPcmOutputStreamTest, ConstructedState) {
+ EXPECT_EQ(AlsaPcmOutputStream::kCreated,
+ test_stream_->shared_data_.state());
+
+ // Only supports 2 channel.
+ test_stream_ = new AlsaPcmOutputStream(kTestDeviceName,
+ kTestFormat,
+ kTestChannels + 1,
+ kTestSampleRate,
+ kTestBitsPerSample,
+ &mock_alsa_wrapper_,
+ &message_loop_);
+ EXPECT_EQ(AlsaPcmOutputStream::kInError,
+ test_stream_->shared_data_.state());
+
+ // Bad bits per sample.
+ test_stream_ = new AlsaPcmOutputStream(kTestDeviceName,
+ kTestFormat,
+ kTestChannels,
+ kTestSampleRate,
+ kTestBitsPerSample - 1,
+ &mock_alsa_wrapper_,
+ &message_loop_);
+ EXPECT_EQ(AlsaPcmOutputStream::kInError,
+ test_stream_->shared_data_.state());
+
+ // Bad format.
+ test_stream_ = new AlsaPcmOutputStream(kTestDeviceName,
+ AudioManager::AUDIO_PCM_DELTA,
+ kTestChannels,
+ kTestSampleRate,
+ kTestBitsPerSample,
+ &mock_alsa_wrapper_,
+ &message_loop_);
+ EXPECT_EQ(AlsaPcmOutputStream::kInError,
+ test_stream_->shared_data_.state());
+}
+
+TEST_F(AlsaPcmOutputStreamTest, OpenClose) {
+ int64 expected_micros = 2 *
+ AlsaPcmOutputStream::FramesToMicros(kTestPacketSize / kTestBytesPerFrame,
+ kTestSampleRate);
+
+ // Open() call opens the playback device, sets the parameters, posts a task
+ // with the resulting configuration data, and transitions the object state to
+ // kIsOpened.
+ EXPECT_CALL(mock_alsa_wrapper_,
+ PcmOpen(_, StrEq(kTestDeviceName),
+ SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK))
+ .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle),
+ Return(0)));
+ EXPECT_CALL(mock_alsa_wrapper_,
+ PcmSetParams(kFakeHandle,
+ SND_PCM_FORMAT_S8,
+ SND_PCM_ACCESS_RW_INTERLEAVED,
+ kTestChannels,
+ kTestSampleRate,
+ 1,
+ expected_micros))
+ .WillOnce(Return(0));
+
+ // Open the stream.
+ ASSERT_TRUE(test_stream_->Open(kTestPacketSize));
+ message_loop_.RunAllPending();
+
+ EXPECT_EQ(AlsaPcmOutputStream::kIsOpened,
+ test_stream_->shared_data_.state());
+ EXPECT_EQ(kFakeHandle, test_stream_->playback_handle_);
+ EXPECT_EQ(kTestFramesPerPacket, test_stream_->frames_per_packet_);
+ EXPECT_TRUE(test_stream_->packet_.get());
+ EXPECT_FALSE(test_stream_->stop_stream_);
+
+ // Now close it and test that everything was released.
+ EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
+ .WillOnce(Return(0));
+ test_stream_->Close();
+ message_loop_.RunAllPending();
+
+ EXPECT_EQ(NULL, test_stream_->playback_handle_);
+ EXPECT_FALSE(test_stream_->packet_.get());
+ EXPECT_TRUE(test_stream_->stop_stream_);
+}
+
+TEST_F(AlsaPcmOutputStreamTest, PcmOpenFailed) {
+ EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+ .WillOnce(Return(kTestFailedErrno));
+ EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
+ .WillOnce(Return(kDummyMessage));
+
+ // If open fails, the stream stays in kCreated because it has effectively had
+ // no changes.
+ ASSERT_FALSE(test_stream_->Open(kTestPacketSize));
+ EXPECT_EQ(AlsaPcmOutputStream::kCreated,
+ test_stream_->shared_data_.state());
+}
+
+TEST_F(AlsaPcmOutputStreamTest, PcmSetParamsFailed) {
+ EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle),
+ Return(0)));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
+ .WillOnce(Return(kTestFailedErrno));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
+ .WillOnce(Return(0));
+ EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
+ .WillOnce(Return(kDummyMessage));
+
+ // If open fails, the stream stays in kCreated because it has effectively had
+ // no changes.
+ ASSERT_FALSE(test_stream_->Open(kTestPacketSize));
+ EXPECT_EQ(AlsaPcmOutputStream::kCreated,
+ test_stream_->shared_data_.state());
+}
+
+TEST_F(AlsaPcmOutputStreamTest, StartStop) {
+ // Open() call opens the playback device, sets the parameters, posts a task
+ // with the resulting configuration data, and transitions the object state to
+ // kIsOpened.
+ EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle),
+ Return(0)));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
+ .WillOnce(Return(0));
+
+ // Open the stream.
+ ASSERT_TRUE(test_stream_->Open(kTestPacketSize));
+ message_loop_.RunAllPending();
+
+ // Expect Device setup.
+ EXPECT_CALL(mock_alsa_wrapper_, PcmDrop(kFakeHandle))
+ .WillOnce(Return(0));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmPrepare(kFakeHandle))
+ .WillOnce(Return(0));
+
+ // Expect the pre-roll.
+ MockAudioSourceCallback mock_callback;
+ EXPECT_CALL(mock_callback,
+ OnMoreData(test_stream_.get(), _, kTestPacketSize))
+ .Times(2)
+ .WillRepeatedly(Return(kTestPacketSize));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
+ .Times(2)
+ .WillRepeatedly(Return(kTestPacketSize));
+
+ // Expect scheduling.
+ EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
+ .WillOnce(Return(1));
+
+ test_stream_->Start(&mock_callback);
+ message_loop_.RunAllPending();
+
+ EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
+ .WillOnce(Return(0));
+ test_stream_->Close();
+ message_loop_.RunAllPending();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket) {
+ // Nothing should happen. Don't set any expectations and Our strict mocks
+ // should verify most of this.
+
+ // Test regular used-up packet.
+ packet_.used = packet_.size;
+ test_stream_->WritePacket(&packet_);
+
+ // Test empty packet.
+ packet_.used = packet_.size = 0;
+ test_stream_->WritePacket(&packet_);
+}
+
+TEST_F(AlsaPcmOutputStreamTest, WritePacket_NormalPacket) {
+ // Write a little less than half the data.
+ EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, packet_.buffer.get(), _))
+ .WillOnce(Return(packet_.size / kTestBytesPerFrame / 2 - 1));
+
+ test_stream_->WritePacket(&packet_);
+
+ ASSERT_EQ(packet_.size / 2 - kTestBytesPerFrame, packet_.used);
+
+ // Write the rest.
+ EXPECT_CALL(mock_alsa_wrapper_,
+ PcmWritei(_, packet_.buffer.get() + packet_.used, _))
+ .WillOnce(Return(packet_.size / kTestBytesPerFrame / 2 + 1));
+ test_stream_->WritePacket(&packet_);
+ EXPECT_EQ(packet_.size, packet_.used);
+}
+
+TEST_F(AlsaPcmOutputStreamTest, WritePacket_WriteFails) {
+ // Fail due to a recoverable error and see that PcmRecover code path
+ // continues normally.
+ EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, _, _))
+ .WillOnce(Return(-EINTR));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmRecover(_, _, _))
+ .WillOnce(Return(packet_.size / kTestBytesPerFrame / 2 - 1));
+
+ test_stream_->WritePacket(&packet_);
+
+ ASSERT_EQ(packet_.size / 2 - kTestBytesPerFrame, packet_.used);
+
+ // Fail the next write, and see that stop_stream_ is set.
+ EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, _, _))
+ .WillOnce(Return(kTestFailedErrno));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmRecover(_, _, _))
+ .WillOnce(Return(kTestFailedErrno));
+ EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
+ .WillOnce(Return(kDummyMessage));
+ test_stream_->WritePacket(&packet_);
+ EXPECT_EQ(packet_.size / 2 - kTestBytesPerFrame, packet_.used);
+ EXPECT_TRUE(test_stream_->stop_stream_);
+}
+
+TEST_F(AlsaPcmOutputStreamTest, WritePacket_StopStream) {
+ // No expectations set on the strict mock because nothing should be called.
+ test_stream_->stop_stream_ = true;
+ test_stream_->WritePacket(&packet_);
+ EXPECT_EQ(packet_.size, packet_.used);
+}
+
+TEST_F(AlsaPcmOutputStreamTest, BufferPacket) {
+ packet_.used = packet_.size;
+
+ // Return a partially filled packet.
+ MockAudioSourceCallback mock_callback;
+ EXPECT_CALL(mock_callback,
+ OnMoreData(test_stream_.get(), packet_.buffer.get(),
+ packet_.capacity))
+ .WillOnce(Return(10));
+
+ test_stream_->source_callback_ = &mock_callback;
+ test_stream_->BufferPacket(&packet_);
+
+ EXPECT_EQ(0u, packet_.used);
+ EXPECT_EQ(10u, packet_.size);
+}
+
+TEST_F(AlsaPcmOutputStreamTest, BufferPacket_UnfinishedPacket) {
+ // No expectations set on the strict mock because nothing should be called.
+ test_stream_->BufferPacket(&packet_);
+ EXPECT_EQ(0u, packet_.used);
+ EXPECT_EQ(kTestPacketSize, packet_.size);
+}
+
+TEST_F(AlsaPcmOutputStreamTest, BufferPacket_StopStream) {
+ test_stream_->stop_stream_ = true;
+ test_stream_->BufferPacket(&packet_);
+ EXPECT_EQ(0u, packet_.used);
+ EXPECT_EQ(0u, packet_.size);
+}
+
+TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite) {
+ test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsOpened);
+ test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+
+ EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
+ .WillOnce(Return(10));
+ test_stream_->ScheduleNextWrite(&packet_);
+
+ test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsClosed);
+}
+
+TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite_StopStream) {
+ test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsOpened);
+ test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+
+ test_stream_->stop_stream_ = true;
+ test_stream_->ScheduleNextWrite(&packet_);
+
+ // TODO(ajwong): Find a way to test whether or not another task has been
+ // posted so we can verify that the Alsa code will indeed break the task
+ // posting loop.
+
+ test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsClosed);
+}
« no previous file with comments | « media/audio/linux/alsa_output.cc ('k') | media/audio/linux/alsa_wrapper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698