alsa_output_unittest.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright 2013 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/message_loop/message_loop.h"
6#include "base/strings/stringprintf.h"
7#include "media/audio/alsa/alsa_output.h"
8#include "media/audio/alsa/alsa_wrapper.h"
9#include "media/audio/alsa/audio_manager_alsa.h"
10#include "media/audio/fake_audio_log_factory.h"
11#include "media/base/data_buffer.h"
12#include "media/base/seekable_buffer.h"
13#include "testing/gmock/include/gmock/gmock.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16using testing::_;
17using testing::AllOf;
18using testing::AtLeast;
19using testing::DoAll;
20using testing::Field;
21using testing::InSequence;
22using testing::Invoke;
23using testing::InvokeWithoutArgs;
24using testing::Mock;
25using testing::MockFunction;
26using testing::Return;
27using testing::SetArgumentPointee;
28using testing::StrictMock;
29using testing::StrEq;
30using testing::Unused;
31
32namespace media {
33
34class MockAlsaWrapper : public AlsaWrapper {
35 public:
36  MOCK_METHOD3(DeviceNameHint, int(int card,
37                                   const char* iface,
38                                   void*** hints));
39  MOCK_METHOD2(DeviceNameGetHint, char*(const void* hint, const char* id));
40  MOCK_METHOD1(DeviceNameFreeHint, int(void** hints));
41
42  MOCK_METHOD4(PcmOpen, int(snd_pcm_t** handle, const char* name,
43                            snd_pcm_stream_t stream, int mode));
44  MOCK_METHOD1(PcmClose, int(snd_pcm_t* handle));
45  MOCK_METHOD1(PcmPrepare, int(snd_pcm_t* handle));
46  MOCK_METHOD1(PcmDrop, int(snd_pcm_t* handle));
47  MOCK_METHOD2(PcmDelay, int(snd_pcm_t* handle, snd_pcm_sframes_t* delay));
48  MOCK_METHOD3(PcmWritei, snd_pcm_sframes_t(snd_pcm_t* handle,
49                                            const void* buffer,
50                                            snd_pcm_uframes_t size));
51  MOCK_METHOD3(PcmReadi, snd_pcm_sframes_t(snd_pcm_t* handle,
52                                           void* buffer,
53                                           snd_pcm_uframes_t size));
54  MOCK_METHOD3(PcmRecover, int(snd_pcm_t* handle, int err, int silent));
55  MOCK_METHOD7(PcmSetParams, int(snd_pcm_t* handle, snd_pcm_format_t format,
56                                 snd_pcm_access_t access, unsigned int channels,
57                                 unsigned int rate, int soft_resample,
58                                 unsigned int latency));
59  MOCK_METHOD3(PcmGetParams, int(snd_pcm_t* handle,
60                                 snd_pcm_uframes_t* buffer_size,
61                                 snd_pcm_uframes_t* period_size));
62  MOCK_METHOD1(PcmName, const char*(snd_pcm_t* handle));
63  MOCK_METHOD1(PcmAvailUpdate, snd_pcm_sframes_t(snd_pcm_t* handle));
64  MOCK_METHOD1(PcmState, snd_pcm_state_t(snd_pcm_t* handle));
65  MOCK_METHOD1(PcmStart, int(snd_pcm_t* handle));
66
67  MOCK_METHOD1(StrError, const char*(int errnum));
68};
69
70class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
71 public:
72  MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus,
73                               AudioBuffersState buffers_state));
74  MOCK_METHOD3(OnMoreIOData, int(AudioBus* source,
75                                 AudioBus* dest,
76                                 AudioBuffersState buffers_state));
77  MOCK_METHOD1(OnError, void(AudioOutputStream* stream));
78};
79
80class MockAudioManagerAlsa : public AudioManagerAlsa {
81 public:
82  MockAudioManagerAlsa() : AudioManagerAlsa(&fake_audio_log_factory_) {}
83  MOCK_METHOD0(Init, void());
84  MOCK_METHOD0(HasAudioOutputDevices, bool());
85  MOCK_METHOD0(HasAudioInputDevices, bool());
86  MOCK_METHOD1(MakeLinearOutputStream, AudioOutputStream*(
87      const AudioParameters& params));
88  MOCK_METHOD3(MakeLowLatencyOutputStream, AudioOutputStream*(
89      const AudioParameters& params,
90      const std::string& device_id,
91      const std::string& input_device_id));
92  MOCK_METHOD2(MakeLowLatencyInputStream, AudioInputStream*(
93      const AudioParameters& params, const std::string& device_id));
94
95  // We need to override this function in order to skip the checking the number
96  // of active output streams. It is because the number of active streams
97  // is managed inside MakeAudioOutputStream, and we don't use
98  // MakeAudioOutputStream to create the stream in the tests.
99  virtual void ReleaseOutputStream(AudioOutputStream* stream) OVERRIDE {
100    DCHECK(stream);
101    delete stream;
102  }
103
104  // We don't mock this method since all tests will do the same thing
105  // and use the current message loop.
106  virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE {
107    return base::MessageLoop::current()->message_loop_proxy();
108  }
109
110 private:
111  FakeAudioLogFactory fake_audio_log_factory_;
112};
113
114class AlsaPcmOutputStreamTest : public testing::Test {
115 protected:
116  AlsaPcmOutputStreamTest() {
117    mock_manager_.reset(new StrictMock<MockAudioManagerAlsa>());
118  }
119
120  virtual ~AlsaPcmOutputStreamTest() {
121  }
122
123  AlsaPcmOutputStream* CreateStream(ChannelLayout layout) {
124    return CreateStream(layout, kTestFramesPerPacket);
125  }
126
127  AlsaPcmOutputStream* CreateStream(ChannelLayout layout,
128                                    int32 samples_per_packet) {
129    AudioParameters params(kTestFormat, layout, kTestSampleRate,
130                           kTestBitsPerSample, samples_per_packet);
131    return new AlsaPcmOutputStream(kTestDeviceName,
132                                   params,
133                                   &mock_alsa_wrapper_,
134                                   mock_manager_.get());
135  }
136
137  // Helper function to malloc the string returned by DeviceNameHint for NAME.
138  static char* EchoHint(const void* name, Unused) {
139    return strdup(static_cast<const char*>(name));
140  }
141
142  // Helper function to malloc the string returned by DeviceNameHint for IOID.
143  static char* OutputHint(Unused, Unused) {
144    return strdup("Output");
145  }
146
147  // Helper function to initialize |test_stream->buffer_|. Must be called
148  // in all tests that use buffer_ without opening the stream.
149  void InitBuffer(AlsaPcmOutputStream* test_stream) {
150    DCHECK(test_stream);
151    packet_ = new media::DataBuffer(kTestPacketSize);
152    packet_->set_data_size(kTestPacketSize);
153    test_stream->buffer_.reset(new media::SeekableBuffer(0, kTestPacketSize));
154    test_stream->buffer_->Append(packet_.get());
155  }
156
157  static const ChannelLayout kTestChannelLayout;
158  static const int kTestSampleRate;
159  static const int kTestBitsPerSample;
160  static const int kTestBytesPerFrame;
161  static const AudioParameters::Format kTestFormat;
162  static const char kTestDeviceName[];
163  static const char kDummyMessage[];
164  static const uint32 kTestFramesPerPacket;
165  static const int kTestPacketSize;
166  static const int kTestFailedErrno;
167  static snd_pcm_t* const kFakeHandle;
168
169  // Used to simulate DeviceNameHint.
170  static char kSurround40[];
171  static char kSurround41[];
172  static char kSurround50[];
173  static char kSurround51[];
174  static char kSurround70[];
175  static char kSurround71[];
176  static void* kFakeHints[];
177
178  StrictMock<MockAlsaWrapper> mock_alsa_wrapper_;
179  scoped_ptr<StrictMock<MockAudioManagerAlsa> > mock_manager_;
180  base::MessageLoop message_loop_;
181  scoped_refptr<media::DataBuffer> packet_;
182
183 private:
184  DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStreamTest);
185};
186
187const ChannelLayout AlsaPcmOutputStreamTest::kTestChannelLayout =
188    CHANNEL_LAYOUT_STEREO;
189const int AlsaPcmOutputStreamTest::kTestSampleRate =
190    AudioParameters::kAudioCDSampleRate;
191const int AlsaPcmOutputStreamTest::kTestBitsPerSample = 8;
192const int AlsaPcmOutputStreamTest::kTestBytesPerFrame =
193    AlsaPcmOutputStreamTest::kTestBitsPerSample / 8 *
194    ChannelLayoutToChannelCount(AlsaPcmOutputStreamTest::kTestChannelLayout);
195const AudioParameters::Format AlsaPcmOutputStreamTest::kTestFormat =
196    AudioParameters::AUDIO_PCM_LINEAR;
197const char AlsaPcmOutputStreamTest::kTestDeviceName[] = "TestDevice";
198const char AlsaPcmOutputStreamTest::kDummyMessage[] = "dummy";
199const uint32 AlsaPcmOutputStreamTest::kTestFramesPerPacket = 1000;
200const int AlsaPcmOutputStreamTest::kTestPacketSize =
201    AlsaPcmOutputStreamTest::kTestFramesPerPacket *
202    AlsaPcmOutputStreamTest::kTestBytesPerFrame;
203const int AlsaPcmOutputStreamTest::kTestFailedErrno = -EACCES;
204snd_pcm_t* const AlsaPcmOutputStreamTest::kFakeHandle =
205    reinterpret_cast<snd_pcm_t*>(1);
206
207char AlsaPcmOutputStreamTest::kSurround40[] = "surround40:CARD=foo,DEV=0";
208char AlsaPcmOutputStreamTest::kSurround41[] = "surround41:CARD=foo,DEV=0";
209char AlsaPcmOutputStreamTest::kSurround50[] = "surround50:CARD=foo,DEV=0";
210char AlsaPcmOutputStreamTest::kSurround51[] = "surround51:CARD=foo,DEV=0";
211char AlsaPcmOutputStreamTest::kSurround70[] = "surround70:CARD=foo,DEV=0";
212char AlsaPcmOutputStreamTest::kSurround71[] = "surround71:CARD=foo,DEV=0";
213void* AlsaPcmOutputStreamTest::kFakeHints[] = {
214    kSurround40, kSurround41, kSurround50, kSurround51,
215    kSurround70, kSurround71, NULL };
216
217// Custom action to clear a memory buffer.
218ACTION(ClearBuffer) {
219  arg0->Zero();
220}
221
222TEST_F(AlsaPcmOutputStreamTest, ConstructedState) {
223  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
224  EXPECT_EQ(AlsaPcmOutputStream::kCreated, test_stream->state());
225  test_stream->Close();
226
227  // Should support mono.
228  test_stream = CreateStream(CHANNEL_LAYOUT_MONO);
229  EXPECT_EQ(AlsaPcmOutputStream::kCreated, test_stream->state());
230  test_stream->Close();
231
232  // Should support multi-channel.
233  test_stream = CreateStream(CHANNEL_LAYOUT_SURROUND);
234  EXPECT_EQ(AlsaPcmOutputStream::kCreated, test_stream->state());
235  test_stream->Close();
236
237  // Bad bits per sample.
238  AudioParameters bad_bps_params(kTestFormat, kTestChannelLayout,
239                                 kTestSampleRate, kTestBitsPerSample - 1,
240                                 kTestFramesPerPacket);
241  test_stream = new AlsaPcmOutputStream(kTestDeviceName,
242                                        bad_bps_params,
243                                        &mock_alsa_wrapper_,
244                                        mock_manager_.get());
245  EXPECT_EQ(AlsaPcmOutputStream::kInError, test_stream->state());
246  test_stream->Close();
247
248  // Bad format.
249  AudioParameters bad_format_params(
250      AudioParameters::AUDIO_LAST_FORMAT, kTestChannelLayout, kTestSampleRate,
251      kTestBitsPerSample, kTestFramesPerPacket);
252  test_stream = new AlsaPcmOutputStream(kTestDeviceName,
253                                        bad_format_params,
254                                        &mock_alsa_wrapper_,
255                                        mock_manager_.get());
256  EXPECT_EQ(AlsaPcmOutputStream::kInError, test_stream->state());
257  test_stream->Close();
258}
259
260TEST_F(AlsaPcmOutputStreamTest, LatencyFloor) {
261  const double kMicrosPerFrame =
262      static_cast<double>(1000000) / kTestSampleRate;
263  const double kPacketFramesInMinLatency =
264      AlsaPcmOutputStream::kMinLatencyMicros / kMicrosPerFrame / 2.0;
265
266  // Test that packets which would cause a latency under less than
267  // AlsaPcmOutputStream::kMinLatencyMicros will get clipped to
268  // AlsaPcmOutputStream::kMinLatencyMicros,
269  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
270      .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle),
271                      Return(0)));
272  EXPECT_CALL(mock_alsa_wrapper_,
273              PcmSetParams(_, _, _, _, _, _,
274                           AlsaPcmOutputStream::kMinLatencyMicros))
275      .WillOnce(Return(0));
276  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
277      .WillOnce(DoAll(SetArgumentPointee<1>(kTestFramesPerPacket),
278                      SetArgumentPointee<2>(kTestFramesPerPacket / 2),
279                      Return(0)));
280
281  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout,
282                                                  kPacketFramesInMinLatency);
283  ASSERT_TRUE(test_stream->Open());
284
285  // Now close it and test that everything was released.
286  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle)).WillOnce(Return(0));
287  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
288      .WillOnce(Return(kTestDeviceName));
289  test_stream->Close();
290
291  Mock::VerifyAndClear(&mock_alsa_wrapper_);
292  Mock::VerifyAndClear(mock_manager_.get());
293
294  // Test that having more packets ends up with a latency based on packet size.
295  const int kOverMinLatencyPacketSize = kPacketFramesInMinLatency + 1;
296  int64 expected_micros = AlsaPcmOutputStream::FramesToTimeDelta(
297      kOverMinLatencyPacketSize * 2, kTestSampleRate).InMicroseconds();
298
299  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
300      .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle), Return(0)));
301  EXPECT_CALL(mock_alsa_wrapper_,
302              PcmSetParams(_, _, _, _, _, _, expected_micros))
303      .WillOnce(Return(0));
304  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
305      .WillOnce(DoAll(SetArgumentPointee<1>(kTestFramesPerPacket),
306                      SetArgumentPointee<2>(kTestFramesPerPacket / 2),
307                      Return(0)));
308
309  test_stream = CreateStream(kTestChannelLayout,
310                             kOverMinLatencyPacketSize);
311  ASSERT_TRUE(test_stream->Open());
312
313  // Now close it and test that everything was released.
314  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
315      .WillOnce(Return(0));
316  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
317      .WillOnce(Return(kTestDeviceName));
318  test_stream->Close();
319
320  Mock::VerifyAndClear(&mock_alsa_wrapper_);
321  Mock::VerifyAndClear(mock_manager_.get());
322}
323
324TEST_F(AlsaPcmOutputStreamTest, OpenClose) {
325  int64 expected_micros = AlsaPcmOutputStream::FramesToTimeDelta(
326      2 * kTestFramesPerPacket, kTestSampleRate).InMicroseconds();
327
328  // Open() call opens the playback device, sets the parameters, posts a task
329  // with the resulting configuration data, and transitions the object state to
330  // kIsOpened.
331  EXPECT_CALL(mock_alsa_wrapper_,
332              PcmOpen(_, StrEq(kTestDeviceName),
333                      SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK))
334      .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle),
335                      Return(0)));
336  EXPECT_CALL(mock_alsa_wrapper_,
337              PcmSetParams(kFakeHandle,
338                           SND_PCM_FORMAT_U8,
339                           SND_PCM_ACCESS_RW_INTERLEAVED,
340                           ChannelLayoutToChannelCount(kTestChannelLayout),
341                           kTestSampleRate,
342                           1,
343                           expected_micros))
344      .WillOnce(Return(0));
345  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(kFakeHandle, _, _))
346      .WillOnce(DoAll(SetArgumentPointee<1>(kTestFramesPerPacket),
347                      SetArgumentPointee<2>(kTestFramesPerPacket / 2),
348                      Return(0)));
349
350  // Open the stream.
351  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
352  ASSERT_TRUE(test_stream->Open());
353
354  EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, test_stream->state());
355  EXPECT_EQ(kFakeHandle, test_stream->playback_handle_);
356  EXPECT_EQ(kTestFramesPerPacket, test_stream->frames_per_packet_);
357  EXPECT_TRUE(test_stream->buffer_.get());
358  EXPECT_FALSE(test_stream->stop_stream_);
359
360  // Now close it and test that everything was released.
361  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
362      .WillOnce(Return(0));
363  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
364      .WillOnce(Return(kTestDeviceName));
365  test_stream->Close();
366}
367
368TEST_F(AlsaPcmOutputStreamTest, PcmOpenFailed) {
369  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
370      .WillOnce(Return(kTestFailedErrno));
371  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
372      .WillOnce(Return(kDummyMessage));
373
374  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
375  ASSERT_FALSE(test_stream->Open());
376  ASSERT_EQ(AlsaPcmOutputStream::kInError, test_stream->state());
377
378  // Ensure internal state is set for a no-op stream if PcmOpen() failes.
379  EXPECT_TRUE(test_stream->stop_stream_);
380  EXPECT_TRUE(test_stream->playback_handle_ == NULL);
381  EXPECT_FALSE(test_stream->buffer_.get());
382
383  // Close the stream since we opened it to make destruction happy.
384  test_stream->Close();
385}
386
387TEST_F(AlsaPcmOutputStreamTest, PcmSetParamsFailed) {
388  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
389      .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle),
390                      Return(0)));
391  EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
392      .WillOnce(Return(kTestFailedErrno));
393  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
394      .WillOnce(Return(0));
395  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
396      .WillOnce(Return(kTestDeviceName));
397  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
398      .WillOnce(Return(kDummyMessage));
399
400  // If open fails, the stream stays in kCreated because it has effectively had
401  // no changes.
402  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
403  ASSERT_FALSE(test_stream->Open());
404  EXPECT_EQ(AlsaPcmOutputStream::kInError, test_stream->state());
405
406  // Ensure internal state is set for a no-op stream if PcmSetParams() failes.
407  EXPECT_TRUE(test_stream->stop_stream_);
408  EXPECT_TRUE(test_stream->playback_handle_ == NULL);
409  EXPECT_FALSE(test_stream->buffer_.get());
410
411  // Close the stream since we opened it to make destruction happy.
412  test_stream->Close();
413}
414
415TEST_F(AlsaPcmOutputStreamTest, StartStop) {
416  // Open() call opens the playback device, sets the parameters, posts a task
417  // with the resulting configuration data, and transitions the object state to
418  // kIsOpened.
419  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
420      .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle),
421                      Return(0)));
422  EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
423      .WillOnce(Return(0));
424  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
425      .WillOnce(DoAll(SetArgumentPointee<1>(kTestFramesPerPacket),
426                      SetArgumentPointee<2>(kTestFramesPerPacket / 2),
427                      Return(0)));
428
429  // Open the stream.
430  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
431  ASSERT_TRUE(test_stream->Open());
432
433  // Expect Device setup.
434  EXPECT_CALL(mock_alsa_wrapper_, PcmDrop(kFakeHandle))
435      .WillOnce(Return(0));
436  EXPECT_CALL(mock_alsa_wrapper_, PcmPrepare(kFakeHandle))
437      .WillOnce(Return(0));
438
439  // Expect the pre-roll.
440  MockAudioSourceCallback mock_callback;
441  EXPECT_CALL(mock_alsa_wrapper_, PcmState(kFakeHandle))
442      .WillRepeatedly(Return(SND_PCM_STATE_RUNNING));
443  EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(kFakeHandle, _))
444      .WillRepeatedly(DoAll(SetArgumentPointee<1>(0), Return(0)));
445  EXPECT_CALL(mock_callback, OnMoreData(_, _))
446      .WillRepeatedly(DoAll(ClearBuffer(), Return(kTestFramesPerPacket)));
447  EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
448      .WillRepeatedly(Return(kTestFramesPerPacket));
449
450  // Expect scheduling.
451  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
452      .Times(AtLeast(2))
453      .WillRepeatedly(Return(kTestFramesPerPacket));
454
455  test_stream->Start(&mock_callback);
456  // Start() will issue a WriteTask() directly and then schedule the next one,
457  // call Stop() immediately after to ensure we don't run the message loop
458  // forever.
459  test_stream->Stop();
460  message_loop_.RunUntilIdle();
461
462  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
463      .WillOnce(Return(0));
464  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
465      .WillOnce(Return(kTestDeviceName));
466  test_stream->Close();
467}
468
469TEST_F(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket) {
470  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
471  InitBuffer(test_stream);
472  test_stream->TransitionTo(AlsaPcmOutputStream::kIsOpened);
473  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
474
475  // Nothing should happen.  Don't set any expectations and Our strict mocks
476  // should verify most of this.
477
478  // Test empty buffer.
479  test_stream->buffer_->Clear();
480  test_stream->WritePacket();
481  test_stream->Close();
482}
483
484TEST_F(AlsaPcmOutputStreamTest, WritePacket_NormalPacket) {
485  // We need to open the stream before writing data to ALSA.
486  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
487      .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle),
488                      Return(0)));
489  EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
490      .WillOnce(Return(0));
491  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
492      .WillOnce(DoAll(SetArgumentPointee<1>(kTestFramesPerPacket),
493                      SetArgumentPointee<2>(kTestFramesPerPacket / 2),
494                      Return(0)));
495  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
496  ASSERT_TRUE(test_stream->Open());
497  InitBuffer(test_stream);
498  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
499
500  // Write a little less than half the data.
501  int written = packet_->data_size() / kTestBytesPerFrame / 2 - 1;
502  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
503        .WillOnce(Return(written));
504  EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, packet_->data(), _))
505      .WillOnce(Return(written));
506
507  test_stream->WritePacket();
508
509  ASSERT_EQ(test_stream->buffer_->forward_bytes(),
510            packet_->data_size() - written * kTestBytesPerFrame);
511
512  // Write the rest.
513  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
514      .WillOnce(Return(kTestFramesPerPacket - written));
515  EXPECT_CALL(mock_alsa_wrapper_,
516              PcmWritei(kFakeHandle,
517                        packet_->data() + written * kTestBytesPerFrame,
518                        _))
519      .WillOnce(Return(packet_->data_size() / kTestBytesPerFrame - written));
520  test_stream->WritePacket();
521  EXPECT_EQ(0, test_stream->buffer_->forward_bytes());
522
523  // Now close it and test that everything was released.
524  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
525      .WillOnce(Return(0));
526  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
527      .WillOnce(Return(kTestDeviceName));
528  test_stream->Close();
529}
530
531TEST_F(AlsaPcmOutputStreamTest, WritePacket_WriteFails) {
532  // We need to open the stream before writing data to ALSA.
533  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
534      .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle),
535                      Return(0)));
536  EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
537      .WillOnce(Return(0));
538  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
539      .WillOnce(DoAll(SetArgumentPointee<1>(kTestFramesPerPacket),
540                      SetArgumentPointee<2>(kTestFramesPerPacket / 2),
541                      Return(0)));
542  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
543  ASSERT_TRUE(test_stream->Open());
544  InitBuffer(test_stream);
545  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
546
547  // Fail due to a recoverable error and see that PcmRecover code path
548  // continues normally.
549  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
550      .WillOnce(Return(kTestFramesPerPacket));
551  EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
552      .WillOnce(Return(-EINTR));
553  EXPECT_CALL(mock_alsa_wrapper_, PcmRecover(kFakeHandle, _, _))
554      .WillOnce(Return(0));
555
556  test_stream->WritePacket();
557
558  ASSERT_EQ(test_stream->buffer_->forward_bytes(), packet_->data_size());
559
560  // Fail the next write, and see that stop_stream_ is set.
561  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
562        .WillOnce(Return(kTestFramesPerPacket));
563  EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
564      .WillOnce(Return(kTestFailedErrno));
565  EXPECT_CALL(mock_alsa_wrapper_, PcmRecover(kFakeHandle, _, _))
566      .WillOnce(Return(kTestFailedErrno));
567  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
568      .WillOnce(Return(kDummyMessage));
569  test_stream->WritePacket();
570  EXPECT_EQ(test_stream->buffer_->forward_bytes(), packet_->data_size());
571  EXPECT_TRUE(test_stream->stop_stream_);
572
573  // Now close it and test that everything was released.
574  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
575      .WillOnce(Return(0));
576  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
577      .WillOnce(Return(kTestDeviceName));
578  test_stream->Close();
579}
580
581TEST_F(AlsaPcmOutputStreamTest, WritePacket_StopStream) {
582  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
583  InitBuffer(test_stream);
584  test_stream->TransitionTo(AlsaPcmOutputStream::kIsOpened);
585  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
586
587  // No expectations set on the strict mock because nothing should be called.
588  test_stream->stop_stream_ = true;
589  test_stream->WritePacket();
590  EXPECT_EQ(0, test_stream->buffer_->forward_bytes());
591  test_stream->Close();
592}
593
594TEST_F(AlsaPcmOutputStreamTest, BufferPacket) {
595  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
596  InitBuffer(test_stream);
597  test_stream->buffer_->Clear();
598
599  MockAudioSourceCallback mock_callback;
600  EXPECT_CALL(mock_alsa_wrapper_, PcmState(_))
601      .WillOnce(Return(SND_PCM_STATE_RUNNING));
602  EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(_, _))
603      .WillOnce(DoAll(SetArgumentPointee<1>(1), Return(0)));
604  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
605      .WillRepeatedly(Return(0));  // Buffer is full.
606
607  // Return a partially filled packet.
608  EXPECT_CALL(mock_callback, OnMoreData(_, _))
609      .WillOnce(DoAll(ClearBuffer(), Return(kTestFramesPerPacket / 2)));
610
611  bool source_exhausted;
612  test_stream->set_source_callback(&mock_callback);
613  test_stream->packet_size_ = kTestPacketSize;
614  test_stream->BufferPacket(&source_exhausted);
615
616  EXPECT_EQ(kTestPacketSize / 2, test_stream->buffer_->forward_bytes());
617  EXPECT_FALSE(source_exhausted);
618  test_stream->Close();
619}
620
621TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Negative) {
622  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
623  InitBuffer(test_stream);
624  test_stream->buffer_->Clear();
625
626  // Simulate where the underrun has occurred right after checking the delay.
627  MockAudioSourceCallback mock_callback;
628  EXPECT_CALL(mock_alsa_wrapper_, PcmState(_))
629      .WillOnce(Return(SND_PCM_STATE_RUNNING));
630  EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(_, _))
631      .WillOnce(DoAll(SetArgumentPointee<1>(-1), Return(0)));
632  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
633      .WillRepeatedly(Return(0));  // Buffer is full.
634  EXPECT_CALL(mock_callback, OnMoreData(_, _))
635      .WillOnce(DoAll(ClearBuffer(), Return(kTestFramesPerPacket / 2)));
636
637  bool source_exhausted;
638  test_stream->set_source_callback(&mock_callback);
639  test_stream->packet_size_ = kTestPacketSize;
640  test_stream->BufferPacket(&source_exhausted);
641
642  EXPECT_EQ(kTestPacketSize / 2, test_stream->buffer_->forward_bytes());
643  EXPECT_FALSE(source_exhausted);
644  test_stream->Close();
645}
646
647TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Underrun) {
648  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
649  InitBuffer(test_stream);
650  test_stream->buffer_->Clear();
651
652  // If ALSA has underrun then we should assume a delay of zero.
653  MockAudioSourceCallback mock_callback;
654  EXPECT_CALL(mock_alsa_wrapper_, PcmState(_))
655      .WillOnce(Return(SND_PCM_STATE_XRUN));
656  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
657      .WillRepeatedly(Return(0));  // Buffer is full.
658  EXPECT_CALL(mock_callback,
659              OnMoreData(_, AllOf(
660                  Field(&AudioBuffersState::pending_bytes, 0),
661                  Field(&AudioBuffersState::hardware_delay_bytes, 0))))
662      .WillOnce(DoAll(ClearBuffer(), Return(kTestFramesPerPacket / 2)));
663
664  bool source_exhausted;
665  test_stream->set_source_callback(&mock_callback);
666  test_stream->packet_size_ = kTestPacketSize;
667  test_stream->BufferPacket(&source_exhausted);
668
669  EXPECT_EQ(kTestPacketSize / 2, test_stream->buffer_->forward_bytes());
670  EXPECT_FALSE(source_exhausted);
671  test_stream->Close();
672}
673
674TEST_F(AlsaPcmOutputStreamTest, BufferPacket_FullBuffer) {
675  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
676  InitBuffer(test_stream);
677  // No expectations set on the strict mock because nothing should be called.
678  bool source_exhausted;
679  test_stream->packet_size_ = kTestPacketSize;
680  test_stream->BufferPacket(&source_exhausted);
681  EXPECT_EQ(kTestPacketSize, test_stream->buffer_->forward_bytes());
682  EXPECT_FALSE(source_exhausted);
683  test_stream->Close();
684}
685
686TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_DeviceSelect) {
687  // Try channels from 1 -> 9. and see that we get the more specific surroundXX
688  // device opened for channels 4-8.  For all other channels, the device should
689  // default to |AlsaPcmOutputStream::kDefaultDevice|.  We should also not
690  // downmix any channel in this case because downmixing is only defined for
691  // channels 4-8, which we are guaranteeing to work.
692  //
693  // Note that the loop starts at "1", so the first parameter is ignored in
694  // these arrays.
695  const char* kExpectedDeviceName[] = { NULL,
696                                        AlsaPcmOutputStream::kDefaultDevice,
697                                        AlsaPcmOutputStream::kDefaultDevice,
698                                        AlsaPcmOutputStream::kDefaultDevice,
699                                        kSurround40, kSurround50, kSurround51,
700                                        kSurround70, kSurround71,
701                                        AlsaPcmOutputStream::kDefaultDevice };
702  bool kExpectedDownmix[] = { false, false, false, false, false, true,
703                              false, false, false, false };
704  ChannelLayout kExpectedLayouts[] = { CHANNEL_LAYOUT_NONE,
705                                       CHANNEL_LAYOUT_MONO,
706                                       CHANNEL_LAYOUT_STEREO,
707                                       CHANNEL_LAYOUT_SURROUND,
708                                       CHANNEL_LAYOUT_4_0,
709                                       CHANNEL_LAYOUT_5_0,
710                                       CHANNEL_LAYOUT_5_1,
711                                       CHANNEL_LAYOUT_7_0,
712                                       CHANNEL_LAYOUT_7_1 };
713
714
715  for (int i = 1; i < 9; ++i) {
716    if (i == 3 || i == 4 || i == 5)  // invalid number of channels
717      continue;
718    SCOPED_TRACE(base::StringPrintf("Attempting %d Channel", i));
719
720    // Hints will only be grabbed for channel numbers that have non-default
721    // devices associated with them.
722    if (kExpectedDeviceName[i] != AlsaPcmOutputStream::kDefaultDevice) {
723      // The DeviceNameHint and DeviceNameFreeHint need to be paired to avoid a
724      // memory leak.
725      EXPECT_CALL(mock_alsa_wrapper_, DeviceNameHint(_, _, _))
726          .WillOnce(DoAll(SetArgumentPointee<2>(&kFakeHints[0]), Return(0)));
727      EXPECT_CALL(mock_alsa_wrapper_, DeviceNameFreeHint(&kFakeHints[0]))
728          .Times(1);
729    }
730
731    EXPECT_CALL(mock_alsa_wrapper_,
732                PcmOpen(_, StrEq(kExpectedDeviceName[i]), _, _))
733        .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle), Return(0)));
734    EXPECT_CALL(mock_alsa_wrapper_,
735                PcmSetParams(kFakeHandle, _, _, i, _, _, _))
736        .WillOnce(Return(0));
737
738    // The parameters are specified by ALSA documentation, and are in constants
739    // in the implementation files.
740    EXPECT_CALL(mock_alsa_wrapper_, DeviceNameGetHint(_, StrEq("IOID")))
741        .WillRepeatedly(Invoke(OutputHint));
742    EXPECT_CALL(mock_alsa_wrapper_, DeviceNameGetHint(_, StrEq("NAME")))
743        .WillRepeatedly(Invoke(EchoHint));
744
745    AlsaPcmOutputStream* test_stream = CreateStream(kExpectedLayouts[i]);
746    EXPECT_TRUE(test_stream->AutoSelectDevice(i));
747    EXPECT_EQ(kExpectedDownmix[i],
748              static_cast<bool>(test_stream->channel_mixer_));
749
750    Mock::VerifyAndClearExpectations(&mock_alsa_wrapper_);
751    Mock::VerifyAndClearExpectations(mock_manager_.get());
752    test_stream->Close();
753  }
754}
755
756TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_FallbackDevices) {
757  using std::string;
758
759  // If there are problems opening a multi-channel device, it the fallbacks
760  // operations should be as follows.  Assume the multi-channel device name is
761  // surround50:
762  //
763  //   1) Try open "surround50"
764  //   2) Try open "plug:surround50".
765  //   3) Try open "default".
766  //   4) Try open "plug:default".
767  //   5) Give up trying to open.
768  //
769  const string first_try = kSurround50;
770  const string second_try = string(AlsaPcmOutputStream::kPlugPrefix) +
771                            kSurround50;
772  const string third_try = AlsaPcmOutputStream::kDefaultDevice;
773  const string fourth_try = string(AlsaPcmOutputStream::kPlugPrefix) +
774                            AlsaPcmOutputStream::kDefaultDevice;
775
776  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameHint(_, _, _))
777      .WillOnce(DoAll(SetArgumentPointee<2>(&kFakeHints[0]), Return(0)));
778  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameFreeHint(&kFakeHints[0]))
779      .Times(1);
780  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameGetHint(_, StrEq("IOID")))
781      .WillRepeatedly(Invoke(OutputHint));
782  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameGetHint(_, StrEq("NAME")))
783      .WillRepeatedly(Invoke(EchoHint));
784  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
785      .WillRepeatedly(Return(kDummyMessage));
786
787  InSequence s;
788  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(first_try.c_str()), _, _))
789      .WillOnce(Return(kTestFailedErrno));
790  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(second_try.c_str()), _, _))
791      .WillOnce(Return(kTestFailedErrno));
792  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(third_try.c_str()), _, _))
793      .WillOnce(Return(kTestFailedErrno));
794  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(fourth_try.c_str()), _, _))
795      .WillOnce(Return(kTestFailedErrno));
796
797  AlsaPcmOutputStream* test_stream = CreateStream(CHANNEL_LAYOUT_5_0);
798  EXPECT_FALSE(test_stream->AutoSelectDevice(5));
799  test_stream->Close();
800}
801
802TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_HintFail) {
803  // Should get |kDefaultDevice|, and force a 2-channel downmix on a failure to
804  // enumerate devices.
805  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameHint(_, _, _))
806      .WillRepeatedly(Return(kTestFailedErrno));
807  EXPECT_CALL(mock_alsa_wrapper_,
808              PcmOpen(_, StrEq(AlsaPcmOutputStream::kDefaultDevice), _, _))
809      .WillOnce(DoAll(SetArgumentPointee<0>(kFakeHandle), Return(0)));
810  EXPECT_CALL(mock_alsa_wrapper_,
811              PcmSetParams(kFakeHandle, _, _, 2, _, _, _))
812      .WillOnce(Return(0));
813  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
814      .WillOnce(Return(kDummyMessage));
815
816  AlsaPcmOutputStream* test_stream = CreateStream(CHANNEL_LAYOUT_5_0);
817  EXPECT_TRUE(test_stream->AutoSelectDevice(5));
818  EXPECT_TRUE(test_stream->channel_mixer_);
819  test_stream->Close();
820}
821
822TEST_F(AlsaPcmOutputStreamTest, BufferPacket_StopStream) {
823  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
824  InitBuffer(test_stream);
825  test_stream->stop_stream_ = true;
826  bool source_exhausted;
827  test_stream->BufferPacket(&source_exhausted);
828  EXPECT_EQ(0, test_stream->buffer_->forward_bytes());
829  EXPECT_TRUE(source_exhausted);
830  test_stream->Close();
831}
832
833TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite) {
834  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
835  test_stream->TransitionTo(AlsaPcmOutputStream::kIsOpened);
836  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
837  InitBuffer(test_stream);
838  DVLOG(1) << test_stream->state();
839  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
840      .WillOnce(Return(10));
841  test_stream->ScheduleNextWrite(false);
842  DVLOG(1) << test_stream->state();
843  // TODO(sergeyu): Figure out how to check that the task has been added to the
844  // message loop.
845
846  // Cleanup the message queue. Currently ~MessageQueue() doesn't free pending
847  // tasks unless running on valgrind. The code below is needed to keep
848  // heapcheck happy.
849
850  test_stream->stop_stream_ = true;
851  DVLOG(1) << test_stream->state();
852  test_stream->TransitionTo(AlsaPcmOutputStream::kIsClosed);
853  DVLOG(1) << test_stream->state();
854  test_stream->Close();
855}
856
857TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite_StopStream) {
858  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
859  test_stream->TransitionTo(AlsaPcmOutputStream::kIsOpened);
860  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
861
862  InitBuffer(test_stream);
863
864  test_stream->stop_stream_ = true;
865  test_stream->ScheduleNextWrite(true);
866
867  // TODO(ajwong): Find a way to test whether or not another task has been
868  // posted so we can verify that the Alsa code will indeed break the task
869  // posting loop.
870
871  test_stream->TransitionTo(AlsaPcmOutputStream::kIsClosed);
872  test_stream->Close();
873}
874
875}  // namespace media
876