Skip to content

Commit

Permalink
Replace MutableRandomGenerator by another structure
Browse files Browse the repository at this point in the history
Fixes #51
  • Loading branch information
dubzzz committed Mar 9, 2018
1 parent 642ff14 commit 1e0a73d
Show file tree
Hide file tree
Showing 40 changed files with 194 additions and 143 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,14 @@ const intNoShrink = fc.int().noShrink();

### Build your own

You can also fully customize your arbitrary and not derived from any of the buit-in arbitraries. What you have to do is to derive from [Arbitrary](https://github.com/dubzzz/fast-check/blob/master/src/check/arbitrary/definition/Arbitrary.ts) and implement `generate(mrng: MutableRandomGenerator): Shrinkable<T>`.

`generate` is responsable for the generation of one new random entity of type `T` (see signature above). In ordedr to fulfill it in a deterministic way it received a `mrng: MutableRandomGenerator`. It can then use it to generate integer values within `min` (included) and `max` (included) by using `UniformDistribution.inRange(min, max)(mrng)[0]`.
You can also fully customize your arbitrary and by not deriving it from any of the buit-in arbitraries. What you have to do is to derive from [Arbitrary](https://github.com/dubzzz/fast-check/blob/master/src/check/arbitrary/definition/Arbitrary.ts) and implement `generate(mrng: Random): Shrinkable<T>`.

`generate` is responsable for the generation of one new random entity of type `T` (see signature above). In order to fulfill it in a deterministic way it received a `mrng: Random`. It comes with multiple built-in helpers to generate values:
- `next(n: number): number`: uniformly distributed n bits value (max value of n = 31)
- `nextBoolean(): boolean`: uniformly distributed boolean value
- `nextInt(): number`: uniformly distributed integer value
- `nextInt(from: number, to: number): number`: uniformly distributed integer value between from (inclusive) and to (inclusive)
- `nextDouble(): number`: uniformly distributed double value between 0.0 (included) and 1.0 (not included)

The generated value also came with a shrink method able to derive _smaller_ values in case of failure. It can be ignored making the arbitrary not shrinkable.

Expand Down
4 changes: 2 additions & 2 deletions prebuild/tuple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const classFor = function(num: number): string {
super();
this.tupleArb = new GenericTupleArbitrary([${arbCommas(num)}]);
}
generate(mrng: MutableRandomGenerator): Shrinkable<[${txCommas(num)}]> {
generate(mrng: Random): Shrinkable<[${txCommas(num)}]> {
return this.tupleArb.generate(mrng) as Shrinkable<[${txCommas(num)}]>;
}
};
Expand All @@ -34,7 +34,7 @@ const generateTuple = function(num: number): string {
// imports
`import Arbitrary from './definition/Arbitrary';`,
`import Shrinkable from './definition/Shrinkable';`,
`import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator';`,
`import Random from '../../random/generator/Random';`,
`import { GenericTupleArbitrary } from './TupleArbitrary.generic';`,
// declare all necessary classes
...iota(num).map(num => classFor(num +1)),
Expand Down
4 changes: 2 additions & 2 deletions src/check/arbitrary/ArrayArbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Arbitrary, ArbitraryWithShrink } from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import { integer } from './IntegerArbitrary'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import { Stream, stream } from '../../stream/Stream'

class ArrayArbitrary<T> extends Arbitrary<T[]> {
Expand All @@ -18,7 +18,7 @@ class ArrayArbitrary<T> extends Arbitrary<T[]> {
items.map(s => s.value),
() => this.shrinkImpl(items).map(v => this.wrapper(v)));
}
generate(mrng: MutableRandomGenerator): Shrinkable<T[]> {
generate(mrng: Random): Shrinkable<T[]> {
const size = this.lengthArb.generate(mrng);
const items = [...Array(size.value)].map(() => this.arb.generate(mrng));
return this.wrapper(items);
Expand Down
2 changes: 1 addition & 1 deletion src/check/arbitrary/CharacterArbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Arbitrary from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import { integer } from './IntegerArbitrary'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import { Stream, stream } from '../../stream/Stream'

function CharacterArbitrary(min: number, max: number, mapToCode: (v: number) => number = v => v) {
Expand Down
4 changes: 2 additions & 2 deletions src/check/arbitrary/ConstantArbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Arbitrary from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import { nat } from './IntegerArbitrary'
import { stream, Stream } from '../../stream/Stream'

Expand All @@ -10,7 +10,7 @@ class ConstantArbitrary<T> extends Arbitrary<T> {
super();
this.idArb = nat(values.length -1);
}
generate(mrng: MutableRandomGenerator): Shrinkable<T> {
generate(mrng: Random): Shrinkable<T> {
if (this.values.length === 1)
return new Shrinkable(this.values[0]);

Expand Down
2 changes: 1 addition & 1 deletion src/check/arbitrary/DictionaryArbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Arbitrary from './definition/Arbitrary'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'

import { set } from './SetArbitrary'
import { tuple } from './TupleArbitrary'
Expand Down
4 changes: 2 additions & 2 deletions src/check/arbitrary/FrequencyArbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Arbitrary from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import { nat } from './IntegerArbitrary'

export interface WeightedArbitrary<T> {
Expand All @@ -21,7 +21,7 @@ class FrequencyArbitrary<T> extends Arbitrary<T> {
.slice(1);
this.idArb = nat(this.summedWarbs[this.summedWarbs.length -1].weight -1);
}
generate(mrng: MutableRandomGenerator): Shrinkable<T> {
generate(mrng: Random): Shrinkable<T> {
const selected = this.idArb.generate(mrng).value;
return this.summedWarbs
.find(warb => selected < warb.weight)!.arbitrary
Expand Down
9 changes: 3 additions & 6 deletions src/check/arbitrary/IntegerArbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import prand from 'pure-rand'

import { ArbitraryWithShrink } from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import { stream, Stream } from '../../stream/Stream'

class IntegerArbitrary extends ArbitraryWithShrink<number> {
Expand All @@ -19,11 +19,8 @@ class IntegerArbitrary extends ArbitraryWithShrink<number> {
private wrapper(value: number): Shrinkable<number> {
return new Shrinkable(value, () => this.shrink(value).map(v => this.wrapper(v)));
}
generate(mrng: MutableRandomGenerator): Shrinkable<number> {
return this.wrapper(this.generateImpl(mrng));
}
private generateImpl(mrng: MutableRandomGenerator): number {
return prand.uniformIntDistribution(this.min, this.max)(mrng)[0];
generate(mrng: Random): Shrinkable<number> {
return this.wrapper(mrng.nextInt(this.min, this.max));
}
private shrink_to(value: number, target: number): Stream<number> {
const gap = value - target;
Expand Down
6 changes: 3 additions & 3 deletions src/check/arbitrary/LoremArbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var loremIpsum: (opt: any) => string = require('lorem-ipsum')
import Arbitrary from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import { nat } from './IntegerArbitrary'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'

class LoremArbitrary extends Arbitrary<string> {
readonly arbWordsCount: Arbitrary<number>;
Expand All @@ -14,12 +14,12 @@ class LoremArbitrary extends Arbitrary<string> {
this.arbWordsCount = nat(maxWordsCount || 5);
this.sentencesMode = sentencesMode || false;
}
generate(mrng: MutableRandomGenerator): Shrinkable<string> {
generate(mrng: Random): Shrinkable<string> {
const numWords = this.arbWordsCount.generate(mrng).value;
const lorem = loremIpsum({
count: numWords,
units: this.sentencesMode ? 'sentences' : 'words',
random: () => mrng.next()[0] / (mrng.max() - mrng.min())
random: () => mrng.nextDouble()
});
return new Shrinkable(lorem);
}
Expand Down
4 changes: 2 additions & 2 deletions src/check/arbitrary/ObjectArbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Arbitrary from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'

import { array } from './ArrayArbitrary'
import { boolean } from './BooleanArbitrary'
Expand Down Expand Up @@ -72,7 +72,7 @@ class ObjectArbitrary extends Arbitrary<any> {
}
return oneof(potentialArbValue[0], ...potentialArbValue.slice(0));
}
generate(mrng: MutableRandomGenerator): Shrinkable<any> {
generate(mrng: Random): Shrinkable<any> {
return dictionary(
this.constraints.key,
ObjectArbitrary.anything(this.constraints)
Expand Down
4 changes: 2 additions & 2 deletions src/check/arbitrary/OneOfArbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Arbitrary from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import { nat } from './IntegerArbitrary'

class OneOfArbitrary<T> extends Arbitrary<T> {
Expand All @@ -9,7 +9,7 @@ class OneOfArbitrary<T> extends Arbitrary<T> {
super();
this.idArb = nat(arbs.length -1);
}
generate(mrng: MutableRandomGenerator): Shrinkable<T> {
generate(mrng: Random): Shrinkable<T> {
const id = this.idArb.generate(mrng);
return this.arbs[id.value].generate(mrng);
}
Expand Down
4 changes: 2 additions & 2 deletions src/check/arbitrary/OptionArbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Arbitrary } from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import { nat } from './IntegerArbitrary'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'

class OptionArbitrary<T> extends Arbitrary<T | null> {
readonly isOptionArb: Arbitrary<number>;
Expand All @@ -17,7 +17,7 @@ class OptionArbitrary<T> extends Arbitrary<T | null> {
s.value,
() => s.shrink().map(OptionArbitrary.extendedShrinkable).join(g()));
}
generate(mrng: MutableRandomGenerator): Shrinkable<T | null> {
generate(mrng: Random): Shrinkable<T | null> {
return this.isOptionArb.generate(mrng).value === 0
? new Shrinkable(null)
: OptionArbitrary.extendedShrinkable(this.arb.generate(mrng));
Expand Down
2 changes: 1 addition & 1 deletion src/check/arbitrary/RecordArbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Arbitrary from './definition/Arbitrary'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'

import { option } from './OptionArbitrary'
import { generic_tuple } from './TupleArbitrary'
Expand Down
2 changes: 1 addition & 1 deletion src/check/arbitrary/StringArbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Shrinkable from './definition/Shrinkable'
import { array } from './ArrayArbitrary'
import { nat } from './IntegerArbitrary'
import { char, ascii, char16bits, unicode, fullUnicode, hexa, base64 } from './CharacterArbitrary'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'

function StringArbitrary(charArb: Arbitrary<string>, aLength?: number, bLength?: number) {
const arrayArb = aLength != null
Expand Down
4 changes: 2 additions & 2 deletions src/check/arbitrary/TupleArbitrary.generic.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Arbitrary from './definition/Arbitrary'
import Shrinkable from './definition/Shrinkable'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import { Stream, stream } from '../../stream/Stream'

class GenericTupleArbitrary extends Arbitrary<any[]> {
Expand All @@ -12,7 +12,7 @@ class GenericTupleArbitrary extends Arbitrary<any[]> {
shrinkables.map(s => s.value),
() => GenericTupleArbitrary.shrinkImpl(shrinkables).map(GenericTupleArbitrary.wrapper));
}
generate(mrng: MutableRandomGenerator): Shrinkable<any[]> {
generate(mrng: Random): Shrinkable<any[]> {
return GenericTupleArbitrary.wrapper(this.arbs.map(a => a.generate(mrng)));
}
private static shrinkImpl(value: Shrinkable<any>[]): Stream<Shrinkable<any>[]> {
Expand Down
12 changes: 6 additions & 6 deletions src/check/arbitrary/definition/Arbitrary.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import MutableRandomGenerator from '../../../random/generator/MutableRandomGenerator'
import Random from '../../../random/generator/Random'
import Shrinkable from './Shrinkable'
import Stream from '../../../stream/Stream'

export default abstract class Arbitrary<T> {
abstract generate(mrng: MutableRandomGenerator): Shrinkable<T>;
abstract generate(mrng: Random): Shrinkable<T>;
filter(predicate: (t: T) => boolean): Arbitrary<T> {
class FilteredArbitrary<T> extends Arbitrary<T> {
constructor(readonly arb: Arbitrary<T>, readonly predicate: (t: T) => boolean) {
super();
}
generate(mrng: MutableRandomGenerator): Shrinkable<T> {
generate(mrng: Random): Shrinkable<T> {
let g = this.arb.generate(mrng);
while (! this.predicate(g.value)) {
g = this.arb.generate(mrng);
Expand All @@ -24,7 +24,7 @@ export default abstract class Arbitrary<T> {
constructor(readonly arb: Arbitrary<T>, readonly mapper: (t: T) => U) {
super();
}
generate(mrng: MutableRandomGenerator): Shrinkable<U> {
generate(mrng: Random): Shrinkable<U> {
return this.arb.generate(mrng).map(this.mapper);
}
}
Expand All @@ -35,7 +35,7 @@ export default abstract class Arbitrary<T> {
constructor(readonly arb: Arbitrary<T>) {
super();
}
generate(mrng: MutableRandomGenerator): Shrinkable<T> {
generate(mrng: Random): Shrinkable<T> {
return new Shrinkable(this.arb.generate(mrng).value);
}
}
Expand All @@ -44,7 +44,7 @@ export default abstract class Arbitrary<T> {
}

abstract class ArbitraryWithShrink<T> extends Arbitrary<T> {
abstract generate(mrng: MutableRandomGenerator): Shrinkable<T>;
abstract generate(mrng: Random): Shrinkable<T>;
abstract shrink(value: T): Stream<T>;
shrinkableFor(value: T): Shrinkable<T> {
return new Shrinkable(value, () => this.shrink(value).map(v => this.shrinkableFor(v)));
Expand Down
4 changes: 2 additions & 2 deletions src/check/property/AsyncProperty.generic.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Arbitrary from '../arbitrary/definition/Arbitrary';
import Shrinkable from '../arbitrary/definition/Shrinkable';
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator';
import Random from '../../random/generator/Random';
import IProperty from './IProperty';

export class AsyncProperty<Ts> implements IProperty<Ts> {
constructor(readonly arb: Arbitrary<Ts>, readonly predicate: (t: Ts) => Promise<boolean | void>) { }
isAsync = () => true;
generate(mrng: MutableRandomGenerator): Shrinkable<Ts> {
generate(mrng: Random): Shrinkable<Ts> {
return this.arb.generate(mrng);
}
async run(v: Ts): Promise<string|null> {
Expand Down
4 changes: 2 additions & 2 deletions src/check/property/IProperty.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Shrinkable from '../arbitrary/definition/Shrinkable'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import Stream from '../../stream/Stream'

export default interface IProperty<Ts> {
isAsync(): boolean;
generate(mrng: MutableRandomGenerator): Shrinkable<Ts>;
generate(mrng: Random): Shrinkable<Ts>;
run(v: Ts): Promise<string|null> | (string|null);
}
4 changes: 2 additions & 2 deletions src/check/property/Property.generic.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Arbitrary from '../arbitrary/definition/Arbitrary'
import Shrinkable from '../arbitrary/definition/Shrinkable'
import { tuple } from '../arbitrary/TupleArbitrary'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import IProperty from './IProperty'

export class Property<Ts> implements IProperty<Ts> {
constructor(readonly arb: Arbitrary<Ts>, readonly predicate: (t: Ts) => (boolean | void)) { }
isAsync = () => false;
generate(mrng: MutableRandomGenerator): Shrinkable<Ts> {
generate(mrng: Random): Shrinkable<Ts> {
return this.arb.generate(mrng);
}
run(v: Ts): (string|null) {
Expand Down
4 changes: 2 additions & 2 deletions src/check/property/TimeoutProperty.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Arbitrary from '../arbitrary/definition/Arbitrary';
import Shrinkable from '../arbitrary/definition/Shrinkable';
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator';
import Random from '../../random/generator/Random';
import IProperty from './IProperty';

const timeoutAfter = function(timeMs: number) {
Expand All @@ -11,7 +11,7 @@ const timeoutAfter = function(timeMs: number) {
export class TimeoutProperty<Ts> implements IProperty<Ts> {
constructor(readonly property: IProperty<Ts>, readonly timeMs: number) { }
isAsync = () => true;
generate(mrng: MutableRandomGenerator): Shrinkable<Ts> {
generate(mrng: Random): Shrinkable<Ts> {
return this.property.generate(mrng);
}
async run(v: Ts): Promise<string|null> {
Expand Down
4 changes: 2 additions & 2 deletions src/check/runner/Tosser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import prand from 'pure-rand'

import Arbitrary from '../arbitrary/definition/Arbitrary'
import Shrinkable from '../arbitrary/definition/Shrinkable'
import MutableRandomGenerator from '../../random/generator/MutableRandomGenerator'
import Random from '../../random/generator/Random'
import IProperty from '../property/IProperty'

function lazyGenerate<Ts>(generator: (IProperty<Ts> | Arbitrary<Ts>), rng: prand.RandomGenerator): () => Shrinkable<Ts> {
return () => generator.generate(new MutableRandomGenerator(rng));
return () => generator.generate(new Random(rng));
}

export default function* toss<Ts>(generator: (IProperty<Ts> | Arbitrary<Ts>), seed: number): IterableIterator<() => Shrinkable<Ts>> {
Expand Down
4 changes: 2 additions & 2 deletions src/fast-check-default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { string, asciiString, string16bits, unicodeString, fullUnicodeString, he
import { set } from './check/arbitrary/SetArbitrary'
import { tuple, generic_tuple } from './check/arbitrary/TupleArbitrary'

import { MutableRandomGenerator } from './random/generator/MutableRandomGenerator'
import { Random } from './random/generator/Random'

import { Stream, stream } from './stream/Stream'

Expand All @@ -46,6 +46,6 @@ export {
Arbitrary, Shrinkable,
// interfaces
ObjectConstraints, Parameters, RecordConstraints, RunDetails,
MutableRandomGenerator,
Random,
Stream, stream,
};
21 changes: 0 additions & 21 deletions src/random/generator/MutableRandomGenerator.ts

This file was deleted.

Loading

0 comments on commit 1e0a73d

Please sign in to comment.