1 // Copyright (c) 2012 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 "media/audio/win/audio_device_listener_win.h"
6 
7 #include <memory>
8 #include <string>
9 
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/macros.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/system/system_monitor.h"
15 #include "base/test/simple_test_tick_clock.h"
16 #include "base/test/task_environment.h"
17 #include "base/win/scoped_com_initializer.h"
18 #include "media/audio/audio_manager.h"
19 #include "media/audio/audio_unittest_util.h"
20 #include "media/audio/win/core_audio_util_win.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 
24 using base::win::ScopedCOMInitializer;
25 
26 namespace media {
27 
28 constexpr char kFirstTestDevice[] = "test_device_0";
29 constexpr char kSecondTestDevice[] = "test_device_1";
30 
31 class AudioDeviceListenerWinTest
32     : public testing::Test,
33       public base::SystemMonitor::DevicesChangedObserver {
34  public:
AudioDeviceListenerWinTest()35   AudioDeviceListenerWinTest() {
36     DCHECK(com_init_.Succeeded());
37     if (!CoreAudioUtil::IsSupported())
38       return;
39 
40     system_monitor_.AddDevicesChangedObserver(this);
41 
42     output_device_listener_ = std::make_unique<AudioDeviceListenerWin>(
43         base::BindRepeating(&AudioDeviceListenerWinTest::OnDeviceChange,
44                             base::Unretained(this)));
45 
46     tick_clock_.Advance(base::TimeDelta::FromSeconds(12345));
47     output_device_listener_->tick_clock_ = &tick_clock_;
48   }
49 
~AudioDeviceListenerWinTest()50   ~AudioDeviceListenerWinTest() override {
51     system_monitor_.RemoveDevicesChangedObserver(this);
52   }
53 
AdvanceLastDeviceChangeTime()54   void AdvanceLastDeviceChangeTime() {
55     tick_clock_.Advance(AudioDeviceListenerWin::kDeviceChangeLimit +
56                         base::TimeDelta::FromMilliseconds(1));
57   }
58 
59   // Simulate a device change where no output devices are available.
SimulateNullDefaultOutputDeviceChange()60   bool SimulateNullDefaultOutputDeviceChange() {
61     auto result = output_device_listener_->OnDefaultDeviceChanged(
62         static_cast<EDataFlow>(eConsole), static_cast<ERole>(eRender), nullptr);
63     task_environment_.RunUntilIdle();
64     return result == S_OK;
65   }
66 
SimulateDefaultOutputDeviceChange(const char * new_device_id)67   bool SimulateDefaultOutputDeviceChange(const char* new_device_id) {
68     auto result = output_device_listener_->OnDefaultDeviceChanged(
69         static_cast<EDataFlow>(eConsole), static_cast<ERole>(eRender),
70         base::ASCIIToUTF16(new_device_id).c_str());
71     task_environment_.RunUntilIdle();
72     return result == S_OK;
73   }
74 
75   MOCK_METHOD0(OnDeviceChange, void());
76   MOCK_METHOD1(OnDevicesChanged, void(base::SystemMonitor::DeviceType));
77 
78  private:
79   ScopedCOMInitializer com_init_;
80   base::test::TaskEnvironment task_environment_;
81   base::SystemMonitor system_monitor_;
82   base::SimpleTestTickClock tick_clock_;
83   std::unique_ptr<AudioDeviceListenerWin> output_device_listener_;
84 
85   DISALLOW_COPY_AND_ASSIGN(AudioDeviceListenerWinTest);
86 };
87 
88 // Simulate a device change events and ensure we get the right callbacks.
TEST_F(AudioDeviceListenerWinTest,OutputDeviceChange)89 TEST_F(AudioDeviceListenerWinTest, OutputDeviceChange) {
90   ABORT_AUDIO_TEST_IF_NOT(CoreAudioUtil::IsSupported());
91 
92   EXPECT_CALL(*this, OnDeviceChange()).Times(1);
93   EXPECT_CALL(*this, OnDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO))
94       .Times(1);
95   ASSERT_TRUE(SimulateDefaultOutputDeviceChange(kFirstTestDevice));
96 
97   testing::Mock::VerifyAndClear(this);
98   AdvanceLastDeviceChangeTime();
99   EXPECT_CALL(*this, OnDeviceChange()).Times(1);
100   EXPECT_CALL(*this, OnDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO))
101       .Times(1);
102   ASSERT_TRUE(SimulateDefaultOutputDeviceChange(kSecondTestDevice));
103 
104   // Since it occurs too soon, the second device event shouldn't call
105   // OnDeviceChange(), but it should notify OnDevicesChanged().
106   EXPECT_CALL(*this, OnDeviceChange()).Times(0);
107   EXPECT_CALL(*this, OnDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO))
108       .Times(1);
109   ASSERT_TRUE(SimulateDefaultOutputDeviceChange(kSecondTestDevice));
110 }
111 
112 // Ensure that null output device changes don't crash.  Simulates the situation
113 // where we have no output devices.
TEST_F(AudioDeviceListenerWinTest,NullOutputDeviceChange)114 TEST_F(AudioDeviceListenerWinTest, NullOutputDeviceChange) {
115   ABORT_AUDIO_TEST_IF_NOT(CoreAudioUtil::IsSupported());
116 
117   EXPECT_CALL(*this, OnDeviceChange()).Times(1);
118   EXPECT_CALL(*this, OnDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO))
119       .Times(1);
120   ASSERT_TRUE(SimulateNullDefaultOutputDeviceChange());
121 
122   testing::Mock::VerifyAndClear(this);
123   AdvanceLastDeviceChangeTime();
124   EXPECT_CALL(*this, OnDeviceChange()).Times(1);
125   EXPECT_CALL(*this, OnDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO))
126       .Times(1);
127   ASSERT_TRUE(SimulateDefaultOutputDeviceChange(kFirstTestDevice));
128 
129   // Since it occurs too soon, the second device event shouldn't call
130   // OnDeviceChange(), but it should notify OnDevicesChanged().
131   testing::Mock::VerifyAndClear(this);
132   EXPECT_CALL(*this, OnDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO))
133       .Times(1);
134   ASSERT_TRUE(SimulateNullDefaultOutputDeviceChange());
135 }
136 
137 }  // namespace media
138