diff --git a/test/commands.ts b/test/commands.ts new file mode 100644 index 0000000..4d18e97 --- /dev/null +++ b/test/commands.ts @@ -0,0 +1,111 @@ +import { assert } from "chai"; +import * as fc from "fast-check"; +import * as L from "../src"; + +type Real = { data: L.List }; +type Model = { data: number[] }; + +export class ConcatCommand implements fc.Command { + constructor(readonly source: number[]) {} + check(/*m: Readonly*/): boolean { + return true; + } + run(m: Model, r: Real): void { + m.data = [...m.data, ...this.source]; + r.data = L.concat(r.data, L.from(this.source)); + assert.deepEqual(L.toArray(r.data), m.data); + } + toString(): string { + return `L.concat(src, L.from([${this.source.join(",")}]))`; + } +} + +export class DropCommand implements fc.Command { + constructor(readonly num: number) {} + check(/*m: Readonly*/): boolean { + return true; + } + run(m: Model, r: Real): void { + m.data = m.data.slice(this.num); + r.data = L.drop(this.num, r.data); + assert.deepEqual(L.toArray(r.data), m.data); + } + toString(): string { + return `L.drop(${this.num}, src)`; + } +} + +export class TakeCommand implements fc.Command { + constructor(readonly num: number) {} + check(/*m: Readonly*/): boolean { + return true; + } + run(m: Model, r: Real): void { + m.data = m.data.slice(0, this.num); + r.data = L.take(this.num, r.data); + assert.deepEqual(L.toArray(r.data), m.data); + } + toString(): string { + return `L.take(${this.num}, src)`; + } +} + +export class MapCommand implements fc.Command { + constructor(readonly val: number) {} + check(/*m: Readonly*/): boolean { + return true; + } + run(m: Model, r: Real): void { + m.data = m.data.map(v => (v * this.val) | 0); + r.data = L.map(v => (v * this.val) | 0, r.data); + assert.deepEqual(L.toArray(r.data), m.data); + } + toString(): string { + return `L.map(v => (v * ${this.val}) | 0, src)`; + } +} + +export class ReverseCommand implements fc.Command { + constructor() {} + check(/*m: Readonly*/): boolean { + return true; + } + run(m: Model, r: Real): void { + m.data = m.data.reverse(); + r.data = L.reverse(r.data); + assert.deepEqual(L.toArray(r.data), m.data); + } + toString(): string { + return `L.reverse(src)`; + } +} + +export class ConcatPreCommand implements fc.Command { + constructor(readonly source: number[]) {} + check(/*m: Readonly*/): boolean { + return true; + } + run(m: Model, r: Real): void { + m.data = [...this.source, ...m.data]; + r.data = L.concat(L.from(this.source), r.data); + assert.deepEqual(L.toArray(r.data), m.data); + } + toString(): string { + return `L.concat(L.from([${this.source.join(",")}]), src)`; + } +} + +export class FilterCommand implements fc.Command { + constructor(readonly val: number) {} + check(/*m: Readonly*/): boolean { + return true; + } + run(m: Model, r: Real): void { + m.data = m.data.filter(v => v >= this.val); + r.data = L.filter(v => v >= this.val, r.data); + assert.deepEqual(L.toArray(r.data), m.data); + } + toString(): string { + return `L.filter(v => v >= ${this.val}, src)`; + } +} diff --git a/test/index.ts b/test/index.ts index 5a84f9e..57f4600 100644 --- a/test/index.ts +++ b/test/index.ts @@ -54,6 +54,15 @@ import { import { checkList, installCheck } from "./check"; import { nothing, just, Maybe, isJust } from "./utils"; import * as R from "ramda"; +import { + DropCommand, + ConcatCommand, + TakeCommand, + MapCommand, + ReverseCommand, + ConcatPreCommand, + FilterCommand +} from "./commands"; const L: typeof Loriginal = installCheck(Loriginal); @@ -179,6 +188,31 @@ class Identity { } describe("List", () => { + it("against built-in array", () => { + const integerArrayArb = fc.array(fc.integer(), 10000); + fc.assert( + fc.property( + integerArrayArb, + fc.commands( + [ + integerArrayArb.map(vs => new ConcatCommand(vs)), + integerArrayArb.map(vs => new ConcatPreCommand(vs)), + fc.nat().map(num => new DropCommand(num)), + fc.nat().map(num => new TakeCommand(num)), + fc.integer().map(val => new FilterCommand(val)), + fc.integer().map(val => new MapCommand(val)), + fc.constant(new ReverseCommand()) + ], + 25 + ), + (initialValues, cmds) => { + const model = { data: Array.from(initialValues) }; + const real = { data: L.from(initialValues) }; + fc.modelRun(() => ({ model, real }), cmds); + } + ) + ); + }); describe("repeat", () => { it("creates list of n repeated elements", () => { [10, 100].forEach(n => {