Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(mersenne): add tests for value ranges #2470

Merged
merged 21 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e22a424
test(mersenne): add tests for value ranges
ST-DDT Oct 11, 2023
f0c38cb
chore: slight improvements
ST-DDT Oct 11, 2023
13bf7fb
chore: remove obsolete snap file
ST-DDT Oct 11, 2023
23bc03d
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Oct 12, 2023
0e5cfdc
chore: apply review suggestions
ST-DDT Oct 15, 2023
4cc2f79
chore: apply review suggestions
ST-DDT Oct 15, 2023
a65723e
docs: improve jsdocs
ST-DDT Oct 15, 2023
ed60992
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Oct 15, 2023
31cdbb9
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Oct 15, 2023
ed3e946
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Oct 20, 2023
2a93680
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Oct 22, 2023
15a356e
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Oct 26, 2023
75e3bb9
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Oct 29, 2023
f5414fb
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Oct 30, 2023
4da0ede
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Nov 6, 2023
212cc79
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Nov 7, 2023
15a8549
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Nov 7, 2023
60cb0f3
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Nov 13, 2023
d4db6df
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Nov 14, 2023
fe32691
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Nov 25, 2023
0c720dd
Merge branch 'next' into test/mersenne-twister/value-range
ST-DDT Dec 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/internal/mersenne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ import type { Randomizer } from '../randomizer';
*
* @internal
*/
class MersenneTwister19937 {
export class MersenneTwister19937 {
private readonly N = 624;
private readonly M = 397;
private readonly MATRIX_A = 0x9908b0df; // constant vector a
Expand Down
4 changes: 2 additions & 2 deletions test/faker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,14 @@ describe('faker', () => {
faker.seed(1);

const actual = faker.animal.cat();
expect(actual).toBe('Korat');
expect(actual).toMatchInlineSnapshot('"Korat"');
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
});

it('seed(number[])', () => {
faker.seed([1, 2, 3]);

const actual = faker.animal.cat();
expect(actual).toBe('Oriental');
expect(actual).toMatchInlineSnapshot('"Oriental"');
});
});

Expand Down
13 changes: 13 additions & 0 deletions test/internal/__snapshots__/mersenne.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`generateMersenne32Randomizer() > seed: [42,1,2] > should return deterministic value for next() 1`] = `0.8562037434894592`;

exports[`generateMersenne32Randomizer() > seed: [1211,1,2] > should return deterministic value for next() 1`] = `0.8916433283593506`;

exports[`generateMersenne32Randomizer() > seed: [1337,1,2] > should return deterministic value for next() 1`] = `0.17990487208589911`;

exports[`generateMersenne32Randomizer() > seed: 42 > should return deterministic value for next() 1`] = `0.37454011430963874`;

exports[`generateMersenne32Randomizer() > seed: 1211 > should return deterministic value for next() 1`] = `0.9285201537422836`;

exports[`generateMersenne32Randomizer() > seed: 1337 > should return deterministic value for next() 1`] = `0.2620246761944145`;
15 changes: 15 additions & 0 deletions test/internal/mersenne-test-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Moved to a separate file to avoid importing the tests

/**
* The maximum value that can be returned by `MersenneTwister19937.genrandReal2()`.
*/
export const TWISTER_32CO_MAX_VALUE = 0.9999999997671694;
/**
* The maximum value that can be returned by `MersenneTwister19937.genrandRes53()`.
*/
export const TWISTER_53CO_MAX_VALUE = 0.9999999999999999;
// Re-exported because the value might change in the future
/**
* The maximum value that can be returned by `next()`.
*/
export const MERSENNE_MAX_VALUE = TWISTER_32CO_MAX_VALUE;
127 changes: 127 additions & 0 deletions test/internal/mersenne.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
import {
generateMersenne32Randomizer,
MersenneTwister19937,
} from '../../src/internal/mersenne';
import type { Randomizer } from '../../src/randomizer';
import { seededRuns } from '../support/seededRuns';
import { times } from '../support/times';
import {
MERSENNE_MAX_VALUE,
TWISTER_32CO_MAX_VALUE,
TWISTER_53CO_MAX_VALUE,
} from './mersenne-test-utils';

const NON_SEEDED_BASED_RUN = 25;

function newTwister(
seed: number = Math.random() * Number.MAX_SAFE_INTEGER
): MersenneTwister19937 {
const twister = new MersenneTwister19937();
twister.initGenrand(seed);
return twister;
}

describe('MersenneTwister19937', () => {
describe('genrandInt32()', () => {
it('should be able to return 0', () => {
const twister = newTwister(257678572);

// There is no single value seed that can produce 0 in the first call
for (let i = 0; i < 5; i++) {
twister.genrandInt32();
}

const actual = twister.genrandInt32();
expect(actual).toBe(0);
});

it('should be able to return 2^32-1', () => {
const twister = newTwister(2855577693);
const actual = twister.genrandInt32();
expect(actual).toBe(2 ** 32 - 1);
});
});

describe('genrandReal2()', () => {
it('should be able to return 0', () => {
const twister = newTwister();
twister.genrandInt32 = () => 0;
const actual = twister.genrandReal2();
expect(actual).toBe(0);
});

it('should be able to return almost 1', () => {
const twister = newTwister();
twister.genrandInt32 = () => 2 ** 32 - 1;
const actual = twister.genrandReal2();
expect(actual).toBe(TWISTER_32CO_MAX_VALUE);
});
xDivisionByZerox marked this conversation as resolved.
Show resolved Hide resolved
});

describe('genrandRes53()', () => {
it('should be able to return 0', () => {
const twister = newTwister();
twister.genrandInt32 = () => 0;
const actual = twister.genrandRes53();
expect(actual).toBe(0);
});

it('should be able to return almost 1', () => {
const twister = newTwister();
twister.genrandInt32 = () => 2 ** 32 - 1;
const actual = twister.genrandRes53();
expect(actual).toBe(TWISTER_53CO_MAX_VALUE);
});
});
});

describe('generateMersenne32Randomizer()', () => {
const randomizer: Randomizer = generateMersenne32Randomizer();

it('should return a result matching the interface', () => {
expect(randomizer).toBeDefined();
expect(randomizer).toBeTypeOf('object');
expect(randomizer.next).toBeTypeOf('function');
expect(randomizer.seed).toBeTypeOf('function');
});

describe.each(
[...seededRuns, ...seededRuns.map((v) => [v, 1, 2])].map((v) => [v])
)('seed: %j', (seed) => {
beforeEach(() => {
randomizer.seed(seed);
});

it('should return deterministic value for next()', () => {
const actual = randomizer.next();

expect(actual).toMatchSnapshot();
});
});

function randomSeed(): number {
return Math.ceil(Math.random() * 1_000_000_000);
}

// Create and log-back the seed for debug purposes
describe.each(
times(NON_SEEDED_BASED_RUN).flatMap(() => [
[randomSeed()],
[[randomSeed(), randomSeed()]],
])
)('random seeded tests %j', (seed) => {
beforeAll(() => {
randomizer.seed(seed);
});

describe('next', () => {
it('should return random number from interval [0, 1)', () => {
const actual = randomizer.next();

expect(actual).toBeGreaterThanOrEqual(0);
expect(actual).toBeLessThanOrEqual(MERSENNE_MAX_VALUE);
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
});
});
});
});
57 changes: 0 additions & 57 deletions test/mersenne.spec.ts
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved

This file was deleted.

8 changes: 4 additions & 4 deletions test/modules/date.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -801,22 +801,22 @@ describe('date', () => {
faker.setDefaultRefDate(() => new Date(Date.UTC(2020, 0, 1)));
faker.seed(20200101);
const date = faker.date.past();
expect(date).toEqual(new Date('2019-02-25T21:52:41.824Z'));
expect(date).toMatchInlineSnapshot('2019-02-25T21:52:41.824Z');

faker.seed(20200101);
const date2 = faker.date.past();
expect(date2).toEqual(new Date('2019-02-25T21:52:41.824Z'));
expect(date2).toMatchInlineSnapshot('2019-02-25T21:52:41.824Z');
});

it('should use the refDateSource when refDate is not provided (with value)', () => {
faker.setDefaultRefDate(Date.UTC(2020, 0, 1));
faker.seed(20200101);
const date = faker.date.past();
expect(date).toEqual(new Date('2019-02-25T21:52:41.824Z'));
expect(date).toMatchInlineSnapshot('2019-02-25T21:52:41.824Z');

faker.seed(20200101);
const date2 = faker.date.past();
expect(date2).toEqual(new Date('2019-02-25T21:52:41.824Z'));
expect(date2).toMatchInlineSnapshot('2019-02-25T21:52:41.824Z');
});
});
});
26 changes: 18 additions & 8 deletions test/modules/helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -811,14 +811,6 @@ describe('helpers', () => {
expect(unique).not.toContainDuplicates();
expect(unique).toHaveLength(2);
});

it('works as expected when seeded', () => {
const input = ['a', 'a', 'a', 'a', 'a', 'f', 'g', 'h', 'i', 'j'];
const length = 5;
faker.seed(100);
const unique = faker.helpers.uniqueArray(input, length);
expect(unique).toStrictEqual(['g', 'a', 'i', 'f', 'j']);
});
});

describe('mustache()', () => {
Expand Down Expand Up @@ -1253,6 +1245,24 @@ Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().`)
}
);

describe('uniqueArray()', () => {
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
it('works as expected when seeded', () => {
const input = ['a', 'a', 'a', 'a', 'a', 'f', 'g', 'h', 'i', 'j'];
const length = 5;
faker.seed(100);
const unique = faker.helpers.uniqueArray(input, length);
expect(unique).toMatchInlineSnapshot(`
[
"g",
"a",
"i",
"f",
"j",
]
`);
});
});

// This test can be only executed once, because the unique function has a global state.
// See: https://github.com/faker-js/faker/issues/371
describe('global unique()', () => {
Expand Down
36 changes: 35 additions & 1 deletion test/modules/number.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import validator from 'validator';
import { describe, expect, it } from 'vitest';
import { faker, FakerError } from '../../src';
import { faker, FakerError, SimpleFaker } from '../../src';
import { MERSENNE_MAX_VALUE } from '../internal/mersenne-test-utils';
import { seededTests } from './../support/seededRuns';

describe('number', () => {
Expand Down Expand Up @@ -507,4 +508,37 @@ describe('number', () => {
});
});
});

describe('value range tests', () => {
const customFaker = new SimpleFaker();
// @ts-expect-error: access private member field
const randomizer = customFaker._randomizer;
describe('int', () => {
it('should be able to return 0', () => {
randomizer.next = () => 0;
const actual = customFaker.number.int();
expect(actual).toBe(0);
});

it.todo('should be able to return MAX_SAFE_INTEGER', () => {
randomizer.next = () => MERSENNE_MAX_VALUE;
const actual = customFaker.number.int();
expect(actual).toBe(Number.MAX_SAFE_INTEGER);
});
});

describe('float', () => {
it('should be able to return 0', () => {
randomizer.next = () => 0;
const actual = customFaker.number.float();
expect(actual).toBe(0);
});

it('should be able to return almost 1', () => {
randomizer.next = () => MERSENNE_MAX_VALUE;
const actual = customFaker.number.float();
expect(actual).toBe(MERSENNE_MAX_VALUE);
});
});
});
});