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