123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- 'use strict';
- // These tests rely on the User Agent providing an implementation of the
- // WebXR Testing API (https://github.com/immersive-web/webxr-test-api).
- //
- // In Chromium-based browsers, this implementation is provided by a JavaScript
- // shim in order to reduce the amount of test-only code shipped to users. To
- // enable these tests the browser must be run with these options:
- //
- // --enable-blink-features=MojoJS,MojoJSTest
- // Debugging message helper, by default does nothing. Implementations can
- // override this.
- var xr_debug = function(name, msg) {};
- function xr_promise_test(name, func, properties, glContextType, glContextProperties) {
- promise_test(async (t) => {
- if (glContextType === 'webgl2') {
- // Fast fail on platforms not supporting WebGL2.
- assert_implements('WebGL2RenderingContext' in window, 'webgl2 not supported.');
- }
- // Perform any required test setup:
- xr_debug(name, 'setup');
- assert_implements(navigator.xr, 'missing navigator.xr - ensure test is run in a secure context.');
- // Only set up once.
- if (!navigator.xr.test) {
- // Load test-only API helpers.
- const script = document.createElement('script');
- script.src = '/resources/test-only-api.js';
- script.async = false;
- const p = new Promise((resolve, reject) => {
- script.onload = () => { resolve(); };
- script.onerror = e => { reject(e); };
- })
- document.head.appendChild(script);
- await p;
- if (isChromiumBased) {
- // Chrome setup
- await loadChromiumResources();
- } else if (isWebKitBased) {
- // WebKit setup
- await setupWebKitWebXRTestAPI();
- }
- }
- // Either the test api needs to be polyfilled and it's not set up above, or
- // something happened to one of the known polyfills and it failed to be
- // setup properly. Either way, the fact that xr_promise_test is being used
- // means that the tests expect navigator.xr.test to be set. By rejecting now
- // we can hopefully provide a clearer indication of what went wrong.
- assert_implements(navigator.xr.test, 'missing navigator.xr.test, even after attempted load');
- let gl = null;
- let canvas = null;
- if (glContextType) {
- canvas = document.createElement('canvas');
- document.body.appendChild(canvas);
- gl = canvas.getContext(glContextType, glContextProperties);
- }
- // Ensure that any devices are disconnected when done. If this were done in
- // a .then() for the success case, a test that expected failure would
- // already be marked done at the time that runs and the shutdown would
- // interfere with the next test.
- t.add_cleanup(async () => {
- // Ensure system state is cleaned up.
- xr_debug(name, 'cleanup');
- await navigator.xr.test.disconnectAllDevices();
- });
- xr_debug(name, 'main');
- return func(t, gl);
- }, name, properties);
- }
- // A utility function for waiting one animation frame before running the callback
- //
- // This is only needed after calling FakeXRDevice methods outside of an animation frame
- //
- // This is so that we can paper over the potential race allowed by the "next animation frame"
- // concept https://immersive-web.github.io/webxr-test-api/#xrsession-next-animation-frame
- function requestSkipAnimationFrame(session, callback) {
- session.requestAnimationFrame(() => {
- session.requestAnimationFrame(callback);
- });
- }
- // A test function which runs through the common steps of requesting a session.
- // Calls the passed in test function with the session, the controller for the
- // device, and the test object.
- function xr_session_promise_test(
- name, func, fakeDeviceInit, sessionMode, sessionInit, properties,
- glcontextPropertiesParam, gllayerPropertiesParam) {
- const glcontextProperties = (glcontextPropertiesParam) ? glcontextPropertiesParam : {};
- const gllayerProperties = (gllayerPropertiesParam) ? gllayerPropertiesParam : {};
- function runTest(t, glContext) {
- let testSession;
- let testDeviceController;
- let sessionObjects = {gl: glContext};
- // Ensure that any pending sessions are ended when done. This needs to
- // use a cleanup function to ensure proper sequencing. If this were
- // done in a .then() for the success case, a test that expected
- // failure would already be marked done at the time that runs, and the
- // shutdown would interfere with the next test which may have started.
- t.add_cleanup(async () => {
- // If a session was created, end it.
- if (testSession) {
- await testSession.end().catch(() => {});
- }
- });
- return navigator.xr.test.simulateDeviceConnection(fakeDeviceInit)
- .then((controller) => {
- testDeviceController = controller;
- return sessionObjects.gl.makeXRCompatible();
- })
- .then(() => new Promise((resolve, reject) => {
- // Perform the session request in a user gesture.
- xr_debug(name, 'simulateUserActivation');
- navigator.xr.test.simulateUserActivation(() => {
- xr_debug(name, 'document.hasFocus()=' + document.hasFocus());
- navigator.xr.requestSession(sessionMode, sessionInit || {})
- .then((session) => {
- xr_debug(name, 'session start');
- testSession = session;
- session.mode = sessionMode;
- session.sessionInit = sessionInit;
- let glLayer = new XRWebGLLayer(session, sessionObjects.gl, gllayerProperties);
- glLayer.context = sessionObjects.gl;
- // Session must have a baseLayer or frame requests
- // will be ignored.
- session.updateRenderState({
- baseLayer: glLayer
- });
- sessionObjects.glLayer = glLayer;
- xr_debug(name, 'session.visibilityState=' + session.visibilityState);
- try {
- resolve(func(session, testDeviceController, t, sessionObjects));
- } catch(err) {
- reject("Test function failed with: " + err);
- }
- })
- .catch((err) => {
- xr_debug(name, 'error: ' + err);
- reject(
- 'Session with params ' +
- JSON.stringify(sessionMode) +
- ' was rejected on device ' +
- JSON.stringify(fakeDeviceInit) +
- ' with error: ' + err);
- });
- });
- }));
- }
- xr_promise_test(
- name + ' - webgl',
- runTest,
- properties,
- 'webgl',
- {alpha: false, antialias: false, ...glcontextProperties}
- );
- xr_promise_test(
- name + ' - webgl2',
- runTest,
- properties,
- 'webgl2',
- {alpha: false, antialias: false, ...glcontextProperties});
- }
- // This function wraps the provided function in a
- // simulateUserActivation() call, and resolves the promise with the
- // result of func(), or an error if one is thrown
- function promise_simulate_user_activation(func) {
- return new Promise((resolve, reject) => {
- navigator.xr.test.simulateUserActivation(() => {
- try { let a = func(); resolve(a); } catch(e) { reject(e); }
- });
- });
- }
- // This functions calls a callback with each API object as specified
- // by https://immersive-web.github.io/webxr/spec/latest/, allowing
- // checks to be made on all ojects.
- // Arguements:
- // callback: A callback function with two arguements, the first
- // being the API object, the second being the name of
- // that API object.
- function forEachWebxrObject(callback) {
- callback(window.navigator.xr, 'navigator.xr');
- callback(window.XRSession, 'XRSession');
- callback(window.XRSessionCreationOptions, 'XRSessionCreationOptions');
- callback(window.XRFrameRequestCallback, 'XRFrameRequestCallback');
- callback(window.XRPresentationContext, 'XRPresentationContext');
- callback(window.XRFrame, 'XRFrame');
- callback(window.XRLayer, 'XRLayer');
- callback(window.XRView, 'XRView');
- callback(window.XRViewport, 'XRViewport');
- callback(window.XRViewerPose, 'XRViewerPose');
- callback(window.XRWebGLLayer, 'XRWebGLLayer');
- callback(window.XRWebGLLayerInit, 'XRWebGLLayerInit');
- callback(window.XRCoordinateSystem, 'XRCoordinateSystem');
- callback(window.XRFrameOfReference, 'XRFrameOfReference');
- callback(window.XRStageBounds, 'XRStageBounds');
- callback(window.XRSessionEvent, 'XRSessionEvent');
- callback(window.XRCoordinateSystemEvent, 'XRCoordinateSystemEvent');
- }
- // Code for loading test API in Chromium.
- async function loadChromiumResources() {
- await loadScript('/resources/chromium/webxr-test-math-helper.js');
- await import('/resources/chromium/webxr-test.js');
- await loadScript('/resources/testdriver.js');
- await loadScript('/resources/testdriver-vendor.js');
- // This infrastructure is also used by Chromium-specific internal tests that
- // may need additional resources (e.g. internal API extensions), this allows
- // those tests to rely on this infrastructure while ensuring that no tests
- // make it into public WPTs that rely on APIs outside of the webxr test API.
- if (typeof(additionalChromiumResources) !== 'undefined') {
- for (const path of additionalChromiumResources) {
- await loadScript(path);
- }
- }
- xr_debug = navigator.xr.test.Debug;
- }
- function setupWebKitWebXRTestAPI() {
- // WebKit setup. The internals object is used by the WebKit test runner
- // to provide JS access to internal APIs. In this case it's used to
- // ensure that XRTest is only exposed to wpt tests.
- navigator.xr.test = internals.xrTest;
- return Promise.resolve();
- }
|