From 14900030d282837a71688d62b6a34c2d5288a576 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Sun, 29 Nov 2020 18:14:11 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Prevent=20infinite=20loops=20whe?= =?UTF-8?q?n=20array=20int=20for=20from=20and=20to=20start=20by=20zeros?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UniformArrayIntDistribution.ts | 2 +- .../UniformArrayIntDistribution.noreg.spec.ts | 1 + .../UniformArrayIntDistribution.spec.ts | 88 ++++++++++++++++--- ...ormArrayIntDistribution.noreg.spec.ts.snap | 65 ++++++++++++++ 4 files changed, 144 insertions(+), 12 deletions(-) diff --git a/src/distribution/UniformArrayIntDistribution.ts b/src/distribution/UniformArrayIntDistribution.ts index 05513a52..f5118208 100644 --- a/src/distribution/UniformArrayIntDistribution.ts +++ b/src/distribution/UniformArrayIntDistribution.ts @@ -11,7 +11,7 @@ import { uniformArrayIntDistributionInternal } from './internals/UniformArrayInt /** @internal */ function uniformArrayIntInternal(from: ArrayInt, to: ArrayInt, rng: RandomGenerator): [ArrayInt, RandomGenerator] { - const rangeSize = addOneToPositiveArrayInt(substractArrayIntToNew(to, from)); + const rangeSize = trimArrayIntInplace(addOneToPositiveArrayInt(substractArrayIntToNew(to, from))); const emptyArrayIntData = rangeSize.data.slice(0); const g = uniformArrayIntDistributionInternal(emptyArrayIntData, rangeSize.data, rng); return [trimArrayIntInplace(addArrayIntToNew({ sign: 1, data: g[0] }, from)), g[1]]; diff --git a/test/unit/distribution/UniformArrayIntDistribution.noreg.spec.ts b/test/unit/distribution/UniformArrayIntDistribution.noreg.spec.ts index 0d72be00..51264f29 100644 --- a/test/unit/distribution/UniformArrayIntDistribution.noreg.spec.ts +++ b/test/unit/distribution/UniformArrayIntDistribution.noreg.spec.ts @@ -15,6 +15,7 @@ describe('uniformArrayIntDistribution [non regression]', () => { ${{ sign: 1, data: [0x00000000, 0x00000000] }} | ${{ sign: 1, data: [0xffffffff, 0xffffffff] }} | ${'64-bit unsigned'} ${{ sign: -1, data: [0x80000000, 0, 0, 0] }} | ${{ sign: 1, data: [0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff] }} | ${'128-bit signed'} ${{ sign: 1, data: [0x12345678, 0x90abcdef] }} | ${{ sign: 1, data: [0xfedcba09, 0x87654321] }} | ${'fuzzy'} + ${{ sign: 1, data: [0, 0] }} | ${{ sign: 1, data: [0, 5] }} | ${'trailing zeros'} `('Should not change its output in asked range except for major bumps ($topic)', ({ from, to }) => { // Remark: // ======================== diff --git a/test/unit/distribution/UniformArrayIntDistribution.spec.ts b/test/unit/distribution/UniformArrayIntDistribution.spec.ts index f9fc3b51..08ef7cda 100644 --- a/test/unit/distribution/UniformArrayIntDistribution.spec.ts +++ b/test/unit/distribution/UniformArrayIntDistribution.spec.ts @@ -21,26 +21,73 @@ describe('uniformArrayIntDistribution', () => { // Skip next tests if BigInt is not supported if (typeof BigInt === 'undefined') return it('no test', () => expect(true).toBe(true)); - it('Should call uniformIntDistributionInternal with correct size of range and source rng', () => + it('Should call uniformArrayIntDistributionInternal with correct range and source rng', () => fc.assert( fc .property(arrayIntArb(), arrayIntArb(), (a, b) => { // Arrange - const { uniformArrayIntDistributionInternal } = mocked(UniformArrayIntDistributionInternalMock); - uniformArrayIntDistributionInternal.mockImplementation((out, _rangeSize, rng) => [out, rng]); - const [from, to] = arrayIntToBigInt(a) < arrayIntToBigInt(b) ? [a, b] : [b, a]; - const rng = buildUniqueRng(); + const { uniformArrayIntDistributionInternal, from, to } = mockInternals(a, b); const expectedRangeSize = arrayIntToBigInt(to) - arrayIntToBigInt(from) + BigInt(1); + const expectedRng = buildUniqueRng(); // Act - uniformArrayIntDistribution(from, to, rng); + uniformArrayIntDistribution(from, to, expectedRng); // Assert - expect(uniformArrayIntDistributionInternal).toHaveBeenCalledTimes(1); - expect(uniformArrayIntDistributionInternal).toHaveBeenCalledWith(expect.any(Array), expect.any(Array), rng); - const params = uniformArrayIntDistributionInternal.mock.calls[0]; - const rangeSize = params[1]; + const { rangeSize, rng } = extractParams(uniformArrayIntDistributionInternal); expect(arrayIntToBigInt({ sign: 1, data: rangeSize })).toBe(expectedRangeSize); + expect(rng).toBe(expectedRng); + }) + .beforeEach(clean) + )); + + it('Should call uniformArrayIntDistributionInternal with non-empty range', () => + fc.assert( + fc + .property(arrayIntArb(), arrayIntArb(), (a, b) => { + // Arrange + const { uniformArrayIntDistributionInternal, from, to } = mockInternals(a, b); + + // Act + uniformArrayIntDistribution(from, to, buildUniqueRng()); + + // Assert + const { rangeSize } = extractParams(uniformArrayIntDistributionInternal); + expect(rangeSize.length).toBeGreaterThanOrEqual(1); + }) + .beforeEach(clean) + )); + + it('Should call uniformArrayIntDistributionInternal with trimmed range (no trailing zeros)', () => + fc.assert( + fc + .property(arrayIntArb(), arrayIntArb(), (a, b) => { + // Arrange + const { uniformArrayIntDistributionInternal, from, to } = mockInternals(a, b); + + // Act + uniformArrayIntDistribution(from, to, buildUniqueRng()); + + // Assert + const { rangeSize } = extractParams(uniformArrayIntDistributionInternal); + expect(rangeSize[0]).not.toBe(0); // rangeSize >= 1 + }) + .beforeEach(clean) + )); + + it('Should call uniformArrayIntDistributionInternal with out having same length as range', () => + fc.assert( + fc + .property(arrayIntArb(), arrayIntArb(), (a, b) => { + // Arrange + const { uniformArrayIntDistributionInternal, from, to } = mockInternals(a, b); + + // Act + uniformArrayIntDistribution(from, to, buildUniqueRng()); + + // Assert + const { out, rangeSize } = extractParams(uniformArrayIntDistributionInternal); + expect(out).toHaveLength(rangeSize.length); }) .beforeEach(clean) )); @@ -63,4 +110,23 @@ function arrayIntToBigInt(arrayInt: ArrayInt): bigint { return current * BigInt(arrayInt.sign); } -//const { uniformArrayIntDistributionInternal } = mocked(UniformArrayIntDistributionInternalMock) +function mockInternals(a: ArrayInt, b: ArrayInt) { + const { uniformArrayIntDistributionInternal } = mocked(UniformArrayIntDistributionInternalMock); + uniformArrayIntDistributionInternal.mockImplementation((out, _rangeSize, rng) => [out, rng]); + const [from, to] = arrayIntToBigInt(a) < arrayIntToBigInt(b) ? [a, b] : [b, a]; + return { uniformArrayIntDistributionInternal, from, to }; +} + +function extractParams( + uniformArrayIntDistributionInternal: ReturnType['uniformArrayIntDistributionInternal'] +) { + expect(uniformArrayIntDistributionInternal).toHaveBeenCalledTimes(1); + expect(uniformArrayIntDistributionInternal).toHaveBeenCalledWith( + expect.any(Array), + expect.any(Array), + expect.anything() + ); + const params = uniformArrayIntDistributionInternal.mock.calls[0]; + const [out, rangeSize, rng] = params; + return { out, rangeSize, rng }; +} diff --git a/test/unit/distribution/__snapshots__/UniformArrayIntDistribution.noreg.spec.ts.snap b/test/unit/distribution/__snapshots__/UniformArrayIntDistribution.noreg.spec.ts.snap index f10b0ec0..71d58555 100644 --- a/test/unit/distribution/__snapshots__/UniformArrayIntDistribution.noreg.spec.ts.snap +++ b/test/unit/distribution/__snapshots__/UniformArrayIntDistribution.noreg.spec.ts.snap @@ -579,3 +579,68 @@ Array [ }, ] `; + +exports[`uniformArrayIntDistribution [non regression] Should not change its output in asked range except for major bumps (trailing zeros) 1`] = ` +Array [ + Object { + "data": Array [ + 4, + ], + "sign": 1, + }, + Object { + "data": Array [ + 3, + ], + "sign": 1, + }, + Object { + "data": Array [ + 0, + ], + "sign": 1, + }, + Object { + "data": Array [ + 2, + ], + "sign": 1, + }, + Object { + "data": Array [ + 1, + ], + "sign": 1, + }, + Object { + "data": Array [ + 2, + ], + "sign": 1, + }, + Object { + "data": Array [ + 3, + ], + "sign": 1, + }, + Object { + "data": Array [ + 4, + ], + "sign": 1, + }, + Object { + "data": Array [ + 5, + ], + "sign": 1, + }, + Object { + "data": Array [ + 2, + ], + "sign": 1, + }, +] +`;