| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/logging.h" |
| 6 #include "media/audio/linux/alsa_output.h" |
| 7 #include "media/audio/linux/alsa_wrapper.h" |
| 8 #include "testing/gmock/include/gmock/gmock.h" |
| 9 #include "testing/gtest/include/gtest/gtest.h" |
| 10 |
| 11 using testing::_; |
| 12 using testing::DoAll; |
| 13 using testing::Eq; |
| 14 using testing::Return; |
| 15 using testing::SetArgumentPointee; |
| 16 using testing::StrictMock; |
| 17 using testing::StrEq; |
| 18 |
| 19 class MockAlsaWrapper : public AlsaWrapper { |
| 20 public: |
| 21 MOCK_METHOD4(PcmOpen, int(snd_pcm_t** handle, const char* name, |
| 22 snd_pcm_stream_t stream, int mode)); |
| 23 MOCK_METHOD1(PcmClose, int(snd_pcm_t* handle)); |
| 24 MOCK_METHOD1(PcmPrepare, int(snd_pcm_t* handle)); |
| 25 MOCK_METHOD1(PcmDrop, int(snd_pcm_t* handle)); |
| 26 MOCK_METHOD3(PcmWritei, snd_pcm_sframes_t(snd_pcm_t* handle, |
| 27 const void* buffer, |
| 28 snd_pcm_uframes_t size)); |
| 29 MOCK_METHOD3(PcmRecover, int(snd_pcm_t* handle, int err, int silent)); |
| 30 MOCK_METHOD7(PcmSetParams, int(snd_pcm_t* handle, snd_pcm_format_t format, |
| 31 snd_pcm_access_t access, unsigned int channels, |
| 32 unsigned int rate, int soft_resample, |
| 33 unsigned int latency)); |
| 34 MOCK_METHOD1(PcmName, const char*(snd_pcm_t* handle)); |
| 35 MOCK_METHOD1(PcmAvailUpdate, snd_pcm_sframes_t (snd_pcm_t* handle)); |
| 36 |
| 37 MOCK_METHOD1(StrError, const char*(int errnum)); |
| 38 }; |
| 39 |
| 40 class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { |
| 41 public: |
| 42 MOCK_METHOD3(OnMoreData, size_t(AudioOutputStream* stream, |
| 43 void* dest, size_t max_size)); |
| 44 MOCK_METHOD1(OnClose, void(AudioOutputStream* stream)); |
| 45 MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); |
| 46 }; |
| 47 |
| 48 class AlsaPcmOutputStreamTest : public testing::Test { |
| 49 protected: |
| 50 AlsaPcmOutputStreamTest() |
| 51 : packet_(kTestPacketSize + 1) { |
| 52 test_stream_ = new AlsaPcmOutputStream(kTestDeviceName, |
| 53 kTestFormat, |
| 54 kTestChannels, |
| 55 kTestSampleRate, |
| 56 kTestBitsPerSample, |
| 57 &mock_alsa_wrapper_, |
| 58 &message_loop_); |
| 59 |
| 60 packet_.size = kTestPacketSize; |
| 61 } |
| 62 |
| 63 virtual ~AlsaPcmOutputStreamTest() { |
| 64 test_stream_ = NULL; |
| 65 } |
| 66 |
| 67 static const int kTestChannels; |
| 68 static const int kTestSampleRate; |
| 69 static const int kTestBitsPerSample; |
| 70 static const int kTestBytesPerFrame; |
| 71 static const AudioManager::Format kTestFormat; |
| 72 static const char kTestDeviceName[]; |
| 73 static const char kDummyMessage[]; |
| 74 static const int kTestFramesPerPacket; |
| 75 static const size_t kTestPacketSize; |
| 76 static const int kTestFailedErrno; |
| 77 static snd_pcm_t* const kFakeHandle; |
| 78 |
| 79 StrictMock<MockAlsaWrapper> mock_alsa_wrapper_; |
| 80 MessageLoop message_loop_; |
| 81 scoped_refptr<AlsaPcmOutputStream> test_stream_; |
| 82 AlsaPcmOutputStream::Packet packet_; |
| 83 |
| 84 private: |
| 85 DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStreamTest); |
| 86 }; |
| 87 |
| 88 const int AlsaPcmOutputStreamTest::kTestChannels = 2; |
| 89 const int AlsaPcmOutputStreamTest::kTestSampleRate = |
| 90 AudioManager::kAudioCDSampleRate; |
| 91 const int AlsaPcmOutputStreamTest::kTestBitsPerSample = 8; |
| 92 const int AlsaPcmOutputStreamTest::kTestBytesPerFrame = |
| 93 AlsaPcmOutputStreamTest::kTestBitsPerSample / 8 * |
| 94 AlsaPcmOutputStreamTest::kTestChannels; |
| 95 const AudioManager::Format AlsaPcmOutputStreamTest::kTestFormat = |
| 96 AudioManager::AUDIO_PCM_LINEAR; |
| 97 const char AlsaPcmOutputStreamTest::kTestDeviceName[] = "TestDevice"; |
| 98 const char AlsaPcmOutputStreamTest::kDummyMessage[] = "dummy"; |
| 99 const int AlsaPcmOutputStreamTest::kTestFramesPerPacket = 100; |
| 100 const size_t AlsaPcmOutputStreamTest::kTestPacketSize = |
| 101 AlsaPcmOutputStreamTest::kTestFramesPerPacket * |
| 102 AlsaPcmOutputStreamTest::kTestBytesPerFrame; |
| 103 const int AlsaPcmOutputStreamTest::kTestFailedErrno = -EACCES; |
| 104 snd_pcm_t* const AlsaPcmOutputStreamTest::kFakeHandle = |
| 105 reinterpret_cast<snd_pcm_t*>(1); |
| 106 |
| 107 TEST_F(AlsaPcmOutputStreamTest, ConstructedState) { |
| 108 EXPECT_EQ(AlsaPcmOutputStream::kCreated, |
| 109 test_stream_->shared_data_.state()); |
| 110 |
| 111 // Only supports 2 channel. |
| 112 test_stream_ = new AlsaPcmOutputStream(kTestDeviceName, |
| 113 kTestFormat, |
| 114 kTestChannels + 1, |
| 115 kTestSampleRate, |
| 116 kTestBitsPerSample, |
| 117 &mock_alsa_wrapper_, |
| 118 &message_loop_); |
| 119 EXPECT_EQ(AlsaPcmOutputStream::kInError, |
| 120 test_stream_->shared_data_.state()); |
| 121 |
| 122 // Bad bits per sample. |
| 123 test_stream_ = new AlsaPcmOutputStream(kTestDeviceName, |
| 124 kTestFormat, |
| 125 kTestChannels, |
| 126 kTestSampleRate, |
| 127 kTestBitsPerSample - 1, |
| 128 &mock_alsa_wrapper_, |
| 129 &message_loop_); |
| 130 EXPECT_EQ(AlsaPcmOutputStream::kInError, |
| 131 test_stream_->shared_data_.state()); |
| 132 |
| 133 // Bad format. |
| 134 test_stream_ = new AlsaPcmOutputStream(kTestDeviceName, |
| 135 AudioManager::AUDIO_PCM_DELTA, |
| 136 kTestChannels, |
| 137 kTestSampleRate, |
| 138 kTestBitsPerSample, |
| 139 &mock_alsa_wrapper_, |
| 140 &message_loop_); |
| 141 EXPECT_EQ(AlsaPcmOutputStream::kInError, |
| 142 test_stream_->shared_data_.state()); |
| 143 } |
| 144 |
| 145 TEST_F(AlsaPcmOutputStreamTest, OpenClose) { |
| 146 int64 expected_micros = 2 * |
| 147 AlsaPcmOutputStream::FramesToMicros(kTestPacketSize / kTestBytesPerFrame, |
| 148 kTestSampleRate); |
| 149 |
| 150 // Open() call opens the playback device, sets the parameters, posts a task |
| 151 // with the resulting configuration data, and transitions the object state to |
| 152 // kIsOpened. |
| 153 EXPECT_CALL(mock_alsa_wrapper_, |
| 154 PcmOpen(_, StrEq(kTestDeviceName), |
| 155 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) |
| 156 .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle), |
| 157 Return(0))); |
| 158 EXPECT_CALL(mock_alsa_wrapper_, |
| 159 PcmSetParams(kFakeHandle, |
| 160 SND_PCM_FORMAT_S8, |
| 161 SND_PCM_ACCESS_RW_INTERLEAVED, |
| 162 kTestChannels, |
| 163 kTestSampleRate, |
| 164 1, |
| 165 expected_micros)) |
| 166 .WillOnce(Return(0)); |
| 167 |
| 168 // Open the stream. |
| 169 ASSERT_TRUE(test_stream_->Open(kTestPacketSize)); |
| 170 message_loop_.RunAllPending(); |
| 171 |
| 172 EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, |
| 173 test_stream_->shared_data_.state()); |
| 174 EXPECT_EQ(kFakeHandle, test_stream_->playback_handle_); |
| 175 EXPECT_EQ(kTestFramesPerPacket, test_stream_->frames_per_packet_); |
| 176 EXPECT_TRUE(test_stream_->packet_.get()); |
| 177 EXPECT_FALSE(test_stream_->stop_stream_); |
| 178 |
| 179 // Now close it and test that everything was released. |
| 180 EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle)) |
| 181 .WillOnce(Return(0)); |
| 182 test_stream_->Close(); |
| 183 message_loop_.RunAllPending(); |
| 184 |
| 185 EXPECT_EQ(NULL, test_stream_->playback_handle_); |
| 186 EXPECT_FALSE(test_stream_->packet_.get()); |
| 187 EXPECT_TRUE(test_stream_->stop_stream_); |
| 188 } |
| 189 |
| 190 TEST_F(AlsaPcmOutputStreamTest, PcmOpenFailed) { |
| 191 EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _)) |
| 192 .WillOnce(Return(kTestFailedErrno)); |
| 193 EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno)) |
| 194 .WillOnce(Return(kDummyMessage)); |
| 195 |
| 196 // If open fails, the stream stays in kCreated because it has effectively had |
| 197 // no changes. |
| 198 ASSERT_FALSE(test_stream_->Open(kTestPacketSize)); |
| 199 EXPECT_EQ(AlsaPcmOutputStream::kCreated, |
| 200 test_stream_->shared_data_.state()); |
| 201 } |
| 202 |
| 203 TEST_F(AlsaPcmOutputStreamTest, PcmSetParamsFailed) { |
| 204 EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _)) |
| 205 .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle), |
| 206 Return(0))); |
| 207 EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _)) |
| 208 .WillOnce(Return(kTestFailedErrno)); |
| 209 EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle)) |
| 210 .WillOnce(Return(0)); |
| 211 EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno)) |
| 212 .WillOnce(Return(kDummyMessage)); |
| 213 |
| 214 // If open fails, the stream stays in kCreated because it has effectively had |
| 215 // no changes. |
| 216 ASSERT_FALSE(test_stream_->Open(kTestPacketSize)); |
| 217 EXPECT_EQ(AlsaPcmOutputStream::kCreated, |
| 218 test_stream_->shared_data_.state()); |
| 219 } |
| 220 |
| 221 TEST_F(AlsaPcmOutputStreamTest, StartStop) { |
| 222 // Open() call opens the playback device, sets the parameters, posts a task |
| 223 // with the resulting configuration data, and transitions the object state to |
| 224 // kIsOpened. |
| 225 EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _)) |
| 226 .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle), |
| 227 Return(0))); |
| 228 EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _)) |
| 229 .WillOnce(Return(0)); |
| 230 |
| 231 // Open the stream. |
| 232 ASSERT_TRUE(test_stream_->Open(kTestPacketSize)); |
| 233 message_loop_.RunAllPending(); |
| 234 |
| 235 // Expect Device setup. |
| 236 EXPECT_CALL(mock_alsa_wrapper_, PcmDrop(kFakeHandle)) |
| 237 .WillOnce(Return(0)); |
| 238 EXPECT_CALL(mock_alsa_wrapper_, PcmPrepare(kFakeHandle)) |
| 239 .WillOnce(Return(0)); |
| 240 |
| 241 // Expect the pre-roll. |
| 242 MockAudioSourceCallback mock_callback; |
| 243 EXPECT_CALL(mock_callback, |
| 244 OnMoreData(test_stream_.get(), _, kTestPacketSize)) |
| 245 .Times(2) |
| 246 .WillRepeatedly(Return(kTestPacketSize)); |
| 247 EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _)) |
| 248 .Times(2) |
| 249 .WillRepeatedly(Return(kTestPacketSize)); |
| 250 |
| 251 // Expect scheduling. |
| 252 EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle)) |
| 253 .WillOnce(Return(1)); |
| 254 |
| 255 test_stream_->Start(&mock_callback); |
| 256 message_loop_.RunAllPending(); |
| 257 |
| 258 EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle)) |
| 259 .WillOnce(Return(0)); |
| 260 test_stream_->Close(); |
| 261 message_loop_.RunAllPending(); |
| 262 } |
| 263 |
| 264 TEST_F(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket) { |
| 265 // Nothing should happen. Don't set any expectations and Our strict mocks |
| 266 // should verify most of this. |
| 267 |
| 268 // Test regular used-up packet. |
| 269 packet_.used = packet_.size; |
| 270 test_stream_->WritePacket(&packet_); |
| 271 |
| 272 // Test empty packet. |
| 273 packet_.used = packet_.size = 0; |
| 274 test_stream_->WritePacket(&packet_); |
| 275 } |
| 276 |
| 277 TEST_F(AlsaPcmOutputStreamTest, WritePacket_NormalPacket) { |
| 278 // Write a little less than half the data. |
| 279 EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, packet_.buffer.get(), _)) |
| 280 .WillOnce(Return(packet_.size / kTestBytesPerFrame / 2 - 1)); |
| 281 |
| 282 test_stream_->WritePacket(&packet_); |
| 283 |
| 284 ASSERT_EQ(packet_.size / 2 - kTestBytesPerFrame, packet_.used); |
| 285 |
| 286 // Write the rest. |
| 287 EXPECT_CALL(mock_alsa_wrapper_, |
| 288 PcmWritei(_, packet_.buffer.get() + packet_.used, _)) |
| 289 .WillOnce(Return(packet_.size / kTestBytesPerFrame / 2 + 1)); |
| 290 test_stream_->WritePacket(&packet_); |
| 291 EXPECT_EQ(packet_.size, packet_.used); |
| 292 } |
| 293 |
| 294 TEST_F(AlsaPcmOutputStreamTest, WritePacket_WriteFails) { |
| 295 // Fail due to a recoverable error and see that PcmRecover code path |
| 296 // continues normally. |
| 297 EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, _, _)) |
| 298 .WillOnce(Return(-EINTR)); |
| 299 EXPECT_CALL(mock_alsa_wrapper_, PcmRecover(_, _, _)) |
| 300 .WillOnce(Return(packet_.size / kTestBytesPerFrame / 2 - 1)); |
| 301 |
| 302 test_stream_->WritePacket(&packet_); |
| 303 |
| 304 ASSERT_EQ(packet_.size / 2 - kTestBytesPerFrame, packet_.used); |
| 305 |
| 306 // Fail the next write, and see that stop_stream_ is set. |
| 307 EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, _, _)) |
| 308 .WillOnce(Return(kTestFailedErrno)); |
| 309 EXPECT_CALL(mock_alsa_wrapper_, PcmRecover(_, _, _)) |
| 310 .WillOnce(Return(kTestFailedErrno)); |
| 311 EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno)) |
| 312 .WillOnce(Return(kDummyMessage)); |
| 313 test_stream_->WritePacket(&packet_); |
| 314 EXPECT_EQ(packet_.size / 2 - kTestBytesPerFrame, packet_.used); |
| 315 EXPECT_TRUE(test_stream_->stop_stream_); |
| 316 } |
| 317 |
| 318 TEST_F(AlsaPcmOutputStreamTest, WritePacket_StopStream) { |
| 319 // No expectations set on the strict mock because nothing should be called. |
| 320 test_stream_->stop_stream_ = true; |
| 321 test_stream_->WritePacket(&packet_); |
| 322 EXPECT_EQ(packet_.size, packet_.used); |
| 323 } |
| 324 |
| 325 TEST_F(AlsaPcmOutputStreamTest, BufferPacket) { |
| 326 packet_.used = packet_.size; |
| 327 |
| 328 // Return a partially filled packet. |
| 329 MockAudioSourceCallback mock_callback; |
| 330 EXPECT_CALL(mock_callback, |
| 331 OnMoreData(test_stream_.get(), packet_.buffer.get(), |
| 332 packet_.capacity)) |
| 333 .WillOnce(Return(10)); |
| 334 |
| 335 test_stream_->source_callback_ = &mock_callback; |
| 336 test_stream_->BufferPacket(&packet_); |
| 337 |
| 338 EXPECT_EQ(0u, packet_.used); |
| 339 EXPECT_EQ(10u, packet_.size); |
| 340 } |
| 341 |
| 342 TEST_F(AlsaPcmOutputStreamTest, BufferPacket_UnfinishedPacket) { |
| 343 // No expectations set on the strict mock because nothing should be called. |
| 344 test_stream_->BufferPacket(&packet_); |
| 345 EXPECT_EQ(0u, packet_.used); |
| 346 EXPECT_EQ(kTestPacketSize, packet_.size); |
| 347 } |
| 348 |
| 349 TEST_F(AlsaPcmOutputStreamTest, BufferPacket_StopStream) { |
| 350 test_stream_->stop_stream_ = true; |
| 351 test_stream_->BufferPacket(&packet_); |
| 352 EXPECT_EQ(0u, packet_.used); |
| 353 EXPECT_EQ(0u, packet_.size); |
| 354 } |
| 355 |
| 356 TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite) { |
| 357 test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsOpened); |
| 358 test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsPlaying); |
| 359 |
| 360 EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_)) |
| 361 .WillOnce(Return(10)); |
| 362 test_stream_->ScheduleNextWrite(&packet_); |
| 363 |
| 364 test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsClosed); |
| 365 } |
| 366 |
| 367 TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite_StopStream) { |
| 368 test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsOpened); |
| 369 test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsPlaying); |
| 370 |
| 371 test_stream_->stop_stream_ = true; |
| 372 test_stream_->ScheduleNextWrite(&packet_); |
| 373 |
| 374 // TODO(ajwong): Find a way to test whether or not another task has been |
| 375 // posted so we can verify that the Alsa code will indeed break the task |
| 376 // posting loop. |
| 377 |
| 378 test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsClosed); |
| 379 } |
| OLD | NEW |