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/environment.h"
6#include "base/logging.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/synchronization/waitable_event.h"
9#include "media/audio/audio_manager.h"
10#include "media/audio/audio_manager_base.h"
11#include "media/audio/fake_audio_log_factory.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14#if defined(USE_ALSA)
15#include "media/audio/alsa/audio_manager_alsa.h"
16#endif  // defined(USE_ALSA)
17
18#if defined(OS_WIN)
19#include "base/win/scoped_com_initializer.h"
20#include "media/audio/win/audio_manager_win.h"
21#include "media/audio/win/wavein_input_win.h"
22#endif
23
24#if defined(USE_PULSEAUDIO)
25#include "media/audio/pulse/audio_manager_pulse.h"
26#endif  // defined(USE_PULSEAUDIO)
27
28namespace media {
29
30// Test fixture which allows us to override the default enumeration API on
31// Windows.
32class AudioManagerTest : public ::testing::Test {
33 protected:
34  AudioManagerTest()
35      : audio_manager_(AudioManager::CreateForTesting())
36#if defined(OS_WIN)
37      , com_init_(base::win::ScopedCOMInitializer::kMTA)
38#endif
39  {
40    // Wait for audio thread initialization to complete.  Otherwise the
41    // enumeration type may not have been set yet.
42    base::WaitableEvent event(false, false);
43    audio_manager_->GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
44        &base::WaitableEvent::Signal, base::Unretained(&event)));
45    event.Wait();
46  }
47
48  AudioManager* audio_manager() { return audio_manager_.get(); };
49
50#if defined(OS_WIN)
51  bool SetMMDeviceEnumeration() {
52    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
53    // Windows Wave is used as default if Windows XP was detected =>
54    // return false since MMDevice is not supported on XP.
55    if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration)
56      return false;
57
58    amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration);
59    return true;
60  }
61
62  void SetWaveEnumeration() {
63    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
64    amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration);
65  }
66
67  std::string GetDeviceIdFromPCMWaveInAudioInputStream(
68      const std::string& device_id) {
69    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
70    AudioParameters parameters(
71        AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
72        AudioParameters::kAudioCDSampleRate, 16,
73        1024);
74    scoped_ptr<PCMWaveInAudioInputStream> stream(
75        static_cast<PCMWaveInAudioInputStream*>(
76            amw->CreatePCMWaveInAudioInputStream(parameters, device_id)));
77    return stream.get() ? stream->device_id_ : std::string();
78  }
79#endif
80
81  // Helper method which verifies that the device list starts with a valid
82  // default record followed by non-default device names.
83  static void CheckDeviceNames(const AudioDeviceNames& device_names) {
84    VLOG(2) << "Got " << device_names.size() << " audio devices.";
85    if (!device_names.empty()) {
86      AudioDeviceNames::const_iterator it = device_names.begin();
87
88      // The first device in the list should always be the default device.
89      EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceName),
90                it->device_name);
91      EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceId), it->unique_id);
92      ++it;
93
94      // Other devices should have non-empty name and id and should not contain
95      // default name or id.
96      while (it != device_names.end()) {
97        EXPECT_FALSE(it->device_name.empty());
98        EXPECT_FALSE(it->unique_id.empty());
99        VLOG(2) << "Device ID(" << it->unique_id
100                << "), label: " << it->device_name;
101        EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceName),
102                  it->device_name);
103        EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceId),
104                  it->unique_id);
105        ++it;
106      }
107    } else {
108      // Log a warning so we can see the status on the build bots.  No need to
109      // break the test though since this does successfully test the code and
110      // some failure cases.
111      LOG(WARNING) << "No input devices detected";
112    }
113  }
114
115  bool CanRunInputTest() {
116    return audio_manager_->HasAudioInputDevices();
117  }
118
119  bool CanRunOutputTest() {
120    return audio_manager_->HasAudioOutputDevices();
121  }
122
123#if defined(USE_ALSA) || defined(USE_PULSEAUDIO)
124  template <class T>
125  void CreateAudioManagerForTesting() {
126    // Only one AudioManager may exist at a time, so destroy the one we're
127    // currently holding before creating a new one.
128    audio_manager_.reset();
129    audio_manager_.reset(T::Create(&fake_audio_log_factory_));
130  }
131#endif
132
133  // Synchronously runs the provided callback/closure on the audio thread.
134  void RunOnAudioThread(const base::Closure& closure) {
135    if (!audio_manager()->GetTaskRunner()->BelongsToCurrentThread()) {
136      base::WaitableEvent event(false, false);
137      audio_manager_->GetTaskRunner()->PostTask(
138          FROM_HERE,
139          base::Bind(&AudioManagerTest::RunOnAudioThreadImpl,
140                     base::Unretained(this),
141                     closure,
142                     &event));
143      event.Wait();
144    } else {
145      closure.Run();
146    }
147  }
148
149  void RunOnAudioThreadImpl(const base::Closure& closure,
150                            base::WaitableEvent* event) {
151    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
152    closure.Run();
153    event->Signal();
154  }
155
156  FakeAudioLogFactory fake_audio_log_factory_;
157  scoped_ptr<AudioManager> audio_manager_;
158
159#if defined(OS_WIN)
160  // The MMDevice API requires COM to be initialized on the current thread.
161  base::win::ScopedCOMInitializer com_init_;
162#endif
163};
164
165// Test that devices can be enumerated.
166TEST_F(AudioManagerTest, EnumerateInputDevices) {
167  if (!CanRunInputTest())
168    return;
169
170  AudioDeviceNames device_names;
171  RunOnAudioThread(
172      base::Bind(&AudioManager::GetAudioInputDeviceNames,
173                 base::Unretained(audio_manager()),
174                 &device_names));
175  CheckDeviceNames(device_names);
176}
177
178// Test that devices can be enumerated.
179TEST_F(AudioManagerTest, EnumerateOutputDevices) {
180  if (!CanRunOutputTest())
181    return;
182
183  AudioDeviceNames device_names;
184  RunOnAudioThread(
185      base::Bind(&AudioManager::GetAudioOutputDeviceNames,
186                 base::Unretained(audio_manager()),
187                 &device_names));
188  CheckDeviceNames(device_names);
189}
190
191// Run additional tests for Windows since enumeration can be done using
192// two different APIs. MMDevice is default for Vista and higher and Wave
193// is default for XP and lower.
194#if defined(OS_WIN)
195
196// Override default enumeration API and force usage of Windows MMDevice.
197// This test will only run on Windows Vista and higher.
198TEST_F(AudioManagerTest, EnumerateInputDevicesWinMMDevice) {
199  if (!CanRunInputTest())
200    return;
201
202  AudioDeviceNames device_names;
203  if (!SetMMDeviceEnumeration()) {
204    // Usage of MMDevice will fail on XP and lower.
205    LOG(WARNING) << "MM device enumeration is not supported.";
206    return;
207  }
208  audio_manager_->GetAudioInputDeviceNames(&device_names);
209  CheckDeviceNames(device_names);
210}
211
212TEST_F(AudioManagerTest, EnumerateOutputDevicesWinMMDevice) {
213  if (!CanRunOutputTest())
214    return;
215
216  AudioDeviceNames device_names;
217  if (!SetMMDeviceEnumeration()) {
218    // Usage of MMDevice will fail on XP and lower.
219    LOG(WARNING) << "MM device enumeration is not supported.";
220    return;
221  }
222  audio_manager_->GetAudioOutputDeviceNames(&device_names);
223  CheckDeviceNames(device_names);
224}
225
226// Override default enumeration API and force usage of Windows Wave.
227// This test will run on Windows XP, Windows Vista and Windows 7.
228TEST_F(AudioManagerTest, EnumerateInputDevicesWinWave) {
229  if (!CanRunInputTest())
230    return;
231
232  AudioDeviceNames device_names;
233  SetWaveEnumeration();
234  audio_manager_->GetAudioInputDeviceNames(&device_names);
235  CheckDeviceNames(device_names);
236}
237
238TEST_F(AudioManagerTest, EnumerateOutputDevicesWinWave) {
239  if (!CanRunOutputTest())
240    return;
241
242  AudioDeviceNames device_names;
243  SetWaveEnumeration();
244  audio_manager_->GetAudioOutputDeviceNames(&device_names);
245  CheckDeviceNames(device_names);
246}
247
248TEST_F(AudioManagerTest, WinXPDeviceIdUnchanged) {
249  if (!CanRunInputTest())
250    return;
251
252  AudioDeviceNames xp_device_names;
253  SetWaveEnumeration();
254  audio_manager_->GetAudioInputDeviceNames(&xp_device_names);
255  CheckDeviceNames(xp_device_names);
256
257  // Device ID should remain unchanged, including the default device ID.
258  for (AudioDeviceNames::iterator i = xp_device_names.begin();
259       i != xp_device_names.end(); ++i) {
260    EXPECT_EQ(i->unique_id,
261              GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id));
262  }
263}
264
265TEST_F(AudioManagerTest, ConvertToWinXPInputDeviceId) {
266  if (!CanRunInputTest())
267    return;
268
269  if (!SetMMDeviceEnumeration()) {
270    // Usage of MMDevice will fail on XP and lower.
271    LOG(WARNING) << "MM device enumeration is not supported.";
272    return;
273  }
274
275  AudioDeviceNames device_names;
276  audio_manager_->GetAudioInputDeviceNames(&device_names);
277  CheckDeviceNames(device_names);
278
279  for (AudioDeviceNames::iterator i = device_names.begin();
280       i != device_names.end(); ++i) {
281    std::string converted_id =
282        GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id);
283    if (i == device_names.begin()) {
284      // The first in the list is the default device ID, which should not be
285      // changed when passed to PCMWaveInAudioInputStream.
286      EXPECT_EQ(i->unique_id, converted_id);
287    } else {
288      // MMDevice-style device IDs should be converted to WaveIn-style device
289      // IDs.
290      EXPECT_NE(i->unique_id, converted_id);
291    }
292  }
293}
294
295#endif  // defined(OS_WIN)
296
297#if defined(USE_PULSEAUDIO)
298// On Linux, there are two implementations available and both can
299// sometimes be tested on a single system. These tests specifically
300// test Pulseaudio.
301
302TEST_F(AudioManagerTest, EnumerateInputDevicesPulseaudio) {
303  if (!CanRunInputTest())
304    return;
305
306  CreateAudioManagerForTesting<AudioManagerPulse>();
307  if (audio_manager_.get()) {
308    AudioDeviceNames device_names;
309    audio_manager_->GetAudioInputDeviceNames(&device_names);
310    CheckDeviceNames(device_names);
311  } else {
312    LOG(WARNING) << "No pulseaudio on this system.";
313  }
314}
315
316TEST_F(AudioManagerTest, EnumerateOutputDevicesPulseaudio) {
317  if (!CanRunOutputTest())
318    return;
319
320  CreateAudioManagerForTesting<AudioManagerPulse>();
321  if (audio_manager_.get()) {
322    AudioDeviceNames device_names;
323    audio_manager_->GetAudioOutputDeviceNames(&device_names);
324    CheckDeviceNames(device_names);
325  } else {
326    LOG(WARNING) << "No pulseaudio on this system.";
327  }
328}
329#endif  // defined(USE_PULSEAUDIO)
330
331#if defined(USE_ALSA)
332// On Linux, there are two implementations available and both can
333// sometimes be tested on a single system. These tests specifically
334// test Alsa.
335
336TEST_F(AudioManagerTest, EnumerateInputDevicesAlsa) {
337  if (!CanRunInputTest())
338    return;
339
340  VLOG(2) << "Testing AudioManagerAlsa.";
341  CreateAudioManagerForTesting<AudioManagerAlsa>();
342  AudioDeviceNames device_names;
343  audio_manager_->GetAudioInputDeviceNames(&device_names);
344  CheckDeviceNames(device_names);
345}
346
347TEST_F(AudioManagerTest, EnumerateOutputDevicesAlsa) {
348  if (!CanRunOutputTest())
349    return;
350
351  VLOG(2) << "Testing AudioManagerAlsa.";
352  CreateAudioManagerForTesting<AudioManagerAlsa>();
353  AudioDeviceNames device_names;
354  audio_manager_->GetAudioOutputDeviceNames(&device_names);
355  CheckDeviceNames(device_names);
356}
357#endif  // defined(USE_ALSA)
358
359TEST_F(AudioManagerTest, GetDefaultOutputStreamParameters) {
360#if defined(OS_WIN) || defined(OS_MACOSX)
361  if (!CanRunInputTest())
362    return;
363
364  AudioParameters params = audio_manager_->GetDefaultOutputStreamParameters();
365  EXPECT_TRUE(params.IsValid());
366#endif  // defined(OS_WIN) || defined(OS_MACOSX)
367}
368
369TEST_F(AudioManagerTest, GetAssociatedOutputDeviceID) {
370#if defined(OS_WIN) || defined(OS_MACOSX)
371  if (!CanRunInputTest() || !CanRunOutputTest())
372    return;
373
374  AudioDeviceNames device_names;
375  audio_manager_->GetAudioInputDeviceNames(&device_names);
376  bool found_an_associated_device = false;
377  for (AudioDeviceNames::iterator it = device_names.begin();
378       it != device_names.end();
379       ++it) {
380    EXPECT_FALSE(it->unique_id.empty());
381    EXPECT_FALSE(it->device_name.empty());
382    std::string output_device_id(
383        audio_manager_->GetAssociatedOutputDeviceID(it->unique_id));
384    if (!output_device_id.empty()) {
385      VLOG(2) << it->unique_id << " matches with " << output_device_id;
386      found_an_associated_device = true;
387    }
388  }
389
390  EXPECT_TRUE(found_an_associated_device);
391#endif  // defined(OS_WIN) || defined(OS_MACOSX)
392}
393
394}  // namespace media
395