| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <linux/input-event-codes.h> |
| #include <wayland-client-core.h> |
| |
| #include "ash/drag_drop/drag_drop_controller.h" |
| #include "ash/shell.h" |
| #include "base/containers/contains.h" |
| #include "base/containers/flat_map.h" |
| #include "base/test/bind.h" |
| #include "components/exo/wayland/test/client_util.h" |
| #include "components/exo/wayland/test/server_util.h" |
| #include "components/exo/wayland/test/shell_client_data.h" |
| #include "components/exo/wayland/test/test_client.h" |
| #include "components/exo/wayland/test/wayland_server_test.h" |
| #include "ui/aura/client/drag_drop_client.h" |
| |
| namespace exo::wayland { |
| |
| using DataDeviceManagerTest = test::WaylandServerTest; |
| |
| namespace { |
| |
| class InputListenerImpl : public test::InputListener { |
| public: |
| // test::InputListener: |
| void OnButtonPressed(uint32_t serial, uint32_t button) override { |
| button_serial_map[button] = serial; |
| } |
| void OnTouchDown(uint32_t serial, |
| wl_surface* surface, |
| int32_t id, |
| const gfx::PointF& point) override { |
| touch_serial_map[id] = serial; |
| } |
| |
| base::flat_map<uint32_t, uint32_t> button_serial_map; |
| base::flat_map<int32_t, uint32_t> touch_serial_map; |
| }; |
| |
| } // namespace |
| |
| // TODO(crbug.com/41494812): enable the flaky test. |
| #if defined(MEMORY_SANITIZER) |
| #define MAYBE_Mouse DISABLED_Mouse |
| #else |
| #define MAYBE_Mouse Mouse |
| #endif |
| TEST_F(DataDeviceManagerTest, MAYBE_Mouse) { |
| test::ResourceKey surface_key; |
| InputListenerImpl* input_listener = nullptr; |
| |
| PostToClientAndWait([&](test::TestClient* client) { |
| ASSERT_TRUE(client->InitShmBufferFactory(256 * 256 * 4)); |
| auto* data_ptr = |
| client->set_data(std::make_unique<test::ShellClientData>(client)); |
| |
| auto input_listener_impl = std::make_unique<InputListenerImpl>(); |
| input_listener = input_listener_impl.get(); |
| data_ptr->set_input_listener(std::move(input_listener_impl)); |
| |
| data_ptr->CreateXdgToplevel(); |
| data_ptr->CreateAndAttachBuffer({256, 256}); |
| data_ptr->Commit(); |
| |
| surface_key = data_ptr->GetSurfaceResourceKey(); |
| }); |
| |
| Surface* surface = test::server_util::GetUserDataForResource<Surface>( |
| server_.get(), surface_key); |
| |
| auto* drag_drop_controller = static_cast<ash::DragDropController*>( |
| aura::client::GetDragDropClient(ash::Shell::GetPrimaryRootWindow())); |
| |
| auto* generator = GetEventGenerator(); |
| { |
| generator->MoveMouseToCenterOf(surface->window()); |
| generator->PressLeftButton(); |
| generator->ReleaseLeftButton(); |
| |
| // process events on client side. This should not start D&D, (thus it will |
| // not start nested loop) because the button has already been released |
| PostToClientAndWait([&](test::TestClient* client) { |
| EXPECT_TRUE(base::Contains(input_listener->button_serial_map, BTN_LEFT)); |
| auto* shell_client_data = client->GetDataAs<test::ShellClientData>(); |
| uint32_t serial = input_listener->button_serial_map[BTN_LEFT]; |
| shell_client_data->StartDrag(serial); |
| }); |
| } |
| |
| { |
| generator->PressLeftButton(); |
| generator->PressButton(ui::EF_MIDDLE_MOUSE_BUTTON); |
| |
| bool nested_loop_started; |
| auto* nested_loop_started_ptr = &nested_loop_started; |
| |
| // This scenario will start D&D, which will run nested loop, so use |
| // the nested loop closure to drive the test. |
| drag_drop_controller->SetLoopClosureForTesting( |
| base::BindLambdaForTesting([generator, nested_loop_started_ptr]() { |
| generator->ReleaseLeftButton(); |
| *nested_loop_started_ptr = true; |
| }), |
| base::DoNothing()); |
| |
| PostToClientAndWait([&](test::TestClient* client) { |
| EXPECT_TRUE(base::Contains(input_listener->button_serial_map, BTN_LEFT)); |
| auto* shell_client_data = client->GetDataAs<test::ShellClientData>(); |
| uint32_t serial = input_listener->button_serial_map[BTN_LEFT]; |
| shell_client_data->StartDrag(serial); |
| }); |
| EXPECT_TRUE(nested_loop_started); |
| } |
| |
| { |
| generator->PressLeftButton(); |
| generator->PressButton(ui::EF_MIDDLE_MOUSE_BUTTON); |
| generator->ReleaseButton(ui::EF_MIDDLE_MOUSE_BUTTON); |
| |
| bool nested_loop_started = false; |
| auto* nested_loop_started_ptr = &nested_loop_started; |
| |
| // This scenario will start D&D, which will run nested loop, so use |
| // the nested loop closure to drive the test. |
| drag_drop_controller->SetLoopClosureForTesting( |
| base::BindLambdaForTesting([generator, nested_loop_started_ptr]() { |
| generator->ReleaseLeftButton(); |
| *nested_loop_started_ptr = true; |
| }), |
| base::DoNothing()); |
| |
| PostToClientAndWait([&](test::TestClient* client) { |
| EXPECT_TRUE(base::Contains(input_listener->button_serial_map, BTN_LEFT)); |
| auto* shell_client_data = client->GetDataAs<test::ShellClientData>(); |
| uint32_t serial = input_listener->button_serial_map[BTN_LEFT]; |
| shell_client_data->StartDrag(serial); |
| }); |
| EXPECT_TRUE(nested_loop_started); |
| } |
| } |
| |
| // TODO(crbug.com/41494812): enable the flaky test. |
| #if defined(MEMORY_SANITIZER) |
| #define MAYBE_Touch DISABLED_Touch |
| #else |
| #define MAYBE_Touch Touch |
| #endif |
| TEST_F(DataDeviceManagerTest, MAYBE_Touch) { |
| test::ResourceKey surface_key; |
| InputListenerImpl* input_listener = nullptr; |
| |
| PostToClientAndWait([&](test::TestClient* client) { |
| ASSERT_TRUE(client->InitShmBufferFactory(256 * 256 * 4)); |
| auto* data_ptr = |
| client->set_data(std::make_unique<test::ShellClientData>(client)); |
| |
| data_ptr->CreateXdgToplevel(); |
| data_ptr->CreateAndAttachBuffer({256, 256}); |
| data_ptr->Commit(); |
| auto input_listener_impl = std::make_unique<InputListenerImpl>(); |
| input_listener = input_listener_impl.get(); |
| data_ptr->set_input_listener(std::move(input_listener_impl)); |
| surface_key = data_ptr->GetSurfaceResourceKey(); |
| }); |
| |
| Surface* surface = test::server_util::GetUserDataForResource<Surface>( |
| server_.get(), surface_key); |
| |
| auto* drag_drop_controller = static_cast<ash::DragDropController*>( |
| aura::client::GetDragDropClient(ash::Shell::GetPrimaryRootWindow())); |
| drag_drop_controller->enable_no_image_touch_drag_for_test(); |
| |
| auto* generator = GetEventGenerator(); |
| { |
| generator->PressTouch( |
| generator->delegate()->CenterOfWindow(surface->window())); |
| generator->ReleaseTouch(); |
| |
| // process events on client side. This should not start D&D, (thus it will |
| // not start nested loop) because the button has already been released |
| PostToClientAndWait([&](test::TestClient* client) { |
| EXPECT_TRUE(base::Contains(input_listener->touch_serial_map, 0)); |
| auto* shell_client_data = client->GetDataAs<test::ShellClientData>(); |
| uint32_t serial = input_listener->touch_serial_map[0]; |
| shell_client_data->StartDrag(serial); |
| }); |
| } |
| |
| { |
| generator->PressTouch(); |
| enum Step { |
| Started, |
| Moved, |
| Released, |
| }; |
| Step step = Step::Started; |
| auto* step_ptr = &step; |
| // This scenario will start D&D, which will run nested loop, so use |
| // the nested loop closure to drive the test. |
| drag_drop_controller->SetLoopClosureForTesting( |
| base::BindLambdaForTesting([generator, step_ptr]() { |
| switch (*step_ptr) { |
| case Started: |
| generator->MoveTouchBy(5, 5); |
| *step_ptr = Moved; |
| break; |
| case Moved: |
| generator->ReleaseTouch(); |
| *step_ptr = Released; |
| break; |
| case Released: |
| break; |
| } |
| }), |
| base::DoNothing()); |
| |
| PostToClientAndWait([&](test::TestClient* client) { |
| EXPECT_TRUE(base::Contains(input_listener->touch_serial_map, 0)); |
| auto* shell_client_data = client->GetDataAs<test::ShellClientData>(); |
| uint32_t serial = input_listener->touch_serial_map[0]; |
| shell_client_data->StartDrag(serial); |
| }); |
| EXPECT_EQ(step, Released); |
| } |
| } |
| |
| } // namespace exo::wayland |