// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/devices/x11/device_data_manager_x11.h"

#include <vector>

#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/devices/device_hotplug_event_observer.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/input_device_event_observer.h"
#include "ui/events/devices/touchscreen_device.h"

namespace ui {
namespace test {
namespace {

class TestInputDeviceObserver : public InputDeviceEventObserver {
 public:
  explicit TestInputDeviceObserver(DeviceDataManagerX11* manager)
      : manager_(manager), change_notified_(false) {
    if (manager_)
      manager_->AddObserver(this);
  }

  ~TestInputDeviceObserver() override {
    if (manager_)
      manager_->RemoveObserver(this);
  }

  // InputDeviceEventObserver implementation.
  void OnKeyboardDeviceConfigurationChanged() override {
    change_notified_ = true;
  }

  int change_notified() const { return change_notified_; }
  void Reset() { change_notified_ = false; }

 private:
  DeviceDataManager* manager_;
  bool change_notified_;

  DISALLOW_COPY_AND_ASSIGN(TestInputDeviceObserver);
};

}  //  namespace

class DeviceDataManagerX11Test : public testing::Test {
 public:
  DeviceDataManagerX11Test() {}
  ~DeviceDataManagerX11Test() override {}

  void SetUp() override { DeviceDataManagerX11::CreateInstance(); }

  void TearDown() override { SetKeyboardDevices(std::vector<InputDevice>()); }

  virtual void SetKeyboardDevices(const std::vector<InputDevice>& devices) {
    DeviceHotplugEventObserver* manager = DeviceDataManagerX11::GetInstance();
    manager->OnKeyboardDevicesUpdated(devices);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(DeviceDataManagerX11Test);
};

// Tests that the the device data manager notifies observers when a device is
// disabled and re-enabled.
TEST_F(DeviceDataManagerX11Test, NotifyOnDisable) {
  DeviceDataManagerX11* manager = DeviceDataManagerX11::GetInstance();
  TestInputDeviceObserver observer(manager);
  std::vector<ui::InputDevice> keyboards;
  keyboards.push_back(ui::InputDevice(
      1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
  keyboards.push_back(ui::InputDevice(
      2, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
  SetKeyboardDevices(keyboards);
  EXPECT_TRUE(observer.change_notified());
  std::vector<InputDevice> devices = manager->GetKeyboardDevices();
  EXPECT_EQ(keyboards.size(), devices.size());
  observer.Reset();
  // Disable the device, should be notified that the device list contains one
  // less device.
  manager->DisableDevice(2);
  EXPECT_TRUE(observer.change_notified());
  devices = manager->GetKeyboardDevices();
  EXPECT_EQ(1u, devices.size());
  InputDevice device = devices.front();
  EXPECT_EQ(1, device.id);
  observer.Reset();
  // Reenable the device, should be notified that the device list contains one
  // more device.
  manager->EnableDevice(2);
  EXPECT_TRUE(observer.change_notified());
  devices = manager->GetKeyboardDevices();
  EXPECT_EQ(keyboards.size(), devices.size());
}

// Tests blocking multiple devices.
TEST_F(DeviceDataManagerX11Test, TestMultipleDisable) {
  DeviceDataManagerX11* manager = DeviceDataManagerX11::GetInstance();
  TestInputDeviceObserver observer(manager);
  std::vector<ui::InputDevice> keyboards;
  keyboards.push_back(ui::InputDevice(
      1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
  keyboards.push_back(ui::InputDevice(
      2, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
  SetKeyboardDevices(keyboards);
  EXPECT_TRUE(observer.change_notified());
  std::vector<InputDevice> devices = manager->GetKeyboardDevices();
  EXPECT_EQ(keyboards.size(), devices.size());
  observer.Reset();
  // Disable the device, should be notified that the device list contains one
  // less device.
  manager->DisableDevice(1);
  EXPECT_TRUE(observer.change_notified());
  devices = manager->GetKeyboardDevices();
  EXPECT_EQ(1u, devices.size());
  observer.Reset();
  // Disable the second device, should be notified that the device list empty.
  manager->DisableDevice(2);
  EXPECT_TRUE(observer.change_notified());
  devices = manager->GetKeyboardDevices();
  EXPECT_EQ(0u, devices.size());
  observer.Reset();
  // Enable the first device, should be notified that one device present.
  manager->EnableDevice(1);
  EXPECT_TRUE(observer.change_notified());
  devices = manager->GetKeyboardDevices();
  EXPECT_EQ(1u, devices.size());
  observer.Reset();
  // Enable the second device, should be notified that both devices present.
  manager->EnableDevice(2);
  EXPECT_TRUE(observer.change_notified());
  devices = manager->GetKeyboardDevices();
  EXPECT_EQ(2u, devices.size());
}

TEST_F(DeviceDataManagerX11Test, UnblockOnDeviceUnplugged) {
  DeviceDataManagerX11* manager = DeviceDataManagerX11::GetInstance();
  TestInputDeviceObserver observer(manager);
  std::vector<ui::InputDevice> all_keyboards;
  all_keyboards.push_back(ui::InputDevice(
      1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
  all_keyboards.push_back(ui::InputDevice(
      2, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
  SetKeyboardDevices(all_keyboards);
  EXPECT_TRUE(observer.change_notified());
  std::vector<InputDevice> devices = manager->GetKeyboardDevices();
  EXPECT_EQ(all_keyboards.size(), devices.size());
  observer.Reset();
  // Expect to be notified that the device is no longer available.
  manager->DisableDevice(2);
  EXPECT_TRUE(observer.change_notified());
  devices = manager->GetKeyboardDevices();
  EXPECT_EQ(1u, devices.size());
  observer.Reset();
  // Unplug the disabled device. Should not be notified, since the active list
  // did not change.
  std::vector<ui::InputDevice> subset_keyboards;
  subset_keyboards.push_back(ui::InputDevice(
      1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
  SetKeyboardDevices(subset_keyboards);
  EXPECT_FALSE(observer.change_notified());
  // Replug in the first device. Should be notified of the new device.
  SetKeyboardDevices(all_keyboards);
  EXPECT_TRUE(observer.change_notified());
  devices = manager->GetKeyboardDevices();
  // Both devices now present.
  EXPECT_EQ(2u, devices.size());
}

}  // namespace test
}  // namespace ui
