From 9a2195ee38276aa39343d8b3f71629bae58f2eec Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 15 Jun 2021 19:44:34 +0200 Subject: [PATCH 1/2] Migrate to fast-check --- package-lock.json | 51 ++-- package.json | 4 +- src/lib/lru-cache.test.ts | 31 +-- src/lib/math.test.ts | 506 +++++++++++++++++++++++--------------- src/lib/utils.test.ts | 48 ++-- 5 files changed, 364 insertions(+), 276 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a2060f9b..54f79174d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4871,6 +4871,15 @@ } } }, + "fast-check": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-2.16.0.tgz", + "integrity": "sha512-r1uoJQoLzKUgfAeGzSZ/7dGyvSLG3OGjmWIKZRJpKtY/791di7n/x389F0Bei3Ry8126Z6MKp78Cbt4+9LUp1g==", + "dev": true, + "requires": { + "pure-rand": "^4.1.1" + } + }, "fast-deep-equal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", @@ -7518,18 +7527,6 @@ "verror": "1.10.0" } }, - "jsverify": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/jsverify/-/jsverify-0.8.3.tgz", - "integrity": "sha1-AukXjhkktar5WcAjmvhO1cbQc4w=", - "dev": true, - "requires": { - "lazy-seq": "^1.0.0", - "rc4": "~0.1.5", - "trampa": "^1.0.0", - "typify-parser": "^1.1.0" - } - }, "jszip": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", @@ -7586,12 +7583,6 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, - "lazy-seq": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazy-seq/-/lazy-seq-1.0.0.tgz", - "integrity": "sha1-iAy4qrJWAmOC4C9T7AiWgqdMW2o=", - "dev": true - }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -9689,6 +9680,12 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, + "pure-rand": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-4.2.1.tgz", + "integrity": "sha512-ESI2eqHP9JlrnTb7H7fgczRUWB6VxMMJ2m9870WCIBhYkBzSGd6gml6WhQVXHK+ZM8k70TqsyI28ixaLPaNz5g==", + "dev": true + }, "purgecss": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-1.4.2.tgz", @@ -9864,12 +9861,6 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true }, - "rc4": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/rc4/-/rc4-0.1.5.tgz", - "integrity": "sha1-CMbgSgFo9utiHCKrbLEVG9n0pk0=", - "dev": true - }, "react-is": { "version": "16.12.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", @@ -11713,12 +11704,6 @@ } } }, - "trampa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trampa/-/trampa-1.0.0.tgz", - "integrity": "sha1-Ukc0esM0gH+mwAAERMuRtjmECtU=", - "dev": true - }, "ts-jest": { "version": "24.3.0", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.3.0.tgz", @@ -11935,12 +11920,6 @@ } } }, - "typify-parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/typify-parser/-/typify-parser-1.1.0.tgz", - "integrity": "sha1-rHO/pfJTQ0aOLQ8zRsYRe8A9PJk=", - "dev": true - }, "uglify-es": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.2.2.tgz", diff --git a/package.json b/package.json index 123b75ae3..165a81003 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "prepack": "./scripts/build-release.sh", "prettier": "prettier --write 'src/**/*.ts' 'src/**/*.tsx'", "lint": "eslint 'src/**/*.ts' 'src/**/*.tsx'", - "jest": "./scripts/test-setup.sh && jest --runInBand", + "jest": "jest --runInBand", "coverage": "npm run jest -- --coverage", "typecheck": "tsc --noEmit", "test": "./scripts/ci.sh", @@ -41,8 +41,8 @@ "eslint": "6.0.0", "eslint-plugin-prettier": "2.6.0", "eslint-plugin-react-hooks": "4.0.2", + "fast-check": "2.16.0", "jest": "24.3.0", - "jsverify": "0.8.3", "jszip": "3.1.5", "pako": "1.0.6", "parcel-bundler": "1.12.4", diff --git a/src/lib/lru-cache.test.ts b/src/lib/lru-cache.test.ts index ad0b79639..6df22e5eb 100644 --- a/src/lib/lru-cache.test.ts +++ b/src/lib/lru-cache.test.ts @@ -1,4 +1,4 @@ -import * as jsc from 'jsverify' +import fc from 'fast-check' import {LRUCache} from './lru-cache' class SlowLRUCache { @@ -90,11 +90,13 @@ type Command = } | {type: 'removeLRU'} -const arbitraryCommand: jsc.Arbitrary = jsc.record({ - type: jsc.elements(['has', 'get', 'getSize', 'insert', 'getOrInsert', 'removeLRU']), - key: jsc.elements(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']), - value: jsc.nat, -}) as any +const arbitraryCommand = fc.record({ + type: fc.constantFrom( + ...(['has', 'get', 'getSize', 'insert', 'getOrInsert', 'removeLRU'] as const), + ), + key: fc.constantFrom('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + value: fc.nat(), +}) function runCommand(cache: ILRUCache, cmd: Command): any { switch (cmd.type) { @@ -131,18 +133,9 @@ test('LRUCache', () => { {type: 'removeLRU'}, ]) - jsc.assert( - jsc.forall( - jsc.suchthat(jsc.uint8, n => n > 0), - jsc.nearray(arbitraryCommand), - (cap, commands) => { - verify(cap, commands) - return true - }, - ), - { - size: 100, - tests: 1000, - }, + fc.assert( + fc.property(fc.integer({min: 1, max: 255}), fc.array(arbitraryCommand), (cap, commands) => { + verify(cap, commands) + }), ) }) diff --git a/src/lib/math.test.ts b/src/lib/math.test.ts index eca343c40..c1dd83504 100644 --- a/src/lib/math.test.ts +++ b/src/lib/math.test.ts @@ -1,26 +1,35 @@ import {clamp, Vec2, AffineTransform, Rect} from './math' -import * as jsc from 'jsverify' +import fc from 'fast-check' -test('clamp', () => { - jsc.assertForall(jsc.number, jsc.number, jsc.number, (a, b, c) => { - const result = clamp(a, b, c) - if (a < b) return result == b - if (a > c) return result == c - return result == a +// Change this to fc.integer() to debug failures more easily +// let numericType = fc.integer() +let numericTypeMinAccuracy = 1e-3 // reduce accuracy of double generator +let numericType = fc + .double({ + next: true, + noDefaultInfinity: true, + noNaN: true, + min: -1 / numericTypeMinAccuracy, + max: 1 / numericTypeMinAccuracy, }) -}) + .map(d => Math.round(d / numericTypeMinAccuracy) * numericTypeMinAccuracy) -// Change this to jsc.integer to debug failures more easily -let numericType = jsc.number +test('clamp', () => { + fc.assert( + fc.property(numericType, numericType, numericType, (a, b, c) => { + const result = clamp(a, b, c) + if (a < b) return result == b + if (a > c) return result == c + return result == a + }), + ) +}) -const arbitraryVec2 = jsc.record({x: jsc.integer, y: numericType}).smap( - v => new Vec2(v.x, v.y), - v => v, -) +const arbitraryVec2 = fc.record({x: numericType, y: numericType}).map(v => new Vec2(v.x, v.y)) -const positiveVec2 = jsc.suchthat(arbitraryVec2, v => v.x > 0 && v.y > 0) +const positiveVec2 = arbitraryVec2.filter(v => v.x > 0 && v.y > 0) -const arbitraryTransform = jsc +const arbitraryTransform = fc .record({ m00: numericType, m01: numericType, @@ -29,235 +38,304 @@ const arbitraryTransform = jsc m11: numericType, m12: numericType, }) - .smap( - t => new AffineTransform(t.m00, t.m01, t.m02, t.m10, t.m11, t.m12), - t => t, - ) + .map(t => new AffineTransform(t.m00, t.m01, t.m02, t.m10, t.m11, t.m12)) -const invertibleTransform = jsc.suchthat(arbitraryTransform, t => t.det() != 0) +const invertibleTransform = arbitraryTransform.filter(t => { + const d = t.det() + return d != 0 && Number.isFinite(d) +}) -const simpleTransform = jsc.suchthat( - jsc.record({scale: arbitraryVec2, translation: arbitraryVec2}).smap( - t => AffineTransform.withScale(t.scale).withTranslation(t.translation), - t => ({scale: t.getScale(), translation: t.getTranslation()}), - ), - t => t.det() != 0, -) +const simpleTransform = fc + .record({scale: arbitraryVec2, translation: arbitraryVec2}) + .map(t => AffineTransform.withScale(t.scale).withTranslation(t.translation)) + .filter(t => { + const d = t.det() + return d != 0 && Number.isFinite(d) + }) -const arbitraryRect = jsc.record({origin: arbitraryVec2, size: positiveVec2}).smap( - r => new Rect(r.origin, r.size), - r => r, -) +const arbitraryRect = fc + .record({origin: arbitraryVec2, size: positiveVec2}) + .map(r => new Rect(r.origin, r.size)) describe('Vec2', () => { test('constructor', () => { - jsc.assertForall(jsc.number, jsc.number, (a, b) => { - const v = new Vec2(a, b) - return v.x == a && v.y == b - }) + fc.assert( + fc.property(numericType, numericType, (a, b) => { + const v = new Vec2(a, b) + return v.x == a && v.y == b + }), + ) }) test('withX', () => { - jsc.assertForall(arbitraryVec2, jsc.number, (v, n) => { - return v.withX(n).x === n - }) + fc.assert( + fc.property(arbitraryVec2, numericType, (v, n) => { + return v.withX(n).x === n + }), + ) }) test('withY', () => { - jsc.assertForall(arbitraryVec2, jsc.number, (v, n) => { - return v.withY(n).y === n - }) + fc.assert( + fc.property(arbitraryVec2, numericType, (v, n) => { + return v.withY(n).y === n + }), + ) }) test('plus', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - const sum = v1.plus(v2) - return sum.x === v1.x + v2.x && sum.y === v1.y + v2.y - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + const sum = v1.plus(v2) + return sum.x === v1.x + v2.x && sum.y === v1.y + v2.y + }), + ) }) test('minus', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - const diff = v1.minus(v2) - return diff.x === v1.x - v2.x && diff.y === v1.y - v2.y - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + const diff = v1.minus(v2) + return diff.x === v1.x - v2.x && diff.y === v1.y - v2.y + }), + ) }) test('times', () => { - jsc.assertForall(arbitraryVec2, jsc.number, (v1, s) => { - const prod = v1.times(s) - return prod.x === v1.x * s && prod.y === v1.y * s - }) + fc.assert( + fc.property(arbitraryVec2, numericType, (v1, s) => { + const prod = v1.times(s) + return prod.x === v1.x * s && prod.y === v1.y * s + }), + ) }) test('timesPointwise', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - const prod = v1.timesPointwise(v2) - return prod.x === v1.x * v2.x && prod.y === v1.y * v2.y - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + const prod = v1.timesPointwise(v2) + return prod.x === v1.x * v2.x && prod.y === v1.y * v2.y + }), + ) }) test('dividedByPointwise', () => { - jsc.assertForall( - arbitraryVec2, - jsc.suchthat(arbitraryVec2, v => v.x !== 0 && v.y !== 0), - (v1, v2) => { - const div = v1.dividedByPointwise(v2) - return div.x === v1.x / v2.x && div.y === v1.y / v2.y - }, + fc.assert( + fc.property( + arbitraryVec2, + arbitraryVec2.filter(v => v.x !== 0 && v.y !== 0), + (v1, v2) => { + const div = v1.dividedByPointwise(v2) + return div.x === v1.x / v2.x && div.y === v1.y / v2.y + }, + ), ) }) test('dot', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return v1.dot(v2) === v1.x * v2.x + v1.y * v2.y - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return v1.dot(v2) === v1.x * v2.x + v1.y * v2.y + }), + ) }) test('equals', () => { - jsc.assertForall(jsc.number, jsc.number, (a, b) => { - return new Vec2(a, b).equals(new Vec2(a, b)) - }) + fc.assert( + fc.property(numericType, numericType, (a, b) => { + return new Vec2(a, b).equals(new Vec2(a, b)) + }), + ) - jsc.assertForall(arbitraryVec2, arbitraryVec2, (a, b) => { - return a.equals(b) === (a.x === b.x && a.y === b.y) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (a, b) => { + return a.equals(b) === (a.x === b.x && a.y === b.y) + }), + ) }) test('length2', () => { - jsc.assertForall(arbitraryVec2, v => { - return v.length2() === v.x * v.x + v.y * v.y - }) + fc.assert( + fc.property(arbitraryVec2, v => { + return v.length2() === v.x * v.x + v.y * v.y + }), + ) }) test('length', () => { - jsc.assertForall(arbitraryVec2, v => { - return v.length() === Math.sqrt(v.x * v.x + v.y * v.y) - }) + fc.assert( + fc.property(arbitraryVec2, v => { + return v.length() === Math.sqrt(v.x * v.x + v.y * v.y) + }), + ) }) test('abs', () => { - jsc.assertForall(arbitraryVec2, v => { - const q = v.abs() - return q.x === Math.abs(v.x) && q.y === Math.abs(v.y) - }) + fc.assert( + fc.property(arbitraryVec2, v => { + const q = v.abs() + return q.x === Math.abs(v.x) && q.y === Math.abs(v.y) + }), + ) }) test('min', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - const min = Vec2.min(v1, v2) - return min.x === Math.min(v1.x, v2.x) && min.y === Math.min(v1.y, v2.y) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + const min = Vec2.min(v1, v2) + return min.x === Math.min(v1.x, v2.x) && min.y === Math.min(v1.y, v2.y) + }), + ) }) test('max', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - const max = Vec2.max(v1, v2) - return max.x === Math.max(v1.x, v2.x) && max.y === Math.max(v1.y, v2.y) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + const max = Vec2.max(v1, v2) + return max.x === Math.max(v1.x, v2.x) && max.y === Math.max(v1.y, v2.y) + }), + ) }) test('flatten', () => { - jsc.assertForall(arbitraryVec2, v1 => { - const flat = v1.flatten() - return flat[0] == v1.x && flat[1] == v1.y - }) + fc.assert( + fc.property(arbitraryVec2, v1 => { + const flat = v1.flatten() + return flat[0] == v1.x && flat[1] == v1.y + }), + ) }) }) describe('Rect', () => { test('isEmpty', () => { - jsc.assertForall(arbitraryVec2, jsc.number, (v, n) => { - return new Rect(v, new Vec2(0, n)).isEmpty() - }) - jsc.assertForall(arbitraryVec2, jsc.number, (v, n) => { - return new Rect(v, new Vec2(n, 0)).isEmpty() - }) - jsc.assertForall(arbitraryVec2, v => { - return !new Rect(v, Vec2.unit).isEmpty() - }) + fc.assert( + fc.property(arbitraryVec2, numericType, (v, n) => { + return new Rect(v, new Vec2(0, n)).isEmpty() + }), + ) + fc.assert( + fc.property(arbitraryVec2, numericType, (v, n) => { + return new Rect(v, new Vec2(n, 0)).isEmpty() + }), + ) + fc.assert( + fc.property(arbitraryVec2, v => { + return !new Rect(v, Vec2.unit).isEmpty() + }), + ) }) test('width', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).width() == v2.x - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).width() == v2.x + }), + ) }) test('height', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).height() == v2.y - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).height() == v2.y + }), + ) }) test('left', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).left() == v1.x - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).left() == v1.x + }), + ) }) test('top', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).top() == v1.y - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).top() == v1.y + }), + ) }) test('right', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).right() == v1.x + v2.x - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).right() == v1.x + v2.x + }), + ) }) test('bottom', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).bottom() == v1.y + v2.y - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).bottom() == v1.y + v2.y + }), + ) }) test('topLeft', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).topLeft().equals(v1) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).topLeft().equals(v1) + }), + ) }) test('topRight', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).topRight().equals(v1.plus(v2.withY(0))) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).topRight().equals(v1.plus(v2.withY(0))) + }), + ) }) test('bottomLeft', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).bottomLeft().equals(v1.plus(v2.withX(0))) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).bottomLeft().equals(v1.plus(v2.withX(0))) + }), + ) }) test('bottomRight', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return new Rect(v1, v2).bottomRight().equals(v1.plus(v2)) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return new Rect(v1, v2).bottomRight().equals(v1.plus(v2)) + }), + ) }) test('withOrigin', () => { - jsc.assertForall(arbitraryRect, arbitraryVec2, (r, v) => { - return r.withOrigin(v).origin.equals(v) - }) + fc.assert( + fc.property(arbitraryRect, arbitraryVec2, (r, v) => { + return r.withOrigin(v).origin.equals(v) + }), + ) }) test('withSize', () => { - jsc.assertForall(arbitraryRect, arbitraryVec2, (r, v) => { - return r.withSize(v).size.equals(v) - }) + fc.assert( + fc.property(arbitraryRect, arbitraryVec2, (r, v) => { + return r.withSize(v).size.equals(v) + }), + ) }) test('closestPointTo', () => { - jsc.assertForall(arbitraryRect, arbitraryVec2, (r, v) => { - const p = r.closestPointTo(v) - return p.x >= r.left() && p.x <= r.right() && p.y >= r.top() && p.y <= r.bottom() - }) + fc.assert( + fc.property(arbitraryRect, arbitraryVec2, (r, v) => { + const p = r.closestPointTo(v) + return p.x >= r.left() && p.x <= r.right() && p.y >= r.top() && p.y <= r.bottom() + }), + ) }) test('hasIntersectionWith', () => { - jsc.assertForall(arbitraryRect, arbitraryRect, (r1, r2) => { - return r1.hasIntersectionWith(r2) === !r1.intersectWith(r2).isEmpty() - }) + fc.assert( + fc.property(arbitraryRect, arbitraryRect, (r1, r2) => { + return r1.hasIntersectionWith(r2) === !r1.intersectWith(r2).isEmpty() + }), + ) }) test('intersectWith', () => { - jsc.assertForall(arbitraryRect, arbitraryRect, (r1, r2) => { - const inter = r1.intersectWith(r2) - return inter.isEmpty() || (r1.hasIntersectionWith(inter) && r2.hasIntersectionWith(inter)) - }) + fc.assert( + fc.property(arbitraryRect, arbitraryRect, (r1, r2) => { + const inter = r1.intersectWith(r2) + return inter.isEmpty() || (r1.hasIntersectionWith(inter) && r2.hasIntersectionWith(inter)) + }), + ) }) }) @@ -265,84 +343,114 @@ describe('AffineTransform', () => { test('inverted', () => { expect(new AffineTransform(0, 0, 0, 0, 0, 0).inverted()).toBe(null) - jsc.assertForall(invertibleTransform, t => { - return t.inverted()!.inverted()!.approxEquals(t) - }) + fc.assert( + fc.property(invertibleTransform, t => { + return t.inverted()!.inverted()!.approxEquals(t) + }), + ) }) test('translation', () => { - jsc.assertForall(arbitraryTransform, arbitraryVec2, (t, v1) => { - return t.withTranslation(v1).getTranslation().equals(v1) - }) + fc.assert( + fc.property(arbitraryTransform, arbitraryVec2, (t, v1) => { + return t.withTranslation(v1).getTranslation().equals(v1) + }), + ) - jsc.assertForall(arbitraryTransform, arbitraryVec2, (t, v1) => { - const initialTranslation = t.getTranslation() - return t.translatedBy(v1).getTranslation().approxEquals(initialTranslation.plus(v1)) - }) + fc.assert( + fc.property(arbitraryTransform, arbitraryVec2, (t, v1) => { + const initialTranslation = t.getTranslation() + return t.translatedBy(v1).getTranslation().approxEquals(initialTranslation.plus(v1)) + }), + ) }) test('scale', () => { - jsc.assertForall(arbitraryTransform, arbitraryVec2, (t, v1) => { - return t.withScale(v1).getScale().equals(v1) - }) + fc.assert( + fc.property(arbitraryTransform, arbitraryVec2, (t, v1) => { + return t.withScale(v1).getScale().equals(v1) + }), + ) }) test('transformVector', () => { // Vector transformation are translation-invariant - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return AffineTransform.withTranslation(v1).transformVector(v2).approxEquals(v2) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return AffineTransform.withTranslation(v1).transformVector(v2).approxEquals(v2) + }), + ) - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return AffineTransform.withScale(v1).transformVector(v2).approxEquals(v2.timesPointwise(v1)) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return AffineTransform.withScale(v1).transformVector(v2).approxEquals(v2.timesPointwise(v1)) + }), + ) }) test('inverseTransformVector', () => { - jsc.assertForall(invertibleTransform, arbitraryVec2, (t, v) => { - return t.inverseTransformVector(t.transformVector(v))!.approxEquals(v) - }) + fc.assert( + fc.property(invertibleTransform, arbitraryVec2, (t, v) => { + return t.inverseTransformVector(t.transformVector(v))!.approxEquals(v) + }), + ) }) test('transformPosition', () => { - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return AffineTransform.withTranslation(v1).transformPosition(v2).approxEquals(v2.plus(v1)) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return AffineTransform.withTranslation(v1).transformPosition(v2).approxEquals(v2.plus(v1)) + }), + ) - jsc.assertForall(arbitraryVec2, arbitraryVec2, (v1, v2) => { - return AffineTransform.withScale(v1).transformPosition(v2).approxEquals(v2.timesPointwise(v1)) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryVec2, (v1, v2) => { + return AffineTransform.withScale(v1) + .transformPosition(v2) + .approxEquals(v2.timesPointwise(v1)) + }), + ) }) test('inverseTransformPosition', () => { - jsc.assertForall(invertibleTransform, arbitraryVec2, (t, v) => { - return t.inverseTransformPosition(t.transformPosition(v))!.approxEquals(v) - }) + fc.assert( + fc.property(invertibleTransform, arbitraryVec2, (t, v) => { + return t.inverseTransformPosition(t.transformPosition(v))!.approxEquals(v) + }), + ) }) test('transformRect', () => { - jsc.assertForall(arbitraryVec2, arbitraryRect, (v, r) => { - return AffineTransform.withTranslation(v) - .transformRect(r) - .equals(r.withOrigin(r.origin.plus(v))) - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryRect, (v, r) => { + return AffineTransform.withTranslation(v) + .transformRect(r) + .equals(r.withOrigin(r.origin.plus(v))) + }), + ) - jsc.assertForall(arbitraryVec2, arbitraryRect, (v, r) => { - const t = AffineTransform.withScale(v) - const rt = t.transformRect(r) - return Math.abs(rt.area() - r.area() * Math.abs(t.det())) < 1e-6 - }) + fc.assert( + fc.property(arbitraryVec2, arbitraryRect, (v, r) => { + const t = AffineTransform.withScale(v) + const rt = t.transformRect(r) + return Math.abs(rt.area() - r.area() * Math.abs(t.det())) < 1e-6 + }), + ) }) test('inverseTransformRect', () => { - jsc.assertForall(simpleTransform, arbitraryRect, (t, r) => { - return t.inverseTransformRect(t.transformRect(r))!.approxEquals(r) - }) + fc.assert( + fc.property(simpleTransform, arbitraryRect, (t, r) => { + return t.inverseTransformRect(t.transformRect(r))!.approxEquals(r) + }), + ) }) test('times', () => { - jsc.assertForall(invertibleTransform, invertibleTransform, (t1, t2) => { - return t1.times(t2).times(t2.inverted()!).approxEquals(t1) - }) + fc.assert( + fc.property(invertibleTransform, invertibleTransform, (t1, t2) => { + return t1.times(t2).times(t2.inverted()!).approxEquals(t1) + }), + ) }) }) diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index cb9d3fe7f..4ccafa17d 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -17,7 +17,7 @@ import { findIndexBisect, } from './utils' -import * as jsc from 'jsverify' +import fc from 'fast-check' test('sortBy', () => { const ls = ['a3', 'b2', 'c1', 'd4'] @@ -144,13 +144,19 @@ test('findIndexBisect', () => { check([3, 5, 5, 7], 7) check([3, 5, 5, 7], 11) - jsc.assertForall(jsc.array(jsc.int8), jsc.int8, (haystack: number[], needle: number) => { - haystack.sort((a, b) => a - b) + fc.assert( + fc.property( + fc.array(fc.integer({min: -128, max: 127})), + fc.integer({min: -128, max: 127}), + (haystack: number[], needle: number) => { + haystack.sort((a, b) => a - b) - const fn = (v: number) => v > needle - expect(findIndexBisect(haystack, fn)).toEqual(haystack.findIndex(fn)) - return true - }) + const fn = (v: number) => v > needle + expect(findIndexBisect(haystack, fn)).toEqual(haystack.findIndex(fn)) + return true + }, + ), + ) }) test('memoizeByReference', () => { @@ -215,17 +221,19 @@ test('objectsHaveShallowEquality', () => { }) test('decodeBase64', () => { - jsc.assertForall(jsc.array(jsc.uint8), byteArray => { - let binaryString = '' - for (let byte of byteArray) { - binaryString += String.fromCharCode(byte) - } - const b64string = btoa(binaryString) - const decoded = decodeBase64(b64string) - - expect(Uint8Array.from(byteArray)).toEqual(decoded) - - // If the above expect(...) assertion fails, we won't reach here. - return true - }) + fc.assert( + fc.property(fc.array(fc.integer({min: 0, max: 255})), byteArray => { + let binaryString = '' + for (let byte of byteArray) { + binaryString += String.fromCharCode(byte) + } + const b64string = btoa(binaryString) + const decoded = decodeBase64(b64string) + + expect(Uint8Array.from(byteArray)).toEqual(decoded) + + // If the above expect(...) assertion fails, we won't reach here. + return true + }), + ) }) From f07e114473c43f951f0b9dab3bb5b131adce876b Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 15 Jun 2021 19:56:39 +0200 Subject: [PATCH 2/2] Revert unwanted change --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 165a81003..be85e6494 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "prepack": "./scripts/build-release.sh", "prettier": "prettier --write 'src/**/*.ts' 'src/**/*.tsx'", "lint": "eslint 'src/**/*.ts' 'src/**/*.tsx'", - "jest": "jest --runInBand", + "jest": "./scripts/test-setup.sh && jest --runInBand", "coverage": "npm run jest -- --coverage", "typecheck": "tsc --noEmit", "test": "./scripts/ci.sh",