chromium/ash/display/touch_calibrator_controller_unittest.cc

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

#include "ash/display/touch_calibrator_controller.h"

#include <vector>

#include "ash/display/touch_calibrator_view.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/touch/ash_touch_transform_controller.h"
#include "base/containers/contains.h"
#include "ui/display/display.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/test/test_display_layout_manager.h"
#include "ui/display/manager/test/touch_device_manager_test_api.h"
#include "ui/display/manager/test/touch_transform_controller_test_api.h"
#include "ui/display/manager/touch_device_manager.h"
#include "ui/display/manager/touch_transform_setter.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/device_data_manager_test_api.h"
#include "ui/events/devices/touch_device_transform.h"
#include "ui/events/event_handler.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/test/event_generator.h"
#include "ui/events/test/events_test_utils.h"
#include "ui/views/widget/unique_widget_ptr.h"
#include "ui/views/widget/widget.h"

namespace ash {
namespace {

ui::TouchscreenDevice GetInternalTouchDevice(int touch_device_id) {
  return ui::TouchscreenDevice(
      touch_device_id, ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
      std::string("test internal touch device"), gfx::Size(1000, 1000), 1);
}

ui::TouchscreenDevice GetExternalTouchDevice(int touch_device_id) {
  return ui::TouchscreenDevice(
      touch_device_id, ui::InputDeviceType::INPUT_DEVICE_USB,
      std::string("test external touch device"), gfx::Size(1000, 1000), 1);
}

}  // namespace

class TouchCalibratorControllerTest : public AshTestBase {
 public:
  TouchCalibratorControllerTest()
      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
  TouchCalibratorControllerTest(const TouchCalibratorControllerTest&) = delete;
  TouchCalibratorControllerTest& operator=(
      const TouchCalibratorControllerTest&) = delete;

  void TearDown() override {
    // Reset all touch device and touch association.
    display::test::TouchDeviceManagerTestApi(touch_device_manager())
        .ResetTouchDeviceManager();
    ui::DeviceDataManagerTestApi().SetTouchscreenDevices({});
    display::test::TouchTransformControllerTestApi(
        Shell::Get()->touch_transformer_controller())
        .touch_transform_setter()
        ->ConfigureTouchDevices(std::vector<ui::TouchDeviceTransform>());
    AshTestBase::TearDown();
  }

  display::TouchDeviceManager* touch_device_manager() {
    return display_manager()->touch_device_manager();
  }

  int GetTouchDeviceId(const TouchCalibratorController& ctrl) {
    return ctrl.touch_device_id_;
  }

  const TouchCalibratorController::CalibrationPointPairQuad& GetTouchPointQuad(
      const TouchCalibratorController& ctrl) {
    return ctrl.touch_point_quad_;
  }

  std::map<int64_t, views::UniqueWidgetPtr>& GetCalibratorViews(
      TouchCalibratorController* ctrl) {
    return ctrl->touch_calibrator_widgets_;
  }

  const display::Display& InitDisplays() {
    // Initialize 2 displays each with resolution 600x500.
    UpdateDisplay("600x500,600x500");
    // Assuming index 0 points to the native display, we will calibrate the
    // touch display at index 1.
    display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
        .SetFirstDisplayAsInternalDisplay();
    const int kTargetDisplayIndex = 1;
    display::DisplayIdList display_id_list =
        display_manager()->GetConnectedDisplayIdList();
    int64_t target_display_id = display_id_list[kTargetDisplayIndex];
    const display::Display& touch_display =
        display_manager()->GetDisplayForId(target_display_id);
    return touch_display;
  }

  void StartCalibrationChecks(TouchCalibratorController* ctrl,
                              const display::Display& target_display) {
    EXPECT_FALSE(ctrl->IsCalibrating());
    EXPECT_FALSE(!!ctrl->touch_calibrator_widgets_.size());

    TouchCalibratorController::TouchCalibrationCallback empty_callback;

    ctrl->StartCalibration(target_display, false /* is_custom_calibration */,
                           std::move(empty_callback));

    EXPECT_TRUE(ctrl->IsCalibrating());
    EXPECT_EQ(ctrl->state_,
              TouchCalibratorController::CalibrationState::kNativeCalibration);

    // There should be a touch calibrator view associated with each of the
    // active displays.
    EXPECT_EQ(ctrl->touch_calibrator_widgets_.size(),
              display_manager()->GetConnectedDisplayIdList().size());

    TouchCalibratorView* target_calibrator_view =
        static_cast<TouchCalibratorView*>(
            GetCalibratorViews(ctrl)[target_display.id()]->GetContentsView());

    // End the background fade in animation.
    target_calibrator_view->SkipCurrentAnimation();

    // TouchCalibratorView on the display being calibrated should be at the
    // state where the first display point is visible.
    EXPECT_EQ(target_calibrator_view->state(),
              TouchCalibratorView::DISPLAY_POINT_1);
  }

  void StartMappingChecks(TouchCalibratorController* ctrl,
                          const display::Display& external_display) {
    EXPECT_FALSE(ctrl->IsCalibrating());
    EXPECT_FALSE(!!ctrl->touch_calibrator_widgets_.size());

    TouchCalibratorController::TouchCalibrationCallback empty_callback;

    ctrl->StartNativeTouchscreenMappingExperience(std::move(empty_callback));

    EXPECT_TRUE(ctrl->IsCalibrating());
    EXPECT_EQ(ctrl->state_, TouchCalibratorController::CalibrationState::
                                kNativeCalibrationTouchscreenMapping);

    // There should be a touch calibrator view associated with each of the
    // active displays.
    EXPECT_EQ(ctrl->touch_calibrator_widgets_.size(),
              display_manager()->GetConnectedDisplayIdList().size());

    TouchCalibratorView* target_calibrator_view =
        static_cast<TouchCalibratorView*>(
            GetCalibratorViews(ctrl)[external_display.id()]->GetContentsView());

    // End the background fade in animation.
    target_calibrator_view->SkipCurrentAnimation();

    // TouchCalibratorView on the display being calibrated should be at the
    // state where the first display point is visible.
    EXPECT_EQ(target_calibrator_view->state(),
              TouchCalibratorView::DISPLAY_POINT_1);
  }

  // Resets the timestamp threshold so that touch events are not discarded.
  void ResetTimestampThreshold(TouchCalibratorController* ctrl) {
    base::Time current = base::Time::Now();
    ctrl->last_touch_timestamp_ =
        current - TouchCalibratorController::kTouchIntervalThreshold;
  }

  // Generates a touch press and release event in the |display| with source
  // device id as |touch_device_id|.
  void GenerateTouchEvent(const display::Display& display,
                          int touch_device_id,
                          const gfx::Point& location = gfx::Point(20, 20)) {
    // Get the correct EventTarget for the given |display|.
    aura::Window::Windows root_windows = Shell::GetAllRootWindows();
    ui::EventTarget* event_target = nullptr;
    for (aura::Window* window : root_windows) {
      if (display::Screen::GetScreen()->GetDisplayNearestWindow(window).id() ==
          display.id()) {
        event_target = window;
        break;
      }
    }

    ui::test::EventGenerator* eg = GetEventGenerator();
    eg->set_current_target(event_target);

    ui::TouchEvent press_touch_event(
        ui::EventType::kTouchPressed, location, ui::EventTimeForNow(),
        ui::PointerDetails(ui::EventPointerType::kTouch, 12, 1.0f, 1.0f, 0.0f),
        0);
    ui::TouchEvent release_touch_event(
        ui::EventType::kTouchReleased, location, ui::EventTimeForNow(),
        ui::PointerDetails(ui::EventPointerType::kTouch, 12, 1.0f, 1.0f, 0.0f),
        0);

    press_touch_event.set_source_device_id(touch_device_id);
    release_touch_event.set_source_device_id(touch_device_id);

    eg->Dispatch(&press_touch_event);
    eg->Dispatch(&release_touch_event);
  }

  ui::TouchscreenDevice InitTouchDevice(
      int64_t display_id,
      const ui::TouchscreenDevice& touchdevice) {
    ui::DeviceDataManagerTestApi().SetTouchscreenDevices({touchdevice});

    std::vector<ui::TouchDeviceTransform> transforms;
    ui::TouchDeviceTransform touch_device_transform;
    touch_device_transform.display_id = display_id;
    touch_device_transform.device_id = touchdevice.id;
    transforms.push_back(touch_device_transform);

    // This makes touchscreen target displays valid for ui::DeviceDataManager.
    display::test::TouchTransformControllerTestApi(
        Shell::Get()->touch_transformer_controller())
        .touch_transform_setter()
        ->ConfigureTouchDevices(transforms);
    return touchdevice;
  }
};

TEST_F(TouchCalibratorControllerTest, StartCalibration) {
  const display::Display& touch_display = InitDisplays();
  TouchCalibratorController touch_calibrator_controller;
  StartCalibrationChecks(&touch_calibrator_controller, touch_display);

  ui::EventTargetTestApi test_api(Shell::Get());
  ui::EventHandlerList handlers = test_api.GetPreTargetHandlers();
  EXPECT_TRUE(base::Contains(handlers, &touch_calibrator_controller));
}

TEST_F(TouchCalibratorControllerTest, StartTouchscreenMapping) {
  const display::Display& touch_display = InitDisplays();
  TouchCalibratorController touch_calibrator_controller;
  StartMappingChecks(&touch_calibrator_controller, touch_display);

  ui::EventTargetTestApi test_api(Shell::Get());
  ui::EventHandlerList handlers = test_api.GetPreTargetHandlers();
  EXPECT_TRUE(base::Contains(handlers, &touch_calibrator_controller));
}

TEST_F(TouchCalibratorControllerTest, Mapping_OneExternalDisplay_FullFlow) {
  const display::Display& touch_display = InitDisplays();
  TouchCalibratorController touch_calibrator_controller;
  StartMappingChecks(&touch_calibrator_controller, touch_display);

  // Initialize touch device so that |event_transformer_| can be computed.
  ui::TouchscreenDevice touchdevice =
      InitTouchDevice(touch_display.id(), GetExternalTouchDevice(12));

  // Clock must be advanced between each touch so the controller recognizes them
  // as discreet touches.
  EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
  task_environment()->AdvanceClock(base::Seconds(1));
  GenerateTouchEvent(touch_display, touchdevice.id);

  EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
  task_environment()->AdvanceClock(base::Seconds(1));
  GenerateTouchEvent(touch_display, touchdevice.id);

  EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
  task_environment()->AdvanceClock(base::Seconds(1));
  GenerateTouchEvent(touch_display, touchdevice.id);

  EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
  task_environment()->AdvanceClock(base::Seconds(1));
  GenerateTouchEvent(touch_display, touchdevice.id);

  // Done calibrating after first display has been touched 4 times.
  EXPECT_FALSE(touch_calibrator_controller.IsCalibrating());
}

TEST_F(TouchCalibratorControllerTest, Mapping_TwoExternalDisplays_FullFlow) {
  UpdateDisplay("600x500,600x500,600x500");
  display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
      .SetFirstDisplayAsInternalDisplay();

  const display::DisplayIdList display_id_list =
      display_manager()->GetConnectedDisplayIdList();
  const display::Display& first_display =
      display_manager()->GetDisplayForId(display_id_list[1]);
  const display::Display& second_display =
      display_manager()->GetDisplayForId(display_id_list[2]);

  TouchCalibratorController touch_calibrator_controller;
  StartMappingChecks(&touch_calibrator_controller, first_display);

  // Initialize touch devices so that |event_transformer_| can be computed.
  // We create 2 touch devices.
  const int kExternalTouchId1 = 10;
  const int kExternalTouchId2 = 11;

  ui::TouchscreenDevice external_touchscreen1(
      kExternalTouchId1, ui::InputDeviceType::INPUT_DEVICE_USB,
      std::string("external touch device 1"), gfx::Size(1000, 1000), 1);
  ui::TouchscreenDevice external_touchscreen2(
      kExternalTouchId2, ui::InputDeviceType::INPUT_DEVICE_USB,
      std::string("external touch device 2"), gfx::Size(1000, 1000), 1);

  ui::DeviceDataManagerTestApi().SetTouchscreenDevices(
      {external_touchscreen1, external_touchscreen2});

  // Associate both touch devices to the first display.
  std::vector<ui::TouchDeviceTransform> transforms;
  ui::TouchDeviceTransform touch_device_transform;
  touch_device_transform.display_id = first_display.id();
  touch_device_transform.device_id = external_touchscreen1.id;
  transforms.push_back(touch_device_transform);

  touch_device_transform.device_id = external_touchscreen2.id;
  transforms.push_back(touch_device_transform);

  display::test::TouchTransformControllerTestApi(
      Shell::Get()->touch_transformer_controller())
      .touch_transform_setter()
      ->ConfigureTouchDevices(transforms);

  // Update touch device manager with the associations.
  display::test::TouchDeviceManagerTestApi(touch_device_manager())
      .Associate(first_display.id(), external_touchscreen1);
  display::test::TouchDeviceManagerTestApi(touch_device_manager())
      .Associate(first_display.id(), external_touchscreen2);

  // Touch the first device four times.
  {
    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(first_display, external_touchscreen1.id);

    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(first_display, external_touchscreen1.id);

    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(first_display, external_touchscreen1.id);

    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(first_display, external_touchscreen1.id);
  }

  // We still should be calibrating for the touchscreen mapping since there are
  // two monitors.
  EXPECT_EQ(touch_calibrator_controller.state_,
            TouchCalibratorController::CalibrationState::
                kNativeCalibrationTouchscreenMapping);
  TouchCalibratorView* target_calibrator_view =
      static_cast<TouchCalibratorView*>(
          GetCalibratorViews(&touch_calibrator_controller)[second_display.id()]
              ->GetContentsView());
  // Need to skip the first animation to get to a state where we can touch the
  // screen.
  target_calibrator_view->SkipCurrentAnimation();

  // Touch the second device four times.
  {
    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(second_display, external_touchscreen2.id);

    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(second_display, external_touchscreen2.id);

    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(second_display, external_touchscreen2.id);

    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(second_display, external_touchscreen2.id);
  }

  // Done calibrating after both displays have been fully calibrated.
  EXPECT_FALSE(touch_calibrator_controller.IsCalibrating());
}

TEST_F(TouchCalibratorControllerTest, Mapping_TwoExternalDisplays_SkipFirst) {
  UpdateDisplay("600x500,600x500,600x500");
  display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
      .SetFirstDisplayAsInternalDisplay();

  const display::DisplayIdList display_id_list =
      display_manager()->GetConnectedDisplayIdList();
  const display::Display& first_display =
      display_manager()->GetDisplayForId(display_id_list[1]);
  const display::Display& second_display =
      display_manager()->GetDisplayForId(display_id_list[2]);

  TouchCalibratorController touch_calibrator_controller;
  StartMappingChecks(&touch_calibrator_controller, first_display);

  // Initialize touch devices so that |event_transformer_| can be computed.
  ui::TouchscreenDevice touchdevice =
      InitTouchDevice(first_display.id(), GetExternalTouchDevice(13));

  // Press escape to skip first display.
  PressAndReleaseKey(ui::VKEY_ESCAPE);

  // We still should be calibrating for the touchscreen mapping since there are
  // two monitors.
  EXPECT_EQ(touch_calibrator_controller.state_,
            TouchCalibratorController::CalibrationState::
                kNativeCalibrationTouchscreenMapping);
  TouchCalibratorView* target_calibrator_view =
      static_cast<TouchCalibratorView*>(
          GetCalibratorViews(&touch_calibrator_controller)[second_display.id()]
              ->GetContentsView());
  // Need to skip the first animation to get to a state where we can touch the
  // screen.
  target_calibrator_view->SkipCurrentAnimation();

  // Touch the second device four times.
  {
    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(second_display, touchdevice.id);

    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(second_display, touchdevice.id);

    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(second_display, touchdevice.id);

    EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
    task_environment()->AdvanceClock(base::Seconds(1));
    GenerateTouchEvent(second_display, touchdevice.id);
  }

  // Done calibrating after both displays have been fully calibrated.
  EXPECT_FALSE(touch_calibrator_controller.IsCalibrating());
}

TEST_F(TouchCalibratorControllerTest, KeyEventIntercept) {
  const display::Display& touch_display = InitDisplays();
  TouchCalibratorController touch_calibrator_controller;
  StartCalibrationChecks(&touch_calibrator_controller, touch_display);

  EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
  PressAndReleaseKey(ui::VKEY_ESCAPE);
  EXPECT_FALSE(touch_calibrator_controller.IsCalibrating());
}

TEST_F(TouchCalibratorControllerTest, TouchThreshold) {
  const display::Display& touch_display = InitDisplays();
  TouchCalibratorController touch_calibrator_controller;
  StartCalibrationChecks(&touch_calibrator_controller, touch_display);

  // Initialize touch device so that |event_transnformer_| can be computed.
  ui::TouchscreenDevice touchdevice =
      InitTouchDevice(touch_display.id(), GetExternalTouchDevice(12));

  base::Time current_timestamp = base::Time::Now();
  touch_calibrator_controller.last_touch_timestamp_ = current_timestamp;

  // This touch event should be rejected as the time threshold has not been
  // crossed.
  GenerateTouchEvent(touch_display, touchdevice.id);

  EXPECT_EQ(touch_calibrator_controller.last_touch_timestamp_,
            current_timestamp);

  task_environment()->AdvanceClock(base::Seconds(1));
  ResetTimestampThreshold(&touch_calibrator_controller);

  // This time the events should be registered as the threshold is crossed.
  GenerateTouchEvent(touch_display, touchdevice.id);

  EXPECT_LT(current_timestamp,
            touch_calibrator_controller.last_touch_timestamp_);
}

TEST_F(TouchCalibratorControllerTest, TouchDeviceIdIsSet) {
  const display::Display& touch_display = InitDisplays();

  TouchCalibratorController touch_calibrator_controller;
  StartCalibrationChecks(&touch_calibrator_controller, touch_display);

  ui::TouchscreenDevice touchdevice =
      InitTouchDevice(touch_display.id(), GetExternalTouchDevice(12));

  ResetTimestampThreshold(&touch_calibrator_controller);

  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            ui::InputDevice::kInvalidId);
  GenerateTouchEvent(touch_display, touchdevice.id);
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller), touchdevice.id);
}

TEST_F(TouchCalibratorControllerTest, CustomCalibration) {
  const display::Display& touch_display = InitDisplays();

  TouchCalibratorController touch_calibrator_controller;
  EXPECT_FALSE(touch_calibrator_controller.IsCalibrating());
  EXPECT_FALSE(!!GetCalibratorViews(&touch_calibrator_controller).size());

  touch_calibrator_controller.StartCalibration(
      touch_display, true /* is_custom_calibration */,
      TouchCalibratorController::TouchCalibrationCallback());

  ui::TouchscreenDevice touchdevice =
      InitTouchDevice(touch_display.id(), GetExternalTouchDevice(12));

  EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
  EXPECT_EQ(touch_calibrator_controller.state_,
            TouchCalibratorController::CalibrationState::kCustomCalibration);

  // Native touch calibration UX should not initialize during custom calibration
  EXPECT_EQ(GetCalibratorViews(&touch_calibrator_controller).size(), 0UL);
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            ui::InputDevice::kInvalidId);

  ResetTimestampThreshold(&touch_calibrator_controller);

  GenerateTouchEvent(touch_display, touchdevice.id);
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller), touchdevice.id);

  display::TouchCalibrationData::CalibrationPointPairQuad points = {
      {std::make_pair(gfx::Point(10, 10), gfx::Point(11, 12)),
       std::make_pair(gfx::Point(190, 10), gfx::Point(195, 8)),
       std::make_pair(gfx::Point(10, 90), gfx::Point(12, 94)),
       std::make_pair(gfx::Point(190, 90), gfx::Point(189, 88))}};
  gfx::Size size(200, 100);
  display::TouchCalibrationData calibration_data(points, size);

  touch_calibrator_controller.CompleteCalibration(points, size);

  const display::ManagedDisplayInfo& info =
      display_manager()->GetDisplayInfo(touch_display.id());

  display::test::TouchDeviceManagerTestApi tdm_test_api(touch_device_manager());
  EXPECT_TRUE(tdm_test_api.AreAssociated(info, touchdevice));
  EXPECT_EQ(calibration_data,
            touch_device_manager()->GetCalibrationData(touchdevice, info.id()));
}

TEST_F(TouchCalibratorControllerTest, CustomCalibrationInvalidTouchId) {
  const display::Display& touch_display = InitDisplays();

  TouchCalibratorController touch_calibrator_controller;
  EXPECT_FALSE(touch_calibrator_controller.IsCalibrating());
  EXPECT_FALSE(!!GetCalibratorViews(&touch_calibrator_controller).size());

  touch_calibrator_controller.StartCalibration(
      touch_display, true /* is_custom_calibration */,
      TouchCalibratorController::TouchCalibrationCallback());

  EXPECT_TRUE(touch_calibrator_controller.IsCalibrating());
  EXPECT_EQ(touch_calibrator_controller.state_,
            TouchCalibratorController::CalibrationState::kCustomCalibration);

  // Native touch calibration UX should not initialize during custom calibration
  EXPECT_EQ(GetCalibratorViews(&touch_calibrator_controller).size(), 0UL);
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            ui::InputDevice::kInvalidId);

  ResetTimestampThreshold(&touch_calibrator_controller);

  display::TouchCalibrationData::CalibrationPointPairQuad points = {
      {std::make_pair(gfx::Point(10, 10), gfx::Point(11, 12)),
       std::make_pair(gfx::Point(190, 10), gfx::Point(195, 8)),
       std::make_pair(gfx::Point(10, 90), gfx::Point(12, 94)),
       std::make_pair(gfx::Point(190, 90), gfx::Point(189, 88))}};
  gfx::Size size(200, 100);
  display::TouchCalibrationData calibration_data(points, size);

  touch_calibrator_controller.CompleteCalibration(points, size);

  const display::ManagedDisplayInfo& info =
      display_manager()->GetDisplayInfo(touch_display.id());

  ui::TouchscreenDevice random_touchdevice(
      15, ui::InputDeviceType::INPUT_DEVICE_USB,
      std::string("random touch device"), gfx::Size(123, 456), 1);
  EXPECT_EQ(calibration_data, touch_device_manager()->GetCalibrationData(
                                  random_touchdevice, info.id()));
}

TEST_F(TouchCalibratorControllerTest, IgnoreInternalTouchDevices) {
  const display::Display& touch_display = InitDisplays();

  // We need to initialize a touch device before starting calibration so that
  // the set |internal_touch_device_ids_| can be initialized.
  ui::TouchscreenDevice internal_touchdevice =
      InitTouchDevice(touch_display.id(), GetInternalTouchDevice(12));

  TouchCalibratorController touch_calibrator_controller;
  StartCalibrationChecks(&touch_calibrator_controller, touch_display);

  // We need to reinitialize the touch device as Starting calibration resets
  // everything.
  internal_touchdevice =
      InitTouchDevice(touch_display.id(), GetInternalTouchDevice(12));

  ResetTimestampThreshold(&touch_calibrator_controller);
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            ui::InputDevice::kInvalidId);
  GenerateTouchEvent(touch_display, internal_touchdevice.id);
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            ui::InputDevice::kInvalidId);

  ui::TouchscreenDevice external_touchdevice =
      InitTouchDevice(touch_display.id(), GetExternalTouchDevice(13));
  ResetTimestampThreshold(&touch_calibrator_controller);
  GenerateTouchEvent(touch_display, external_touchdevice.id);
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            external_touchdevice.id);
}

TEST_F(TouchCalibratorControllerTest, HighDPIMonitorsCalibration) {
  // Initialize 3 displays each with different device scale factors.
  UpdateDisplay("600x500*2,400x300*3,600x500*1.5");

  // Index 0 points to the native internal display, we will calibrate the touch
  // display at index 2.
  const int kTargetDisplayIndex = 2;
  display::DisplayIdList display_id_list =
      display_manager()->GetConnectedDisplayIdList();

  int64_t internal_display_id = display_id_list[1];
  display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
                                                         internal_display_id);

  int64_t target_display_id = display_id_list[kTargetDisplayIndex];
  const display::Display& touch_display =
      display_manager()->GetDisplayForId(target_display_id);

  // We create 2 touch devices.
  const int kInternalTouchId = 10;
  const int kExternalTouchId = 11;

  ui::TouchscreenDevice internal_touchdevice(
      kInternalTouchId, ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
      std::string("internal touch device"), gfx::Size(1000, 1000), 1);
  ui::TouchscreenDevice external_touchdevice(
      kExternalTouchId, ui::InputDeviceType::INPUT_DEVICE_USB,
      std::string("external touch device"), gfx::Size(1000, 1000), 1);

  ui::DeviceDataManagerTestApi().SetTouchscreenDevices(
      {internal_touchdevice, external_touchdevice});

  // Associate both touch devices to the internal display.
  std::vector<ui::TouchDeviceTransform> transforms;
  ui::TouchDeviceTransform touch_device_transform;
  touch_device_transform.display_id = internal_display_id;
  touch_device_transform.device_id = internal_touchdevice.id;
  transforms.push_back(touch_device_transform);

  touch_device_transform.device_id = external_touchdevice.id;
  transforms.push_back(touch_device_transform);

  display::test::TouchTransformControllerTestApi(
      Shell::Get()->touch_transformer_controller())
      .touch_transform_setter()
      ->ConfigureTouchDevices(transforms);

  // Update touch device manager with the associations.
  display::test::TouchDeviceManagerTestApi(touch_device_manager())
      .Associate(internal_display_id, internal_touchdevice);
  display::test::TouchDeviceManagerTestApi(touch_device_manager())
      .Associate(internal_display_id, external_touchdevice);

  TouchCalibratorController touch_calibrator_controller;
  touch_calibrator_controller.StartCalibration(
      touch_display, false /* is_custom_calibration */,
      TouchCalibratorController::TouchCalibrationCallback());

  // Skip any UI animations associated with the start of calibration.
  static_cast<TouchCalibratorView*>(
      GetCalibratorViews(&touch_calibrator_controller)[touch_display.id()]
          ->GetContentsView())
      ->SkipCurrentAnimation();

  // Reinitialize the transforms, as starting calibration resets them.
  display::test::TouchTransformControllerTestApi(
      Shell::Get()->touch_transformer_controller())
      .touch_transform_setter()
      ->ConfigureTouchDevices(transforms);

  // The touch device id has not been set yet, as the first touch event has not
  // been generated.
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            ui::InputDevice::kInvalidId);

  ResetTimestampThreshold(&touch_calibrator_controller);

  // Generate a touch event at point (100, 100) on the external touch device.
  gfx::Point touch_point(100, 100);
  GenerateTouchEvent(display_manager()->GetDisplayForId(internal_display_id),
                     external_touchdevice.id, touch_point);
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            external_touchdevice.id);

  // The touch event should be received as is.
  EXPECT_EQ(GetTouchPointQuad(touch_calibrator_controller).at(0).second,
            gfx::Point(100, 100));

  // The display point should have the root transform applied.
  EXPECT_EQ(GetTouchPointQuad(touch_calibrator_controller).at(0).first,
            gfx::Point(210, 210));
}

TEST_F(TouchCalibratorControllerTest, RotatedHighDPIMonitorsCalibration) {
  // Initialize 2 displays each with resolution 500x500. One of them at 2x
  // device scale factor.
  UpdateDisplay("600x500*2,600x500*1.5/r");

  // Index 0 points to the native internal display, we will calibrate the touch
  // display at index 1.
  const int kTargetDisplayIndex = 1;
  display::DisplayIdList display_id_list =
      display_manager()->GetConnectedDisplayIdList();

  int64_t internal_display_id = display_id_list[0];
  display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
                                                         internal_display_id);

  int64_t target_display_id = display_id_list[kTargetDisplayIndex];
  const display::Display& touch_display =
      display_manager()->GetDisplayForId(target_display_id);

  // We create 2 touch devices.
  const int kInternalTouchId = 10;
  const int kExternalTouchId = 11;

  ui::TouchscreenDevice internal_touchdevice(
      kInternalTouchId, ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
      std::string("internal touch device"), gfx::Size(1000, 1000), 1);
  ui::TouchscreenDevice external_touchdevice(
      kExternalTouchId, ui::InputDeviceType::INPUT_DEVICE_USB,
      std::string("external touch device"), gfx::Size(1000, 1000), 1);

  ui::DeviceDataManagerTestApi().SetTouchscreenDevices(
      {internal_touchdevice, external_touchdevice});

  // Associate both touch devices to the internal display.
  std::vector<ui::TouchDeviceTransform> transforms;
  ui::TouchDeviceTransform touch_device_transform;
  touch_device_transform.display_id = internal_display_id;
  touch_device_transform.device_id = internal_touchdevice.id;
  transforms.push_back(touch_device_transform);

  touch_device_transform.device_id = external_touchdevice.id;
  transforms.push_back(touch_device_transform);

  display::test::TouchTransformControllerTestApi(
      Shell::Get()->touch_transformer_controller())
      .touch_transform_setter()
      ->ConfigureTouchDevices(transforms);

  // Update touch device manager with the associations.
  display::test::TouchDeviceManagerTestApi(touch_device_manager())
      .Associate(internal_display_id, internal_touchdevice);
  display::test::TouchDeviceManagerTestApi(touch_device_manager())
      .Associate(internal_display_id, external_touchdevice);

  TouchCalibratorController touch_calibrator_controller;
  touch_calibrator_controller.StartCalibration(
      touch_display, false /* is_custom_calibration */,
      TouchCalibratorController::TouchCalibrationCallback());

  // Skip any UI animations associated with the start of calibration.
  static_cast<TouchCalibratorView*>(
      GetCalibratorViews(&touch_calibrator_controller)[touch_display.id()]
          ->GetContentsView())
      ->SkipCurrentAnimation();

  // Reinitialize the transforms, as starting calibration resets them.
  display::test::TouchTransformControllerTestApi(
      Shell::Get()->touch_transformer_controller())
      .touch_transform_setter()
      ->ConfigureTouchDevices(transforms);

  // The touch device id has not been set yet, as the first touch event has not
  // been generated.
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            ui::InputDevice::kInvalidId);

  ResetTimestampThreshold(&touch_calibrator_controller);

  // Generate a touch event at point (100, 100) on the external touch device.
  gfx::Point touch_point(100, 100);

  // Simulate the touch event for external touch device. Since the device is
  // currently associated with the internal display, we generate the touch event
  // for the internal display.
  GenerateTouchEvent(display_manager()->GetDisplayForId(internal_display_id),
                     external_touchdevice.id, touch_point);
  EXPECT_EQ(GetTouchDeviceId(touch_calibrator_controller),
            external_touchdevice.id);

  // The touch event should now be within the bounds the the target display.
  EXPECT_EQ(GetTouchPointQuad(touch_calibrator_controller).at(0).second,
            gfx::Point(100, 100));

  // The display point should have the root transform applied.
  EXPECT_EQ(GetTouchPointQuad(touch_calibrator_controller).at(0).first,
            gfx::Point(390, 210));
}

TEST_F(TouchCalibratorControllerTest, InternalTouchDeviceIsRejected) {
  const display::Display& touch_display = InitDisplays();

  // We need to initialize a touch device before starting calibration so that
  // the set |internal_touch_device_ids_| can be initialized.
  ui::TouchscreenDevice internal_touchdevice =
      InitTouchDevice(touch_display.id(), GetInternalTouchDevice(12));

  TouchCalibratorController touch_calibrator_controller;
  touch_calibrator_controller.touch_device_id_ = internal_touchdevice.id;
  touch_calibrator_controller.target_display_ = touch_display;

  display::TouchCalibrationData::CalibrationPointPairQuad points = {
      {std::make_pair(gfx::Point(10, 10), gfx::Point(11, 12)),
       std::make_pair(gfx::Point(190, 10), gfx::Point(195, 8)),
       std::make_pair(gfx::Point(10, 90), gfx::Point(12, 94)),
       std::make_pair(gfx::Point(190, 90), gfx::Point(189, 88))}};
  gfx::Size size(200, 100);
  display::TouchCalibrationData calibration_data(points, size);

  touch_calibrator_controller.CompleteCalibration(points, size);

  const display::ManagedDisplayInfo& info =
      display_manager()->GetDisplayInfo(touch_display.id());

  display::test::TouchDeviceManagerTestApi tdm_test_api(touch_device_manager());
  EXPECT_FALSE(tdm_test_api.AreAssociated(info, internal_touchdevice));
  EXPECT_TRUE(touch_device_manager()
                  ->GetCalibrationData(internal_touchdevice, info.id())
                  .IsEmpty());
}

}  // namespace ash