// Copyright 2013 The Flutter 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 "flutter/lib/ui/window/pointer_data_packet_converter.h"

#include <cstring>

#include "gtest/gtest.h"

namespace flutter {
namespace testing {

void CreateSimulatedPointerData(PointerData& data,  // NOLINT
                                PointerData::Change change,
                                int64_t device,
                                double dx,
                                double dy,
                                int64_t buttons) {
  data.time_stamp = 0;
  data.change = change;
  data.kind = PointerData::DeviceKind::kTouch;
  data.signal_kind = PointerData::SignalKind::kNone;
  data.device = device;
  data.pointer_identifier = 0;
  data.physical_x = dx;
  data.physical_y = dy;
  data.physical_delta_x = 0.0;
  data.physical_delta_y = 0.0;
  data.buttons = buttons;
  data.obscured = 0;
  data.synthesized = 0;
  data.pressure = 0.0;
  data.pressure_min = 0.0;
  data.pressure_max = 0.0;
  data.distance = 0.0;
  data.distance_max = 0.0;
  data.size = 0.0;
  data.radius_major = 0.0;
  data.radius_minor = 0.0;
  data.radius_min = 0.0;
  data.radius_max = 0.0;
  data.orientation = 0.0;
  data.tilt = 0.0;
  data.platformData = 0;
  data.scroll_delta_x = 0.0;
  data.scroll_delta_y = 0.0;
}

void CreateSimulatedMousePointerData(PointerData& data,  // NOLINT
                                     PointerData::Change change,
                                     PointerData::SignalKind signal_kind,
                                     int64_t device,
                                     double dx,
                                     double dy,
                                     double scroll_delta_x,
                                     double scroll_delta_y,
                                     int64_t buttons) {
  data.time_stamp = 0;
  data.change = change;
  data.kind = PointerData::DeviceKind::kMouse;
  data.signal_kind = signal_kind;
  data.device = device;
  data.pointer_identifier = 0;
  data.physical_x = dx;
  data.physical_y = dy;
  data.physical_delta_x = 0.0;
  data.physical_delta_y = 0.0;
  data.buttons = buttons;
  data.obscured = 0;
  data.synthesized = 0;
  data.pressure = 0.0;
  data.pressure_min = 0.0;
  data.pressure_max = 0.0;
  data.distance = 0.0;
  data.distance_max = 0.0;
  data.size = 0.0;
  data.radius_major = 0.0;
  data.radius_minor = 0.0;
  data.radius_min = 0.0;
  data.radius_max = 0.0;
  data.orientation = 0.0;
  data.tilt = 0.0;
  data.platformData = 0;
  data.scroll_delta_x = scroll_delta_x;
  data.scroll_delta_y = scroll_delta_y;
}

void CreateSimulatedTrackpadGestureData(PointerData& data,  // NOLINT
                                        PointerData::Change change,
                                        int64_t device,
                                        double dx,
                                        double dy,
                                        double pan_x,
                                        double pan_y,
                                        double scale,
                                        double rotation) {
  data.time_stamp = 0;
  data.change = change;
  data.kind = PointerData::DeviceKind::kMouse;
  data.signal_kind = PointerData::SignalKind::kNone;
  data.device = device;
  data.pointer_identifier = 0;
  data.physical_x = dx;
  data.physical_y = dy;
  data.physical_delta_x = 0.0;
  data.physical_delta_y = 0.0;
  data.buttons = 0;
  data.obscured = 0;
  data.synthesized = 0;
  data.pressure = 0.0;
  data.pressure_min = 0.0;
  data.pressure_max = 0.0;
  data.distance = 0.0;
  data.distance_max = 0.0;
  data.size = 0.0;
  data.radius_major = 0.0;
  data.radius_minor = 0.0;
  data.radius_min = 0.0;
  data.radius_max = 0.0;
  data.orientation = 0.0;
  data.tilt = 0.0;
  data.platformData = 0;
  data.scroll_delta_x = 0.0;
  data.scroll_delta_y = 0.0;
  data.pan_x = pan_x;
  data.pan_y = pan_y;
  data.pan_delta_x = 0.0;
  data.pan_delta_y = 0.0;
  data.scale = scale;
  data.rotation = rotation;
}

void UnpackPointerPacket(std::vector<PointerData>& output,  // NOLINT
                         std::unique_ptr<PointerDataPacket> packet) {
  for (size_t i = 0; i < packet->GetLength(); i++) {
    PointerData pointer_data = packet->GetPointerData(i);
    output.push_back(pointer_data);
  }
  packet.reset();
}

TEST(PointerDataPacketConverterTest, CanConvertPointerDataPacket) {
  PointerDataPacketConverter converter;
  auto packet = std::make_unique<PointerDataPacket>(6);
  PointerData data;
  CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
  packet->SetPointerData(0, data);
  CreateSimulatedPointerData(data, PointerData::Change::kHover, 0, 3.0, 0.0, 0);
  packet->SetPointerData(1, data);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 3.0, 0.0, 1);
  packet->SetPointerData(2, data);
  CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 4.0, 1);
  packet->SetPointerData(3, data);
  CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 4.0, 0);
  packet->SetPointerData(4, data);
  CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 4.0,
                             0);
  packet->SetPointerData(5, data);
  auto converted_packet = converter.Convert(std::move(packet));

  std::vector<PointerData> result;
  UnpackPointerPacket(result, std::move(converted_packet));

  ASSERT_EQ(result.size(), (size_t)6);
  ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[0].synthesized, 0);

  ASSERT_EQ(result[1].change, PointerData::Change::kHover);
  ASSERT_EQ(result[1].synthesized, 0);
  ASSERT_EQ(result[1].physical_delta_x, 3.0);
  ASSERT_EQ(result[1].physical_delta_y, 0.0);

  ASSERT_EQ(result[2].change, PointerData::Change::kDown);
  ASSERT_EQ(result[2].pointer_identifier, 1);
  ASSERT_EQ(result[2].synthesized, 0);

  ASSERT_EQ(result[3].change, PointerData::Change::kMove);
  ASSERT_EQ(result[3].pointer_identifier, 1);
  ASSERT_EQ(result[3].synthesized, 0);
  ASSERT_EQ(result[3].physical_delta_x, 0.0);
  ASSERT_EQ(result[3].physical_delta_y, 4.0);

  ASSERT_EQ(result[4].change, PointerData::Change::kUp);
  ASSERT_EQ(result[4].pointer_identifier, 1);
  ASSERT_EQ(result[4].synthesized, 0);

  ASSERT_EQ(result[5].change, PointerData::Change::kRemove);
  ASSERT_EQ(result[5].synthesized, 0);
}

TEST(PointerDataPacketConverterTest, CanSynthesizeDownAndUp) {
  PointerDataPacketConverter converter;
  auto packet = std::make_unique<PointerDataPacket>(4);
  PointerData data;
  CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
  packet->SetPointerData(0, data);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 3.0, 0.0, 1);
  packet->SetPointerData(1, data);
  CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 4.0, 0);
  packet->SetPointerData(2, data);
  CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 4.0,
                             0);
  packet->SetPointerData(3, data);
  auto converted_packet = converter.Convert(std::move(packet));

  std::vector<PointerData> result;
  UnpackPointerPacket(result, std::move(converted_packet));

  ASSERT_EQ(result.size(), (size_t)6);
  ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[0].synthesized, 0);

  // A hover should be synthesized.
  ASSERT_EQ(result[1].change, PointerData::Change::kHover);
  ASSERT_EQ(result[1].synthesized, 1);
  ASSERT_EQ(result[1].physical_delta_x, 3.0);
  ASSERT_EQ(result[1].physical_delta_y, 0.0);
  ASSERT_EQ(result[1].buttons, 0);

  ASSERT_EQ(result[2].change, PointerData::Change::kDown);
  ASSERT_EQ(result[2].pointer_identifier, 1);
  ASSERT_EQ(result[2].synthesized, 0);
  ASSERT_EQ(result[2].buttons, 1);

  // A move should be synthesized.
  ASSERT_EQ(result[3].change, PointerData::Change::kMove);
  ASSERT_EQ(result[3].pointer_identifier, 1);
  ASSERT_EQ(result[3].synthesized, 1);
  ASSERT_EQ(result[3].physical_delta_x, 0.0);
  ASSERT_EQ(result[3].physical_delta_y, 4.0);
  ASSERT_EQ(result[3].buttons, 1);

  ASSERT_EQ(result[4].change, PointerData::Change::kUp);
  ASSERT_EQ(result[4].pointer_identifier, 1);
  ASSERT_EQ(result[4].synthesized, 0);
  ASSERT_EQ(result[4].buttons, 0);

  ASSERT_EQ(result[5].change, PointerData::Change::kRemove);
  ASSERT_EQ(result[5].synthesized, 0);
}

TEST(PointerDataPacketConverterTest, CanUpdatePointerIdentifier) {
  PointerDataPacketConverter converter;
  auto packet = std::make_unique<PointerDataPacket>(7);
  PointerData data;
  CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
  packet->SetPointerData(0, data);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1);
  packet->SetPointerData(1, data);
  CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0, 0);
  packet->SetPointerData(2, data);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1);
  packet->SetPointerData(3, data);
  CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 0.0, 1);
  packet->SetPointerData(4, data);
  CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 0.0, 0);
  packet->SetPointerData(5, data);
  CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 0.0,
                             0);
  packet->SetPointerData(6, data);
  auto converted_packet = converter.Convert(std::move(packet));

  std::vector<PointerData> result;
  UnpackPointerPacket(result, std::move(converted_packet));

  ASSERT_EQ(result.size(), (size_t)7);
  ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[0].synthesized, 0);

  ASSERT_EQ(result[1].change, PointerData::Change::kDown);
  ASSERT_EQ(result[1].pointer_identifier, 1);
  ASSERT_EQ(result[1].synthesized, 0);

  ASSERT_EQ(result[2].change, PointerData::Change::kUp);
  ASSERT_EQ(result[2].pointer_identifier, 1);
  ASSERT_EQ(result[2].synthesized, 0);

  // Pointer count increase to 2.
  ASSERT_EQ(result[3].change, PointerData::Change::kDown);
  ASSERT_EQ(result[3].pointer_identifier, 2);
  ASSERT_EQ(result[3].synthesized, 0);

  ASSERT_EQ(result[4].change, PointerData::Change::kMove);
  ASSERT_EQ(result[4].pointer_identifier, 2);
  ASSERT_EQ(result[4].synthesized, 0);
  ASSERT_EQ(result[4].physical_delta_x, 3.0);
  ASSERT_EQ(result[4].physical_delta_y, 0.0);

  ASSERT_EQ(result[5].change, PointerData::Change::kUp);
  ASSERT_EQ(result[5].pointer_identifier, 2);
  ASSERT_EQ(result[5].synthesized, 0);

  ASSERT_EQ(result[6].change, PointerData::Change::kRemove);
  ASSERT_EQ(result[6].synthesized, 0);
}

TEST(PointerDataPacketConverterTest, AlwaysForwardMoveEvent) {
  PointerDataPacketConverter converter;
  auto packet = std::make_unique<PointerDataPacket>(4);
  PointerData data;
  CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
  packet->SetPointerData(0, data);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1);
  packet->SetPointerData(1, data);
  // Creates a move event without a location change.
  CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 0.0, 0.0, 1);
  packet->SetPointerData(2, data);
  CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0, 0);
  packet->SetPointerData(3, data);

  auto converted_packet = converter.Convert(std::move(packet));

  std::vector<PointerData> result;
  UnpackPointerPacket(result, std::move(converted_packet));

  ASSERT_EQ(result.size(), (size_t)4);
  ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[0].synthesized, 0);

  ASSERT_EQ(result[1].change, PointerData::Change::kDown);
  ASSERT_EQ(result[1].pointer_identifier, 1);
  ASSERT_EQ(result[1].synthesized, 0);

  // Does not filter out the move event.
  ASSERT_EQ(result[2].change, PointerData::Change::kMove);
  ASSERT_EQ(result[2].pointer_identifier, 1);
  ASSERT_EQ(result[2].synthesized, 0);

  ASSERT_EQ(result[3].change, PointerData::Change::kUp);
  ASSERT_EQ(result[3].pointer_identifier, 1);
  ASSERT_EQ(result[3].synthesized, 0);
}

TEST(PointerDataPacketConverterTest, CanWorkWithDifferentDevices) {
  PointerDataPacketConverter converter;
  auto packet = std::make_unique<PointerDataPacket>(12);
  PointerData data;
  CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
  packet->SetPointerData(0, data);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1);
  packet->SetPointerData(1, data);
  CreateSimulatedPointerData(data, PointerData::Change::kAdd, 1, 0.0, 0.0, 0);
  packet->SetPointerData(2, data);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 1, 0.0, 0.0, 1);
  packet->SetPointerData(3, data);
  CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0, 0);
  packet->SetPointerData(4, data);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1);
  packet->SetPointerData(5, data);
  CreateSimulatedPointerData(data, PointerData::Change::kMove, 1, 0.0, 4.0, 1);
  packet->SetPointerData(6, data);
  CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 0.0, 1);
  packet->SetPointerData(7, data);
  CreateSimulatedPointerData(data, PointerData::Change::kUp, 1, 0.0, 4.0, 0);
  packet->SetPointerData(8, data);
  CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 0.0, 0);
  packet->SetPointerData(9, data);
  CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 0.0,
                             0);
  packet->SetPointerData(10, data);
  CreateSimulatedPointerData(data, PointerData::Change::kRemove, 1, 0.0, 4.0,
                             0);
  packet->SetPointerData(11, data);
  auto converted_packet = converter.Convert(std::move(packet));

  std::vector<PointerData> result;
  UnpackPointerPacket(result, std::move(converted_packet));

  ASSERT_EQ(result.size(), (size_t)12);
  ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[0].device, 0);
  ASSERT_EQ(result[0].synthesized, 0);

  ASSERT_EQ(result[1].change, PointerData::Change::kDown);
  ASSERT_EQ(result[1].device, 0);
  ASSERT_EQ(result[1].pointer_identifier, 1);
  ASSERT_EQ(result[1].synthesized, 0);

  ASSERT_EQ(result[2].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[2].device, 1);
  ASSERT_EQ(result[2].synthesized, 0);

  ASSERT_EQ(result[3].change, PointerData::Change::kDown);
  ASSERT_EQ(result[3].device, 1);
  ASSERT_EQ(result[3].pointer_identifier, 2);
  ASSERT_EQ(result[3].synthesized, 0);

  ASSERT_EQ(result[4].change, PointerData::Change::kUp);
  ASSERT_EQ(result[4].device, 0);
  ASSERT_EQ(result[4].pointer_identifier, 1);
  ASSERT_EQ(result[4].synthesized, 0);

  ASSERT_EQ(result[5].change, PointerData::Change::kDown);
  ASSERT_EQ(result[5].device, 0);
  ASSERT_EQ(result[5].pointer_identifier, 3);
  ASSERT_EQ(result[5].synthesized, 0);

  ASSERT_EQ(result[6].change, PointerData::Change::kMove);
  ASSERT_EQ(result[6].device, 1);
  ASSERT_EQ(result[6].pointer_identifier, 2);
  ASSERT_EQ(result[6].synthesized, 0);
  ASSERT_EQ(result[6].physical_delta_x, 0.0);
  ASSERT_EQ(result[6].physical_delta_y, 4.0);

  ASSERT_EQ(result[7].change, PointerData::Change::kMove);
  ASSERT_EQ(result[7].device, 0);
  ASSERT_EQ(result[7].pointer_identifier, 3);
  ASSERT_EQ(result[7].synthesized, 0);
  ASSERT_EQ(result[7].physical_delta_x, 3.0);
  ASSERT_EQ(result[7].physical_delta_y, 0.0);

  ASSERT_EQ(result[8].change, PointerData::Change::kUp);
  ASSERT_EQ(result[8].device, 1);
  ASSERT_EQ(result[8].pointer_identifier, 2);
  ASSERT_EQ(result[8].synthesized, 0);

  ASSERT_EQ(result[9].change, PointerData::Change::kUp);
  ASSERT_EQ(result[9].device, 0);
  ASSERT_EQ(result[9].pointer_identifier, 3);
  ASSERT_EQ(result[9].synthesized, 0);

  ASSERT_EQ(result[10].change, PointerData::Change::kRemove);
  ASSERT_EQ(result[10].device, 0);
  ASSERT_EQ(result[10].synthesized, 0);

  ASSERT_EQ(result[11].change, PointerData::Change::kRemove);
  ASSERT_EQ(result[11].device, 1);
  ASSERT_EQ(result[11].synthesized, 0);
}

TEST(PointerDataPacketConverterTest, CanSynthesizeAdd) {
  PointerDataPacketConverter converter;
  auto packet = std::make_unique<PointerDataPacket>(2);
  PointerData data;
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 330.0, 450.0,
                             1);
  packet->SetPointerData(0, data);
  CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0, 0);
  packet->SetPointerData(1, data);
  auto converted_packet = converter.Convert(std::move(packet));

  std::vector<PointerData> result;
  UnpackPointerPacket(result, std::move(converted_packet));

  ASSERT_EQ(result.size(), (size_t)4);
  // A add should be synthesized.
  ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[0].physical_x, 330.0);
  ASSERT_EQ(result[0].physical_y, 450.0);
  ASSERT_EQ(result[0].synthesized, 1);
  ASSERT_EQ(result[0].buttons, 0);

  ASSERT_EQ(result[1].change, PointerData::Change::kDown);
  ASSERT_EQ(result[1].physical_x, 330.0);
  ASSERT_EQ(result[1].physical_y, 450.0);
  ASSERT_EQ(result[1].synthesized, 0);
  ASSERT_EQ(result[1].buttons, 1);

  // A move should be synthesized.
  ASSERT_EQ(result[2].change, PointerData::Change::kMove);
  ASSERT_EQ(result[2].physical_delta_x, -330.0);
  ASSERT_EQ(result[2].physical_delta_y, -450.0);
  ASSERT_EQ(result[2].physical_x, 0.0);
  ASSERT_EQ(result[2].physical_y, 0.0);
  ASSERT_EQ(result[2].synthesized, 1);
  ASSERT_EQ(result[2].buttons, 1);

  ASSERT_EQ(result[3].change, PointerData::Change::kUp);
  ASSERT_EQ(result[3].physical_x, 0.0);
  ASSERT_EQ(result[3].physical_y, 0.0);
  ASSERT_EQ(result[3].synthesized, 0);
  ASSERT_EQ(result[3].buttons, 0);
}

TEST(PointerDataPacketConverterTest, CanHandleThreeFingerGesture) {
  // Regression test https://github.com/flutter/flutter/issues/20517.
  PointerDataPacketConverter converter;
  PointerData data;
  std::vector<PointerData> result;
  // First finger down.
  auto packet = std::make_unique<PointerDataPacket>(1);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1);
  packet->SetPointerData(0, data);
  auto converted_packet = converter.Convert(std::move(packet));
  UnpackPointerPacket(result, std::move(converted_packet));
  // Second finger down.
  packet = std::make_unique<PointerDataPacket>(1);
  CreateSimulatedPointerData(data, PointerData::Change::kDown, 1, 33.0, 44.0,
                             1);
  packet->SetPointerData(0, data);
  converted_packet = converter.Convert(std::move(packet));
  UnpackPointerPacket(result, std::move(converted_packet));
  // Triggers three cancels.
  packet = std::make_unique<PointerDataPacket>(3);
  CreateSimulatedPointerData(data, PointerData::Change::kCancel, 1, 33.0, 44.0,
                             0);
  packet->SetPointerData(0, data);
  CreateSimulatedPointerData(data, PointerData::Change::kCancel, 0, 0.0, 0.0,
                             0);
  packet->SetPointerData(1, data);
  CreateSimulatedPointerData(data, PointerData::Change::kCancel, 2, 40.0, 50.0,
                             0);
  packet->SetPointerData(2, data);
  converted_packet = converter.Convert(std::move(packet));
  UnpackPointerPacket(result, std::move(converted_packet));

  ASSERT_EQ(result.size(), (size_t)6);
  ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[0].device, 0);
  ASSERT_EQ(result[0].physical_x, 0.0);
  ASSERT_EQ(result[0].physical_y, 0.0);
  ASSERT_EQ(result[0].synthesized, 1);
  ASSERT_EQ(result[0].buttons, 0);

  ASSERT_EQ(result[1].change, PointerData::Change::kDown);
  ASSERT_EQ(result[1].device, 0);
  ASSERT_EQ(result[1].physical_x, 0.0);
  ASSERT_EQ(result[1].physical_y, 0.0);
  ASSERT_EQ(result[1].synthesized, 0);
  ASSERT_EQ(result[1].buttons, 1);

  ASSERT_EQ(result[2].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[2].device, 1);
  ASSERT_EQ(result[2].physical_x, 33.0);
  ASSERT_EQ(result[2].physical_y, 44.0);
  ASSERT_EQ(result[2].synthesized, 1);
  ASSERT_EQ(result[2].buttons, 0);

  ASSERT_EQ(result[3].change, PointerData::Change::kDown);
  ASSERT_EQ(result[3].device, 1);
  ASSERT_EQ(result[3].physical_x, 33.0);
  ASSERT_EQ(result[3].physical_y, 44.0);
  ASSERT_EQ(result[3].synthesized, 0);
  ASSERT_EQ(result[3].buttons, 1);

  ASSERT_EQ(result[4].change, PointerData::Change::kCancel);
  ASSERT_EQ(result[4].device, 1);
  ASSERT_EQ(result[4].physical_x, 33.0);
  ASSERT_EQ(result[4].physical_y, 44.0);
  ASSERT_EQ(result[4].synthesized, 0);

  ASSERT_EQ(result[5].change, PointerData::Change::kCancel);
  ASSERT_EQ(result[5].device, 0);
  ASSERT_EQ(result[5].physical_x, 0.0);
  ASSERT_EQ(result[5].physical_y, 0.0);
  ASSERT_EQ(result[5].synthesized, 0);
  // Third cancel should be dropped
}

TEST(PointerDataPacketConverterTest, CanConvertPointerSignals) {
  PointerData::SignalKind signal_kinds[] = {
      PointerData::SignalKind::kScroll,
      PointerData::SignalKind::kScrollInertiaCancel,
      PointerData::SignalKind::kScale,
  };
  for (const PointerData::SignalKind& kind : signal_kinds) {
    PointerDataPacketConverter converter;
    auto packet = std::make_unique<PointerDataPacket>(6);
    PointerData data;
    CreateSimulatedMousePointerData(data, PointerData::Change::kAdd,
                                    PointerData::SignalKind::kNone, 0, 0.0, 0.0,
                                    0.0, 0.0, 0);
    packet->SetPointerData(0, data);
    CreateSimulatedMousePointerData(data, PointerData::Change::kAdd,
                                    PointerData::SignalKind::kNone, 1, 0.0, 0.0,
                                    0.0, 0.0, 0);
    packet->SetPointerData(1, data);
    CreateSimulatedMousePointerData(data, PointerData::Change::kDown,
                                    PointerData::SignalKind::kNone, 1, 0.0, 0.0,
                                    0.0, 0.0, 1);
    packet->SetPointerData(2, data);
    CreateSimulatedMousePointerData(data, PointerData::Change::kHover, kind, 0,
                                    34.0, 34.0, 30.0, 0.0, 0);
    packet->SetPointerData(3, data);
    CreateSimulatedMousePointerData(data, PointerData::Change::kHover, kind, 1,
                                    49.0, 49.0, 50.0, 0.0, 0);
    packet->SetPointerData(4, data);
    CreateSimulatedMousePointerData(data, PointerData::Change::kHover, kind, 2,
                                    10.0, 20.0, 30.0, 40.0, 0);
    packet->SetPointerData(5, data);
    auto converted_packet = converter.Convert(std::move(packet));

    std::vector<PointerData> result;
    UnpackPointerPacket(result, std::move(converted_packet));

    ASSERT_EQ(result.size(), (size_t)9);
    ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
    ASSERT_EQ(result[0].signal_kind, PointerData::SignalKind::kNone);
    ASSERT_EQ(result[0].device, 0);
    ASSERT_EQ(result[0].physical_x, 0.0);
    ASSERT_EQ(result[0].physical_y, 0.0);
    ASSERT_EQ(result[0].synthesized, 0);

    ASSERT_EQ(result[1].change, PointerData::Change::kAdd);
    ASSERT_EQ(result[1].signal_kind, PointerData::SignalKind::kNone);
    ASSERT_EQ(result[1].device, 1);
    ASSERT_EQ(result[1].physical_x, 0.0);
    ASSERT_EQ(result[1].physical_y, 0.0);
    ASSERT_EQ(result[1].synthesized, 0);

    ASSERT_EQ(result[2].change, PointerData::Change::kDown);
    ASSERT_EQ(result[2].signal_kind, PointerData::SignalKind::kNone);
    ASSERT_EQ(result[2].device, 1);
    ASSERT_EQ(result[2].physical_x, 0.0);
    ASSERT_EQ(result[2].physical_y, 0.0);
    ASSERT_EQ(result[2].synthesized, 0);

    // Converter will synthesize a hover to position for device 0.
    ASSERT_EQ(result[3].change, PointerData::Change::kHover);
    ASSERT_EQ(result[3].signal_kind, PointerData::SignalKind::kNone);
    ASSERT_EQ(result[3].device, 0);
    ASSERT_EQ(result[3].physical_x, 34.0);
    ASSERT_EQ(result[3].physical_y, 34.0);
    ASSERT_EQ(result[3].physical_delta_x, 34.0);
    ASSERT_EQ(result[3].physical_delta_y, 34.0);
    ASSERT_EQ(result[3].buttons, 0);
    ASSERT_EQ(result[3].synthesized, 1);

    ASSERT_EQ(result[4].change, PointerData::Change::kHover);
    ASSERT_EQ(result[4].signal_kind, kind);
    ASSERT_EQ(result[4].device, 0);
    ASSERT_EQ(result[4].physical_x, 34.0);
    ASSERT_EQ(result[4].physical_y, 34.0);
    ASSERT_EQ(result[4].scroll_delta_x, 30.0);
    ASSERT_EQ(result[4].scroll_delta_y, 0.0);

    // Converter will synthesize a move to position for device 1.
    ASSERT_EQ(result[5].change, PointerData::Change::kMove);
    ASSERT_EQ(result[5].signal_kind, PointerData::SignalKind::kNone);
    ASSERT_EQ(result[5].device, 1);
    ASSERT_EQ(result[5].physical_x, 49.0);
    ASSERT_EQ(result[5].physical_y, 49.0);
    ASSERT_EQ(result[5].physical_delta_x, 49.0);
    ASSERT_EQ(result[5].physical_delta_y, 49.0);
    ASSERT_EQ(result[5].buttons, 1);
    ASSERT_EQ(result[5].synthesized, 1);

    ASSERT_EQ(result[6].change, PointerData::Change::kHover);
    ASSERT_EQ(result[6].signal_kind, kind);
    ASSERT_EQ(result[6].device, 1);
    ASSERT_EQ(result[6].physical_x, 49.0);
    ASSERT_EQ(result[6].physical_y, 49.0);
    ASSERT_EQ(result[6].scroll_delta_x, 50.0);
    ASSERT_EQ(result[6].scroll_delta_y, 0.0);

    // Converter will synthesize an add for device 2.
    ASSERT_EQ(result[7].change, PointerData::Change::kAdd);
    ASSERT_EQ(result[7].signal_kind, PointerData::SignalKind::kNone);
    ASSERT_EQ(result[7].device, 2);
    ASSERT_EQ(result[7].physical_x, 10.0);
    ASSERT_EQ(result[7].physical_y, 20.0);
    ASSERT_EQ(result[7].synthesized, 1);

    ASSERT_EQ(result[8].change, PointerData::Change::kHover);
    ASSERT_EQ(result[8].signal_kind, kind);
    ASSERT_EQ(result[8].device, 2);
    ASSERT_EQ(result[8].physical_x, 10.0);
    ASSERT_EQ(result[8].physical_y, 20.0);
    ASSERT_EQ(result[8].scroll_delta_x, 30.0);
    ASSERT_EQ(result[8].scroll_delta_y, 40.0);
  }
}

TEST(PointerDataPacketConverterTest, CanConvertTrackpadGesture) {
  PointerDataPacketConverter converter;
  auto packet = std::make_unique<PointerDataPacket>(3);
  PointerData data;
  CreateSimulatedTrackpadGestureData(data, PointerData::Change::kPanZoomStart,
                                     0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
  packet->SetPointerData(0, data);
  CreateSimulatedTrackpadGestureData(data, PointerData::Change::kPanZoomUpdate,
                                     0, 0.0, 0.0, 3.0, 4.0, 1.0, 0.0);
  packet->SetPointerData(1, data);
  CreateSimulatedTrackpadGestureData(data, PointerData::Change::kPanZoomEnd, 0,
                                     0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
  packet->SetPointerData(2, data);
  auto converted_packet = converter.Convert(std::move(packet));

  std::vector<PointerData> result;
  UnpackPointerPacket(result, std::move(converted_packet));

  ASSERT_EQ(result.size(), (size_t)4);
  ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
  ASSERT_EQ(result[0].device, 0);
  ASSERT_EQ(result[0].synthesized, 1);

  ASSERT_EQ(result[1].change, PointerData::Change::kPanZoomStart);
  ASSERT_EQ(result[1].signal_kind, PointerData::SignalKind::kNone);
  ASSERT_EQ(result[1].device, 0);
  ASSERT_EQ(result[1].physical_x, 0.0);
  ASSERT_EQ(result[1].physical_y, 0.0);
  ASSERT_EQ(result[1].synthesized, 0);

  ASSERT_EQ(result[2].change, PointerData::Change::kPanZoomUpdate);
  ASSERT_EQ(result[2].signal_kind, PointerData::SignalKind::kNone);
  ASSERT_EQ(result[2].device, 0);
  ASSERT_EQ(result[2].physical_x, 0.0);
  ASSERT_EQ(result[2].physical_y, 0.0);
  ASSERT_EQ(result[2].pan_x, 3.0);
  ASSERT_EQ(result[2].pan_y, 4.0);
  ASSERT_EQ(result[2].pan_delta_x, 3.0);
  ASSERT_EQ(result[2].pan_delta_y, 4.0);
  ASSERT_EQ(result[2].scale, 1.0);
  ASSERT_EQ(result[2].rotation, 0.0);
  ASSERT_EQ(result[2].synthesized, 0);

  ASSERT_EQ(result[3].change, PointerData::Change::kPanZoomEnd);
  ASSERT_EQ(result[3].signal_kind, PointerData::SignalKind::kNone);
  ASSERT_EQ(result[3].device, 0);
  ASSERT_EQ(result[3].physical_x, 0.0);
  ASSERT_EQ(result[3].physical_y, 0.0);
  ASSERT_EQ(result[3].synthesized, 0);
}

}  // namespace testing
}  // namespace flutter
