// Copyright 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Tests for audio device handler.

#include "audio_device_handler_mock.h"

#include <string>

#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_number_conversions.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>

using base::FilePath;
using base::IntToString;
using base::ScopedTempDir;
using base::WriteFile;
using brillo::AudioDeviceHandlerMock;
using testing::_;
using testing::AnyNumber;
using testing::AtLeast;

namespace brillo {

class AudioDeviceHandlerTest : public testing::Test {
 public:
  void SetUp() override {
    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
    h2w_file_path_ = temp_dir_.path().Append("h2wstate");
  }

  void TearDown() override { handler_.Reset(); }

  // Method to store the current state of the audio jack to a file.
  //
  // |value| - Value in the h2w file.
  void WriteToH2WFile(int value) {
    std::string value_string = IntToString(value);
    WriteFile(h2w_file_path_, value_string.c_str(), value_string.length());
  }

  AudioDeviceHandlerMock handler_;
  FilePath h2w_file_path_;

 private:
  ScopedTempDir temp_dir_;
};

// Test that DisconnectAllSupportedDevices() calls NotifyAudioPolicyService()
// the right number of times.
TEST_F(AudioDeviceHandlerTest, DisconnectAllSupportedDevicesCallsDisconnect) {
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(
                  _, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)).Times(3);
  handler_.DisconnectAllSupportedDevices();
  EXPECT_EQ(handler_.changed_devices_.size(), 3);
}

// Test that Init() calls DisconnectAllSupportedDevices().
TEST_F(AudioDeviceHandlerTest, InitCallsDisconnectAllSupportedDevices) {
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(
                  _, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)).Times(3);
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesDisconnected))
      .Times(AtLeast(1));
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(
                  _, AUDIO_POLICY_DEVICE_STATE_AVAILABLE)).Times(AnyNumber());
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesConnected))
      .Times(AnyNumber());
  handler_.Init(nullptr);
}

// Test GetInitialAudioDeviceState() with just a microphone.
TEST_F(AudioDeviceHandlerTest, InitialAudioStateMic) {
  WriteToH2WFile(2);
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_IN_WIRED_HEADSET,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesConnected));
  handler_.GetInitialAudioDeviceState(h2w_file_path_);
  EXPECT_NE(
      handler_.connected_input_devices_.find(AUDIO_DEVICE_IN_WIRED_HEADSET),
      handler_.connected_input_devices_.end());
  EXPECT_EQ(handler_.connected_output_devices_.size(), 0);
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], AUDIO_DEVICE_IN_WIRED_HEADSET);
}

// Test GetInitialAudioDeviceState() with a headphone.
TEST_F(AudioDeviceHandlerTest, InitialAudioStateHeadphone) {
  WriteToH2WFile(1);
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesConnected));
  handler_.GetInitialAudioDeviceState(h2w_file_path_);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 0);
  EXPECT_NE(
      handler_.connected_output_devices_.find(AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
      handler_.connected_output_devices_.end());
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
}

// Test GetInitialAudioDeviceState() with a headset.
TEST_F(AudioDeviceHandlerTest, InitialAudioStateHeadset) {
  WriteToH2WFile(3);
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesConnected));
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_IN_WIRED_HEADSET,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_OUT_WIRED_HEADSET,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  handler_.GetInitialAudioDeviceState(h2w_file_path_);
  EXPECT_NE(
      handler_.connected_input_devices_.find(AUDIO_DEVICE_IN_WIRED_HEADSET),
      handler_.connected_input_devices_.end());
  EXPECT_NE(
      handler_.connected_output_devices_.find(AUDIO_DEVICE_OUT_WIRED_HEADSET),
      handler_.connected_output_devices_.end());
  EXPECT_EQ(handler_.changed_devices_.size(), 2);
}

// Test GetInitialAudioDeviceState() without any devices connected to the audio
// jack. No need to call NotifyAudioPolicyService() since that's already handled
// by Init().
TEST_F(AudioDeviceHandlerTest, InitialAudioStateNone) {
  WriteToH2WFile(0);
  EXPECT_CALL(handler_, TriggerCallback(_));
  handler_.GetInitialAudioDeviceState(h2w_file_path_);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 0);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 0);
  EXPECT_EQ(handler_.changed_devices_.size(), 0);
}

// Test GetInitialAudioDeviceState() with an invalid file. The audio handler
// should not fail in this case because it should work on boards that don't
// support audio jacks.
TEST_F(AudioDeviceHandlerTest, InitialAudioStateInvalid) {
  FilePath path = h2w_file_path_;
  handler_.GetInitialAudioDeviceState(h2w_file_path_);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 0);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 0);
}

// Test ProcessEvent() with an empty input_event arg.
TEST_F(AudioDeviceHandlerTest, ProcessEventEmpty) {
  struct input_event event;
  event.type = 0;
  event.code = 0;
  event.value = 0;
  EXPECT_CALL(handler_, TriggerCallback(_));
  handler_.ProcessEvent(event);
  EXPECT_FALSE(handler_.headphone_);
  EXPECT_FALSE(handler_.microphone_);
}

// Test ProcessEvent() with a microphone present input_event arg.
TEST_F(AudioDeviceHandlerTest, ProcessEventMicrophonePresent) {
  struct input_event event;
  event.type = EV_SW;
  event.code = SW_MICROPHONE_INSERT;
  event.value = 1;
  handler_.ProcessEvent(event);
  EXPECT_FALSE(handler_.headphone_);
  EXPECT_TRUE(handler_.microphone_);
}

// Test ProcessEvent() with a headphone present input_event arg.
TEST_F(AudioDeviceHandlerTest, ProcessEventHeadphonePresent) {
  struct input_event event;
  event.type = EV_SW;
  event.code = SW_HEADPHONE_INSERT;
  event.value = 1;
  handler_.ProcessEvent(event);
  EXPECT_TRUE(handler_.headphone_);
  EXPECT_FALSE(handler_.microphone_);
}

// Test ProcessEvent() with a microphone not present input_event arg.
TEST_F(AudioDeviceHandlerTest, ProcessEventMicrophoneNotPresent) {
  struct input_event event;
  event.type = EV_SW;
  event.code = SW_MICROPHONE_INSERT;
  event.value = 0;
  handler_.ProcessEvent(event);
  EXPECT_FALSE(handler_.headphone_);
  EXPECT_FALSE(handler_.microphone_);
}

// Test ProcessEvent() with a headphone not preset input_event arg.
TEST_F(AudioDeviceHandlerTest, ProcessEventHeadphoneNotPresent) {
  struct input_event event;
  event.type = EV_SW;
  event.code = SW_HEADPHONE_INSERT;
  event.value = 0;
  handler_.ProcessEvent(event);
  EXPECT_FALSE(handler_.headphone_);
  EXPECT_FALSE(handler_.microphone_);
}

// Test ProcessEvent() with an unsupported input_event arg.
TEST_F(AudioDeviceHandlerTest, ProcessEventInvalid) {
  struct input_event event;
  event.type = EV_SW;
  event.code = SW_MAX;
  event.value = 0;
  handler_.ProcessEvent(event);
  EXPECT_FALSE(handler_.headphone_);
  EXPECT_FALSE(handler_.microphone_);
}

// Test UpdateAudioSystem() without any devices connected.
TEST_F(AudioDeviceHandlerTest, UpdateAudioSystemNone) {
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(
                  _, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)).Times(0);
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesDisconnected));
  handler_.UpdateAudioSystem(handler_.headphone_, handler_.microphone_);
  EXPECT_EQ(handler_.changed_devices_.size(), 0);
}

// Test UpdateAudioSystem() when disconnecting a microphone.
TEST_F(AudioDeviceHandlerTest, UpdateAudioSystemDisconnectMic) {
  audio_devices_t device = AUDIO_DEVICE_IN_WIRED_HEADSET;
  handler_.connected_input_devices_.insert(device);
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(device,
                                       AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesDisconnected));
  handler_.UpdateAudioSystem(handler_.headphone_, handler_.microphone_);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 0);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 0);
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], device);
}

// Test UpdateAudioSystem() when disconnecting a headphone.
TEST_F(AudioDeviceHandlerTest, UpdateAudioSystemDisconnectHeadphone) {
  audio_devices_t device = AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
  handler_.connected_output_devices_.insert(device);
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(device,
                                       AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesDisconnected));
  handler_.UpdateAudioSystem(handler_.headphone_, handler_.microphone_);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 0);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 0);
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], device);
}

// Test UpdateAudioSystem() when disconnecting a headset & headphones.
TEST_F(AudioDeviceHandlerTest, UpdateAudioSystemDisconnectHeadset) {
  handler_.connected_input_devices_.insert(AUDIO_DEVICE_IN_WIRED_HEADSET);
  handler_.connected_output_devices_.insert(AUDIO_DEVICE_OUT_WIRED_HEADSET);
  handler_.connected_output_devices_.insert(AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_IN_WIRED_HEADSET,
                                       AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_OUT_WIRED_HEADSET,
                                       AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
                                       AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesDisconnected));
  handler_.UpdateAudioSystem(handler_.headphone_, handler_.microphone_);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 0);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 0);
  EXPECT_EQ(handler_.changed_devices_.size(), 3);
}

// Test UpdateAudioSystem() when connecting a microphone.
TEST_F(AudioDeviceHandlerTest, UpdateAudioSystemConnectMic) {
  handler_.microphone_ = true;
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_IN_WIRED_HEADSET,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesConnected));
  handler_.UpdateAudioSystem(handler_.headphone_, handler_.microphone_);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 1);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 0);
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], AUDIO_DEVICE_IN_WIRED_HEADSET);
}

// Test UpdateAudioSystem() when connecting a headphone.
TEST_F(AudioDeviceHandlerTest, UpdateAudioSystemConnectHeadphone) {
  handler_.headphone_ = true;
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesConnected));
  handler_.UpdateAudioSystem(handler_.headphone_, handler_.microphone_);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 0);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
}

// Test UpdateAudioSystem() when connecting a headset.
TEST_F(AudioDeviceHandlerTest, UpdateAudioSystemConnectHeadset) {
  handler_.headphone_ = true;
  handler_.microphone_ = true;
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_IN_WIRED_HEADSET,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(AUDIO_DEVICE_OUT_WIRED_HEADSET,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  EXPECT_CALL(handler_, TriggerCallback(
      AudioDeviceHandlerMock::DeviceConnectionState::kDevicesConnected));
  handler_.UpdateAudioSystem(handler_.headphone_, handler_.microphone_);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 1);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_.size(), 2);
}

// Test ConnectAudioDevice() with an input device.
TEST_F(AudioDeviceHandlerTest, ConnectAudioDeviceInput) {
  audio_devices_t device = AUDIO_DEVICE_IN_WIRED_HEADSET;
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(device,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  handler_.ConnectAudioDevice(device);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 0);
  EXPECT_NE(
      handler_.connected_input_devices_.find(device),
      handler_.connected_input_devices_.end());
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], device);
}

// Test ConnectAudioDevice() with an output device.
TEST_F(AudioDeviceHandlerTest, ConnectAudioDeviceOutput) {
  audio_devices_t device = AUDIO_DEVICE_OUT_WIRED_HEADSET;
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(device,
                                       AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
  handler_.ConnectAudioDevice(device);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 0);
  EXPECT_NE(
      handler_.connected_output_devices_.find(device),
      handler_.connected_output_devices_.end());
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], device);
}

// Test DisconnectAudioDevice() with an input device.
TEST_F(AudioDeviceHandlerTest, DisconnectAudioDeviceInput) {
  audio_devices_t device = AUDIO_DEVICE_IN_WIRED_HEADSET;
  handler_.connected_input_devices_.insert(device);
  handler_.connected_output_devices_.insert(device);
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(device,
                                       AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
  handler_.DisconnectAudioDevice(device);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 0);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], device);
}

// Test DisconnectAudioDevice() with an output device.
TEST_F(AudioDeviceHandlerTest, DisconnectAudioDeviceOutput) {
  audio_devices_t device = AUDIO_DEVICE_OUT_WIRED_HEADSET;
  handler_.connected_input_devices_.insert(device);
  handler_.connected_output_devices_.insert(device);
  EXPECT_CALL(handler_,
              NotifyAudioPolicyService(device,
                                       AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
  handler_.DisconnectAudioDevice(device);
  EXPECT_EQ(handler_.connected_input_devices_.size(), 1);
  EXPECT_EQ(handler_.connected_output_devices_.size(), 0);
  EXPECT_EQ(handler_.changed_devices_.size(), 1);
  EXPECT_EQ(handler_.changed_devices_[0], device);
}

}  // namespace brillo