Skip to content

Commit

Permalink
Migrate to tslint naming convention
Browse files Browse the repository at this point in the history
Fixes #70
  • Loading branch information
dubzzz committed Apr 20, 2018
1 parent cf27b5b commit e294eed
Show file tree
Hide file tree
Showing 13 changed files with 73 additions and 66 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,12 @@ It can be parametrized using its second argument.
```typescript
export interface Parameters {
seed?: number; // optional, initial seed of the generator: Date.now() by default
num_runs?: number; // optional, number of runs before success: 100 by default
numRuns?: number; // optional, number of runs before success: 100 by default
timeout?: number; // optional, only taken into account for asynchronous runs (asyncProperty)
// specify a timeout in milliseconds, maximum time for the predicate to return its result
// only works for async code, will not interrupt a synchronous code: disabled by default
path?: string; // optional, way to replay a failing property directly with the counterexample
// it can be fed with the counterexample_path returned by the failing test (requires seed too)
// it can be fed with the counterexamplePath returned by the failing test (requires seed too)
logger?: (v: string) => void; // optional, log output: console.log by default
}
```
Expand All @@ -166,14 +166,14 @@ The details returned by `fc.check` are the following:
```typescript
interface RunDetails<Ts> {
failed: boolean, // false in case of failure, true otherwise
num_runs: number, // number of runs (all runs if success, up and including the first failure if failed)
num_shrinks: number, // number of shrinks (depth required to get the minimal failing example)
numRuns: number, // number of runs (all runs if success, up and including the first failure if failed)
numShrinks: number, // number of shrinks (depth required to get the minimal failing example)
seed: number, // seed used for the test
counterexample: Ts|null, // failure only: shrunk conterexample causig the property to fail
counterexample_path: string|null, // failure only: the exact path to re-run the counterexample
// In order to replay the failing case directly,
// this value as to be set as path attribute in the Parameters (with the seed)
// of assert, check, sample or even statistics
counterexamplePath: string|null, // failure only: the exact path to re-run the counterexample
// In order to replay the failing case directly,
// this value as to be set as path attribute in the Parameters (with the seed)
// of assert, check, sample or even statistics
error: string|null, // failure only: stack trace and error details
}
```
Expand All @@ -193,7 +193,7 @@ type Generator<Ts> = Arbitrary<Ts> | IProperty<Ts>;

function sample<Ts>(generator: Generator<Ts>): Ts[];
function sample<Ts>(generator: Generator<Ts>, params: Parameters): Ts[];
function sample<Ts>(generator: Generator<Ts>, num_generated: number): Ts[];
function sample<Ts>(generator: Generator<Ts>, numGenerated: number): Ts[];
```

- `fc.statistics`: classify the values produced by an `Arbitrary<T>` or `Property<T>`
Expand All @@ -211,7 +211,7 @@ type Classifier<Ts> = ((v: Ts) => string) | ((v: Ts) => string[]);

function statistics<Ts>(generator: Generator<Ts>, classify: Classifier<Ts>): void;
function statistics<Ts>(generator: Generator<Ts>, classify: Classifier<Ts>, params: Parameters): void;
function statistics<Ts>(generator: Generator<Ts>, classify: Classifier<Ts>, num_generated: number): void;
function statistics<Ts>(generator: Generator<Ts>, classify: Classifier<Ts>, numGenerated: number): void;
```

### Arbitraries
Expand Down Expand Up @@ -276,7 +276,7 @@ Strings that mimic real strings, with words and sentences:
- `fc.set<T>(arb: Arbitrary<T>): Arbitrary<T[]>`, `fc.set<T>(arb: Arbitrary<T>, maxLength: number): Arbitrary<T[]>` or `fc.set<T>(arb: Arbitrary<T>, minLength: number, maxLength: number): Arbitrary<T[]>` set of random length containing unique values generated by `arb`. All the values in the set are unique given the default `comparator = (a: T, b: T) => a === b` which can be overriden by giving another comparator function as the last argument on previous signatures
- `fc.tuple<T1,T2,...>(arb1: Arbitrary<T1>, arb2: Arbitrary<T2>, ...): Arbitrary<[T1,T2,...]>` tuple generated by aggregating the values of `arbX` like `generate: () => [arb1.generate(), arb2.generate(), ...]`. This arbitrary perfectly handle shrinks and is able to shink on all the generators
- `fc.dictionary<T>(keyArb: Arbitrary<string>, valueArb: Arbitrary<T>): Arbitrary<{[Key:string]:T}>` dictionary containing keys generated using `keyArb` and values gneerated by `valueArb`
- `fc.record<T>(recordModel: {[Key:string]: Arbitrary<T>}): Arbitrary<{[Key:string]: T}>` or `fc.record<T>(recordModel: {[Key:string]: Arbitrary<T>}, constraints: RecordConstraints): Arbitrary<{[Key:string]: T}>` record using the incoming arbitraries to generate its values. It comes very useful when dealing with settings. It takes an optional parameter of type `RecordConstraints` to configure some of its properties. The setting `with_deleted_keys=true` instructs the record generator that it can omit some keys
- `fc.record<T>(recordModel: {[Key:string]: Arbitrary<T>}): Arbitrary<{[Key:string]: T}>` or `fc.record<T>(recordModel: {[Key:string]: Arbitrary<T>}, constraints: RecordConstraints): Arbitrary<{[Key:string]: T}>` record using the incoming arbitraries to generate its values. It comes very useful when dealing with settings. It takes an optional parameter of type `RecordConstraints` to configure some of its properties. The setting `withDeletedKeys=true` instructs the record generator that it can omit some keys

#### Objects (:any)

Expand Down
2 changes: 1 addition & 1 deletion example/optional-settings-combination/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('createArray', () => {
fc.record({
minimum_size: fc.nat(100),
maximum_size: fc.nat(100)
}, {with_deleted_keys: true}),
}, {withDeletedKeys: true}),
(settings) => {
const out = createArray(() => 0, settings);
if (settings.minimum_size != null)
Expand Down
6 changes: 4 additions & 2 deletions src/check/arbitrary/RecordArbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { option } from './OptionArbitrary';
import { genericTuple } from './TupleArbitrary';

export interface RecordConstraints {
with_deleted_keys?: boolean;
withDeletedKeys?: boolean;
/** @depreciated Prefer withDeletedKeys */ with_deleted_keys?: boolean;
}

function rawRecord<T>(recordModel: { [key: string]: Arbitrary<T> }): Arbitrary<{ [key: string]: T }> {
Expand All @@ -27,7 +28,8 @@ function record<T>(
recordModel: { [key: string]: Arbitrary<T> },
constraints?: RecordConstraints
): Arbitrary<{ [key: string]: T }> {
if (constraints == null || constraints.with_deleted_keys !== true) return rawRecord(recordModel);
if (constraints == null || constraints.withDeletedKeys !== true || constraints.with_deleted_keys !== true)
return rawRecord(recordModel);

const updatedRecordModel: {
[key: string]: Arbitrary<{ value: T } | null>;
Expand Down
2 changes: 1 addition & 1 deletion src/check/runner/Runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function check<Ts>(rawProperty: IProperty<Ts>, params?: Parameters) {
const generator = toss(property, qParams.seed);

function* g() {
for (let idx = 0; idx < qParams.num_runs; ++idx) yield generator.next().value();
for (let idx = 0; idx < qParams.numRuns; ++idx) yield generator.next().value();
}
const initialValues = qParams.path.length === 0 ? g() : pathWalk(qParams.path, g());
return property.isAsync()
Expand Down
6 changes: 3 additions & 3 deletions src/check/runner/Sampler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ function streamSample<Ts>(
const qParams: QualifiedParameters = QualifiedParameters.readOrNumRuns(params);
const tossedValues: Stream<() => Shrinkable<Ts>> = stream(toss(generator, qParams.seed));
if (qParams.path.length === 0) {
return tossedValues.take(qParams.num_runs).map(s => s().value);
return tossedValues.take(qParams.numRuns).map(s => s().value);
}
return stream(pathWalk(qParams.path, tossedValues.map(s => s())))
.take(qParams.num_runs)
.take(qParams.numRuns)
.map(s => s.value);
}

Expand Down Expand Up @@ -74,7 +74,7 @@ function statistics<Ts>(
}
const data = Object_entries(recorded)
.sort((a, b) => b[1] - a[1])
.map(i => [i[0], `${(i[1] * 100.0 / qParams.num_runs).toFixed(2)}%`]);
.map(i => [i[0], `${(i[1] * 100.0 / qParams.numRuns).toFixed(2)}%`]);
const longestName = data.map(i => i[0].length).reduce((p, c) => Math.max(p, c), 0);
const longestPercent = data.map(i => i[1].length).reduce((p, c) => Math.max(p, c), 0);
for (const item of data) {
Expand Down
44 changes: 25 additions & 19 deletions src/check/runner/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
interface Parameters {
seed?: number;
num_runs?: number;
numRuns?: number;
timeout?: number;
path?: string;
logger?(v: string): void;
}
class QualifiedParameters {
seed: number;
num_runs: number;
numRuns: number;
timeout: number | null;
path: string;
logger: (v: string) => void;

private static readSeed = (p?: Parameters): number => (p != null && p.seed != null ? p.seed : Date.now());
private static readNumRuns = (p?: Parameters): number => (p != null && p.num_runs != null ? p.num_runs : 100);
private static readNumRuns = (p?: Parameters): number => {
const defaultValue = 100;
if (p == null) return defaultValue;
if (p.numRuns != null) return p.numRuns;
if ((p as { num_runs?: number }).num_runs != null) return (p as { num_runs?: number }).num_runs;
return defaultValue;
};
private static readTimeout = (p?: Parameters): number | null => (p != null && p.timeout != null ? p.timeout : null);
private static readPath = (p?: Parameters): string => (p != null && p.path != null ? p.path : '');
private static readLogger = (p?: Parameters): ((v: string) => void) => {
Expand All @@ -27,55 +33,55 @@ class QualifiedParameters {
static read(p?: Parameters): QualifiedParameters {
return {
seed: QualifiedParameters.readSeed(p),
num_runs: QualifiedParameters.readNumRuns(p),
numRuns: QualifiedParameters.readNumRuns(p),
timeout: QualifiedParameters.readTimeout(p),
logger: QualifiedParameters.readLogger(p),
path: QualifiedParameters.readPath(p)
};
}
static readOrNumRuns(p?: Parameters | number): QualifiedParameters {
if (p == null) return QualifiedParameters.read();
if (typeof p === 'number') return QualifiedParameters.read({ num_runs: p });
if (typeof p === 'number') return QualifiedParameters.read({ numRuns: p });
return QualifiedParameters.read(p);
}
}

interface RunDetails<Ts> {
failed: boolean;
num_runs: number;
num_shrinks: number;
numRuns: number;
numShrinks: number;
seed: number;
counterexample: Ts | null;
error: string | null;
counterexample_path: string | null;
counterexamplePath: string | null;
}

function successFor<Ts>(qParams: QualifiedParameters): RunDetails<Ts> {
return {
failed: false,
num_runs: qParams.num_runs,
num_shrinks: 0,
numRuns: qParams.numRuns,
numShrinks: 0,
seed: qParams.seed,
counterexample: null,
counterexample_path: null,
counterexamplePath: null,
error: null
};
}
function failureFor<Ts>(
qParams: QualifiedParameters,
num_runs: number,
num_shrinks: number,
numRuns: number,
numShrinks: number,
counterexample: Ts,
counterexample_path: string,
counterexamplePath: string,
error: string
): RunDetails<Ts> {
return {
failed: true,
num_runs,
num_shrinks,
numRuns,
numShrinks,
seed: qParams.seed,
counterexample,
counterexample_path,
counterexamplePath,
error
};
}
Expand Down Expand Up @@ -136,10 +142,10 @@ function pretty<Ts>(value: Ts): string {
function throwIfFailed<Ts>(out: RunDetails<Ts>) {
if (out.failed) {
throw new Error(
`Property failed after ${out.num_runs} tests (seed: ${out.seed}, path: ${out.counterexample_path}): ${pretty(
`Property failed after ${out.numRuns} tests (seed: ${out.seed}, path: ${out.counterexamplePath}): ${pretty(
out.counterexample
)}
Shrunk ${out.num_shrinks} time(s)
Shrunk ${out.numShrinks} time(s)
Got error: ${out.error}`
);
}
Expand Down
26 changes: 13 additions & 13 deletions test/e2e/ReplayFailures.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe(`ReplayFailures (seed: ${seed})`, () => {
const out = fc.check(prop, { seed: seed });
assert.ok(out.failed, 'Should have failed');
assert.deepStrictEqual(
fc.sample(propArbitrary, { seed: seed, path: out.counterexample_path, num_runs: 1 })[0],
fc.sample(propArbitrary, { seed: seed, path: out.counterexamplePath, numRuns: 1 })[0],
out.counterexample[0]
);
});
Expand All @@ -38,10 +38,10 @@ describe(`ReplayFailures (seed: ${seed})`, () => {
assert.ok(out.failed, 'Should have failed');

let replayedFailures = [];
const segments = out.counterexample_path.split(':');
const segments = out.counterexamplePath.split(':');
for (let idx = 1; idx !== segments.length + 1; ++idx) {
const p = segments.slice(0, idx).join(':');
const g = fc.sample(propArbitrary, { seed: seed, path: p, num_runs: 1 });
const g = fc.sample(propArbitrary, { seed: seed, path: p, numRuns: 1 });
replayedFailures.push(g[0]);
}
assert.deepStrictEqual(replayedFailures, failuresRecorded);
Expand All @@ -65,40 +65,40 @@ describe(`ReplayFailures (seed: ${seed})`, () => {
++numCalls;
return propCheck(data);
}),
{ seed: seed, path: out.counterexample_path }
{ seed: seed, path: out.counterexamplePath }
);
assert.equal(numValidCalls, 1);
assert.equal(validCallIndex, 0);
assert.equal(out2.num_runs, 1);
assert.equal(out2.num_shrinks, 0);
assert.equal(out2.counterexample_path, out.counterexample_path);
assert.equal(out2.numRuns, 1);
assert.equal(out2.numShrinks, 0);
assert.equal(out2.counterexamplePath, out.counterexamplePath);
assert.deepStrictEqual(out2.counterexample, out.counterexample);
});
it('Should start from any position in the path', () => {
const out = fc.check(prop, { seed: seed });
assert.ok(out.failed, 'Should have failed');

const segments = out.counterexample_path.split(':');
const segments = out.counterexamplePath.split(':');
for (let idx = 1; idx !== segments.length + 1; ++idx) {
const p = segments.slice(0, idx).join(':');
const outMiddlePath = fc.check(prop, { seed: seed, path: p });
assert.equal(outMiddlePath.num_runs, 1);
assert.equal(outMiddlePath.num_shrinks, out.num_shrinks - idx + 1);
assert.equal(outMiddlePath.counterexample_path, out.counterexample_path);
assert.equal(outMiddlePath.numRuns, 1);
assert.equal(outMiddlePath.numShrinks, out.numShrinks - idx + 1);
assert.equal(outMiddlePath.counterexamplePath, out.counterexamplePath);
assert.deepStrictEqual(outMiddlePath.counterexample, out.counterexample);
}
});
it('Should take initial path into account when computing path', () => {
const out = fc.check(prop, { seed: seed });
assert.ok(out.failed, 'Should have failed');

const segments = out.counterexample_path.split(':');
const segments = out.counterexamplePath.split(':');
const playOnIndex = seed % segments.length;

for (let offset = 0; offset !== +segments[playOnIndex]; ++offset) {
const p = [...segments.slice(0, playOnIndex), offset].join(':');
const outMiddlePath = fc.check(prop, { seed: seed, path: p });
assert.equal(outMiddlePath.counterexample_path, out.counterexample_path);
assert.equal(outMiddlePath.counterexamplePath, out.counterexamplePath);
assert.deepStrictEqual(outMiddlePath.counterexample, out.counterexample);
}
});
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/arbitraries/ObjectArbitrary.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe(`ObjectArbitrary (seed: ${seed})`, () => {
return true;
}
}),
{ seed: seed, num_runs: 1000 }
{ seed: seed, numRuns: 1000 }
);
assert.ok(out.failed, 'Should have failed');
assert.deepEqual(
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/arbitraries/RecordArbitrary.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe(`RecordArbitrary (seed: ${seed})`, () => {
cc: fc.string()
};
const out = fc.check(
fc.property(fc.record(recordModel, { with_deleted_keys: true }), (obj: any) => obj['bb'] == null),
fc.property(fc.record(recordModel, { withDeletedKeys: true }), (obj: any) => obj['bb'] == null),
{ seed: seed }
);
assert.ok(out.failed, 'Should have failed');
Expand All @@ -41,7 +41,7 @@ describe(`RecordArbitrary (seed: ${seed})`, () => {
force_negative_output: fc.boolean()
};
const out = fc.check(
fc.property(fc.record(recordModel, { with_deleted_keys: true }), (obj: any) => {
fc.property(fc.record(recordModel, { withDeletedKeys: true }), (obj: any) => {
if (obj.force_positive_output === true && obj.force_negative_output === true) return false;
return true;
}),
Expand Down
2 changes: 1 addition & 1 deletion test/unit/check/arbitrary/RecordArbitrary.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('RecordArbitrary', () => {
const recordModel = {};
for (const k of keys) recordModel[k] = constant(`_${k}_`);

const arb = record(recordModel, { with_deleted_keys: true });
const arb = record(recordModel, { withDeletedKeys: true });
for (let idx = 0; idx != 1000; ++idx) {
const g = arb.generate(mrng).value;
if (!g.hasOwnProperty(keys[missingIdx % keys.length])) return true;
Expand Down
14 changes: 7 additions & 7 deletions test/unit/check/runner/Runner.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe('Runner', () => {
`Should have stopped run (because no shrink) at first failing run (run number ${num})`
);
assert.ok(out.failed, 'Should have failed');
assert.equal(out.num_runs, num, `Should have failed after ${num} tests`);
assert.equal(out.numRuns, num, `Should have failed after ${num} tests`);
assert.equal(out.seed, seed, `Should attach the failing seed`);
return true;
})
Expand All @@ -105,7 +105,7 @@ describe('Runner', () => {
return null;
}
};
const out = check(p, { num_runs: num }) as RunDetails<[number]>;
const out = check(p, { numRuns: num }) as RunDetails<[number]>;
assert.equal(num_calls_generate, num, `Should have called generate ${num} times`);
assert.equal(num_calls_run, num, `Should have called run ${num} times`);
assert.equal(out.failed, false, 'Should not have failed');
Expand Down Expand Up @@ -136,7 +136,7 @@ describe('Runner', () => {
return true;
})
));
it('Should build the right counterexample_path', () =>
it('Should build the right counterexamplePath', () =>
fc.assert(
fc.property(fc.integer(), fc.array(fc.nat(99), 1, 100), (seed, failurePoints) => {
// Each entry (at index idx) in failurePoints represents the number of runs
Expand Down Expand Up @@ -167,9 +167,9 @@ describe('Runner', () => {
const out = check(p, { seed: seed }) as RunDetails<[number]>;
assert.ok(out.failed);
assert.equal(out.seed, seed);
assert.equal(out.num_runs, failurePoints[0] + 1);
assert.equal(out.num_shrinks, failurePoints.length - 1);
assert.equal(out.counterexample_path, expectedFailurePath);
assert.equal(out.numRuns, failurePoints[0] + 1);
assert.equal(out.numShrinks, failurePoints.length - 1);
assert.equal(out.counterexamplePath, expectedFailurePath);
})
));
it('Should wait on async properties to complete', async () =>
Expand Down Expand Up @@ -224,7 +224,7 @@ describe('Runner', () => {
`Should have stopped run one shrink after first failing run (run number ${num + 1})`
);
assert.ok(out.failed, 'Should have failed');
assert.equal(out.num_runs, num, `Should have failed after ${num} tests`);
assert.equal(out.numRuns, num, `Should have failed after ${num} tests`);
assert.equal(out.seed, seed, `Should attach the failing seed`);
assert.deepStrictEqual(
out.counterexample,
Expand Down
Loading

0 comments on commit e294eed

Please sign in to comment.