Introduce a Test Idler to have a remotely controlled idler
Summary: To test things that depend on `Idler`, we would so far need to depend on timing in the unit tests, which is very error prone. So introduced a `TestIdler` as well to make sure we can create an idler we can control remotely (as demonstrated in the unit test) Note that idler smells like generator functions all over the place, so maybe I'll take a stab later to see if we can replace idlers with generators, which gives a much clearer control flow imho. Reviewed By: nikoant Differential Revision: D19158369 fbshipit-source-id: 605d120860ecb02883442524df6f876e050ff092
This commit is contained in:
committed by
Facebook Github Bot
parent
f4fdeef692
commit
0a5df48639
@@ -8,16 +8,21 @@
|
||||
*/
|
||||
|
||||
import {CancelledPromiseError} from './errors';
|
||||
import {sleep} from './promiseTimeout';
|
||||
|
||||
export class Idler {
|
||||
lastIdle: number;
|
||||
interval: number;
|
||||
kill: boolean;
|
||||
export interface BaseIdler {
|
||||
shouldIdle(): boolean;
|
||||
idle(): Promise<void>;
|
||||
cancel(): void;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.lastIdle = 0;
|
||||
this.interval = 3;
|
||||
this.kill = false;
|
||||
export class Idler implements BaseIdler {
|
||||
lastIdle = performance.now();
|
||||
interval = 16;
|
||||
kill = false;
|
||||
|
||||
shouldIdle(): boolean {
|
||||
return this.kill || performance.now() - this.lastIdle > this.interval;
|
||||
}
|
||||
|
||||
idle(): Promise<void> {
|
||||
@@ -36,3 +41,65 @@ export class Idler {
|
||||
this.kill = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This smills like we should be using generators :)
|
||||
export class TestIdler implements BaseIdler {
|
||||
resolver?: () => void;
|
||||
kill = false;
|
||||
autoRun = false;
|
||||
hasProgressed = false;
|
||||
|
||||
shouldIdle() {
|
||||
if (this.kill) {
|
||||
return true;
|
||||
}
|
||||
if (this.autoRun) {
|
||||
return false;
|
||||
}
|
||||
// In turn we signal idle is needed and that it isn't
|
||||
this.hasProgressed = !this.hasProgressed;
|
||||
return !this.hasProgressed;
|
||||
}
|
||||
|
||||
async idle() {
|
||||
if (this.kill) {
|
||||
throw new CancelledPromiseError('Idler got killed');
|
||||
}
|
||||
if (this.autoRun) {
|
||||
return undefined;
|
||||
}
|
||||
if (this.resolver) {
|
||||
throw new Error('Already idling');
|
||||
}
|
||||
return new Promise<void>(resolve => {
|
||||
this.resolver = () => {
|
||||
this.resolver = undefined;
|
||||
// this.hasProgressed = false;
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.kill = true;
|
||||
this.run();
|
||||
}
|
||||
|
||||
async next() {
|
||||
if (!this.resolver) {
|
||||
throw new Error('Not yet idled');
|
||||
}
|
||||
|
||||
this.resolver();
|
||||
// make sure waiting promise runs first
|
||||
await sleep(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically progresses through all idle calls
|
||||
*/
|
||||
run() {
|
||||
this.resolver?.();
|
||||
this.autoRun = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {Idler} from '../Idler.tsx';
|
||||
import {Idler, TestIdler} from '../Idler.tsx';
|
||||
import {sleep} from '../promiseTimeout.tsx';
|
||||
|
||||
test('Idler should interrupt', async () => {
|
||||
const idler = new Idler();
|
||||
@@ -15,7 +16,9 @@ test('Idler should interrupt', async () => {
|
||||
try {
|
||||
for (; i < 500; i++) {
|
||||
if (i == 100) {
|
||||
expect(idler.shouldIdle()).toBe(false);
|
||||
idler.cancel();
|
||||
expect(idler.shouldIdle()).toBe(true);
|
||||
}
|
||||
await idler.idle();
|
||||
}
|
||||
@@ -24,3 +27,47 @@ test('Idler should interrupt', async () => {
|
||||
expect(i).toEqual(100);
|
||||
}
|
||||
});
|
||||
|
||||
test('Idler should want to idle', async () => {
|
||||
const idler = new Idler();
|
||||
expect(idler.shouldIdle()).toBe(false);
|
||||
await sleep(10);
|
||||
expect(idler.shouldIdle()).toBe(false);
|
||||
await sleep(200);
|
||||
expect(idler.shouldIdle()).toBe(true);
|
||||
await idler.idle();
|
||||
expect(idler.shouldIdle()).toBe(false);
|
||||
});
|
||||
|
||||
test('TestIdler can be controlled', async () => {
|
||||
const idler = new TestIdler();
|
||||
|
||||
expect(idler.shouldIdle()).toBe(false);
|
||||
expect(idler.shouldIdle()).toBe(true);
|
||||
let resolved = false;
|
||||
idler.idle().then(() => {
|
||||
resolved = true;
|
||||
});
|
||||
expect(resolved).toBe(false);
|
||||
await idler.next();
|
||||
expect(resolved).toBe(true);
|
||||
|
||||
expect(idler.shouldIdle()).toBe(false);
|
||||
expect(idler.shouldIdle()).toBe(true);
|
||||
idler.idle();
|
||||
await idler.next();
|
||||
|
||||
idler.cancel();
|
||||
expect(idler.shouldIdle()).toBe(true);
|
||||
|
||||
let threw = false;
|
||||
const p = idler.idle().catch(e => {
|
||||
threw = true;
|
||||
expect(e).toMatchInlineSnapshot(
|
||||
`[CancelledPromiseError: Idler got killed]`,
|
||||
);
|
||||
});
|
||||
|
||||
await p;
|
||||
expect(threw).toBe(true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user