1 // Copyright 2014 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 "extensions/shell/browser/shell_audio_controller_chromeos.h"
6
7 #include <stdint.h>
8
9 #include <memory>
10
11 #include "base/macros.h"
12 #include "base/memory/ptr_util.h"
13 #include "chromeos/audio/audio_device.h"
14 #include "chromeos/audio/audio_devices_pref_handler.h"
15 #include "chromeos/audio/cras_audio_handler.h"
16 #include "chromeos/dbus/audio/audio_node.h"
17 #include "chromeos/dbus/audio/fake_cras_audio_client.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 using chromeos::AudioDevice;
21 using chromeos::AudioNode;
22 using chromeos::AudioNodeList;
23
24 namespace extensions {
25
26 class ShellAudioControllerTest : public testing::Test {
27 public:
ShellAudioControllerTest()28 ShellAudioControllerTest() : next_node_id_(1) {
29 chromeos::CrasAudioClient::InitializeFake();
30 audio_client()->SetAudioNodesForTesting(AudioNodeList());
31 chromeos::CrasAudioHandler::InitializeForTesting();
32
33 controller_.reset(new ShellAudioController());
34 }
35
~ShellAudioControllerTest()36 ~ShellAudioControllerTest() override {
37 controller_.reset();
38 chromeos::CrasAudioHandler::Shutdown();
39 chromeos::CrasAudioClient::Shutdown();
40 }
41
42 protected:
43 // Fills a AudioNode for use by tests.
CreateNode(chromeos::AudioDeviceType type)44 AudioNode CreateNode(chromeos::AudioDeviceType type) {
45 AudioNode node;
46 node.is_input =
47 type == chromeos::AUDIO_TYPE_MIC ||
48 type == chromeos::AUDIO_TYPE_INTERNAL_MIC ||
49 type == chromeos::AUDIO_TYPE_KEYBOARD_MIC;
50 node.id = next_node_id_++;
51 node.type = AudioDevice::GetTypeString(type);
52 return node;
53 }
54
55 // Changes the active state of the node with |id| in |nodes|.
SetNodeActive(AudioNodeList * nodes,uint64_t id,bool active)56 void SetNodeActive(AudioNodeList* nodes, uint64_t id, bool active) {
57 for (AudioNodeList::iterator it = nodes->begin();
58 it != nodes->end(); ++it) {
59 if (it->id == id) {
60 it->active = active;
61 return;
62 }
63 }
64 ASSERT_TRUE(false) << "Didn't find ID " << id;
65 }
66
audio_client()67 chromeos::FakeCrasAudioClient* audio_client() {
68 return chromeos::FakeCrasAudioClient::Get();
69 }
70
audio_handler()71 chromeos::CrasAudioHandler* audio_handler() {
72 return chromeos::CrasAudioHandler::Get();
73 }
74
75 std::unique_ptr<ShellAudioController> controller_;
76
77 // Next audio node ID to be returned by CreateNode().
78 uint64_t next_node_id_;
79
80 private:
81 DISALLOW_COPY_AND_ASSIGN(ShellAudioControllerTest);
82 };
83
84 // Tests that higher-priority devices are activated as soon as they're
85 // connected.
TEST_F(ShellAudioControllerTest,SelectBestDevices)86 TEST_F(ShellAudioControllerTest, SelectBestDevices) {
87 AudioNode internal_speaker =
88 CreateNode(chromeos::AUDIO_TYPE_INTERNAL_SPEAKER);
89 AudioNode internal_mic = CreateNode(chromeos::AUDIO_TYPE_INTERNAL_MIC);
90 AudioNode headphone = CreateNode(chromeos::AUDIO_TYPE_HEADPHONE);
91 AudioNode external_mic = CreateNode(chromeos::AUDIO_TYPE_MIC);
92
93 // AudioDevice gives the headphone jack a higher priority than the internal
94 // speaker and an external mic a higher priority than the internal mic, so we
95 // should start out favoring headphones and the external mic.
96 AudioNodeList all_nodes;
97 all_nodes.push_back(internal_speaker);
98 all_nodes.push_back(internal_mic);
99 all_nodes.push_back(headphone);
100 all_nodes.push_back(external_mic);
101 audio_client()->SetAudioNodesAndNotifyObserversForTesting(all_nodes);
102 EXPECT_EQ(headphone.id, audio_handler()->GetPrimaryActiveOutputNode());
103 EXPECT_EQ(external_mic.id, audio_handler()->GetPrimaryActiveInputNode());
104
105 // Unplug the headphones and mic and check that we switch to the internal
106 // devices.
107 AudioNodeList internal_nodes;
108 internal_nodes.push_back(internal_speaker);
109 internal_nodes.push_back(internal_mic);
110 audio_client()->SetAudioNodesAndNotifyObserversForTesting(internal_nodes);
111 EXPECT_EQ(internal_speaker.id, audio_handler()->GetPrimaryActiveOutputNode());
112 EXPECT_EQ(internal_mic.id, audio_handler()->GetPrimaryActiveInputNode());
113
114 // Switch back to the external devices. Mark the previously-activated internal
115 // devices as being active so CrasAudioHandler doesn't complain.
116 SetNodeActive(&all_nodes, internal_speaker.id, true);
117 SetNodeActive(&all_nodes, internal_mic.id, true);
118 audio_client()->SetAudioNodesAndNotifyObserversForTesting(all_nodes);
119 EXPECT_EQ(headphone.id, audio_handler()->GetPrimaryActiveOutputNode());
120 EXPECT_EQ(external_mic.id, audio_handler()->GetPrimaryActiveInputNode());
121 }
122
123 // Tests that active audio devices are unmuted and have correct initial volume.
TEST_F(ShellAudioControllerTest,InitialVolume)124 TEST_F(ShellAudioControllerTest, InitialVolume) {
125 AudioNodeList nodes;
126 nodes.push_back(CreateNode(chromeos::AUDIO_TYPE_INTERNAL_SPEAKER));
127 nodes.push_back(CreateNode(chromeos::AUDIO_TYPE_INTERNAL_MIC));
128 audio_client()->SetAudioNodesAndNotifyObserversForTesting(nodes);
129
130 EXPECT_FALSE(audio_handler()->IsOutputMuted());
131 EXPECT_FALSE(audio_handler()->IsInputMuted());
132 EXPECT_EQ(static_cast<double>(
133 chromeos::AudioDevicesPrefHandler::kDefaultOutputVolumePercent),
134 audio_handler()->GetOutputVolumePercent());
135
136 // TODO(rkc): The default value for gain is wrong. http://crbug.com/442489
137 EXPECT_EQ(75.0, audio_handler()->GetInputGainPercent());
138 }
139
140 } // namespace extensions
141