-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add missing unit tests (bezier, color converter, interpolate) (#6034)
## Summary Add unit test we have been missing in our repo. Tests of normalizeColor were moved together with other test files and expanded ## Test plan <details><summary> Bezier test </summary> ![image](https://github.com/software-mansion/react-native-reanimated/assets/56199675/4080c9c9-0daf-4daa-be5c-fb7872d9f6ed) </details> <details><summary> Colors test</summary> <table> <tr> <td> ![image](https://github.com/software-mansion/react-native-reanimated/assets/56199675/d485029e-caac-4f88-b883-a00b83e161b7) </td> <td> ![image](https://github.com/software-mansion/react-native-reanimated/assets/56199675/3b33009d-897f-4ecf-9500-f8eb14cf6bcb) </td> </tr> </table> </details> <details><summary> Interpolate test </summary> ![image](https://github.com/software-mansion/react-native-reanimated/assets/56199675/c9a96b0f-becf-48f3-a9e2-d6158ce05a10) </details>
- Loading branch information
Showing
5 changed files
with
799 additions
and
147 deletions.
There are no files selected for viewing
239 changes: 239 additions & 0 deletions
239
packages/react-native-reanimated/__tests__/bezier.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
import { Bezier } from '../src/Bezier'; | ||
|
||
// spell-checker:disable | ||
/* | ||
* https://github.com/gre/bezier-easing/blob/master/test/test.js | ||
* BezierEasing - use bezier curve for transition easing function | ||
* by Gaëtan Renaudeau 2014 - 2015 – MIT License | ||
*/ | ||
// spell-checker:enable | ||
|
||
function repeat(n: number) { | ||
return (f: () => void) => { | ||
for (let i = 0; i < n; ++i) { | ||
f(); | ||
} | ||
}; | ||
} | ||
|
||
function functionsAreEqual( | ||
fun1: (x: number) => number, | ||
fun2: (x: number) => number, | ||
maxError = 0 | ||
) { | ||
let allPointsAreEqual = true; | ||
|
||
const points = Array.from(Array(100).keys()).map((x) => x / 100); | ||
points.forEach((point) => { | ||
if (Math.abs(fun1(point) - fun2(point)) > maxError) { | ||
allPointsAreEqual = false; | ||
} | ||
}); | ||
return allPointsAreEqual; | ||
} | ||
|
||
describe('Test `Bezier` function', () => { | ||
it('Should be a function', () => { | ||
expect(typeof Bezier === 'function').toBeTruthy(); | ||
}); | ||
it('Should create a function', () => { | ||
expect(typeof Bezier(0, 0, 1, 1) === 'function').toBeTruthy(); | ||
expect(typeof Bezier(0, 0, 1, 1)(0.5) === 'number').toBeTruthy(); | ||
}); | ||
|
||
describe('Function throws error in case of incorrect arguments', () => { | ||
test.each([ | ||
[0.5, 0.5, -5, 0.5], | ||
[0, 0.5, -0.005, 0.5], | ||
[0.5, 0.5, 5, 0.5], | ||
[-2, 0.5, 0.5, 0.5], | ||
[2, 0.5, 0.5, 0.5], | ||
])( | ||
'Invalid arguments point1 = (%d, %d) point2 = (%d, %d), x should be in range [0,1]', | ||
(x1, x2, y1, y2) => { | ||
expect(() => { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const _func = Bezier(x1, x2, y1, y2); | ||
}).toThrow(); | ||
} | ||
); | ||
|
||
test.each([ | ||
[0, 0, 1, 1], | ||
[0.5, 100, 0.5, 50], | ||
[0.5, -100, 0.5, 50], | ||
[0.5, 0, 0.5, -50], | ||
[1, 0, 0.5, -50], | ||
])( | ||
'Valid arguments point1 = (%d, %d) point2 = (%d, %d)', | ||
(x1, x2, y1, y2) => { | ||
expect(() => { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const _func = Bezier(x1, x2, y1, y2); | ||
}).not.toThrow(); | ||
} | ||
); | ||
}); | ||
|
||
describe('Function bezier(a,a,b,b) is alway linear', () => { | ||
test.each([ | ||
[0.0000001, 0.0000001, 0, 0], | ||
[0, 0, 0, 0], | ||
[0, 0, 1, 1], | ||
[1, 1, 0, 0], | ||
[1, 1, 1, 1], | ||
])(`Bezier(%d,%d,%d,%d)`, (a, b, c, d) => { | ||
expect(functionsAreEqual(Bezier(a, b, c, d), (x) => x)).toBeTruthy(); | ||
}); | ||
|
||
const MONKEY_TRIES = 1000; | ||
let allTestPass = true; | ||
repeat(MONKEY_TRIES)(() => { | ||
const a = Math.random(); | ||
const b = Math.random(); | ||
|
||
const easing = Bezier(a, a, b, b); | ||
|
||
if (!functionsAreEqual(easing, (x: number) => x)) { | ||
allTestPass = false; | ||
test(`Bezier(${a},${a}, ${b}, ${b}) is linear`, () => { | ||
expect(false).toBeTruthy(); | ||
}); | ||
} | ||
}); | ||
test(`All ${MONKEY_TRIES} monkey tests pass`, () => { | ||
expect(allTestPass).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('Should satisfy that bezier(0) = 0 and bezier(1)=1', () => { | ||
test.each([ | ||
[0.0000001, 0.0000001, 0, 0], | ||
[0, 0, 0, 0], | ||
[0, 0, 1, 1000000], | ||
[1, 1000000, 0, 1000000], | ||
[1, 1, 1, -1000000], | ||
[1, -1000000, 1, -1000000], | ||
[1, -1000000, 1, 0], | ||
])(`Bezier(%d,%d,%d,%d)`, (a, b, c, d) => { | ||
expect(Bezier(a, b, c, d)(0)).toBe(0); | ||
expect(Bezier(a, b, c, d)(1)).toBe(1); | ||
}); | ||
|
||
const MONKEY_TRIES = 1000; | ||
let allTestPass = true; | ||
repeat(MONKEY_TRIES)(() => { | ||
const a = Math.random(); | ||
const b = 3 * Math.random() - 1; | ||
const c = Math.random(); | ||
const d = 3 * Math.random() - 1; | ||
const easing = Bezier(a, b, c, d); | ||
|
||
const satisfiesCondition = easing(0) === 0 && easing(1) === 1; | ||
if (!satisfiesCondition) { | ||
allTestPass = false; | ||
test(`Bezier(${a},${b}, ${c}, ${d})(0) = 0 \n Bezier(${a},${b}, ${c}, ${d})(1) = 1`, () => { | ||
expect(easing(0)).toBe(0); | ||
expect(easing(1)).toBe(1); | ||
}); | ||
} | ||
}); | ||
test(`All ${MONKEY_TRIES} monkey tests pass`, () => { | ||
expect(allTestPass).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('Bezier(a,b,c,d) and Bezier(b,a,d,c) are symmetric about the axis f(x)=x', () => { | ||
// The b1 = bezier(a,b,c,d) curve is defined through four points: (0,0), (a,b), (c,d) and (1,1) | ||
// The b2 = bezier(b,a,d,c) curve is defined through four points: (0,0), (b,a), (d,c) and (1,1) | ||
// These two bezier curves are each others reflection relative to line f(x)=x | ||
// So if b1(x) = y, then b2(y) = x and b2(b1(x)) = x | ||
|
||
test.each([ | ||
[0.0000001, 0, 0.0000001, 0, 0.00000001], | ||
[0.0000001, 0, 0.005, 0, 0.00000001], | ||
[0.1, 0.2, 0.05, 0.7, 0.00000001], | ||
[0.98, 0.45, 0.05, 0.17, 0.00000001], | ||
[0.98, 0.45, 0.85, 0.17, 0.00000001], | ||
|
||
// Precision drop when c gets close to one (same drop when d gets close to one due to symmetry) | ||
// may be worth to investigate it in the future | ||
[0.98, 0.45, 0.9, 0.17, 0.00000001], | ||
[0.98, 0.45, 0.99, 0.17, 0.00000001], | ||
[0.98, 0.45, 0.999, 0.17, 0.000001], | ||
[0.98, 0.45, 0.9999, 0.17, 0.01], | ||
[0.98, 0.45, 1, 0.17, 0.01], | ||
|
||
// Precision drop when a gets close to zero (same drop when b gets close to one due to symmetry) | ||
[0.99, 0.45, 0.85, 0.17, 0.00000001], | ||
[0.99, 0.45, 0.85, 0.17, 0.00000001], | ||
[0.999, 0.45, 0.85, 0.17, 0.00000001], | ||
[0.9999, 0.45, 0.85, 0.17, 0.00000001], | ||
[0.99999, 0.45, 1, 0.85, 0.01], | ||
[1, 0.45, 1, 0.85, 0.01], | ||
|
||
[0, 0, 1, 0, 0.01], | ||
[1, 1, 0, 1, 0.1], | ||
[0, 1, 0, 1, 0.1], | ||
])(`Bezier(%d,%d,%d,%d)`, (a, b, c, d, precision) => { | ||
const easing1 = Bezier(a, b, c, d); | ||
const easing2 = Bezier(b, a, d, c); | ||
expect( | ||
functionsAreEqual( | ||
(x) => easing1(easing2(x)), | ||
(x: number) => x, | ||
precision | ||
) | ||
).toBeTruthy(); | ||
}); | ||
|
||
const MONKEY_TRIES = 20000; | ||
const PRECISION_0 = 0.01; | ||
let allTestPass0 = true; | ||
repeat(MONKEY_TRIES)(() => { | ||
const a = Math.random(); | ||
const b = Math.random(); | ||
const c = Math.random(); | ||
const d = Math.random(); | ||
|
||
const easing1 = Bezier(a, b, c, d); | ||
const easing2 = Bezier(b, a, d, c); | ||
|
||
const almostIdentity = (x: number) => easing1(easing2(x)); | ||
if (!functionsAreEqual(almostIdentity, (x: number) => x, PRECISION_0)) { | ||
allTestPass0 = false; | ||
test(`Bezier(${a},${b}, ${c}, ${d}) is symmetric to its reflection about the axis f(x)=x`, () => { | ||
expect(false).toBeTruthy(); | ||
}); | ||
} | ||
}); | ||
test(`All ${MONKEY_TRIES} monkey tests for random a,b,c,d in [0,1] pass, precision=${PRECISION_0}`, () => { | ||
expect(allTestPass0).toBeTruthy(); | ||
}); | ||
|
||
const PRECISION_1 = 1e-12; | ||
let allTestPass1 = true; | ||
repeat(MONKEY_TRIES)(() => { | ||
const a = 0.9 * Math.random() + 0.05; | ||
const b = 0.9 * Math.random() + 0.05; | ||
const c = 0.9 * Math.random() + 0.05; | ||
const d = 0.9 * Math.random() + 0.05; | ||
|
||
const easing1 = Bezier(a, b, c, d); | ||
const easing2 = Bezier(b, a, d, c); | ||
|
||
const almostIdentity = (x: number) => easing1(easing2(x)); | ||
if (!functionsAreEqual(almostIdentity, (x: number) => x, PRECISION_1)) { | ||
allTestPass1 = false; | ||
test(`Bezier(${a},${b}, ${c}, ${d}) is symmetric to its reflection about the axis f(x)=x`, () => { | ||
expect(false).toBeTruthy(); | ||
}); | ||
} | ||
}); | ||
test(`All ${MONKEY_TRIES} monkey tests for random a,b,c,d in [0.05,0.95] pass, precision=${PRECISION_1}`, () => { | ||
expect(allTestPass1).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('Bezier(a,b,(1-a),(1-b)) should have point symmetry at point (0.5, 0.5)', () => {}); | ||
}); |
Oops, something went wrong.