Bug 1323328 - Part 3: Add VR display request present mochitest; r=kip
authorDaosheng Mu <daoshengmu@gmail.com>
Thu, 02 Mar 2017 00:00:23 +0800
changeset 394916 659795097ae3e147629d539a423bc5d6a8c22ccf
parent 394915 39365e72a1133d38439489a779c800cc1cca4eb9
child 394917 473acbe8453d4b763b173539db1954c3d7efb9e1
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskip
bugs1323328
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
@@ -5031,16 +5031,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