// Copyright 2015 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 "components/exo/gaming_seat.h" #include #include "ash/shell.h" #include "base/command_line.h" #include "base/run_loop.h" #include "base/time/time.h" #include "components/exo/buffer.h" #include "components/exo/gamepad.h" #include "components/exo/gamepad_delegate.h" #include "components/exo/gaming_seat_delegate.h" #include "components/exo/shell_surface.h" #include "components/exo/surface.h" #include "components/exo/test/exo_test_base.h" #include "components/exo/test/exo_test_helper.h" #include "device/gamepad/gamepad_test_helpers.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/focus_client.h" #include "ui/events/ozone/gamepad/gamepad_provider_ozone.h" namespace exo { namespace { class MockGamepadDelegate : public GamepadDelegate { public: MockGamepadDelegate() {} // Overridden from GamepadDelegate: MOCK_METHOD(void, OnRemoved, (), (override)); MOCK_METHOD(void, OnAxis, (int axis, double value, base::TimeTicks timestamp), (override)); MOCK_METHOD(void, OnButton, (int button, bool pressed, base::TimeTicks timestamp), (override)); MOCK_METHOD(void, OnFrame, (base::TimeTicks timestamp), (override)); }; class MockGamingSeatDelegate : public GamingSeatDelegate { public: MOCK_METHOD(bool, CanAcceptGamepadEventsForSurface, (Surface * surface), (const, override)); MOCK_METHOD(void, GamepadAdded, (Gamepad & gamepad), (override)); MOCK_METHOD(void, Die, (), ()); void OnGamingSeatDestroying(GamingSeat*) override { delete this; } ~MockGamingSeatDelegate() { Die(); } }; class GamingSeatTest : public test::ExoTestBase { public: GamingSeatTest() {} void InitializeGamingSeat(MockGamingSeatDelegate* delegate) { gaming_seat_.reset(new GamingSeat(delegate)); } void DestroyGamingSeat(MockGamingSeatDelegate* delegate) { EXPECT_CALL(*delegate, Die()).Times(1); gaming_seat_.reset(); } void UpdateGamepadDevice(const std::vector& gamepad_device_ids) { std::vector gamepad_devices; for (auto& id : gamepad_device_ids) { gamepad_devices.emplace_back( ui::InputDevice(id, ui::InputDeviceType::INPUT_DEVICE_USB, "gamepad"), std::vector(), /*supports_vibration_rumble=*/false); } ui::GamepadProviderOzone::GetInstance()->DispatchGamepadDevicesUpdated( gamepad_devices); } void SendFrameToGamepads(const std::vector& gamepad_device_ids) { for (auto& id : gamepad_device_ids) { ui::GamepadEvent event(id, ui::GamepadEventType::FRAME, 0, 0, base::TimeTicks()); ui::GamepadProviderOzone::GetInstance()->DispatchGamepadEvent(event); } } void SendButtonToGamepads(const std::vector& gamepad_device_ids, base::TimeTicks timestamp) { for (auto& id : gamepad_device_ids) { ui::GamepadEvent event(id, ui::GamepadEventType::BUTTON, 310, 1, timestamp); ui::GamepadProviderOzone::GetInstance()->DispatchGamepadEvent(event); } } protected: std::unique_ptr gaming_seat_; DISALLOW_COPY_AND_ASSIGN(GamingSeatTest); }; TEST_F(GamingSeatTest, ConnectionChange) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); testing::StrictMock* gaming_seat_delegate = new testing::StrictMock(); EXPECT_CALL(*gaming_seat_delegate, CanAcceptGamepadEventsForSurface(testing::_)) .WillOnce(testing::Return(true)); InitializeGamingSeat(gaming_seat_delegate); std::unique_ptr gamepad_delegates[6]; for (auto& delegate : gamepad_delegates) delegate = std::make_unique>(); { // Test sequence testing::InSequence s; // Connect 2 gamepads. EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) .WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) { gamepad.SetDelegate(std::move(gamepad_delegates[0])); })); EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) .WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) { gamepad.SetDelegate(std::move(gamepad_delegates[1])); })); // Send frame to connected gamepad. EXPECT_CALL(*gamepad_delegates[0], OnFrame(testing::_)).Times(1); EXPECT_CALL(*gamepad_delegates[1], OnFrame(testing::_)).Times(1); // Connect 3 more. EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) .WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) { gamepad.SetDelegate(std::move(gamepad_delegates[2])); })); EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) .WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) { gamepad.SetDelegate(std::move(gamepad_delegates[3])); })); EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) .WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) { gamepad.SetDelegate(std::move(gamepad_delegates[4])); })); // Send frame to all gamepads. EXPECT_CALL(*gamepad_delegates[0], OnFrame(testing::_)).Times(1); EXPECT_CALL(*gamepad_delegates[1], OnFrame(testing::_)).Times(1); EXPECT_CALL(*gamepad_delegates[2], OnFrame(testing::_)).Times(1); EXPECT_CALL(*gamepad_delegates[3], OnFrame(testing::_)).Times(1); EXPECT_CALL(*gamepad_delegates[4], OnFrame(testing::_)).Times(1); // Disconnect gamepads 0, 2 and 4. EXPECT_CALL(*gamepad_delegates[0], OnRemoved()).Times(1); EXPECT_CALL(*gamepad_delegates[2], OnRemoved()).Times(1); EXPECT_CALL(*gamepad_delegates[4], OnRemoved()).Times(1); // Connect a new gamepad. EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) .WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) { gamepad.SetDelegate(std::move(gamepad_delegates[5])); })); // Send frame to all gamepads. EXPECT_CALL(*gamepad_delegates[1], OnFrame(testing::_)).Times(1); EXPECT_CALL(*gamepad_delegates[3], OnFrame(testing::_)).Times(1); EXPECT_CALL(*gamepad_delegates[5], OnFrame(testing::_)).Times(1); } // The rest of gamepads should be disconnected after GamingSeat is // destroyed. EXPECT_CALL(*gamepad_delegates[1], OnRemoved()).Times(1); EXPECT_CALL(*gamepad_delegates[3], OnRemoved()).Times(1); EXPECT_CALL(*gamepad_delegates[5], OnRemoved()).Times(1); // Gamepad connected. UpdateGamepadDevice({0, 1}); SendFrameToGamepads({0, 1}); UpdateGamepadDevice({0, 1, 2, 3, 4}); SendFrameToGamepads({0, 1, 2, 3, 4}); UpdateGamepadDevice({1, 2, 3, 4}); UpdateGamepadDevice({1, 3, 4}); UpdateGamepadDevice({1, 3}); UpdateGamepadDevice({1, 3, 5}); SendFrameToGamepads({1, 2, 3, 4, 5}); DestroyGamingSeat(gaming_seat_delegate); UpdateGamepadDevice({}); } TEST_F(GamingSeatTest, Timestamp) { std::unique_ptr surface(new Surface); std::unique_ptr shell_surface(new ShellSurface(surface.get())); gfx::Size buffer_size(10, 10); std::unique_ptr buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); surface->Attach(buffer.get()); surface->Commit(); testing::StrictMock* gaming_seat_delegate = new testing::StrictMock(); EXPECT_CALL(*gaming_seat_delegate, CanAcceptGamepadEventsForSurface(testing::_)) .WillOnce(testing::Return(true)); InitializeGamingSeat(gaming_seat_delegate); auto gamepad_delegate = std::make_unique>(); base::TimeTicks expected_time = base::TimeTicks::Now(); { // Test sequence testing::InSequence s; // Connect gamepad. EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) .WillOnce(testing::Invoke([&gamepad_delegate](auto& gamepad) { gamepad.SetDelegate(std::move(gamepad_delegate)); })); // Send button to connected gamepad. Expect correct timestamp. EXPECT_CALL(*gamepad_delegate, OnButton(testing::_, testing::_, testing::Eq(expected_time))) .Times(1); } // Disconnect gamepad. EXPECT_CALL(*gamepad_delegate, OnRemoved()).Times(1); // Gamepad connected. UpdateGamepadDevice({1}); SendButtonToGamepads({1}, expected_time); UpdateGamepadDevice({}); DestroyGamingSeat(gaming_seat_delegate); } } // namespace } // namespace exo