blob: 6f2a5862b6ebf17fc96ab2e69c2ece7def9abce2 [file] [log] [blame]
<!DOCTYPE html>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
<script src="../xr/resources/xr-internal-device-mocking.js"></script>
<script src="../xr/resources/xr-test-utils.js"></script>
<script src="../xr/resources/test-constants.js"></script>
<canvas id="webgl-canvas"></canvas>
<script>
let testName = "Outstanding promises resolve appropriately if device disconencts";
// Expose the ability to get get the VRService and close the device on that VRService.
ChromeXRTest.prototype.getService = function() {
return this.mockVRService_;
}
MockVRService.prototype.closeDevice = function() {
this.deviceBinding_.close();
this.devicePtr_ = null;
}
// Override the default implementations of requestSession and supportsSession
// from XRDevice so that we can choose to either return an answer immediately or
// return a promise that will never resolve to guarantee we have an outstanding
// promise on device disconnect.
MockVRService.prototype.requestSession = function(sessionOptions, was_activation) {
return new Promise((resolve,reject) => { });
}
let immediatelyResolveSupportsSession = true;
MockVRService.prototype.supportsSession = function(sessionOptions) {
if (immediatelyResolveSupportsSession) {
return Promise.resolve({ supportsSession: true });
}
return new Promise((resolve, reject) => { });
}
// Override the default requestDevice implementation so that we can fail if it's
// called when we don't expect it to be called (typically this would be because
// we aren't planning to force another device closure and would leave any
// outstanding promises unresolved, and thus cause the test to timeout), and so
// that we can store the device binding so that we can force it to be closed.
let failIfRequestDeviceCalled = false;
MockVRService.prototype.requestDevice = function() {
if (failIfRequestDeviceCalled) {
assert_unreached("requestDevice shouldn't be called at this time");
}
if (!this.devicePtr_) {
this.devicePtr_ = new device.mojom.XRDevicePtr();
this.deviceBinding_ = new mojo.Binding(
device.mojom.XRDevice, this, mojo.makeRequest(this.devicePtr_));
}
return Promise.resolve({device: this.devicePtr_});
}
// Convenience methods to turn a resolve/reject into an appropraite string
// which Promise.All can check to tell us which methods failed.
// If we just let the promises assert, then Promise.all would only fail on the
// first assert.
let successMessage = "PASS";
let failMessageStart = "FAIL: "
function WrapResolve(promise, name, errorMsg) {
return promise.then(() => {
return successMessage;
}, () => {
return failMessageStart + name + ": " + errorMsg;
});
}
function WrapReject(promise, name, errorMsg, errorType) {
return promise.then(() => {
return failMessageStart + name + ": " + errorMsg;
}, (err) => {
if (err.name === errorType) {
return successMessage;
}
return failMessageStart + name + ": expected: " + errorType + " but got: " + err.name;
});
}
// Per security requirements, requesting an immersive session requires a user gesture.
function requestImmersiveSession() {
return new Promise((resolve, reject) => {
runWithUserGesture(() => {
navigator.xr.requestSession('immersive-vr').then((session) => {
resolve(session);
}, (err) => {
reject(err);
});
});
});
}
let validateDeviceDisconnectPromise = function() {
// Ensure that the state is properly set-up for our helper functions so that
// we can be called multiple times.
failIfRequestDeviceCalled = false;
immediatelyResolveSupportsSession = true;
// Make a supports session call so that we can ensure that the underlying code
// has gotten a devicePtr set up. Note that inline, since it's always
// guaranteed doesn't ensure that the device is set up, where-as a call to see
// if we support immersive does require a device
return navigator.xr.supportsSession('immersive-vr').then(() => {
// Cause supportsSession to stop returning and make future calls "hang"/
immediatelyResolveSupportsSession = false;
// We don't expect a new device to be requested, and if it is we aren't
// going to close it during this test, so any of our mocked calls will cause
// a timeout.
failIfRequestDeviceCalled = true;
let promises = [];
// Note that inline session requests still call out through the device.
promises.push(WrapResolve(navigator.xr.requestSession('inline'), "Request Inline",
"Inline should always be available"));
promises.push(WrapReject(requestImmersiveSession(), "Request Immersive",
"Immersive should be rejected once device is disconnected", "NotSupportedError"));
promises.push(WrapReject(navigator.xr.supportsSession('immersive-vr'), "Supports Immersive",
"Immersive should not be supported once device is disconnected", "NotSupportedError"));
// Force the device disconnect, which should cause the promises to resolve.
XRTest.getService().closeDevice();
// Call this after we close the device, because we don't expect this to rely
// on (or request) the presence of a device.
promises.push(WrapResolve(navigator.xr.supportsSession('inline'), "Supports Inline",
"Inline support should be available without calling to a device"));
return Promise.all(promises).then((results) => {
let error_messages = [];
for (let i = 0; i < results.length; i++) {
if (results[i] !== successMessage) {
error_messages.push(results[i]);
}
}
if (error_messages.length !== 0) {
assert_unreached(error_messages);
}
})
});
}
let testFunction = function(t) {
return validateDeviceDisconnectPromise().then(() => {
// Validate that even after disconnecting and resolving the promises,
// we can still request a new device, and that it will resolve any promises
// on it's disconnect.
return validateDeviceDisconnectPromise();
})
}
promise_test(testFunction, testName);
</script>