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