Bug 1323328 - Part 3: Add VR display request present mochitest; r?kip draft
authorDaosheng Mu <daoshengmu@gmail.com>
Thu, 02 Mar 2017 00:00:23 +0800
changeset 493539 9045fcde6dd4abd3c6867c1c36ecc1bc91198489
parent 493251 f48d74c699fe96402aa6b3ec183d123563ab5e87
child 493540 d7d94f50dce007036547c496c13e88c23494f006
push id47795
push userbmo:dmu@mozilla.com
push dateSat, 04 Mar 2017 20:02:00 +0000
reviewerskip
bugs1323328
milestone54.0a1
Bug 1323328 - Part 3: Add VR display request present mochitest; r?kip MozReview-Commit-ID: 13WuFhUFcTo
dom/vr/moz.build
dom/vr/test/VRSimulationDriver.js
dom/vr/test/WebVRHelpers.js
dom/vr/test/mochitest.ini
dom/vr/test/requestPresent.js
dom/vr/test/runVRTest.js
dom/vr/test/test_vrDisplay_requestPresent.html
modules/libpref/init/all.js
--- a/dom/vr/moz.build
+++ b/dom/vr/moz.build
@@ -22,8 +22,10 @@ UNIFIED_SOURCES = [
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/dom/base'
 ]
+
+MOCHITEST_MANIFESTS += ['test/mochitest.ini']
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/VRSimulationDriver.js
@@ -0,0 +1,45 @@
+
+var VRServiceTest;
+
+var VRSimulationDriver = (function() {
+"use strict";
+
+var AttachWebVRDisplay = function() {
+  return VRServiceTest.attachVRDisplay("VRDisplayTest");
+};
+
+var SetVRDisplayPose = function(vrDisplay, position,
+                                linearVelocity, linearAcceleration,
+                                orientation, angularVelocity,
+                                angularAcceleration) {
+  vrDisplay.setPose(position, linearVelocity, linearAcceleration,
+                    orientation, angularVelocity, angularAcceleration);
+};
+
+var SetEyeResolution = function(width, height) {
+  vrDisplay.setEyeResolution(width, height);
+}
+
+var SetEyeParameter = function(vrDisplay, eye, offsetX, offsetY, offsetZ,
+                               upDegree, rightDegree, downDegree, leftDegree) {
+  vrDisplay.setEyeParameter(eye, offsetX, offsetY, offsetZ, upDegree, rightDegree,
+                            downDegree, leftDegree);
+}
+
+var UpdateVRDisplay = function(vrDisplay) {
+  vrDisplay.update();
+}
+
+var API = {
+  AttachWebVRDisplay: AttachWebVRDisplay,
+  SetVRDisplayPose: SetVRDisplayPose,
+  SetEyeResolution: SetEyeResolution,
+  SetEyeParameter: SetEyeParameter,
+  UpdateVRDisplay: UpdateVRDisplay,
+
+  none: false
+};
+
+return API;
+
+}());
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/WebVRHelpers.js
@@ -0,0 +1,21 @@
+
+var WebVRHelpers = (function() {
+"use strict";
+
+var RequestPresentOnVRDisplay = function(vrDisplay, vrLayers, callback) {
+  if (callback) {
+    callback();
+  }
+
+  return vrDisplay.requestPresent(vrLayers);
+};
+
+var API = {
+  RequestPresentOnVRDisplay: RequestPresentOnVRDisplay,
+
+  none: false
+};
+
+return API;
+
+}());
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/mochitest.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+  VRSimulationDriver.js
+  requestPresent.js
+  runVRTest.js
+  WebVRHelpers.js
+
+[test_vrDisplay_requestPresent.html]
+skip-if = true
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/requestPresent.js
@@ -0,0 +1,34 @@
+// requestPresent.js
+//
+// This file provides helpers for testing VRDisplay requestPresent.
+
+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);
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/runVRTest.js
@@ -0,0 +1,9 @@
+function runVRTest(callback) {
+  SpecialPowers.pushPrefEnv({"set" : [["dom.vr.enabled", true],
+                                      ["dom.vr.puppet.enabled", true],
+                                      ["dom.vr.test.enabled", true]]},
+  () => {
+    VRServiceTest = navigator.requestVRServiceTest();
+    callback();
+  });
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/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>
\ No newline at end of file
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5039,16 +5039,18 @@ pref("dom.vr.openvr.enabled", false);
 pref("dom.vr.poseprediction.enabled", false);
 // path to openvr DLL
 pref("gfx.vr.openvr-runtime", "");
 // path to OSVR DLLs
 pref("gfx.vr.osvr.utilLibPath", "");
 pref("gfx.vr.osvr.commonLibPath", "");
 pref("gfx.vr.osvr.clientLibPath", "");
 pref("gfx.vr.osvr.clientKitLibPath", "");
+// Puppet device, used for simulating VR hardware within tests and dev tools
+pref("dom.vr.puppet.enabled", false);
 pref("dom.vr.test.enabled", false);
 // MMS UA Profile settings
 pref("wap.UAProf.url", "");
 pref("wap.UAProf.tagname", "x-wap-profile");
 
 // MMS version 1.1 = 0x11 (or decimal 17)
 // MMS version 1.3 = 0x13 (or decimal 19)
 // @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.34