blob: b13559ae3b62aec6d3a6f088d9f35f017f9ea660 [file] [log] [blame]
// Copyright 2017 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 "base/run_loop.h"
#include "chrome/browser/vr/test/mock_openvr_device_hook_base.h"
#include "chrome/browser/vr/test/webvr_browser_test.h"
#include "chrome/browser/vr/test/webxr_vr_browser_test.h"
#include "device/vr/openvr/test/test_hook.h"
#include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
#include "third_party/openvr/src/headers/openvr.h"
// Browser test equivalent of
// chrome/android/javatests/src/.../browser/vr/WebXrVrInputTest.java.
// End-to-end tests for user input interaction with WebXR/WebVR.
namespace vr {
// Test that focus is locked to the presenting display for the purposes of VR/XR
// input.
void TestPresentationLocksFocusImpl(WebXrVrBrowserTestBase* t,
std::string filename) {
t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
t->EnterSessionWithUserGestureOrFail();
t->ExecuteStepAndWait("stepSetupFocusLoss()");
t->EndTest();
}
IN_PROC_BROWSER_TEST_F(WebVrBrowserTestStandard, TestPresentationLocksFocus) {
TestPresentationLocksFocusImpl(this, "test_presentation_locks_focus");
}
IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard, TestPresentationLocksFocus) {
TestPresentationLocksFocusImpl(this, "webxr_test_presentation_locks_focus");
}
class WebXrControllerInputOpenVRMock : public MockOpenVRDeviceHookBase {
public:
void OnFrameSubmitted(
device_test::mojom::SubmittedFrameDataPtr frame_data,
device_test::mojom::XRTestHook::OnFrameSubmittedCallback callback) final;
void WaitNumFrames(unsigned int num_frames) {
DCHECK(!wait_loop_);
target_submitted_frames_ = num_submitted_frames_ + num_frames;
wait_loop_ = new base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed);
wait_loop_->Run();
delete wait_loop_;
wait_loop_ = nullptr;
}
void ToggleTrigger(unsigned int index,
device::ControllerFrameData& controller_data) {
uint64_t trigger_mask = vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Trigger);
controller_data.packet_number++;
controller_data.buttons_pressed ^= trigger_mask;
UpdateController(index, controller_data);
// TODO(https://crbug.com/887726): Figure out why waiting for OpenVR to grab
// the updated state instead of waiting for a number of frames causes frames
// to be submitted at an extremely slow rate. Once fixed, switch away from
// waiting on number of frames.
WaitNumFrames(30);
}
void PressReleaseTrigger(unsigned int index,
device::ControllerFrameData& controller_data) {
ToggleTrigger(index, controller_data);
ToggleTrigger(index, controller_data);
}
private:
base::RunLoop* wait_loop_ = nullptr;
unsigned int num_submitted_frames_ = 0;
unsigned int target_submitted_frames_ = 0;
};
void WebXrControllerInputOpenVRMock::OnFrameSubmitted(
device_test::mojom::SubmittedFrameDataPtr frame_data,
device_test::mojom::XRTestHook::OnFrameSubmittedCallback callback) {
num_submitted_frames_++;
if (wait_loop_ && target_submitted_frames_ == num_submitted_frames_) {
wait_loop_->Quit();
}
std::move(callback).Run();
}
// Test that OpenVR controller input is registered via WebXR's input method.
// Equivalent to
// WebXrVrInputTest#testControllerClicksRegisteredOnDaydream_WebXr.
IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
TestControllerInputRegistered) {
WebXrControllerInputOpenVRMock my_mock;
// Connect a controller.
auto controller_data = my_mock.CreateValidController(
device::ControllerRole::kControllerRoleRight);
unsigned int controller_index = my_mock.ConnectController(controller_data);
// Load the test page and enter presentation.
this->LoadUrlAndAwaitInitialization(
this->GetFileUrlForHtmlTestFile("test_webxr_input"));
this->EnterSessionWithUserGestureOrFail();
unsigned int num_iterations = 10;
this->RunJavaScriptOrFail("stepSetupListeners(" +
std::to_string(num_iterations) + ")");
// Press and unpress the controller's trigger a bunch of times and make sure
// they're all registered.
for (unsigned int i = 0; i < num_iterations; ++i) {
my_mock.PressReleaseTrigger(controller_index, controller_data);
// After each trigger release, wait for the JavaScript to receive the
// "select" event.
this->WaitOnJavaScriptStep();
}
this->EndTest();
}
// Test that OpenVR controller input is registered via the Gamepad API.
// Equivalent to
// WebXrVrInputTest#testControllerClicksRegisteredOnDaydream
IN_PROC_BROWSER_TEST_F(WebVrBrowserTestStandard,
TestControllerInputRegistered) {
WebXrControllerInputOpenVRMock my_mock;
// Connect a controller.
auto controller_data = my_mock.CreateValidController(
device::ControllerRole::kControllerRoleRight);
// openvr_gamepad_helper assumes axis index 1 is the trigger, so we need to
// set that here, otherwise it won't check whether it's pressed or not.
controller_data.axis_data[1].axis_type = vr::k_eControllerAxis_Trigger;
unsigned int controller_index = my_mock.ConnectController(controller_data);
// Load the test page and enter presentation.
this->LoadUrlAndAwaitInitialization(
this->GetFileUrlForHtmlTestFile("test_gamepad_button"));
this->EnterSessionWithUserGestureOrFail();
// We need to have this, otherwise the JavaScript side of the Gamepad API
// doesn't seem to pick up the correct button state? I.e. if we don't have
// this, openvr_gamepad_helper properly sets the gamepad's button state,
// but JavaScript still shows no buttons pressed.
// TODO(bsheedy): Figure out why this is the case.
my_mock.PressReleaseTrigger(controller_index, controller_data);
// Setting this in the Android version of the test needs to happen after a
// flakiness workaround. Coincidentally, it's also helpful for the different
// issue solved by the above PressReleaseTrigger, so make sure to set it here
// so that the above press/release isn't caught by the test code.
this->RunJavaScriptOrFail("canStartTest = true");
// Press and release the trigger, ensuring the Gamepad API detects both.
my_mock.ToggleTrigger(controller_index, controller_data);
this->WaitOnJavaScriptStep();
my_mock.ToggleTrigger(controller_index, controller_data);
this->WaitOnJavaScriptStep();
this->EndTest();
}
} // namespace vr