Chromium Code Reviews| Index: chromeos/audio/cras_audio_handler_unittest.cc | 
| diff --git a/chromeos/audio/cras_audio_handler_unittest.cc b/chromeos/audio/cras_audio_handler_unittest.cc | 
| index de2c1e48b172f700b3a328b0811ddcdaa0e8750f..306d43aefbd72167d7f7419b523558471f4fbb0d 100644 | 
| --- a/chromeos/audio/cras_audio_handler_unittest.cc | 
| +++ b/chromeos/audio/cras_audio_handler_unittest.cc | 
| @@ -15,6 +15,7 @@ | 
| #include "base/run_loop.h" | 
| #include "base/thread_task_runner_handle.h" | 
| #include "base/values.h" | 
| +#include "chromeos/audio/audio_devices_pref_handler.h" | 
| #include "chromeos/audio/audio_devices_pref_handler_stub.h" | 
| #include "chromeos/dbus/audio_node.h" | 
| #include "chromeos/dbus/dbus_thread_manager.h" | 
| @@ -324,6 +325,40 @@ class CrasAudioHandlerTest : public testing::Test { | 
| message_loop_.RunUntilIdle(); | 
| } | 
| + // Set up cras audio handlers with |audio_nodes| and set the active state of | 
| + // |active_device_in_pref| as active and |activate_by_user| in the pref, | 
| + // and rest of nodes in |audio_nodes_in_pref| as inactive. | 
| + void SetupCrasAudioHandlerWithActiveNodeInPref( | 
| + const AudioNodeList& audio_nodes, | 
| + const AudioNodeList& audio_nodes_in_pref, | 
| + const AudioDevice& active_device_in_pref, | 
| + bool activate_by_user) { | 
| + DBusThreadManager::Initialize(); | 
| + fake_cras_audio_client_ = static_cast<FakeCrasAudioClient*>( | 
| + DBusThreadManager::Get()->GetCrasAudioClient()); | 
| + audio_pref_handler_ = new AudioDevicesPrefHandlerStub(); | 
| + bool active; | 
| + for (const AudioNode& node : audio_nodes_in_pref) { | 
| + active = node.id == active_device_in_pref.id; | 
| + audio_pref_handler_->SetDeviceActive(AudioDevice(node), active, | 
| + activate_by_user); | 
| + } | 
| + | 
| + bool activate_by; | 
| + EXPECT_TRUE(audio_pref_handler_->GetDeviceActive(active_device_in_pref, | 
| + &active, &activate_by)); | 
| + EXPECT_TRUE(active); | 
| + EXPECT_EQ(activate_by, activate_by_user); | 
| + | 
| + fake_cras_audio_client_->SetAudioNodesForTesting(audio_nodes); | 
| + CrasAudioHandler::Initialize(audio_pref_handler_); | 
| + | 
| + cras_audio_handler_ = CrasAudioHandler::Get(); | 
| + test_observer_.reset(new TestObserver); | 
| + cras_audio_handler_->AddAudioObserver(test_observer_.get()); | 
| + message_loop_.RunUntilIdle(); | 
| + } | 
| + | 
| void SetUpCrasAudioHandlerWithPrimaryActiveNode( | 
| const AudioNodeList& audio_nodes, | 
| const AudioNode& primary_active_node) { | 
| @@ -559,7 +594,8 @@ TEST_F(CrasAudioHandlerTest, SwitchActiveOutputDevice) { | 
| // Switch the active output to internal speaker. | 
| AudioDevice internal_speaker(kInternalSpeaker); | 
| - cras_audio_handler_->SwitchToDevice(internal_speaker, true); | 
| + cras_audio_handler_->SwitchToDevice(internal_speaker, true, | 
| + CrasAudioHandler::ACTIVATE_BY_USER); | 
| // Verify the active output is switched to internal speaker, and the | 
| // ActiveOutputNodeChanged event is fired. | 
| @@ -586,7 +622,8 @@ TEST_F(CrasAudioHandlerTest, SwitchActiveInputDevice) { | 
| // Switch the active input to internal mic. | 
| AudioDevice internal_mic(kInternalMic); | 
| - cras_audio_handler_->SwitchToDevice(internal_mic, true); | 
| + cras_audio_handler_->SwitchToDevice(internal_mic, true, | 
| + CrasAudioHandler::ACTIVATE_BY_USER); | 
| // Verify the active output is switched to internal speaker, and the active | 
| // ActiveInputNodeChanged event is fired. | 
| @@ -1140,7 +1177,8 @@ TEST_F(CrasAudioHandlerTest, UnplugUSBHeadphonesWithActiveSpeaker) { | 
| // Select the speaker to be the active output device. | 
| AudioDevice internal_speaker(kInternalSpeaker); | 
| - cras_audio_handler_->SwitchToDevice(internal_speaker, true); | 
| + cras_audio_handler_->SwitchToDevice(internal_speaker, true, | 
| + CrasAudioHandler::ACTIVATE_BY_USER); | 
| // Verify the active output is switched to internal speaker, and the | 
| // ActiveOutputNodeChanged event is fired. | 
| @@ -1371,7 +1409,8 @@ TEST_F(CrasAudioHandlerTest, PlugUSBMicNotAffectActiveOutput) { | 
| // Switch the active output to internal speaker. | 
| AudioDevice internal_speaker(kInternalSpeaker); | 
| - cras_audio_handler_->SwitchToDevice(internal_speaker, true); | 
| + cras_audio_handler_->SwitchToDevice(internal_speaker, true, | 
| + CrasAudioHandler::ACTIVATE_BY_USER); | 
| // Verify the active output is switched to internal speaker, and the | 
| // ActiveOutputNodeChanged event is fired. | 
| @@ -2066,7 +2105,8 @@ TEST_F(CrasAudioHandlerTest, ActiveDeviceSelectionWithStableDeviceId) { | 
| // Change the active device to internal speaker, now USB headphone becomes | 
| // inactive. | 
| AudioDevice speaker(kInternalSpeaker); | 
| - cras_audio_handler_->SwitchToDevice(speaker, true); | 
| + cras_audio_handler_->SwitchToDevice(speaker, true, | 
| + CrasAudioHandler::ACTIVATE_BY_USER); | 
| EXPECT_NE(kUSBHeadphone1.id, | 
| cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| @@ -2112,6 +2152,11 @@ TEST_F(CrasAudioHandlerTest, ActiveDeviceSelectionWithStableDeviceId) { | 
| // by its priority. | 
| EXPECT_EQ(usb_headset.id, cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| + audio_nodes.clear(); | 
| + internal_speaker.active = false; | 
| + audio_nodes.push_back(internal_speaker); | 
| + usb_headset.active = true; | 
| + audio_nodes.push_back(usb_headset); | 
| usb_headset2.active = false; | 
| usb_headset2.plugged_time = 80000002; | 
| audio_nodes.push_back(usb_headset2); | 
| @@ -2122,6 +2167,212 @@ TEST_F(CrasAudioHandlerTest, ActiveDeviceSelectionWithStableDeviceId) { | 
| EXPECT_EQ(usb_headset2.id, cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| } | 
| +// Test the device new session case, either via reboot or logout, if there | 
| +// is an active device in the previous session, that device should still | 
| +// be set as active after the new session starts. | 
| +TEST_F(CrasAudioHandlerTest, PersistActiveDeviceAcrossSession) { | 
| + // Set the active device to internal speaker before the session starts. | 
| + AudioNodeList audio_nodes; | 
| + audio_nodes.push_back(kInternalSpeaker); | 
| + audio_nodes.push_back(kHeadphone); | 
| + SetupCrasAudioHandlerWithActiveNodeInPref( | 
| + audio_nodes, audio_nodes, AudioDevice(kInternalSpeaker), true); | 
| + | 
| + // Verify the audio devices size. | 
| + AudioDeviceList audio_devices; | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + | 
| + // Verify the active device is the internal speaker, which is of a lower | 
| + // priority, but selected as active since it was the active device previously. | 
| + EXPECT_EQ(kInternalSpeaker.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| +} | 
| + | 
| +TEST_F(CrasAudioHandlerTest, PersistActiveSpeakerAcrossReboot) { | 
| + // Simulates the device was shut down with three audio devices, and | 
| + // internal speaker being the active one selected by user. | 
| + AudioNodeList audio_nodes_in_pref; | 
| + audio_nodes_in_pref.push_back(kInternalSpeaker); | 
| + audio_nodes_in_pref.push_back(kHeadphone); | 
| + audio_nodes_in_pref.push_back(kUSBHeadphone1); | 
| + | 
| + // Simulate the first NodesChanged signal coming with only one node. | 
| + AudioNodeList audio_nodes; | 
| + audio_nodes.push_back(kUSBHeadphone1); | 
| + | 
| + SetupCrasAudioHandlerWithActiveNodeInPref( | 
| + audio_nodes, audio_nodes_in_pref, AudioDevice(kInternalSpeaker), true); | 
| + | 
| + // Verify the usb headphone has been made active. | 
| + AudioDeviceList audio_devices; | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kUSBHeadphone1.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| + | 
| + // Simulate another NodesChanged signal coming later with all ndoes. | 
| + audio_nodes.push_back(kInternalSpeaker); | 
| + audio_nodes.push_back(kHeadphone); | 
| + ChangeAudioNodes(audio_nodes); | 
| + | 
| + // Verify the active output has been restored to internal speaker. | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kInternalSpeaker.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| +} | 
| + | 
| +TEST_F(CrasAudioHandlerTest, | 
| + PersistActiveUsbHeadphoneAcrossRebootUsbComeLater) { | 
| + // Simulates the device was shut down with three audio devices, and | 
| + // usb headphone being the active one selected by priority. | 
| + AudioNodeList audio_nodes_in_pref; | 
| + audio_nodes_in_pref.push_back(kInternalSpeaker); | 
| + audio_nodes_in_pref.push_back(kHeadphone); | 
| + audio_nodes_in_pref.push_back(kUSBHeadphone1); | 
| + | 
| + // Simulate the first NodesChanged signal coming with only internal speaker | 
| + // and the headphone. | 
| + AudioNodeList audio_nodes; | 
| + audio_nodes.push_back(kInternalSpeaker); | 
| + audio_nodes.push_back(kHeadphone); | 
| + | 
| + SetupCrasAudioHandlerWithActiveNodeInPref(audio_nodes, audio_nodes_in_pref, | 
| + AudioDevice(kUSBHeadphone1), false); | 
| + | 
| + // Verify the headphone has been made active. | 
| + AudioDeviceList audio_devices; | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kHeadphone.id, cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| + | 
| + // Simulate USB node comes later with all ndoes. | 
| + AudioNode usb_node(kUSBHeadphone1); | 
| + usb_node.plugged_time = 80000000; | 
| + audio_nodes.push_back(usb_node); | 
| + ChangeAudioNodes(audio_nodes); | 
| + | 
| + // Verify the active output has been restored to usb headphone. | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kUSBHeadphone1.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| +} | 
| + | 
| +TEST_F(CrasAudioHandlerTest, | 
| + PersistActiveUsbHeadphoneAcrossRebootUsbComeFirst) { | 
| + // Simulates the device was shut down with three audio devices, and | 
| + // usb headphone being the active one selected by priority. | 
| + AudioNodeList audio_nodes_in_pref; | 
| + audio_nodes_in_pref.push_back(kInternalSpeaker); | 
| + audio_nodes_in_pref.push_back(kHeadphone); | 
| + audio_nodes_in_pref.push_back(kUSBHeadphone1); | 
| + | 
| + // Simulate the first NodesChanged signal coming with only internal speaker | 
| + // and the USB headphone. | 
| + AudioNodeList audio_nodes; | 
| + audio_nodes.push_back(kInternalSpeaker); | 
| + audio_nodes.push_back(kUSBHeadphone1); | 
| + | 
| + SetupCrasAudioHandlerWithActiveNodeInPref(audio_nodes, audio_nodes_in_pref, | 
| + AudioDevice(kUSBHeadphone1), false); | 
| + | 
| + // Verify the USB headphone has been made active. | 
| + AudioDeviceList audio_devices; | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kUSBHeadphone1.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| + | 
| + // Simulate another NodesChanged signal coming later with all ndoes. | 
| + AudioNode headphone_node(kHeadphone); | 
| + headphone_node.plugged_time = 80000000; | 
| + audio_nodes.push_back(headphone_node); | 
| + ChangeAudioNodes(audio_nodes); | 
| + | 
| + // Verify the active output has been restored to USB headphone. | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kUSBHeadphone1.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| +} | 
| + | 
| +// This covers the crbug.com/586026. Cras lost the active state of the internal | 
| +// speaker when user unplugs the headphone, which is a bug in cras. However, | 
| +// chrome code is still resilient and set the internal speaker back to active. | 
| +TEST_F(CrasAudioHandlerTest, UnplugHeadphoneLostActiveInternalSpeakerByCras) { | 
| + // Set up with three nodes. | 
| + AudioNodeList audio_nodes; | 
| + audio_nodes.push_back(kInternalSpeaker); | 
| + audio_nodes.push_back(kHeadphone); | 
| + audio_nodes.push_back(kUSBHeadphone1); | 
| + SetUpCrasAudioHandler(audio_nodes); | 
| + | 
| + // Switch the active output to internal speaker. | 
| + cras_audio_handler_->SwitchToDevice(AudioDevice(kInternalSpeaker), true, | 
| + CrasAudioHandler::ACTIVATE_BY_PRIORITY); | 
| 
 
hychao
2016/03/02 15:34:05
When there are headphone and USB-headphone plugged
 
jennyz
2016/03/02 19:33:37
Good catch. Fixed.
 
 | 
| + | 
| + // Verify internal speaker has been made active. | 
| + AudioDeviceList audio_devices; | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kInternalSpeaker.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| + | 
| + // Simulate unplug the headphone. Cras sends NodesChanged signal with | 
| + // both internal speaker and usb headphone being inactive. | 
| + audio_nodes.clear(); | 
| + audio_nodes.push_back(kInternalSpeaker); | 
| + audio_nodes.push_back(kUSBHeadphone1); | 
| + EXPECT_FALSE(kInternalSpeaker.active); | 
| + EXPECT_FALSE(kUSBHeadphone1.active); | 
| + ChangeAudioNodes(audio_nodes); | 
| + | 
| + // Verify the active output is set back to internal speaker. | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kInternalSpeaker.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| +} | 
| + | 
| +TEST_F(CrasAudioHandlerTest, RemoveNonActiveDevice) { | 
| + // Set up with three nodes. | 
| + AudioNodeList audio_nodes; | 
| + audio_nodes.push_back(kInternalSpeaker); | 
| + audio_nodes.push_back(kHeadphone); | 
| + audio_nodes.push_back(kUSBHeadphone1); | 
| + SetUpCrasAudioHandler(audio_nodes); | 
| + | 
| + // Switch the active output to internal speaker. | 
| + cras_audio_handler_->SwitchToDevice(AudioDevice(kInternalSpeaker), true, | 
| + CrasAudioHandler::ACTIVATE_BY_PRIORITY); | 
| 
 
hychao
2016/03/02 15:34:05
same question as above
 
jennyz
2016/03/02 19:33:37
Fixed.
 
 | 
| + | 
| + // Verify internal speaker has been made active. | 
| + AudioDeviceList audio_devices; | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kInternalSpeaker.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| + | 
| + // Remove headphone, which is an non-active device. | 
| + audio_nodes.clear(); | 
| + AudioNode speaker(kInternalSpeaker); | 
| + speaker.active = true; | 
| + audio_nodes.push_back(speaker); | 
| + AudioNode usb_headphone(kUSBHeadphone1); | 
| + usb_headphone.active = false; | 
| + audio_nodes.push_back(usb_headphone); | 
| + | 
| + ChangeAudioNodes(audio_nodes); | 
| + | 
| + // Verify the active output remains as internal speaker. | 
| + cras_audio_handler_->GetAudioDevices(&audio_devices); | 
| + EXPECT_EQ(audio_nodes.size(), audio_devices.size()); | 
| + EXPECT_EQ(kInternalSpeaker.id, | 
| + cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| +} | 
| + | 
| TEST_F(CrasAudioHandlerTest, ChangeActiveNodesHotrodInit) { | 
| AudioNodeList audio_nodes; | 
| audio_nodes.push_back(kHDMIOutput); | 
| @@ -2552,7 +2803,8 @@ TEST_F(CrasAudioHandlerTest, HotPlugHDMINotChangeActiveOutput) { | 
| // Manually set the active output to internal speaker. | 
| AudioDevice internal_output(kInternalSpeaker); | 
| - cras_audio_handler_->SwitchToDevice(internal_output, true); | 
| + cras_audio_handler_->SwitchToDevice(internal_output, true, | 
| + CrasAudioHandler::ACTIVATE_BY_USER); | 
| // Verify the active output is switched to internal speaker. | 
| EXPECT_EQ(internal_speaker.id, | 
| @@ -2631,7 +2883,8 @@ TEST_F(CrasAudioHandlerTest, HDMIRemainInactiveAfterSuspendResume) { | 
| EXPECT_EQ(hdmi_output.id, cras_audio_handler_->GetPrimaryActiveOutputNode()); | 
| // Manually set the active output to internal speaker. | 
| - cras_audio_handler_->SwitchToDevice(AudioDevice(internal_speaker), true); | 
| + cras_audio_handler_->SwitchToDevice(AudioDevice(internal_speaker), true, | 
| + CrasAudioHandler::ACTIVATE_BY_USER); | 
| EXPECT_EQ(internal_speaker.id, | 
| cras_audio_handler_->GetPrimaryActiveOutputNode()); |