diff options
Diffstat (limited to 'dom/vr/test')
22 files changed, 1251 insertions, 0 deletions
diff --git a/dom/vr/test/crashtests/crashtests.list b/dom/vr/test/crashtests/crashtests.list new file mode 100644 index 0000000000..8e40e0ccce --- /dev/null +++ b/dom/vr/test/crashtests/crashtests.list @@ -0,0 +1 @@ +pref(dom.vr.enabled,true) pref(dom.vr.always_support_vr,true) load enumerate_vr_on_dying_window.html
diff --git a/dom/vr/test/crashtests/enumerate_vr_on_dying_window.html b/dom/vr/test/crashtests/enumerate_vr_on_dying_window.html new file mode 100644 index 0000000000..2906faa79c --- /dev/null +++ b/dom/vr/test/crashtests/enumerate_vr_on_dying_window.html @@ -0,0 +1,14 @@ +<html> +<head> +<script> +window.onload = function(){ + var frame = document.getElementById('test_iframe'); + var win = frame.contentWindow; + frame.remove(); + win.onvrdisplayactivate = function () {} +}; +</script></head> +<body> + <iframe id="test_iframe"></iframe> +</body> +</html> diff --git a/dom/vr/test/mochitest/VRSimulationDriver.js b/dom/vr/test/mochitest/VRSimulationDriver.js new file mode 100644 index 0000000000..5b197b88e4 --- /dev/null +++ b/dom/vr/test/mochitest/VRSimulationDriver.js @@ -0,0 +1,95 @@ +var VRServiceTest; +var vrMockDisplay; + +var VRSimulationDriver = (function () { + "use strict"; + + var AttachWebVRDisplay = function () { + if (vrMockDisplay) { + // Avoid creating multiple displays + return Promise.resolve(vrMockDisplay); + } + var promise = VRServiceTest.attachVRDisplay("VRDisplayTest"); + promise.then(function (display) { + assert_true(display != null, "AttachWebVRDisplay should success."); + vrMockDisplay = display; + }); + + return promise; + }; + + var SetVRDisplayPose = function ( + position, + linearVelocity, + linearAcceleration, + orientation, + angularVelocity, + angularAcceleration + ) { + vrMockDisplay.setPose( + position, + linearVelocity, + linearAcceleration, + orientation, + angularVelocity, + angularAcceleration + ); + }; + + var SetEyeResolution = function (width, height) { + vrMockDisplay.setEyeResolution(width, height); + }; + + var SetEyeParameter = function ( + eye, + offsetX, + offsetY, + offsetZ, + upDegree, + rightDegree, + downDegree, + leftDegree + ) { + vrMockDisplay.setEyeParameter( + eye, + offsetX, + offsetY, + offsetZ, + upDegree, + rightDegree, + downDegree, + leftDegree + ); + }; + + var SetMountState = function (isMounted) { + vrMockDisplay.setMountState(isMounted); + }; + + var UpdateVRDisplay = function () { + vrMockDisplay.update(); + }; + + var AttachVRController = function () { + var promise = VRServiceTest.attachVRController("VRControllerTest"); + promise.then(function (controller) { + assert_true(controller != null, "AttachVRController should success."); + }); + + return promise; + }; + + var API = { + AttachWebVRDisplay, + SetVRDisplayPose, + SetEyeResolution, + SetEyeParameter, + SetMountState, + UpdateVRDisplay, + AttachVRController, + + none: false, + }; + + return API; +})(); diff --git a/dom/vr/test/mochitest/WebVRHelpers.js b/dom/vr/test/mochitest/WebVRHelpers.js new file mode 100644 index 0000000000..e19294a236 --- /dev/null +++ b/dom/vr/test/mochitest/WebVRHelpers.js @@ -0,0 +1,19 @@ +var WebVRHelpers = (function () { + "use strict"; + + var RequestPresentOnVRDisplay = function (vrDisplay, vrLayers, callback) { + if (callback) { + callback(); + } + + return vrDisplay.requestPresent(vrLayers); + }; + + var API = { + RequestPresentOnVRDisplay, + + none: false, + }; + + return API; +})(); diff --git a/dom/vr/test/mochitest/mochitest.ini b/dom/vr/test/mochitest/mochitest.ini new file mode 100644 index 0000000000..87a4174a21 --- /dev/null +++ b/dom/vr/test/mochitest/mochitest.ini @@ -0,0 +1,31 @@ +# Please confirm there is no other VR display connected. Otherwise, VRPuppetDisplay can't be attached. +[DEFAULT] +support-files = + VRSimulationDriver.js + requestPresent.js + runVRTest.js + WebVRHelpers.js +[test_vrController_displayId.html] +# Enable Linux after Bug 1310655 # TIMED_OUT for Android. +# skip-if = (os != "win" && release_or_beta) || (os == "android") +# Dependencies for re-enabling these are tracked by meta bug 1555185. +skip-if = true +[test_vrDisplay_canvas2d.html] +# skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655 +# Dependencies for re-enabling these are tracked by meta bug 1555185. +skip-if = true +[test_vrDisplay_exitPresent.html] +# skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655 +# Dependencies for re-enabling these are tracked by meta bug 1555185. +skip-if = true +[test_vrDisplay_getFrameData.html] +# Enable Linux after Bug 1310655, enable Android after Bug 1348246 +# skip-if = (os != "win" && release_or_beta) || (os == "android") +# Dependencies for re-enabling these are tracked by meta bug 1555185. +skip-if = true +[test_vrDisplay_onvrdisplayconnect.html] +skip-if = true +[test_vrDisplay_onvrdisplaydeactivate_crosscontent.html] +skip-if = true +[test_vrDisplay_requestPresent.html] +skip-if = true diff --git a/dom/vr/test/mochitest/requestPresent.js b/dom/vr/test/mochitest/requestPresent.js new file mode 100644 index 0000000000..a2f9dd4d11 --- /dev/null +++ b/dom/vr/test/mochitest/requestPresent.js @@ -0,0 +1,74 @@ +// requestPresent.js +// +// This file provides helpers for testing VRDisplay requestPresent. + +function attachVRDisplay(test) { + assert_equals( + typeof navigator.getVRDisplays, + "function", + "'navigator.getVRDisplays()' must be defined." + ); + return VRSimulationDriver.AttachWebVRDisplay(); +} + +function setupVRDisplay(test) { + assert_equals( + typeof navigator.getVRDisplays, + "function", + "'navigator.getVRDisplays()' must be defined." + ); + return VRSimulationDriver.AttachWebVRDisplay() + .then(() => { + return navigator.getVRDisplays(); + }) + .then(displays => { + assert_equals( + displays.length, + 1, + "displays.length must be one after attach." + ); + vrDisplay = displays[0]; + return validateNewVRDisplay(test, vrDisplay); + }); +} + +// Validate the settings off a freshly created VRDisplay (prior to calling +// requestPresent). +function validateNewVRDisplay(test, display) { + assert_true( + display.capabilities.canPresent, + "display.capabilities.canPresent must always be true for HMDs." + ); + assert_equals( + display.capabilities.maxLayers, + 1, + "display.capabilities.maxLayers must always be 1 when display.capabilities.canPresent is true for current spec revision." + ); + assert_false( + display.isPresenting, + "display.isPresenting must be false before calling requestPresent." + ); + assert_equals( + display.getLayers().length, + 0, + "display.getLayers() should have no layers if not presenting." + ); + var promise = display.exitPresent(); + return promise_rejects(test, null, promise); +} + +// Validate the settings off a VRDisplay after requestPresent promise is +// rejected or after exitPresent is fulfilled. +function validateDisplayNotPresenting(test, display) { + assert_false( + display.isPresenting, + "display.isPresenting must be false if requestPresent is rejected or after exitPresent is fulfilled." + ); + assert_equals( + display.getLayers().length, + 0, + "display.getLayers() should have no layers if requestPresent is rejected or after exitPresent is fulfilled." + ); + var promise = display.exitPresent(); + return promise_rejects(test, null, promise); +} diff --git a/dom/vr/test/mochitest/runVRTest.js b/dom/vr/test/mochitest/runVRTest.js new file mode 100644 index 0000000000..c15003a6c3 --- /dev/null +++ b/dom/vr/test/mochitest/runVRTest.js @@ -0,0 +1,18 @@ +function runVRTest(callback) { + SpecialPowers.pushPrefEnv( + { + set: [ + ["dom.vr.enabled", true], + ["dom.vr.puppet.enabled", true], + ["dom.vr.require-gesture", false], + ["dom.vr.test.enabled", true], + ["dom.vr.display.enumerate.interval", 0], + ["dom.vr.controller.enumerate.interval", 0], + ], + }, + () => { + VRServiceTest = navigator.requestVRServiceTest(); + callback(); + } + ); +} diff --git a/dom/vr/test/mochitest/test_vrController_displayId.html b/dom/vr/test/mochitest/test_vrController_displayId.html new file mode 100644 index 0000000000..e69e3920d2 --- /dev/null +++ b/dom/vr/test/mochitest/test_vrController_displayId.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html> + <head> + <title>VRController DisplayId</title> + <meta name="timeout" content="long"/> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="VRSimulationDriver.js"></script> + <script src="runVRTest.js"></script> + </head> + <body> + <script> + "use strict"; + var vrDisplay; + var controllerCount = 0; + + function addController() { + promise_test((test) => { + return VRSimulationDriver.AttachVRController().then((controller) => { + controller.newButtonEvent(0, true); + }); + }, "Finish to add VRController."); + } + + function listenControllerEvents() { + async_test(function(t) { + window.addEventListener("gamepadbuttondown", function(e) { + assert_equals(e.gamepad.displayId, vrDisplay.displayId, "gamepad.displayId should be equal to vrDisplay."); + assert_equals(e.gamepad.id, "Puppet Gamepad", "gamepad.id must be equal to 'Puppet Gamepad'."); + ++controllerCount; + if (controllerCount == 2) { + t.done(); + } + }); + }, "Finish to verify VRController.displayId."); + } + + function startTest() { + promise_test((test) => { + listenControllerEvents(); + return VRSimulationDriver.AttachWebVRDisplay().then(() => { + return navigator.getVRDisplays().then((displays) => { + vrDisplay = displays[0]; + assert_equals(displays.length, 1, "displays.length must be one after attach."); + assert_equals(vrDisplay.displayName, "Puppet HMD", "display.displayName must be equal to 'Puppet HMD'."); + addController(); + addController(); + }); + }); + }, "Finish to add VRDisplay."); + } + + runVRTest(startTest); + </script> + </body> +</html> diff --git a/dom/vr/test/mochitest/test_vrDisplay_canvas2d.html b/dom/vr/test/mochitest/test_vrDisplay_canvas2d.html new file mode 100644 index 0000000000..4d20e21352 --- /dev/null +++ b/dom/vr/test/mochitest/test_vrDisplay_canvas2d.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<html> + <head> + <title>VRDisplay Canvas2D</title> + <meta name="timeout" content="long"/> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="VRSimulationDriver.js"></script> + <script src="runVRTest.js"></script> + </head> + <body> + <script> + "use strict"; + var vrDisplay; + + function requestPresentTest() { + async_test(function (test) { + vrDisplay.requestAnimationFrame(callback); + + function callback() { + vrDisplay.resetPose(); + vrDisplay.getLayers(); + vrDisplay.submitFrame(); + vrDisplay.getEyeParameters("right"); + test.done(); + } + }, "Finish WebVR Canvas2D requestPresentTest."); + } + + function startTest() { + promise_test((test) => { + var canvas = document.createElement('canvas'); + (document.body || document.documentElement).appendChild(canvas); + var context = canvas.getContext('2d'); + var img = document.createElement('img'); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACw="; + + return VRSimulationDriver.AttachWebVRDisplay().then(() => { + return navigator.getVRDisplays().then((displays) => { + assert_equals(displays.length, 1, "displays.length must be one after attach."); + vrDisplay = displays[0]; + var frameData = new VRFrameData(); + return vrDisplay.requestPresent([{source: canvas}]).then(() => { + requestPresentTest(); + }); + }); + }); + }, "Finish running WebVR Canvas2D test."); + } + + runVRTest(startTest); + </script> + </body> +</html> diff --git a/dom/vr/test/mochitest/test_vrDisplay_exitPresent.html b/dom/vr/test/mochitest/test_vrDisplay_exitPresent.html new file mode 100644 index 0000000000..dc2c700723 --- /dev/null +++ b/dom/vr/test/mochitest/test_vrDisplay_exitPresent.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> + <head> + <title>VRDisplay ExitPresent</title> + <meta name="timeout" content="long"/> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="VRSimulationDriver.js"></script> + <script src="runVRTest.js"></script> + </head> + <body> + <script> + function testExitPresentOnOtherIframe(content) { + return content.navigator.getVRDisplays().then((displays) => { + content.vrDisplay = displays[0]; + return content.vrDisplay.exitPresent(); + }); + } + var initVRPresentation = function(content) { + return VRSimulationDriver.AttachWebVRDisplay().then(() => { + return content.navigator.getVRDisplays().then((displays) => { + content.vrDisplay = displays[0]; + content.canvas = content.document.createElement("canvas"); + content.canvas.id = "vrCanvas"; + return content.vrDisplay.requestPresent([{source:content.canvas}]); + }); + }); + } + function startTest() { + var ifr1 = document.getElementById("iframe1"); + var ifr2 = document.getElementById("iframe2"); + var frame1 = ifr1.contentWindow; + var frame2 = ifr2.contentWindow; + promise_test((test) => { + return VRSimulationDriver.AttachWebVRDisplay().then(() => { + return initVRPresentation(frame1).then(() => { + promise_test((test) => { + return promise_rejects(test, null, testExitPresentOnOtherIframe(frame2)); + }, "We cannot exit VR presentation established by another content, this promise is expected to be rejected.") + }); + }); + }, "Finish running WebVR exitPresent test."); + } + runVRTest(startTest); + </script> + + <iframe id="iframe1"></iframe> + <iframe id="iframe2"></iframe> + </body> +</html> diff --git a/dom/vr/test/mochitest/test_vrDisplay_getFrameData.html b/dom/vr/test/mochitest/test_vrDisplay_getFrameData.html new file mode 100644 index 0000000000..c8a986ae8e --- /dev/null +++ b/dom/vr/test/mochitest/test_vrDisplay_getFrameData.html @@ -0,0 +1,148 @@ +<!DOCTYPE html> +<html> +<head> + <title>VRDisplay GetFrameData</title> + <meta name="timeout" content="long"/> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="VRSimulationDriver.js"></script> + <script src="WebVRHelpers.js"></script> + <script src="requestPresent.js"></script> + <script src="runVRTest.js"></script> +</head> +<body id="body"> + <canvas id="webglCanvas"></canvas> + <script> + "use strict"; + var vrDisplay; + var vrRAF; + var canvas = document.getElementById('webglCanvas'); + function startTest() { + promise_test((test) => { + return attachVRDisplay(test).then(() => { + VRSimulationDriver.SetEyeResolution(1332, 1586); + VRSimulationDriver.SetEyeParameter("left", -0.029, 0, 0, 41.65, 35.57, 48.00, 43.97); + VRSimulationDriver.SetEyeParameter("right", 0.029, 0, 0, 41.65, 43.97, 48.00, 35.57); + var poseOrient = new Float32Array([-0.188, -0.007, 0.045, -0.980]); + var posePos = new Float32Array([-0.161, 0.076, -0.250]); + var poseAngVel = new Float32Array([0.008, -0.002, -0.006]); + var poseAngAcc = new Float32Array([3.404, -1.469, -5.901]); + var poseLinVel = new Float32Array([0.001, -0.003, -0.002]); + var poseLinAcc = new Float32Array([0.007, 0.068, -0.052]); + VRSimulationDriver.SetVRDisplayPose(posePos, poseLinVel, poseLinAcc, + poseOrient, poseAngVel, poseAngAcc); + VRSimulationDriver.UpdateVRDisplay(); + }).then(() => { + return promise_test((test) => { + return setupVRDisplay(test).then(() => { + return WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, + [{ source: canvas }]); + }).then(() => { + assert_true(vrDisplay.isPresenting, "vrDisplay.isPresenting must be true if requestPresent is fulfilled."); + assert_equals(vrDisplay.getLayers().length, 1, "vrDisplay.getLayers() should return one layer."); + + verifyFrameData(); + }) + }, "WebVR requestPresent fulfilled"); + }) + }, "Finish setting up VR test data."); + + function verifyFrameData() { + async_test(function (test) { + navigator.getVRDisplays().then((displays) => { + assert_equals(displays.length, 1, "displays.length must be one after attach."); + vrDisplay = displays[0]; + vrDisplay.requestAnimationFrame(callback); + + function callback() { + var frameData1 = new VRFrameData(); + vrDisplay.getFrameData(frameData1); + + // We insert a new frame to confirm we still can get + // the same data as the last getter. + insertNewFrameData(); + + var frameData2 = new VRFrameData(); + vrDisplay.getFrameData(frameData2); + + assert_equals(frameData1.timestamp, frameData2.timestamp, + "frameData.timestamp at a frame should be equal."); + + assert_true(checkValueInFloat32Array(frameData1.leftProjectionMatrix, + frameData2.leftProjectionMatrix), + "frameData.leftProjectionMatrix at a frame should be equal."); + + assert_true(checkValueInFloat32Array(frameData1.leftViewMatrix, + frameData2.leftViewMatrix), + "frameData.leftViewMatrix at a frame should be equal."); + + assert_true(checkValueInFloat32Array(frameData1.rightProjectionMatrix, + frameData2.rightProjectionMatrix), + "frameData.rightProjectionMatrix at a frame should be equal."); + + assert_true(checkValueInFloat32Array(frameData1.rightViewMatrix, + frameData2.rightViewMatrix), + "frameData.rightViewMatrix at a frame should be equal."); + + var pose1 = frameData1.pose; + var pose2 = frameData2.pose; + assert_true(checkValueInFloat32Array(pose1.position, + pose2.position), + "pose.position at a frame should be equal."); + + assert_true(checkValueInFloat32Array(pose1.linearVelocity, + pose2.linearVelocity), + "pose.linearVelocity at a frame should be equal."); + + assert_true(checkValueInFloat32Array(pose1.linearAcceleration, + pose2.linearAcceleration), + "pose.linearAcceleration at a frame should be equal."); + + assert_true(checkValueInFloat32Array(pose1.orientation, + pose2.orientation), + "pose.orientation at a frame should be equal."); + + assert_true(checkValueInFloat32Array(pose1.angularVelocity, + pose2.angularVelocity), + "pose.angularVelocity at a frame should be equal."); + + assert_true(checkValueInFloat32Array(pose1.angularAcceleration, + pose2.angularAcceleration), + "pose.angularAcceleration at a frame should be equal."); + test.done(); + } + }); + }, "WebVR returns the same frameData within a frame fulfilled"); + } + + function insertNewFrameData() { + var poseOrient = new Float32Array([-0.208, -0.017, 0.055, -0.930]); + var posePos = new Float32Array([-0.261, 0.036, -0.150]); + var poseAngVel = new Float32Array([0.018, -0.001, -0.003]); + var poseAngAcc = new Float32Array([1.504, -1.339, -4.901]); + var poseLinVel = new Float32Array([0.002, -0.001, -0.003]); + var poseLinAcc = new Float32Array([0.017, 0.061, -0.022]); + VRSimulationDriver.SetVRDisplayPose(posePos, poseLinVel, poseLinAcc, + poseOrient, poseAngVel, poseAngAcc); + VRSimulationDriver.UpdateVRDisplay(); + } + + function checkValueInFloat32Array(array1, array2) { + if (array1.length != array2.length) { + return false; + } + var index = 0; + while (index < array2.length) { + if (array1[index] != array2[index]) { + return false; + } + ++index; + } + return true; + } + } + + runVRTest(startTest); + </script> +</body> +</html> diff --git a/dom/vr/test/mochitest/test_vrDisplay_onvrdisplayconnect.html b/dom/vr/test/mochitest/test_vrDisplay_onvrdisplayconnect.html new file mode 100644 index 0000000000..e1912c7c41 --- /dev/null +++ b/dom/vr/test/mochitest/test_vrDisplay_onvrdisplayconnect.html @@ -0,0 +1,43 @@ +<html> + <head> + <title>VRDisplay onvrdisplayconnect test</title> + <meta name="timeout" content="long"/> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="VRSimulationDriver.js"></script> + <script src="WebVRHelpers.js"></script> + <script src="requestPresent.js"></script> + <script src="runVRTest.js"></script> + </head> + <body> + <script> + + function eventAfterConnectedTest() { + async_test(function (test) { + window.addEventListener("vrdisplayconnect", () => { + test.done(); + }); + }, "vrdisplayconnect should fire as soon as content listens for it, \ + even if the VR display was already connected."); + } + + function startTest() { + promise_test((test) => { + return attachVRDisplay(test).then(() => { + return promise_test((test) => { + return setupVRDisplay(test).then(() => { + VRSimulationDriver.UpdateVRDisplay(); + eventAfterConnectedTest(); + VRSimulationDriver.UpdateVRDisplay(); + }); + }); + }); + }); + } + + runVRTest(startTest); + </script> + <iframe id="iframe1"></iframe> + </body> +</html> diff --git a/dom/vr/test/mochitest/test_vrDisplay_onvrdisplaydeactivate_crosscontent.html b/dom/vr/test/mochitest/test_vrDisplay_onvrdisplaydeactivate_crosscontent.html new file mode 100644 index 0000000000..6c58e5efd1 --- /dev/null +++ b/dom/vr/test/mochitest/test_vrDisplay_onvrdisplaydeactivate_crosscontent.html @@ -0,0 +1,54 @@ +<html> + <head> + <title>VRDisplay onvrdisplaydeactivate Crosscontent test</title> + <meta name="timeout" content="long"/> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="VRSimulationDriver.js"></script> + <script src="WebVRHelpers.js"></script> + <script src="requestPresent.js"></script> + <script src="runVRTest.js"></script> + </head> + <body> + <canvas id="vrCanvas"></canvas> + <script> + + function startTest() { + var canvas = document.getElementById("vrCanvas"); + var iframe1 = document.getElementById("iframe1").contentWindow; + var t = async_test("vrdisplaydeactivate crosscontent test"); + + window.addEventListener("vrdisplaydeactivate", () => { + t.step(() => { + assert_true(vrDisplay.isPresenting, + "VRDisplay should be still presenting now without being affected by the event."); + t.done(); + }); + }); + + iframe1.addEventListener("vrdisplaydeactivate", () => { + t.unreached_func("vrdisplaydeactivate should not be received by other iframe."); + }); + + promise_test((test) => { + return attachVRDisplay(test).then(() => { + return promise_test((test) => { + return setupVRDisplay(test).then(() => { + VRSimulationDriver.SetMountState(true); + VRSimulationDriver.UpdateVRDisplay(); + return WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{source: canvas}]); + }).then(() => { + VRSimulationDriver.SetMountState(false); + VRSimulationDriver.UpdateVRDisplay(); + }); + }); + }); + }); + } + + runVRTest(startTest); + </script> + <iframe id="iframe1"></iframe> + </body> +</html> diff --git a/dom/vr/test/mochitest/test_vrDisplay_requestPresent.html b/dom/vr/test/mochitest/test_vrDisplay_requestPresent.html new file mode 100644 index 0000000000..4d021b6e0f --- /dev/null +++ b/dom/vr/test/mochitest/test_vrDisplay_requestPresent.html @@ -0,0 +1,130 @@ +<!DOCTYPE html> +<html> +<head> + <title>VRDisplay RequestPresent</title> + <meta name="timeout" content="long"/> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="VRSimulationDriver.js"></script> + <script src="WebVRHelpers.js"></script> + <script src="requestPresent.js"></script> + <script src="runVRTest.js"></script> +</head> +<body id="body"> + <canvas id="webglCanvas"></canvas> + <div id="testDiv"></div> + <script> + "use strict"; + var vrDisplay; + var canvas = document.getElementById('webglCanvas'); + var div = document.getElementById('testDiv'); + function startTest() { + promise_test((test) => { + return setupVRDisplay(test).then(() => { + return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{}])); + }).then(() => { + return validateDisplayNotPresenting(test, vrDisplay); + }); + }, "WebVR requestPresent rejected with empty frames"); + + promise_test((test) => { + return setupVRDisplay(test).then(() => { + return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas, leftBounds: [0.0, 0.0] }])); + }).then(() => { + return validateDisplayNotPresenting(test, vrDisplay); + }); + }, "WebVR requestPresent rejected with incorrect bounds (bounds arrays must be 0 or 4 long)"); + + promise_test((test) => { + return setupVRDisplay(test).then(() => { + return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: div }])); + }).then(() => { + return validateDisplayNotPresenting(test, vrDisplay); + }); + }, "WebVR requestPresent rejected with invalid source (must be canvas element)"); + + promise_test((test) => { + return setupVRDisplay(test).then(() => { + return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas, leftBounds: [div] }])); + }).then(() => { + return validateDisplayNotPresenting(test, vrDisplay); + }); + }, "WebVR requestPresent rejected with invalid bounds data type (must be able to convert to float)"); + + const invalidBounds = [ + [2.0, 0.0, 0.0, 0.0], + [0.0, 2.0, 0.0, 0.0], + [0.0, 0.0, 2.0, 0.0], + [0.0, 0.0, 0.0, 2.0], + [-1.0, 0.0, 0.0, 0.0], + [0.0, -1.0, 0.0, 0.0], + [0.0, 0.0, -1.0, 0.0], + [0.0, 0.0, 0.0, -1.0]]; + + invalidBounds.forEach((bound) => { + promise_test((test) => { + return setupVRDisplay(test).then(() => { + return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas, leftBounds: bound }])); + }).then(() => { + return validateDisplayNotPresenting(test, vrDisplay); + }); + }, "WebVR requestPresent rejected with bounds in invalid range: [" + bound + "]"); + }); + + promise_test((test) => { + return setupVRDisplay(test).then(() => { + var promise = vrDisplay.requestPresent({ source: canvas }); + return promise_rejects(test, null, promise); + }).then(() => { + return validateDisplayNotPresenting(test, vrDisplay); + }); + }, "WebVR requestPresent rejected without user initiated action"); + + promise_test((test) => { + return setupVRDisplay(test).then(() => { + return promise_rejects(test, null, WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas }, { source: canvas }])); + }).then(() => { + return validateDisplayNotPresenting(test, vrDisplay); + }); + }, "WebVR requestPresent rejected with more frames than max layers"); + + promise_test((test) => { + return setupVRDisplay(test).then(() => { + function requestAgain() { + // Callback for immediate requestPresent call for further testing. + // Cache this promise on global object since it seems to be the only object + // in scope across calls. + window.promiseSecond = vrDisplay.requestPresent([{ source: canvas }]); + } + return WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas }], requestAgain); + }).then(() => { + // First promise succeeded + assert_true(vrDisplay.isPresenting, "First promise should successfully fulfill"); + // Now, validate that the subsequent requestPresent was rejected + return promise_rejects(test, null, window.promiseSecond); + }).then(() => { + delete window.promiseSecond; + assert_true(vrDisplay.isPresenting, "Should still be presenting after rejected second promise"); + return vrDisplay.exitPresent(); + }); + }, "WebVR requestPresent fails while another one is in progress"); + + promise_test((test) => { + return setupVRDisplay(test).then(() => { + return WebVRHelpers.RequestPresentOnVRDisplay(vrDisplay, [{ source: canvas }]); + }).then(() => { + assert_true(vrDisplay.isPresenting, "vrDisplay.isPresenting must be true if requestPresent is fulfilled."); + assert_equals(vrDisplay.getLayers().length, 1, "vrDisplay.getLayers() should return one layer."); + return vrDisplay.exitPresent(); + }).then(() => { + assert_false(vrDisplay.isPresenting, "vrDisplay.isPresenting must be false if exitPresent is fulfilled."); + // exitPresent() should reject since we are no longer presenting. + return promise_rejects(test, null, vrDisplay.exitPresent()); + }); + }, "WebVR requestPresent fulfilled"); + } + + runVRTest(startTest); + </script> +</body> +</html> diff --git a/dom/vr/test/reftest/VRSimulationDriver.js b/dom/vr/test/reftest/VRSimulationDriver.js new file mode 100644 index 0000000000..971cdb8626 --- /dev/null +++ b/dom/vr/test/reftest/VRSimulationDriver.js @@ -0,0 +1,60 @@ + +var VRServiceTest; +var vrMockDisplay; + +var VRSimulationDriver = (function() { +"use strict"; + +var AttachWebVRDisplay = function() { + if (vrMockDisplay) { + // Avoid creating multiple displays + return Promise.resolve(vrMockDisplay); + } + var promise = VRServiceTest.attachVRDisplay("VRDisplayTest"); + promise.then(function (display) { + vrMockDisplay = display; + }); + + return promise; +}; + +var SetVRDisplayPose = function(position, + linearVelocity, linearAcceleration, + orientation, angularVelocity, + angularAcceleration) { + vrMockDisplay.setPose(position, linearVelocity, linearAcceleration, + orientation, angularVelocity, angularAcceleration); +}; + +var SetEyeResolution = function(width, height) { + vrMockDisplay.setEyeResolution(width, height); +} + +var SetEyeParameter = function(eye, offsetX, offsetY, offsetZ, + upDegree, rightDegree, downDegree, leftDegree) { + vrMockDisplay.setEyeParameter(eye, offsetX, offsetY, offsetZ, upDegree, rightDegree, + downDegree, leftDegree); +} + +var SetMountState = function(isMounted) { + vrMockDisplay.setMountState(isMounted); +} + +var UpdateVRDisplay = function() { + vrMockDisplay.update(); +} + +var API = { + AttachWebVRDisplay: AttachWebVRDisplay, + SetVRDisplayPose: SetVRDisplayPose, + SetEyeResolution: SetEyeResolution, + SetEyeParameter: SetEyeParameter, + SetMountState: SetMountState, + UpdateVRDisplay: UpdateVRDisplay, + + none: false +}; + +return API; + +}());
\ No newline at end of file diff --git a/dom/vr/test/reftest/change_size.html b/dom/vr/test/reftest/change_size.html new file mode 100644 index 0000000000..87d59f6a3c --- /dev/null +++ b/dom/vr/test/reftest/change_size.html @@ -0,0 +1,168 @@ +<!DOCTYPE html> +<meta charset='UTF-8'> +<!-- Viewport size change in WebGL and submit it to the VR device as a base64 image. +If this fails, something is seriously wrong. --> +<html class="reftest-wait"> +<head> + <script type='text/javascript' src='webgl-util.js'></script> + <script type='text/javascript' src="VRSimulationDriver.js"></script> + <script id="vs" type="x-shader/x-vertex"> + attribute vec2 aVertCoord; + + void main(void) { + gl_Position = vec4(aVertCoord, 0.0, 1.0); + } + </script> + <script id="fs" type="x-shader/x-fragment"> + precision mediump float; + + void main(void) { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } + </script> + <script type='text/javascript'> + 'use strict'; + + var submitResult = null; + var vrDisplay = null; + var webglCanvas = null; + var gl = null; + var prog = null; + var img = null; + // The resolution is 540 : 300 (the ratio of 2160 * 1200, like Vive and Oculus) + const eyeWidth = 270; + const eyeHeight = 300; + + function setStatus(text) { + var elem = document.getElementById('status'); + elem.innerHTML = text; + } + + function initVRMock() { + VRServiceTest = navigator.requestVRServiceTest(); + if (!VRServiceTest) { + setStatus('VRServiceTest get failed.'); + return; + } + + VRSimulationDriver.AttachWebVRDisplay().then(() => { + VRSimulationDriver.SetEyeResolution(eyeWidth, eyeHeight); + VRSimulationDriver.UpdateVRDisplay(); + }).then(() => { + // Looking for VR displays + if (navigator.getVRDisplays) { + submitResult = new VRSubmitFrameResult(); + navigator.getVRDisplays().then(function (displays) { + if (displays.length > 0) { + window.addEventListener('vrdisplaypresentchange', onVRPresentChange, false); + + vrDisplay = displays[0]; + vrDisplay.requestPresent([{ source: webglCanvas }]); + vrDisplay.requestAnimationFrame(onAnimationFrame); + } + }); + } + }); + } + + function onVRPresentChange() { + if (vrDisplay && vrDisplay.isPresenting) { + const leftEye = vrDisplay.getEyeParameters("left"); + const rightEye = vrDisplay.getEyeParameters("right"); + + if (leftEye.renderWidth != rightEye.renderWidth || + leftEye.renderWidth != eyeWidth) { + setStatus('renderWidth is not equal to eyeWidth.'); + } + + if (leftEye.renderHeight != rightEye.renderHeight || + leftEye.renderHeight != eyeHeight) { + setStatus('renderHeight is not equal to eyeHeight.'); + } + webglCanvas.width = leftEye.renderWidth * 2; + webglCanvas.height = leftEye.renderHeight; + } + } + + function onAnimationFrame() { + if (!vrDisplay.isPresenting) { + return; + } + + gl.clearColor(0.0, 1.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Presenting render a stereo view. + gl.viewport(0, 0, webglCanvas.width * 0.5, webglCanvas.height); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + + gl.viewport(webglCanvas.width * 0.5, 0, webglCanvas.width * 0.5, webglCanvas.height); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + + // Indicate VRDisplay we're done rendering. + vrDisplay.submitFrame(); + if (vrDisplay.getSubmitFrameResult(submitResult)) { + if (!img) { + img = document.createElement("img"); + img.onload = function(){ + // img width will not be eyeWidth * 2 (540), it would + // be 544. It is because D3D11 CopyResource changes + // the destination image size. + if ((img.height == eyeHeight)) { + webglCanvas.style.display = 'none'; + vrDisplay.exitPresent(); + setTimeout(testComplete, 0); + } + }; + img.src = submitResult.base64Image; + document.body.appendChild(img); + } else { + img.src = submitResult.base64Image; + } + } + vrDisplay.requestAnimationFrame(onAnimationFrame); + } + + function runTest() { + webglCanvas = document.getElementById('canvas'); + gl = webglCanvas.getContext('webgl'); + if (!gl) { + setStatus('WebGL context creation failed.'); + return; + } + gl.disable(gl.DEPTH_TEST); + prog = WebGLUtil.createProgramByIds(gl, 'vs', 'fs'); + if (!prog) { + setStatus('Program linking failed.'); + return; + } + prog.aVertCoord = gl.getAttribLocation(prog, "aVertCoord"); + + var vertCoordArr = new Float32Array([ + -0.5, -0.5, + 0.5, -0.5, + -0.5, 0.5, + 0.5, 0.5, + ]); + var vertCoordBuff = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertCoordBuff); + gl.bufferData(gl.ARRAY_BUFFER, vertCoordArr, gl.STATIC_DRAW); + gl.useProgram(prog); + gl.enableVertexAttribArray(prog.aVertCoord); + gl.vertexAttribPointer(prog.aVertCoord, 2, gl.FLOAT, false, 0, 0); + + initVRMock(); + } + + function testComplete() { + document.documentElement.removeAttribute("class"); + } + </script> +</head> + +<body onload='runTest();'> + <canvas id='canvas' width='128' height='128'></canvas> + <div id='status'></div> +</body> + +</html> diff --git a/dom/vr/test/reftest/change_size.png b/dom/vr/test/reftest/change_size.png Binary files differnew file mode 100644 index 0000000000..fe03114b20 --- /dev/null +++ b/dom/vr/test/reftest/change_size.png diff --git a/dom/vr/test/reftest/draw_rect.html b/dom/vr/test/reftest/draw_rect.html new file mode 100644 index 0000000000..acb8580c1b --- /dev/null +++ b/dom/vr/test/reftest/draw_rect.html @@ -0,0 +1,136 @@ +<!DOCTYPE html> +<meta charset='UTF-8'> +<!-- Draw rect in WebGL and submit it to the VR device as a base64 image. +If this fails, something is seriously wrong. --> +<html class="reftest-wait"> +<head> + <script type='text/javascript' src='webgl-util.js'></script> + <script type='text/javascript' src="VRSimulationDriver.js"></script> + <script id="vs" type="x-shader/x-vertex"> + attribute vec2 aVertCoord; + + void main(void) { + gl_Position = vec4(aVertCoord, 0.0, 1.0); + } + </script> + <script id="fs" type="x-shader/x-fragment"> + precision mediump float; + + void main(void) { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } + </script> + <script type='text/javascript'> + 'use strict'; + + var submitResult = null; + var vrDisplay = null; + var webglCanvas = null; + var gl = null; + var prog = null; + var img = null; + + function setStatus(text) { + var elem = document.getElementById('status'); + elem.innerHTML = text; + } + + function initVRMock() { + VRServiceTest = navigator.requestVRServiceTest(); + if (!VRServiceTest) { + setStatus('VRServiceTest get failed.'); + return; + } + + VRSimulationDriver.AttachWebVRDisplay().then(() => { + // Looking for VR displays + if (navigator.getVRDisplays) { + submitResult = new VRSubmitFrameResult(); + navigator.getVRDisplays().then(function (displays) { + if (displays.length > 0) { + vrDisplay = displays[0]; + vrDisplay.requestPresent([{ source: webglCanvas }]); + vrDisplay.requestAnimationFrame(onAnimationFrame); + } + }); + } + }); + } + + function onAnimationFrame() { + if (!vrDisplay.isPresenting) { + return; + } + + gl.clearColor(0.0, 1.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Presenting render a stereo view. + gl.viewport(0, 0, webglCanvas.width * 0.5, webglCanvas.height); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + + gl.viewport(webglCanvas.width * 0.5, 0, webglCanvas.width * 0.5, webglCanvas.height); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + + // Indicate VRDisplay we're done rendering. + vrDisplay.submitFrame(); + if (vrDisplay.getSubmitFrameResult(submitResult)) { + if (!img) { + img = document.createElement("img"); + img.onload = function(){ + webglCanvas.style.display = 'none'; + vrDisplay.exitPresent(); + setTimeout(testComplete, 0); + }; + img.src = submitResult.base64Image; + document.body.appendChild(img); + } else { + img.src = submitResult.base64Image; + } + } + vrDisplay.requestAnimationFrame(onAnimationFrame); + } + + function runTest() { + webglCanvas = document.getElementById('canvas'); + gl = webglCanvas.getContext('webgl'); + if (!gl) { + setStatus('WebGL context creation failed.'); + return; + } + gl.disable(gl.DEPTH_TEST); + prog = WebGLUtil.createProgramByIds(gl, 'vs', 'fs'); + if (!prog) { + setStatus('Program linking failed.'); + return; + } + prog.aVertCoord = gl.getAttribLocation(prog, "aVertCoord"); + + var vertCoordArr = new Float32Array([ + -0.5, -0.5, + 0.5, -0.5, + -0.5, 0.5, + 0.5, 0.5, + ]); + var vertCoordBuff = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertCoordBuff); + gl.bufferData(gl.ARRAY_BUFFER, vertCoordArr, gl.STATIC_DRAW); + gl.useProgram(prog); + gl.enableVertexAttribArray(prog.aVertCoord); + gl.vertexAttribPointer(prog.aVertCoord, 2, gl.FLOAT, false, 0, 0); + + initVRMock(); + } + + function testComplete() { + document.documentElement.removeAttribute("class"); + } + </script> +</head> + +<body onload='runTest();'> + <canvas id='canvas' width='256' height='256'></canvas> + <div id='status'></div> +</body> + +</html> diff --git a/dom/vr/test/reftest/draw_rect.png b/dom/vr/test/reftest/draw_rect.png Binary files differnew file mode 100644 index 0000000000..0f4d24a0d1 --- /dev/null +++ b/dom/vr/test/reftest/draw_rect.png diff --git a/dom/vr/test/reftest/reftest.list b/dom/vr/test/reftest/reftest.list new file mode 100644 index 0000000000..c1ca292c71 --- /dev/null +++ b/dom/vr/test/reftest/reftest.list @@ -0,0 +1,10 @@ +# WebVR Reftests +# Please confirm there is no other VR display connected. Otherwise, VRPuppetDisplay can't be attached. +defaults pref(dom.vr.enabled,true) pref(dom.vr.puppet.enabled,true) pref(dom.vr.test.enabled,true) pref(dom.vr.require-gesture,false) pref(dom.vr.puppet.submitframe,1) pref(dom.vr.display.rafMaxDuration,200) pref(dom.vr.display.enumerate.interval,0) pref(dom.vr.controller.enumerate.interval,0) +# WebVR Tests have been disabled as refactoring of gfxVRPuppet is landing. Dependencies for re-enabling these are tracked by meta bug 1555185. +# VR SubmitFrame is only implemented for D3D11.1 and MacOSX now. +# Our Windows 7 test machines don't support D3D11.1, so we run these tests on Windows 8+ only. +# skip-if((!winWidget&&release_or_beta)||Android||gtkWidget||!layersGPUAccelerated) == draw_rect.html wrapper.html?draw_rect.png +# On MacOSX platform, getting different color interpolation result. +# For lower resolution Mac hardware, we need to adjust it to fuzzy-if(cocoaWidget,0-1,0-1200). +# fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||cocoaWidget,0-1,0-600) skip-if((!winWidget&&release_or_beta)||Android||gtkWidget||!layersGPUAccelerated) == change_size.html wrapper.html?change_size.png diff --git a/dom/vr/test/reftest/webgl-util.js b/dom/vr/test/reftest/webgl-util.js new file mode 100644 index 0000000000..42f1c5ccd5 --- /dev/null +++ b/dom/vr/test/reftest/webgl-util.js @@ -0,0 +1,61 @@ +WebGLUtil = (function() { + // --------------------------------------------------------------------------- + // WebGL helpers + + // Returns a valid shader, or null on errors. + function createShaderById(gl, id) { + var elem = document.getElementById(id); + if (!elem) { + throw new Error( + "Failed to create shader from non-existent id '" + id + "'." + ); + } + + var src = elem.innerHTML.trim(); + + var shader; + if (elem.type == "x-shader/x-fragment") { + shader = gl.createShader(gl.FRAGMENT_SHADER); + } else if (elem.type == "x-shader/x-vertex") { + shader = gl.createShader(gl.VERTEX_SHADER); + } else { + throw new Error( + "Bad MIME type for shader '" + id + "': " + elem.type + "." + ); + } + + gl.shaderSource(shader, src); + gl.compileShader(shader); + + return shader; + } + + function createProgramByIds(gl, vsId, fsId) { + var vs = createShaderById(gl, vsId); + var fs = createShaderById(gl, fsId); + if (!vs || !fs) { + return null; + } + + var prog = gl.createProgram(); + gl.attachShader(prog, vs); + gl.attachShader(prog, fs); + gl.linkProgram(prog); + + if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) { + var str = "Shader program linking failed:"; + str += "\nShader program info log:\n" + gl.getProgramInfoLog(prog); + str += "\n\nVert shader log:\n" + gl.getShaderInfoLog(vs); + str += "\n\nFrag shader log:\n" + gl.getShaderInfoLog(fs); + console.error(str); + return null; + } + + return prog; + } + + return { + createShaderById, + createProgramByIds, + }; +})(); diff --git a/dom/vr/test/reftest/wrapper.html b/dom/vr/test/reftest/wrapper.html new file mode 100644 index 0000000000..40d0de6e42 --- /dev/null +++ b/dom/vr/test/reftest/wrapper.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<html class="reftest-wait"> +<head> +<title>Image reftest wrapper</title> +<style type="text/css"> + #image1 { background-color: rgb(10, 100, 250); } +</style> +<script> + // The image is loaded async after the page loads + // wait for it to finish loading + function onImageLoad() { + document.documentElement.removeAttribute("class"); + }; +</script> +</head> +<body> +<img id="image1"> +<script> + // Use as "wrapper.html?image.png" + var imgURL = document.location.search.substr(1); + document.images[0].onload = onImageLoad; + document.images[0].onerror = onImageLoad; + document.images[0].src = imgURL; +</script> +</body> +</html> |