blob: 6fb901db41b956f9021994b4f4f363d7c45b9fff [file] [log] [blame]
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {
c,
cmpFromExpr,
cmpFromSort,
ConcreteEventSet,
Direction,
EmptyEventSet,
EmptyKeySet,
eq,
Event,
EventSet,
isConcreteEventSet,
isEmptyEventSet,
KeySet,
Num,
Str,
UntypedEvent,
v,
} from './event_set';
describe('EventSet', () => {
test('Event', () => {
{
const keyset: EmptyKeySet = {};
const event: Event<typeof keyset> = {
id: 'foo',
};
void event;
} {
const keyset = {
'bar': Num,
};
const event: Event<typeof keyset> = {
id: 'foo',
bar: 42,
};
void event;
}
});
describe('EmptyEventSet', () => {
test('isEmpty', async () => {
const events = EmptyEventSet.get();
expect(await events.isEmpty()).toEqual(true);
expect(await events.count()).toEqual(0);
});
test('isEmptyEventSet', () => {
const events: EventSet<KeySet> = EmptyEventSet.get();
expect(isEmptyEventSet(events)).toEqual(true);
});
test('materialise', async () => {
const events: EventSet<KeySet> = EmptyEventSet.get();
const materialised = await events.materialise({});
expect(await materialised.isEmpty()).toEqual(true);
expect(await materialised.count()).toEqual(0);
expect(materialised.events).toEqual([]);
expect(isConcreteEventSet(materialised)).toEqual(true);
});
test('union', async () => {
const a: EventSet<KeySet> = EmptyEventSet.get();
const b: EventSet<KeySet> = EmptyEventSet.get();
const aUnionB = a.union(b);
expect(await aUnionB.isEmpty()).toEqual(true);
expect(await aUnionB.count()).toEqual(0);
});
test('intersect', async () => {
const a: EventSet<KeySet> = EmptyEventSet.get();
const b: EventSet<KeySet> = EmptyEventSet.get();
const aIntersectB = a.intersect(b);
expect(await aIntersectB.isEmpty()).toEqual(true);
expect(await aIntersectB.count()).toEqual(0);
});
test('filter', async () => {
const events: EventSet<KeySet> = EmptyEventSet.get();
const filtered = await events.filter(c(true));
// TODO(hjd): This should be true after the optimisation() lands.
// expect(filtered).toBe(events);
expect(await filtered.isEmpty()).toEqual(true);
expect(await filtered.count()).toEqual(0);
});
test('sort', async () => {
const events: EventSet<KeySet> = EmptyEventSet.get();
const sorted = await events.sort({
direction: Direction.ASC,
expression: c(0),
});
// TODO(hjd): This should be true after the optimisations land.
// expect(sorted).toBe(events);
expect(await sorted.isEmpty()).toEqual(true);
expect(await sorted.count()).toEqual(0);
});
});
describe('ConcreteEventSet', () => {
test('isEmpty', async () => {
const event: Event<EmptyKeySet> = {
id: 'foo',
};
const empty = new ConcreteEventSet<EmptyKeySet>({}, []);
const events = new ConcreteEventSet<EmptyKeySet>({}, [event]);
expect(await empty.isEmpty()).toEqual(true);
expect(await empty.count()).toEqual(0);
expect(await events.isEmpty()).toEqual(false);
expect(await events.count()).toEqual(1);
});
test('isConcreteEventSet', () => {
expect(isConcreteEventSet(new ConcreteEventSet<EmptyKeySet>({}, [])))
.toEqual(true);
expect(isConcreteEventSet(EmptyEventSet.get())).toEqual(false);
});
test('materialise', async () => {
const keys = {
num: Num,
char: Str,
};
const a: Event<typeof keys> = {
id: 'a',
num: 97,
char: 'a',
};
const b: Event<typeof keys> = {
id: 'b',
num: 98,
char: 'b',
};
const d: Event<typeof keys> = {
id: 'd',
num: 100,
char: 'd',
};
const events = new ConcreteEventSet(keys, [a, b, d]);
expect((await events.materialise(keys)).events).toEqual([a, b, d]);
expect((await events.materialise(keys, 1)).events).toEqual([b, d]);
expect((await events.materialise(keys, 1, 1)).events).toEqual([b]);
expect((await events.materialise(keys, 99)).events).toEqual([]);
expect((await events.materialise(keys, 99, 0)).events).toEqual([]);
expect((await events.materialise({num: Num})).events).toEqual([
{id: 'a', num: 97},
{id: 'b', num: 98},
{id: 'd', num: 100},
]);
expect((await events.materialise({char: Str}, 1, 1)).events).toEqual([
{id: 'b', char: 'b'},
]);
});
test('union', async () => {
const a: Event<EmptyKeySet> = {
id: 'a',
};
const b: Event<EmptyKeySet> = {
id: 'b',
};
const d: Event<EmptyKeySet> = {
id: 'd',
};
const empty = EmptyEventSet.get();
const justA = new ConcreteEventSet({}, [a]);
const justB = new ConcreteEventSet({}, [b]);
const justD = new ConcreteEventSet({}, [d]);
const aAndB = justA.union(justB);
const aAndA = justA.union(justA);
const aAndD = justA.union(justD);
const aAndBAndEmpty = aAndB.union(empty);
const aAndDAndAAndB = aAndD.union(aAndB);
expect((await aAndB.materialise({})).events).toEqual([a, b]);
expect((await aAndA.materialise({})).events).toEqual([a]);
expect((await aAndD.materialise({})).events).toEqual([a, d]);
expect((await aAndBAndEmpty.materialise({})).events).toEqual([a, b]);
expect((await aAndDAndAAndB.materialise({})).events).toEqual([a, d, b]);
expect(await aAndB.isEmpty()).toEqual(false);
expect(await aAndA.isEmpty()).toEqual(false);
expect(await aAndD.isEmpty()).toEqual(false);
expect(await aAndBAndEmpty.isEmpty()).toEqual(false);
expect(await aAndDAndAAndB.isEmpty()).toEqual(false);
expect(await aAndB.count()).toEqual(2);
expect(await aAndA.count()).toEqual(1);
expect(await aAndD.count()).toEqual(2);
expect(await aAndBAndEmpty.count()).toEqual(2);
expect(await aAndDAndAAndB.count()).toEqual(3);
});
test('intersection', async () => {
const a: Event<EmptyKeySet> = {
id: 'a',
};
const b: Event<EmptyKeySet> = {
id: 'b',
};
const d: Event<EmptyKeySet> = {
id: 'd',
};
const empty = EmptyEventSet.get();
const justA = new ConcreteEventSet({}, [a]);
const justB = new ConcreteEventSet({}, [b]);
const justD = new ConcreteEventSet({}, [d]);
const aAndB = justA.intersect(justB);
const aAndA = justA.intersect(justA);
const aAndD = justA.intersect(justD);
const aBAndEmpty = justA.union(justB).intersect(empty);
const aDAndAB = justA.union(justB).intersect(justA.union(justD));
expect((await aAndB.materialise({})).events).toEqual([]);
expect((await aAndA.materialise({})).events).toEqual([a]);
expect((await aAndD.materialise({})).events).toEqual([]);
expect((await aBAndEmpty.materialise({})).events).toEqual([]);
expect((await aDAndAB.materialise({})).events).toEqual([a]);
expect(await aAndB.isEmpty()).toEqual(true);
expect(await aAndA.isEmpty()).toEqual(false);
expect(await aAndD.isEmpty()).toEqual(true);
expect(await aBAndEmpty.isEmpty()).toEqual(true);
expect(await aDAndAB.isEmpty()).toEqual(false);
expect(await aAndB.count()).toEqual(0);
expect(await aAndA.count()).toEqual(1);
expect(await aAndD.count()).toEqual(0);
expect(await aBAndEmpty.count()).toEqual(0);
expect(await aDAndAB.count()).toEqual(1);
});
test('filter', async () => {
const keys = {
num: Num,
char: Str,
};
const a: Event<typeof keys> = {
id: 'a',
num: 97,
char: 'a',
};
const b: Event<typeof keys> = {
id: 'b',
num: 98,
char: 'b',
};
const d: Event<typeof keys> = {
id: 'd',
num: 100,
char: 'd',
};
const events = new ConcreteEventSet(keys, [a, b, d]);
const justA = events.filter(eq(v('id'), c('a')));
const justD = events.filter(eq(v('num'), c(100)));
expect((await justA.materialise(keys)).events).toEqual([a]);
expect((await justD.materialise(keys)).events).toEqual([d]);
});
test('sort', async () => {
const keys = {
num: Num,
char: Str,
};
const a: Event<typeof keys> = {
id: 'a',
num: 97,
char: 'a',
};
const b: Event<typeof keys> = {
id: 'b',
num: 98,
char: 'b',
};
const d: Event<typeof keys> = {
id: 'd',
num: 100,
char: 'd',
};
const events = new ConcreteEventSet(keys, [a, b, d]);
const byNum = events.sort({
expression: v('num'),
direction: Direction.ASC,
});
const byStr = events.sort({
expression: v('char'),
direction: Direction.ASC,
});
expect((await byNum.materialise(keys)).events).toEqual([a, b, d]);
expect((await byStr.materialise(keys)).events).toEqual([a, b, d]);
});
test('sort desc', async () => {
const keys = {
num: Num,
char: Str,
};
const a: Event<typeof keys> = {
id: 'a',
num: 97,
char: 'a',
};
const b: Event<typeof keys> = {
id: 'b',
num: 98,
char: 'b',
};
const d: Event<typeof keys> = {
id: 'd',
num: 100,
char: 'd',
};
const events = new ConcreteEventSet(keys, [a, b, d]);
const byNum = events.sort({
expression: v('num'),
direction: Direction.DESC,
});
const byStr = events.sort({
expression: v('char'),
direction: Direction.DESC,
});
expect((await byNum.materialise(keys)).events).toEqual([d, b, a]);
expect((await byStr.materialise(keys)).events).toEqual([d, b, a]);
});
});
});
describe('cmpFromExpr', () => {
test('simple', () => {
const a: UntypedEvent = {
id: 'a',
x: 0,
};
const b: UntypedEvent = {
id: 'b',
x: 42,
};
const c: UntypedEvent = {
id: 'c',
x: 0,
};
const cmp = cmpFromExpr(v('x'));
expect(cmp(a, b)).toEqual(-1);
expect(cmp(a, a)).toEqual(0);
expect(cmp(b, a)).toEqual(1);
expect(cmp(a, c)).toEqual(0);
});
test('kinds', () => {
const nullEvent: UntypedEvent = {
id: 'nullEvent',
x: null,
};
const sevenEvent: UntypedEvent = {
id: 'sevenEvent',
x: 7,
};
const oneEvent: UntypedEvent = {
id: 'oneEvent',
x: 1,
};
const zeroEvent: UntypedEvent = {
id: 'zeroEvent',
x: 0,
};
const trueEvent: UntypedEvent = {
id: 'trueEvent',
x: true,
};
const falseEvent: UntypedEvent = {
id: 'falseEvent',
x: false,
};
const aardvarkEvent: UntypedEvent = {
id: 'aardvarkEvent',
x: 'aardvark',
};
const zigguratEvent: UntypedEvent = {
id: 'zigguratEvent',
x: 'ziggurat',
};
const bigZeroEvent: UntypedEvent = {
id: 'bigZeroEvent',
x: 0n,
};
const bigOneEvent: UntypedEvent = {
id: 'bigOneEvent',
x: 1n,
};
const bigTwoEvent: UntypedEvent = {
id: 'bigTwoEvent',
x: 2n,
};
const cmp = cmpFromExpr(v('x'));
// Everything is equal to itself:
expect(cmp(nullEvent, nullEvent)).toEqual(0);
expect(cmp(sevenEvent, sevenEvent)).toEqual(0);
expect(cmp(oneEvent, oneEvent)).toEqual(0);
expect(cmp(zeroEvent, zeroEvent)).toEqual(0);
expect(cmp(falseEvent, falseEvent)).toEqual(0);
expect(cmp(trueEvent, trueEvent)).toEqual(0);
expect(cmp(aardvarkEvent, aardvarkEvent)).toEqual(0);
expect(cmp(zigguratEvent, zigguratEvent)).toEqual(0);
expect(cmp(bigZeroEvent, bigZeroEvent)).toEqual(0);
expect(cmp(bigOneEvent, bigOneEvent)).toEqual(0);
expect(cmp(bigTwoEvent, bigTwoEvent)).toEqual(0);
// BigInt(x) == x
expect(cmp(bigZeroEvent, zeroEvent)).toEqual(0);
expect(cmp(bigOneEvent, oneEvent)).toEqual(0);
// one = true, zero = false:
expect(cmp(oneEvent, trueEvent)).toEqual(0);
expect(cmp(zeroEvent, falseEvent)).toEqual(0);
expect(cmp(bigOneEvent, trueEvent)).toEqual(0);
expect(cmp(bigZeroEvent, falseEvent)).toEqual(0);
// 0 < 1 < 7
expect(cmp(zeroEvent, oneEvent)).toEqual(-1);
expect(cmp(sevenEvent, oneEvent)).toEqual(1);
// 0n < 1n < 2n
expect(cmp(bigZeroEvent, bigOneEvent)).toEqual(-1);
expect(cmp(bigTwoEvent, bigOneEvent)).toEqual(1);
// 0 < 1n < 7
expect(cmp(zeroEvent, bigOneEvent)).toEqual(-1);
expect(cmp(sevenEvent, bigOneEvent)).toEqual(1);
// aardvark < ziggurat
expect(cmp(aardvarkEvent, zigguratEvent)).toEqual(-1);
// null < {bools, numbers, BigInt} < strings
expect(cmp(nullEvent, falseEvent)).toEqual(-1);
expect(cmp(aardvarkEvent, sevenEvent)).toEqual(1);
expect(cmp(nullEvent, bigZeroEvent)).toEqual(-1);
expect(cmp(bigZeroEvent, sevenEvent)).toEqual(-1);
expect(cmp(nullEvent, falseEvent)).toEqual(-1);
expect(cmp(falseEvent, sevenEvent)).toEqual(-1);
});
});
describe('cmpFromSort', () => {
test('simple asc', () => {
const a: UntypedEvent = {
id: 'a',
x: 0,
};
const b: UntypedEvent = {
id: 'b',
x: 42,
};
const c: UntypedEvent = {
id: 'c',
x: 0,
};
const cmp = cmpFromSort({
expression: v('x'),
direction: Direction.ASC,
});
expect(cmp(a, b)).toEqual(-1);
expect(cmp(a, a)).toEqual(0);
expect(cmp(b, a)).toEqual(1);
expect(cmp(a, c)).toEqual(0);
});
test('kinds asc', () => {
const nullEvent: UntypedEvent = {
id: 'nullEvent',
x: null,
};
const sevenEvent: UntypedEvent = {
id: 'sevenEvent',
x: 7,
};
const oneEvent: UntypedEvent = {
id: 'oneEvent',
x: 1,
};
const zeroEvent: UntypedEvent = {
id: 'zeroEvent',
x: 0,
};
const trueEvent: UntypedEvent = {
id: 'trueEvent',
x: true,
};
const falseEvent: UntypedEvent = {
id: 'falseEvent',
x: false,
};
const aardvarkEvent: UntypedEvent = {
id: 'aardvarkEvent',
x: 'aardvark',
};
const zigguratEvent: UntypedEvent = {
id: 'zigguratEvent',
x: 'ziggurat',
};
const bigZeroEvent: UntypedEvent = {
id: 'bigZeroEvent',
x: 0n,
};
const bigOneEvent: UntypedEvent = {
id: 'bigOneEvent',
x: 1n,
};
const bigTwoEvent: UntypedEvent = {
id: 'bigTwoEvent',
x: 2n,
};
const cmp = cmpFromSort({
expression: v('x'),
direction: Direction.ASC,
});
// Everything is equal to itself:
expect(cmp(nullEvent, nullEvent)).toEqual(0);
expect(cmp(sevenEvent, sevenEvent)).toEqual(0);
expect(cmp(oneEvent, oneEvent)).toEqual(0);
expect(cmp(zeroEvent, zeroEvent)).toEqual(0);
expect(cmp(falseEvent, falseEvent)).toEqual(0);
expect(cmp(trueEvent, trueEvent)).toEqual(0);
expect(cmp(aardvarkEvent, aardvarkEvent)).toEqual(0);
expect(cmp(zigguratEvent, zigguratEvent)).toEqual(0);
expect(cmp(bigZeroEvent, bigZeroEvent)).toEqual(0);
expect(cmp(bigOneEvent, bigOneEvent)).toEqual(0);
expect(cmp(bigTwoEvent, bigTwoEvent)).toEqual(0);
// BigInt(x) == x
expect(cmp(bigZeroEvent, zeroEvent)).toEqual(0);
expect(cmp(bigOneEvent, oneEvent)).toEqual(0);
// one = true, zero = false:
expect(cmp(oneEvent, trueEvent)).toEqual(0);
expect(cmp(zeroEvent, falseEvent)).toEqual(0);
expect(cmp(bigOneEvent, trueEvent)).toEqual(0);
expect(cmp(bigZeroEvent, falseEvent)).toEqual(0);
// 0 < 1 < 7
expect(cmp(zeroEvent, oneEvent)).toEqual(-1);
expect(cmp(sevenEvent, oneEvent)).toEqual(1);
// 0n < 1n < 2n
expect(cmp(bigZeroEvent, bigOneEvent)).toEqual(-1);
expect(cmp(bigTwoEvent, bigOneEvent)).toEqual(1);
// 0 < 1n < 7
expect(cmp(zeroEvent, bigOneEvent)).toEqual(-1);
expect(cmp(sevenEvent, bigOneEvent)).toEqual(1);
// aardvark < ziggurat
expect(cmp(aardvarkEvent, zigguratEvent)).toEqual(-1);
// null < {bools, numbers, BigInt} < strings
expect(cmp(nullEvent, falseEvent)).toEqual(-1);
expect(cmp(aardvarkEvent, sevenEvent)).toEqual(1);
expect(cmp(nullEvent, bigZeroEvent)).toEqual(-1);
expect(cmp(bigZeroEvent, sevenEvent)).toEqual(-1);
expect(cmp(nullEvent, falseEvent)).toEqual(-1);
expect(cmp(falseEvent, sevenEvent)).toEqual(-1);
});
test('simple desc', () => {
const a: UntypedEvent = {
id: 'a',
x: 0,
};
const b: UntypedEvent = {
id: 'b',
x: 42,
};
const c: UntypedEvent = {
id: 'c',
x: 0,
};
const cmp = cmpFromSort({
expression: v('x'),
direction: Direction.DESC,
});
expect(cmp(a, b)).toEqual(1);
expect(cmp(a, a)).toEqual(0);
expect(cmp(b, a)).toEqual(-1);
expect(cmp(a, c)).toEqual(0);
});
test('kinds desc', () => {
const nullEvent: UntypedEvent = {
id: 'nullEvent',
x: null,
};
const sevenEvent: UntypedEvent = {
id: 'sevenEvent',
x: 7,
};
const oneEvent: UntypedEvent = {
id: 'oneEvent',
x: 1,
};
const zeroEvent: UntypedEvent = {
id: 'zeroEvent',
x: 0,
};
const trueEvent: UntypedEvent = {
id: 'trueEvent',
x: true,
};
const falseEvent: UntypedEvent = {
id: 'falseEvent',
x: false,
};
const aardvarkEvent: UntypedEvent = {
id: 'aardvarkEvent',
x: 'aardvark',
};
const zigguratEvent: UntypedEvent = {
id: 'zigguratEvent',
x: 'ziggurat',
};
const bigZeroEvent: UntypedEvent = {
id: 'bigZeroEvent',
x: 0n,
};
const bigOneEvent: UntypedEvent = {
id: 'bigOneEvent',
x: 1n,
};
const bigTwoEvent: UntypedEvent = {
id: 'bigTwoEvent',
x: 2n,
};
const cmp = cmpFromSort({
expression: v('x'),
direction: Direction.DESC,
});
// Everything is equal to itself:
expect(cmp(nullEvent, nullEvent)).toEqual(0);
expect(cmp(sevenEvent, sevenEvent)).toEqual(0);
expect(cmp(oneEvent, oneEvent)).toEqual(0);
expect(cmp(zeroEvent, zeroEvent)).toEqual(0);
expect(cmp(falseEvent, falseEvent)).toEqual(0);
expect(cmp(trueEvent, trueEvent)).toEqual(0);
expect(cmp(aardvarkEvent, aardvarkEvent)).toEqual(0);
expect(cmp(zigguratEvent, zigguratEvent)).toEqual(0);
expect(cmp(bigZeroEvent, bigZeroEvent)).toEqual(0);
expect(cmp(bigOneEvent, bigOneEvent)).toEqual(0);
expect(cmp(bigTwoEvent, bigTwoEvent)).toEqual(0);
// BigInt(x) == x
expect(cmp(bigZeroEvent, zeroEvent)).toEqual(0);
expect(cmp(bigOneEvent, oneEvent)).toEqual(0);
// one = true, zero = false:
expect(cmp(oneEvent, trueEvent)).toEqual(0);
expect(cmp(zeroEvent, falseEvent)).toEqual(0);
expect(cmp(bigOneEvent, trueEvent)).toEqual(0);
expect(cmp(bigZeroEvent, falseEvent)).toEqual(0);
// 0 < 1 < 7
expect(cmp(zeroEvent, oneEvent)).toEqual(1);
expect(cmp(sevenEvent, oneEvent)).toEqual(-1);
// 0n < 1n < 2n
expect(cmp(bigZeroEvent, bigOneEvent)).toEqual(1);
expect(cmp(bigTwoEvent, bigOneEvent)).toEqual(-1);
// 0 < 1n < 7
expect(cmp(zeroEvent, bigOneEvent)).toEqual(1);
expect(cmp(sevenEvent, bigOneEvent)).toEqual(-1);
// aardvark < ziggurat
expect(cmp(aardvarkEvent, zigguratEvent)).toEqual(1);
// null < {bools, numbers, BigInt} < strings
expect(cmp(nullEvent, falseEvent)).toEqual(1);
expect(cmp(aardvarkEvent, sevenEvent)).toEqual(-1);
expect(cmp(nullEvent, bigZeroEvent)).toEqual(1);
expect(cmp(bigZeroEvent, sevenEvent)).toEqual(1);
expect(cmp(nullEvent, falseEvent)).toEqual(1);
expect(cmp(falseEvent, sevenEvent)).toEqual(1);
});
});