spica
Version:
Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.
355 lines (313 loc) • 11.8 kB
text/typescript
import { Observation, ListenerItem } from './observer';
import { suppressAsyncException } from './exception';
describe('Unit: lib/observer', function () {
describe('Observation', function () {
const enum ListenerType {
Monitor,
Subscriber,
}
class TestEvent {
constructor(public type: string, public namespace: string[] = []) {
}
}
function throwError(err: unknown) {
throw err;
}
it('refs', function () {
const ob = new Observation<string[], void, void>();
function id<T>(a: T): T {
return a;
}
assert.deepStrictEqual(ob.refs([]), []);
ob.on([], id);
const m1 = ob.monitor([], id);
assert.deepStrictEqual(ob.refs([]).map(convert), [
[[], id, ListenerType.Monitor],
[[], id, ListenerType.Subscriber],
]);
ob.once([''], id);
const m2 = ob.monitor([''], id);
assert.deepStrictEqual(ob.refs([]).map(convert), [
[[], id, ListenerType.Monitor],
[[], id, ListenerType.Subscriber],
[[''], id, ListenerType.Monitor],
[[''], id, ListenerType.Subscriber],
]);
ob.on(['0'], id);
ob.on(['a'], id);
ob.on(['1'], id);
ob.on(['z'], id);
ob.on([''], id);
ob.on([], id);
assert.deepStrictEqual(ob.refs([]).map(convert), [
[[], id, ListenerType.Monitor],
[[], id, ListenerType.Subscriber],
[[], id, ListenerType.Subscriber],
[[''], id, ListenerType.Monitor],
[[''], id, ListenerType.Subscriber],
[[''], id, ListenerType.Subscriber],
[['0'], id, ListenerType.Subscriber],
[['a'], id, ListenerType.Subscriber],
[['1'], id, ListenerType.Subscriber],
[['z'], id, ListenerType.Subscriber],
]);
ob.off([]);
assert.deepStrictEqual(ob.refs([]).map(convert), [
[[], id, ListenerType.Monitor],
[[''], id, ListenerType.Monitor],
]);
m1();
m2();
assert.deepStrictEqual(ob.refs([]), []);
ob.on([''], id);
assert.deepStrictEqual(ob.refs([]).map(convert), [
[[''], id, ListenerType.Subscriber],
]);
ob.off([''], id);
assert.deepStrictEqual(ob.refs([]), []);
return;
function convert(register: ListenerItem<unknown[], unknown, unknown>) {
return [
register.namespace,
register.listener,
register.type,
];
}
});
it('count', function () {
const ob = new Observation<string[], void, void>();
function id<T>(a: T): T {
return a;
}
assert(ob.refs([]).length === 0);
const m1 = ob.monitor([], id);
ob.on([], id);
assert(ob.refs([]).length === 2);
const m2 = ob.monitor([''], id);
ob.on([''], id);
assert(ob.refs([]).length === 4);
assert(ob.refs(['']).length === 2);
const m3 = ob.monitor(['', ''], id);
ob.on(['', ''], id);
assert(ob.refs([]).length === 6);
assert(ob.refs(['']).length === 4);
assert(ob.refs(['', '']).length === 2);
ob.off([''], id);
assert(ob.refs([]).length === 5);
assert(ob.refs(['']).length === 3);
assert(ob.refs(['', '']).length === 2);
ob.off(['']);
assert(ob.refs([]).length === 4);
assert(ob.refs(['']).length === 2);
assert(ob.refs(['', '']).length === 1);
ob.off([''], id);
assert(ob.refs([]).length === 4);
assert(ob.refs(['']).length === 2);
assert(ob.refs(['', '']).length === 1);
m3();
assert(ob.refs([]).length === 3);
assert(ob.refs(['']).length === 1);
assert(ob.refs(['', '']).length === 0);
m2();
assert(ob.refs([]).length === 2);
assert(ob.refs(['']).length === 0);
assert(ob.refs(['', '']).length === 0);
ob.off([]);
assert(ob.refs([]).length === 1);
m1();
assert(ob.refs([]).length === 0);
});
it('emit', function (done) {
const ob = new Observation<string[], TestEvent, void>();
ob.on([''], () => done());
ob.emit([''], new TestEvent('TEST'));
});
it('emit namespace', function (done) {
const ob = new Observation<[string, number], TestEvent, void>();
ob.on(['', 1], () => done());
ob.emit(['', 1], new TestEvent('TEST'));
});
it('emit recursive', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.once([''], () => ob.emit([''], 1, data => assert(cnt === 0 && data === 1 && ++cnt)));
ob.emit([''], 0, data => void assert(cnt === 1 && data === 0 && ++cnt));
assert(cnt === 2);
});
it('reflect', function () {
const ob = new Observation<string[], void, void>();
ob.on([''], () => 1);
ob.on([''], () => 2);
assert.deepStrictEqual(ob.reflect([''], undefined), [1, 2]);
});
it('monitor', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.monitor([''], data => assert(cnt === 0 && data === 0 && ++cnt));
ob.monitor([''], data => assert(cnt === 1 && data === 0 && ++cnt));
ob.emit([''], 0, data => void assert(cnt === 2 && data === 0 && ++cnt));
assert(cnt === 3);
});
it('monitor once', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.monitor([''], data => assert(cnt === 0 && data === 1 && ++cnt), { once: true });
ob.emit([''], 1, data => assert(cnt === 1 && data === 1 && ++cnt));
assert.deepStrictEqual(ob.refs([]), []);
ob.monitor([''], data => assert(cnt === 2 && data === 2 && ++cnt), { once: true });
ob.emit(['', ''], 2, data => assert(cnt === 3 && data === 2 && ++cnt));
assert.deepStrictEqual(ob.refs([]), []);
ob.emit([''], 3, data => void assert(cnt === 4 && data === 3 && ++cnt));
assert(cnt === 5);
});
it('on', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.on([''], data => assert(cnt === 0 && data === 0 && ++cnt));
ob.on([''], data => assert(cnt === 1 && data === 0 && ++cnt));
ob.emit([''], 0, data => void assert(cnt === 2 && data === 0 && ++cnt));
assert(cnt === 3);
});
it('on once', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.on([''], data => assert(cnt === 0 && data === 1 && ++cnt), { once: true });
ob.emit([''], 1, data => assert(cnt === 1 && data === 1 && ++cnt));
assert.deepStrictEqual(ob.refs([]), []);
ob.on([''], data => assert(cnt === 2 && data === 2 && ++cnt), { once: true });
ob.emit([], 2, data => assert(cnt === 3 && data === 2 && ++cnt));
assert.deepStrictEqual(ob.refs([]), []);
ob.on([''], throwError, { once: true });
ob.off([''], throwError);
ob.emit([''], 3, data => void assert(cnt === 4 && data === 3 && ++cnt));
assert(cnt === 5);
});
it('once', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.once([''], data => assert(cnt === 0 && data === 1 && ++cnt));
ob.emit([''], 1, data => assert(cnt === 1 && data === 1 && ++cnt));
ob.once([''], data => assert(cnt === 2 && data === 2 && ++cnt));
ob.emit([''], 2, data => assert(cnt === 3 && data === 2 && ++cnt));
ob.once([''], throwError);
ob.off([''], throwError);
ob.emit([''], 3, data => void assert(cnt === 4 && data === 3 && ++cnt));
assert(cnt === 5);
});
it('off', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.monitor([''], data => assert(cnt === 2 && data === 0 && ++cnt));
ob.on([''], data => assert(cnt === 0 && data === 0 && ++cnt));
ob.on([''], throwError);
ob.on([''], data => assert(cnt === 1 && data === 0 && ++cnt));
ob.monitor([''], data => void assert(cnt === 3 && data === 0 && ++cnt));
ob.off([''], throwError);
ob.emit([''], 0);
assert(cnt === 4);
});
it('off scope', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.on([''], throwError);
ob.monitor([''], data => void assert(cnt === 1 && data === 0 && ++cnt));
ob.on([''], throwError);
ob.off(['']);
assert(ob.refs(['']).length === 1);
ob.on([''], data => assert(cnt === 0 && data === 0 && ++cnt));
ob.emit([''], 0);
assert(cnt === 2);
});
it('off sync', function () {
const ob = new Observation<string[], number, void>();
const f = () => 1;
ob.on([], f);
ob.once([], () => {
ob.off([], f);
ob.off([], g);
ob.off(['']);
ob.on([], () => 5);
return 2;
});
ob.on([], () => 3);
const g = () => 4;
ob.on([], g);
ob.on([''], () => 6);
assert.deepStrictEqual(ob.reflect([], 0), [1, 2, 3]);
ob.off([]);
ob.on([], () => {
ob.off([]);
return 1;
});
assert.deepStrictEqual(ob.reflect([], 0), [1]);
});
it('recovery', suppressAsyncException(function (done) {
let cnt = 0;
const ob = new Observation<string[], void, void>();
ob.on([''], throwError);
ob.on([''], throwError);
ob.on([''], throwError);
ob.on([''], () => assert(cnt === 0 && ++cnt));
ob.on([''], throwError);
ob.on([''], throwError);
ob.on([''], throwError);
ob.emit([''], undefined, () => void assert(cnt === 1 && ++cnt));
assert(cnt === 2);
done();
}));
it('on namespace', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.on([''], throwError);
ob.on(['', '0'], data => assert(cnt === 0 && data === 0 && ++cnt));
ob.on(['', '0', '0'], data => assert(cnt === 1 && data === 0 && ++cnt));
ob.on(['', '0', '0', '0'], data => void assert(cnt === 2 && data === 0 && ++cnt));
ob.emit(['', '0'], 0);
assert(cnt === 3);
});
it('off namespace', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
ob.monitor([''], data => void assert(cnt === 4 && data === 0 && ++cnt));
ob.on([''], throwError);
ob.monitor(['', '0'], data => assert(cnt === 3 && data === 0 && ++cnt));
ob.on(['', '0'], throwError);
ob.on(['', '0'], data => assert(cnt === 0 && data === 0 && ++cnt));
ob.monitor(['', '0', '0'], throwError);
ob.on(['', '0', '0'], data => assert(cnt === 1 && data === 0 && ++cnt));
ob.on(['', '0', '0', '0'], data => assert(cnt === 2 && data === 0 && ++cnt));
ob.off(['', '0'], throwError);
ob.emit(['', '0'], 0);
assert(cnt === 5);
});
it('mixed index types', function () {
let cnt = 0;
const sym = Symbol();
const ob = new Observation<[number, symbol], number, void>();
ob.on([NaN, sym], data => assert(cnt === 0 && data === 1 && ++cnt));
// @ts-expect-error
ob.emit([NaN, NaN], 0);
ob.emit([NaN, Symbol()], 0);
// @ts-expect-error
ob.emit(['NaN', sym], 0);
ob.emit([NaN, sym], 1);
ob.off([NaN, sym]);
ob.monitor([NaN, sym], data => void assert(cnt === 1 && data === 2 && ++cnt));
ob.emit([NaN, sym], 2);
assert(cnt === 2);
});
it('relay', function () {
let cnt = 0;
const ob = new Observation<string[], number, void>();
const source = new Observation<string[], number, void>();
ob.relay(source);
ob.once(['a'], (data, index) => {
assert(data === 0);
assert.deepStrictEqual(index, ['a']);
assert(cnt === 0 && ++cnt);
});
source.emit(['a'], 0);
assert(cnt === 1);
});
});
});