188 lines
6.3 KiB
TypeScript
188 lines
6.3 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {IdleScheduler} from '../src/idle';
|
|
import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|
import {envIsSupported} from '../testing/utils';
|
|
|
|
(function() {
|
|
// Skip environments that don't support the minimum APIs needed to run the SW tests.
|
|
if (!envIsSupported()) {
|
|
return;
|
|
}
|
|
|
|
describe('IdleScheduler', () => {
|
|
let scope: SwTestHarness;
|
|
let idle: IdleScheduler;
|
|
|
|
beforeEach(() => {
|
|
scope = new SwTestHarnessBuilder().build();
|
|
idle = new IdleScheduler(scope, 1000, 3000, {
|
|
log: (v, context) => console.error(v, context),
|
|
});
|
|
});
|
|
|
|
// Validate that a single idle task executes when trigger()
|
|
// is called and the idle timeout passes.
|
|
it('executes scheduled work on time', async () => {
|
|
// Set up a single idle task to set the completed flag to true when it runs.
|
|
let completed: boolean = false;
|
|
idle.schedule('work', async () => {
|
|
completed = true;
|
|
});
|
|
|
|
// Simply scheduling the task should not cause it to execute.
|
|
expect(completed).toEqual(false);
|
|
|
|
// Trigger the idle mechanism. This returns a Promise that should resolve
|
|
// once the idle timeout has passed.
|
|
const trigger = idle.trigger();
|
|
|
|
// Advance the clock beyond the idle timeout, causing the idle tasks to run.
|
|
scope.advance(1100);
|
|
|
|
// It should now be possible to wait for the trigger, and for the idle queue
|
|
// to be empty.
|
|
await trigger;
|
|
await idle.empty;
|
|
|
|
// The task should now have run.
|
|
expect(completed).toEqual(true);
|
|
});
|
|
|
|
it('waits for multiple tasks to complete serially', async () => {
|
|
// Schedule several tasks that will increase a counter according to its
|
|
// current value. If these tasks execute in parallel, the writes to the counter
|
|
// will race, and the test will fail.
|
|
let counter: number = 2;
|
|
idle.schedule('double counter', async () => {
|
|
let local = counter;
|
|
await Promise.resolve();
|
|
local *= 2;
|
|
await Promise.resolve();
|
|
counter = local * 2;
|
|
});
|
|
idle.schedule('triple counter', async () => {
|
|
// If this expect fails, it comes out of the 'await trigger' below.
|
|
expect(counter).toEqual(8);
|
|
|
|
// Multiply the counter by 3 twice.
|
|
let local = counter;
|
|
await Promise.resolve();
|
|
local *= 3;
|
|
await Promise.resolve();
|
|
counter = local * 3;
|
|
});
|
|
|
|
// Trigger the idle mechanism once.
|
|
const trigger = idle.trigger();
|
|
|
|
// Advance the clock beyond the idle timeout, causing the idle tasks to run, and
|
|
// wait for them to complete.
|
|
scope.advance(1100);
|
|
await trigger;
|
|
await idle.empty;
|
|
|
|
// Assert that both tasks executed in the correct serial sequence by validating
|
|
// that the counter reached the correct value.
|
|
expect(counter).toEqual(2 * 2 * 2 * 3 * 3);
|
|
});
|
|
|
|
// Validate that a single idle task does not execute until trigger() has been called
|
|
// and sufficient time passes without it being called again.
|
|
it('does not execute work until timeout passes with no triggers', async () => {
|
|
// Set up a single idle task to set the completed flag to true when it runs.
|
|
let completed: boolean = false;
|
|
idle.schedule('work', async () => {
|
|
completed = true;
|
|
});
|
|
|
|
// Trigger the queue once. This trigger will start a timer for the idle timeout,
|
|
// but another trigger() will be called before that timeout passes.
|
|
const firstTrigger = idle.trigger();
|
|
|
|
// Advance the clock a little, but not enough to actually cause tasks to execute.
|
|
scope.advance(500);
|
|
|
|
// Assert that the task has not yet run.
|
|
expect(completed).toEqual(false);
|
|
|
|
// Next, trigger the queue again.
|
|
const secondTrigger = idle.trigger();
|
|
|
|
// Advance the clock beyond the timeout for the first trigger, but not the second.
|
|
// This should cause the first trigger to resolve, but without running the task.
|
|
scope.advance(600);
|
|
await firstTrigger;
|
|
expect(completed).toEqual(false);
|
|
|
|
// Schedule a third trigger. This is the one that will eventually resolve the task.
|
|
const thirdTrigger = idle.trigger();
|
|
|
|
// Again, advance beyond the second trigger and verify it didn't resolve the task.
|
|
scope.advance(500);
|
|
await secondTrigger;
|
|
expect(completed).toEqual(false);
|
|
|
|
// Finally, advance beyond the third trigger, which should cause the task to be
|
|
// executed finally.
|
|
scope.advance(600);
|
|
await thirdTrigger;
|
|
await idle.empty;
|
|
|
|
// The task should have executed.
|
|
expect(completed).toEqual(true);
|
|
});
|
|
|
|
it('executes tasks after max delay even with newer triggers', async () => {
|
|
// Set up a single idle task to set the completed flag to true when it runs.
|
|
let completed: boolean = false;
|
|
idle.schedule('work', async () => {
|
|
completed = true;
|
|
});
|
|
|
|
// Trigger the queue once. This trigger will start a timer for the idle timeout,
|
|
// but another `trigger()` will be called before that timeout passes.
|
|
const firstTrigger = idle.trigger();
|
|
|
|
// Advance the clock a little, but not enough to actually cause tasks to execute.
|
|
scope.advance(999);
|
|
expect(completed).toBe(false);
|
|
|
|
// Next, trigger the queue again.
|
|
const secondTrigger = idle.trigger();
|
|
|
|
// Advance the clock beyond the timeout for the first trigger, but not the second.
|
|
// This should cause the first trigger to resolve, but without running the task.
|
|
scope.advance(999);
|
|
await firstTrigger;
|
|
expect(completed).toBe(false);
|
|
|
|
// Next, trigger the queue again.
|
|
const thirdTrigger = idle.trigger();
|
|
|
|
// Advance the clock beyond the timeout for the second trigger, but not the third.
|
|
// This should cause the second trigger to resolve, but without running the task.
|
|
scope.advance(999);
|
|
await secondTrigger;
|
|
expect(completed).toBe(false);
|
|
|
|
// Next, trigger the queue again.
|
|
const forthTrigger = idle.trigger();
|
|
|
|
// Finally, advance the clock beyond `maxDelay` (3000) from the first trigger, but not beyond
|
|
// the timeout for the forth. This should cause the task to be executed nontheless.
|
|
scope.advance(3);
|
|
await Promise.all([thirdTrigger, forthTrigger]);
|
|
|
|
// The task should have executed.
|
|
expect(completed).toBe(true);
|
|
});
|
|
});
|
|
})();
|