mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-16 16:16:40 +03:00
0eb3b620d3
Change-Id: I7c001f1c6d68a994342c1184e394f4643fd56ac9
630 lines
22 KiB
JavaScript
630 lines
22 KiB
JavaScript
/*! @license
|
|
* Shaka Player
|
|
* Copyright 2016 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
goog.require('shaka.test.Util');
|
|
goog.require('shaka.util.AbortableOperation');
|
|
goog.require('shaka.util.Error');
|
|
goog.require('shaka.util.PublicPromise');
|
|
|
|
describe('AbortableOperation', () => {
|
|
const Util = shaka.test.Util;
|
|
|
|
describe('promise', () => {
|
|
it('is resolved by the constructor argument', async () => {
|
|
const promise = new shaka.util.PublicPromise();
|
|
const abort = () => Promise.resolve();
|
|
|
|
const operation = new shaka.util.AbortableOperation(promise, abort);
|
|
promise.resolve(100);
|
|
|
|
const value = await operation.promise;
|
|
expect(value).toBe(100);
|
|
});
|
|
});
|
|
|
|
describe('abort', () => {
|
|
it('calls the abort argument from the constructor', () => {
|
|
const promise = Promise.resolve();
|
|
const abort =
|
|
jasmine.createSpy('abort').and.returnValue(Promise.resolve());
|
|
|
|
const operation = new shaka.util.AbortableOperation(
|
|
promise, shaka.test.Util.spyFunc(abort));
|
|
operation.abort();
|
|
expect(abort).toHaveBeenCalled();
|
|
});
|
|
|
|
it('is resolved when the underlying abort() is resolved', async () => {
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p = new shaka.util.PublicPromise();
|
|
const abort = jasmine.createSpy('abort').and.returnValue(p);
|
|
|
|
const operation = new shaka.util.AbortableOperation(
|
|
new shaka.util.PublicPromise(), shaka.test.Util.spyFunc(abort));
|
|
|
|
const abortComplete = jasmine.createSpy('abort complete');
|
|
operation.abort().then(shaka.test.Util.spyFunc(abortComplete), fail);
|
|
|
|
expect(abortComplete).not.toHaveBeenCalled();
|
|
await shaka.test.Util.shortDelay();
|
|
// Nothing has happened yet, so abort is not complete.
|
|
expect(abortComplete).not.toHaveBeenCalled();
|
|
// Resolve the underlying Promise.
|
|
p.resolve();
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
// The abort is now complete.
|
|
expect(abortComplete).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('failed', () => {
|
|
it('creates a failed operation with the given error', async () => {
|
|
const error = new shaka.util.Error(
|
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
shaka.util.Error.Category.NETWORK,
|
|
shaka.util.Error.Code.MALFORMED_DATA_URI);
|
|
|
|
const operation = shaka.util.AbortableOperation.failed(error);
|
|
await expectAsync(operation.promise)
|
|
.toBeRejectedWith(Util.jasmineError(error));
|
|
});
|
|
});
|
|
|
|
describe('aborted', () => {
|
|
it('creates a failed operation with OPERATION_ABORTED', async () => {
|
|
const error = Util.jasmineError(new shaka.util.Error(
|
|
shaka.util.Error.Severity.CRITICAL,
|
|
shaka.util.Error.Category.PLAYER,
|
|
shaka.util.Error.Code.OPERATION_ABORTED));
|
|
|
|
const operation = shaka.util.AbortableOperation.aborted();
|
|
await expectAsync(operation.promise).toBeRejectedWith(error);
|
|
});
|
|
});
|
|
|
|
describe('completed', () => {
|
|
it('creates a completed operation with the given value', async () => {
|
|
const operation = shaka.util.AbortableOperation.completed(100);
|
|
const value = await operation.promise;
|
|
expect(value).toBe(100);
|
|
});
|
|
});
|
|
|
|
describe('notAbortable', () => {
|
|
it('creates an operation from the given promise', async () => {
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const promise = new shaka.util.PublicPromise();
|
|
const operation = shaka.util.AbortableOperation.notAbortable(promise);
|
|
|
|
let isAborted = false;
|
|
operation.abort().then(() => {
|
|
isAborted = true;
|
|
});
|
|
|
|
let isComplete = false;
|
|
operation.promise.catch(fail).then((value) => {
|
|
isComplete = true;
|
|
expect(value).toBe(100);
|
|
});
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
// Even though we called abort(), the operation hasn't completed
|
|
// because it isn't abortable. The abort() Promise hasn't been
|
|
// resolved yet, either.
|
|
expect(isComplete).toBe(false);
|
|
expect(isAborted).toBe(false);
|
|
|
|
promise.resolve(100);
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
// Now that we resolved the underlying promise, the operation is
|
|
// complete, and so is the abort() Promise.
|
|
expect(isComplete).toBe(true);
|
|
expect(isAborted).toBe(true);
|
|
});
|
|
}); // describe('notAbortable')
|
|
|
|
describe('all', () => {
|
|
it('creates a successful operation when all succeed', async () => {
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p1 = new shaka.util.PublicPromise();
|
|
const op1 = shaka.util.AbortableOperation.notAbortable(p1);
|
|
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p2 = new shaka.util.PublicPromise();
|
|
const op2 = shaka.util.AbortableOperation.notAbortable(p2);
|
|
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p3 = new shaka.util.PublicPromise();
|
|
const op3 = shaka.util.AbortableOperation.notAbortable(p3);
|
|
|
|
const all = shaka.util.AbortableOperation.all([op1, op2, op3]);
|
|
|
|
const onSuccessSpy = jasmine.createSpy('onSuccess');
|
|
const onSuccess = shaka.test.Util.spyFunc(onSuccessSpy);
|
|
const onErrorSpy = jasmine.createSpy('onError');
|
|
const onError = shaka.test.Util.spyFunc(onErrorSpy);
|
|
|
|
all.promise.then(onSuccess, onError);
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(onSuccessSpy).not.toHaveBeenCalled();
|
|
expect(onErrorSpy).not.toHaveBeenCalled();
|
|
p1.resolve();
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
expect(onSuccessSpy).not.toHaveBeenCalled();
|
|
expect(onErrorSpy).not.toHaveBeenCalled();
|
|
p2.resolve();
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
expect(onSuccessSpy).not.toHaveBeenCalled();
|
|
expect(onErrorSpy).not.toHaveBeenCalled();
|
|
p3.resolve();
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
expect(onSuccessSpy).toHaveBeenCalled();
|
|
expect(onErrorSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('creates a failed operation when any fail', async () => {
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p1 = new shaka.util.PublicPromise();
|
|
const op1 = shaka.util.AbortableOperation.notAbortable(p1);
|
|
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p2 = new shaka.util.PublicPromise();
|
|
const op2 = shaka.util.AbortableOperation.notAbortable(p2);
|
|
|
|
const p3 = new shaka.util.PublicPromise();
|
|
const op3 = shaka.util.AbortableOperation.notAbortable(p3);
|
|
|
|
const all = shaka.util.AbortableOperation.all([op1, op2, op3]);
|
|
|
|
const onSuccessSpy = jasmine.createSpy('onSuccess');
|
|
const onSuccess = shaka.test.Util.spyFunc(onSuccessSpy);
|
|
const onErrorSpy = jasmine.createSpy('onError');
|
|
const onError = shaka.test.Util.spyFunc(onErrorSpy);
|
|
|
|
all.promise.then(onSuccess, onError);
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(onSuccessSpy).not.toHaveBeenCalled();
|
|
expect(onErrorSpy).not.toHaveBeenCalled();
|
|
p1.resolve();
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
expect(onSuccessSpy).not.toHaveBeenCalled();
|
|
expect(onErrorSpy).not.toHaveBeenCalled();
|
|
p2.reject('error');
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
expect(onSuccessSpy).not.toHaveBeenCalled();
|
|
expect(onErrorSpy).toHaveBeenCalledWith('error');
|
|
});
|
|
|
|
it('aborts all operations on abort', async () => {
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p1 = new shaka.util.PublicPromise();
|
|
const abort1Spy = jasmine.createSpy('abort1')
|
|
.and.callFake(() => p1.reject());
|
|
const abort1 = shaka.test.Util.spyFunc(abort1Spy);
|
|
const op1 = new shaka.util.AbortableOperation(p1, abort1);
|
|
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p2 = new shaka.util.PublicPromise();
|
|
const abort2Spy = jasmine.createSpy('abort2')
|
|
.and.callFake(() => p2.reject());
|
|
const abort2 = shaka.test.Util.spyFunc(abort2Spy);
|
|
const op2 = new shaka.util.AbortableOperation(p2, abort2);
|
|
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p3 = new shaka.util.PublicPromise();
|
|
const abort3Spy = jasmine.createSpy('abort3')
|
|
.and.callFake(() => p3.reject());
|
|
const abort3 = shaka.test.Util.spyFunc(abort3Spy);
|
|
const op3 = new shaka.util.AbortableOperation(p3, abort3);
|
|
|
|
/** @type {!shaka.util.AbortableOperation} */
|
|
const all = shaka.util.AbortableOperation.all([op1, op2, op3]);
|
|
|
|
const onSuccessSpy = jasmine.createSpy('onSuccess');
|
|
const onSuccess = shaka.test.Util.spyFunc(onSuccessSpy);
|
|
const onErrorSpy = jasmine.createSpy('onError');
|
|
const onError = shaka.test.Util.spyFunc(onErrorSpy);
|
|
|
|
all.promise.then(onSuccess, onError);
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(onSuccessSpy).not.toHaveBeenCalled();
|
|
expect(onErrorSpy).not.toHaveBeenCalled();
|
|
|
|
expect(abort1Spy).not.toHaveBeenCalled();
|
|
expect(abort2Spy).not.toHaveBeenCalled();
|
|
expect(abort3Spy).not.toHaveBeenCalled();
|
|
|
|
all.abort();
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
expect(onSuccessSpy).not.toHaveBeenCalled();
|
|
expect(onErrorSpy).toHaveBeenCalled();
|
|
|
|
expect(abort1Spy).toHaveBeenCalled();
|
|
expect(abort2Spy).toHaveBeenCalled();
|
|
expect(abort3Spy).toHaveBeenCalled();
|
|
});
|
|
}); // describe('all')
|
|
|
|
describe('finally', () => {
|
|
it('executes after the operation is successful', async () => {
|
|
let isDone = false;
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const promise = new shaka.util.PublicPromise();
|
|
|
|
shaka.util.AbortableOperation.notAbortable(promise).finally((ok) => {
|
|
expect(ok).toBe(true);
|
|
isDone = true;
|
|
});
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(isDone).toBe(false);
|
|
promise.resolve(100);
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(isDone).toBe(true);
|
|
});
|
|
|
|
it('executes after the operation fails', async () => {
|
|
let isDone = false;
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const promise = new shaka.util.PublicPromise();
|
|
|
|
shaka.util.AbortableOperation.notAbortable(promise).finally((ok) => {
|
|
expect(ok).toBe(false);
|
|
isDone = true;
|
|
});
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(isDone).toBe(false);
|
|
promise.reject(0);
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(isDone).toBe(true);
|
|
});
|
|
|
|
it('executes after the chain is successful', async () => {
|
|
let isDone = false;
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const promise1 = new shaka.util.PublicPromise();
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const promise2 = new shaka.util.PublicPromise();
|
|
|
|
shaka.util.AbortableOperation.notAbortable(promise1).chain(() => {
|
|
return shaka.util.AbortableOperation.notAbortable(promise2);
|
|
}).finally((ok) => {
|
|
expect(ok).toBe(true);
|
|
isDone = true;
|
|
});
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(isDone).toBe(false);
|
|
promise1.resolve();
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
expect(isDone).toBe(false);
|
|
promise2.resolve();
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
expect(isDone).toBe(true);
|
|
});
|
|
|
|
it('executes after the chain fails', async () => {
|
|
let isDone = false;
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const promise1 = new shaka.util.PublicPromise();
|
|
const promise2 = new shaka.util.PublicPromise();
|
|
|
|
shaka.util.AbortableOperation.notAbortable(promise1).chain(() => {
|
|
return shaka.util.AbortableOperation.notAbortable(promise2);
|
|
}).finally((ok) => {
|
|
expect(ok).toBe(false);
|
|
isDone = true;
|
|
});
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(isDone).toBe(false);
|
|
promise1.reject(0);
|
|
await shaka.test.Util.shortDelay();
|
|
|
|
expect(isDone).toBe(true);
|
|
});
|
|
|
|
it('executes after a complex chain', async () => {
|
|
let isDone = false;
|
|
|
|
shaka.util.AbortableOperation.completed(0).chain(() => {
|
|
return shaka.util.AbortableOperation.aborted();
|
|
}).chain(() => {
|
|
fail('Should not be reachable');
|
|
}, (e) => {
|
|
return shaka.util.AbortableOperation.completed(100);
|
|
}).finally((ok) => {
|
|
expect(ok).toBe(true);
|
|
isDone = true;
|
|
});
|
|
|
|
await shaka.test.Util.shortDelay();
|
|
expect(isDone).toBe(true);
|
|
});
|
|
}); // describe('finally')
|
|
|
|
describe('chain', () => {
|
|
it('passes the value to the next operation on success', async () => {
|
|
/** @type {!Array.<number>} */
|
|
const values = [];
|
|
|
|
const op = shaka.util.AbortableOperation.completed(100).chain((value) => {
|
|
values.push(value);
|
|
expect(value).toBe(100);
|
|
// Plain value
|
|
return 200;
|
|
}).chain((value) => {
|
|
values.push(value);
|
|
expect(value).toBe(200);
|
|
// Resolved Promise
|
|
return Promise.resolve(300);
|
|
}).chain((value) => {
|
|
values.push(value);
|
|
expect(value).toBe(300);
|
|
// Delayed Promise
|
|
return shaka.test.Util.shortDelay().then(() => 400);
|
|
}).chain((value) => {
|
|
values.push(value);
|
|
expect(value).toBe(400);
|
|
// Abortable operation
|
|
return shaka.util.AbortableOperation.completed(500);
|
|
}).chain((value) => {
|
|
values.push(value);
|
|
expect(value).toBe(500);
|
|
}).finally((ok) => {
|
|
expect(ok).toBe(true);
|
|
// The bug https://github.com/google/shaka-player/issues/1260 makes this
|
|
// expectation fail because some stages were skipped. Without this
|
|
// check, the test would pass, even though the bug shows up first in the
|
|
// basic functionality of 'chain'.
|
|
expect(values).toEqual([100, 200, 300, 400, 500]);
|
|
});
|
|
await op.promise;
|
|
});
|
|
|
|
it('skips the onSuccess callbacks on error', async () => {
|
|
const error = new shaka.util.Error(
|
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
shaka.util.Error.Category.NETWORK,
|
|
shaka.util.Error.Code.MALFORMED_DATA_URI);
|
|
|
|
const op = shaka.util.AbortableOperation.failed(error)
|
|
.chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error);
|
|
throw error; // rethrow
|
|
}).chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error);
|
|
throw error; // rethrow
|
|
}).chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error);
|
|
}).finally((ok) => {
|
|
expect(ok).toBe(true); // Last stage did not rethrow
|
|
});
|
|
await op.promise;
|
|
});
|
|
|
|
it('can fall back to other operations in onError callback', async () => {
|
|
const error1 = new shaka.util.Error(
|
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
shaka.util.Error.Category.NETWORK,
|
|
shaka.util.Error.Code.MALFORMED_DATA_URI);
|
|
const error2 = new shaka.util.Error(
|
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
shaka.util.Error.Category.TEXT,
|
|
shaka.util.Error.Code.INVALID_XML);
|
|
const error3 = new shaka.util.Error(
|
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
shaka.util.Error.Category.MEDIA,
|
|
shaka.util.Error.Code.EBML_BAD_FLOATING_POINT_SIZE);
|
|
|
|
const op = shaka.util.AbortableOperation.failed(error1)
|
|
.chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error1);
|
|
return shaka.util.AbortableOperation.failed(error2);
|
|
}).chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error2);
|
|
return shaka.util.AbortableOperation.failed(error3);
|
|
}).chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error3);
|
|
return shaka.util.AbortableOperation.completed(400);
|
|
}).chain((value) => {
|
|
expect(value).toBe(400);
|
|
}).finally((ok) => {
|
|
expect(ok).toBe(true);
|
|
});
|
|
await op.promise;
|
|
});
|
|
|
|
it('fails when an error is thrown', async () => {
|
|
const error1 = new shaka.util.Error(
|
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
shaka.util.Error.Category.NETWORK,
|
|
shaka.util.Error.Code.MALFORMED_DATA_URI);
|
|
const error2 = new shaka.util.Error(
|
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
shaka.util.Error.Category.TEXT,
|
|
shaka.util.Error.Code.INVALID_XML);
|
|
|
|
const op = shaka.util.AbortableOperation.completed(100).chain((value) => {
|
|
throw error1;
|
|
}).chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error1);
|
|
throw error2;
|
|
}).chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error2);
|
|
}).finally((ok) => {
|
|
expect(ok).toBe(true); // Last stage did not rethrow
|
|
});
|
|
await op.promise;
|
|
});
|
|
|
|
it('goes to success state when onError returns undefined', async () => {
|
|
const error = new shaka.util.Error(
|
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
shaka.util.Error.Category.NETWORK,
|
|
shaka.util.Error.Code.MALFORMED_DATA_URI);
|
|
|
|
const op = shaka.util.AbortableOperation.failed(error)
|
|
.chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error);
|
|
// no return value
|
|
}).chain((value) => {
|
|
expect(value).toBe(undefined);
|
|
}, fail).finally((ok) => {
|
|
expect(ok).toBe(true);
|
|
});
|
|
await op.promise;
|
|
});
|
|
|
|
it('does not need return when onSuccess omitted', async () => {
|
|
const operation = shaka.util.AbortableOperation.completed(100)
|
|
.chain(undefined, fail).chain(undefined, fail).chain((value) => {
|
|
expect(value).toBe(100);
|
|
}).finally((ok) => {
|
|
expect(ok).toBe(true);
|
|
});
|
|
await operation.promise;
|
|
});
|
|
|
|
it('does not need rethrow when onError omitted', async () => {
|
|
const error = new shaka.util.Error(
|
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
shaka.util.Error.Category.NETWORK,
|
|
shaka.util.Error.Code.MALFORMED_DATA_URI);
|
|
|
|
const operation = shaka.util.AbortableOperation.failed(error)
|
|
.chain(fail).chain(fail).chain(fail).chain(fail, (e) => {
|
|
shaka.test.Util.expectToEqualError(e, error);
|
|
}).finally((ok) => {
|
|
expect(ok).toBe(true); // Last stage did not rethrow
|
|
});
|
|
await operation.promise;
|
|
});
|
|
|
|
it('ensures abort is called with the correct "this"', async () => {
|
|
// During testing and development, an early version of chain() would
|
|
// sometimes unbind an abort method from an earlier stage of the chain.
|
|
// Make sure this doesn't happen.
|
|
let innerOperation;
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const p = new shaka.util.PublicPromise();
|
|
let abortCalled = false;
|
|
|
|
/**
|
|
* NOTE: This is a subtle thing, but this must be an ES5 anonymous
|
|
* function for the test to work. ES6 arrow functions would always be
|
|
* called with the "this" of the test itself, regardless of what the
|
|
* library is doing.
|
|
*
|
|
* @this {shaka.util.AbortableOperation}
|
|
* @return {!Promise}
|
|
*/
|
|
function abort() {
|
|
expect(this).toBe(innerOperation);
|
|
abortCalled = true;
|
|
return Promise.resolve();
|
|
}
|
|
|
|
// Since the issue was with the calling of operation.abort, rather than
|
|
// the onAbort_ callback, we make an operation-like thing instead of using
|
|
// the AbortableOperation constructor.
|
|
innerOperation = {promise: p, abort: abort};
|
|
|
|
// The second stage of the chain returns innerOperation. A brief moment
|
|
// later, the outer chain is aborted.
|
|
const operation =
|
|
shaka.util.AbortableOperation.completed(100)
|
|
.chain(() => {
|
|
shaka.test.Util.shortDelay().then(() => {
|
|
operation.abort();
|
|
p.resolve();
|
|
});
|
|
return innerOperation;
|
|
})
|
|
.finally((ok) => {
|
|
// We resolved the non-abortable inner operation
|
|
expect(ok).toBe(true);
|
|
expect(abortCalled).toBe(true);
|
|
});
|
|
await operation.promise;
|
|
});
|
|
|
|
it('aborts nested AbortableOperation objects', async () => {
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const promise = new shaka.util.PublicPromise();
|
|
const abort = jasmine.createSpy('abort');
|
|
|
|
/** @type {!shaka.util.AbortableOperation} */
|
|
const operation = shaka.util.AbortableOperation.completed(0).chain(() => {
|
|
return shaka.util.AbortableOperation.completed(1)
|
|
.chain(() => 2)
|
|
.chain(() => {
|
|
return new shaka.util.AbortableOperation(
|
|
promise, Util.spyFunc(abort));
|
|
})
|
|
.chain(() => 3);
|
|
});
|
|
await Util.shortDelay(); // Ensure we are waiting on the "promise".
|
|
operation.abort();
|
|
promise.resolve();
|
|
await expectAsync(operation.promise).toBeRejected();
|
|
expect(abort).toHaveBeenCalled();
|
|
});
|
|
|
|
it('aborts even with a failure callback', async () => {
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const promise = new shaka.util.PublicPromise();
|
|
|
|
/** @type {!shaka.util.AbortableOperation} */
|
|
const operation = shaka.util.AbortableOperation.completed(0)
|
|
.chain(() => promise)
|
|
.chain(
|
|
() => fail('Promise should be rejected'),
|
|
() => {});
|
|
await Util.shortDelay(); // Ensure we are waiting on the "promise".
|
|
operation.abort();
|
|
promise.reject();
|
|
await expectAsync(operation.promise).toBeRejected();
|
|
});
|
|
|
|
it('abort waits for plain Promise to be resolved', async () => {
|
|
/** @type {!shaka.util.PublicPromise} */
|
|
const promise = new shaka.util.PublicPromise();
|
|
const resolved = jasmine.createSpy('resolve');
|
|
|
|
/** @type {!shaka.util.AbortableOperation} */
|
|
const operation = shaka.util.AbortableOperation.completed(0)
|
|
.chain(() => promise);
|
|
await Util.shortDelay(); // Ensure we are waiting on the "promise".
|
|
const p = operation.abort().then(
|
|
Util.spyFunc(resolved), Util.spyFunc(resolved));
|
|
// Even after waiting for some Promises, the abort() promise should not
|
|
// get resolved until the "promise" is finished.
|
|
await Util.shortDelay();
|
|
expect(resolved).not.toHaveBeenCalled();
|
|
// Now the abort() can get resolved.
|
|
promise.resolve();
|
|
await p;
|
|
});
|
|
}); // describe('chain')
|
|
}); // describe('AbortableOperation')
|