Skip to content

Commit

Permalink
🏷️ Adapt and expose IRawProperty, IProperty and IAsyncProperty types (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
dubzzz committed Nov 4, 2019
1 parent c2b95ea commit 5f54476
Show file tree
Hide file tree
Showing 19 changed files with 128 additions and 102 deletions.
13 changes: 9 additions & 4 deletions src/check/property/AsyncProperty.generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@ import { Random } from '../../random/generator/Random';
import { Arbitrary } from '../arbitrary/definition/Arbitrary';
import { Shrinkable } from '../arbitrary/definition/Shrinkable';
import { PreconditionFailure } from '../precondition/PreconditionFailure';
import { IProperty, runIdToFrequency } from './IProperty';
import { IRawProperty, runIdToFrequency } from './IRawProperty';

/**
* Asynchronous property, see {@link IProperty}
* Interface for asynchronous property, see {@link IRawProperty}
*/
export interface IAsyncProperty<Ts> extends IRawProperty<Ts, true> {}

/**
* Asynchronous property, see {@link IAsyncProperty}
*
* Prefer using {@link asyncProperty} instead
*/
export class AsyncProperty<Ts> implements IProperty<Ts> {
export class AsyncProperty<Ts> implements IAsyncProperty<Ts> {
static dummyHook: () => Promise<void> = async () => {
return;
};
private beforeEachHook: () => Promise<void> = AsyncProperty.dummyHook;
private afterEachHook: () => Promise<void> = AsyncProperty.dummyHook;
constructor(readonly arb: Arbitrary<Ts>, readonly predicate: (t: Ts) => Promise<boolean | void>) {}
isAsync = () => true;
isAsync = () => true as const;
generate(mrng: Random, runId?: number): Shrinkable<Ts> {
return runId != null ? this.arb.withBias(runIdToFrequency(runId)).generate(mrng) : this.arb.generate(mrng);
}
Expand Down
4 changes: 2 additions & 2 deletions src/check/property/AsyncProperty.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { asyncProperty } from './AsyncProperty.generated';
import { AsyncProperty } from './AsyncProperty.generic';
import { IAsyncProperty } from './AsyncProperty.generic';

export { asyncProperty, AsyncProperty };
export { asyncProperty, IAsyncProperty };
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import { PreconditionFailure } from '../precondition/PreconditionFailure';
* - Arbitraries: how to generate the inputs for the algorithm
* - Predicate: how to confirm the algorithm succeeded?
*/
export interface IProperty<Ts> {
export interface IRawProperty<Ts, IsAsync extends boolean = boolean> {
/**
* Is the property asynchronous?
*
* true in case of asynchronous property, false otherwise
*/
isAsync(): boolean;
isAsync(): IsAsync;
/**
* Generate values of type Ts
*
Expand All @@ -27,7 +27,11 @@ export interface IProperty<Ts> {
* Check the predicate for v
* @param v Value of which we want to check the predicate
*/
run(v: Ts): Promise<PreconditionFailure | string | null> | (PreconditionFailure | string | null);
run(
v: Ts
):
| (IsAsync extends true ? Promise<PreconditionFailure | string | null> : never)
| (IsAsync extends false ? (PreconditionFailure | string | null) : never);
}

/**
Expand Down
9 changes: 7 additions & 2 deletions src/check/property/Property.generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { Random } from '../../random/generator/Random';
import { Arbitrary } from '../arbitrary/definition/Arbitrary';
import { Shrinkable } from '../arbitrary/definition/Shrinkable';
import { PreconditionFailure } from '../precondition/PreconditionFailure';
import { IProperty, runIdToFrequency } from './IProperty';
import { IRawProperty, runIdToFrequency } from './IRawProperty';

/**
* Interface for synchronous property, see {@link IRawProperty}
*/
export interface IProperty<Ts> extends IRawProperty<Ts, false> {}

/**
* Property, see {@link IProperty}
Expand All @@ -16,7 +21,7 @@ export class Property<Ts> implements IProperty<Ts> {
private beforeEachHook: () => void = Property.dummyHook;
private afterEachHook: () => void = Property.dummyHook;
constructor(readonly arb: Arbitrary<Ts>, readonly predicate: (t: Ts) => boolean | void) {}
isAsync = () => false;
isAsync = () => false as const;
generate(mrng: Random, runId?: number): Shrinkable<Ts> {
return runId != null ? this.arb.withBias(runIdToFrequency(runId)).generate(mrng) : this.arb.generate(mrng);
}
Expand Down
4 changes: 2 additions & 2 deletions src/check/property/Property.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { property } from './Property.generated';
import { Property } from './Property.generic';
import { IProperty } from './Property.generic';

export { property, Property };
export { property, IProperty };
13 changes: 9 additions & 4 deletions src/check/property/SkipAfterProperty.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Random } from '../../random/generator/Random';
import { PreconditionFailure } from '../precondition/PreconditionFailure';
import { IProperty } from './IProperty';
import { IRawProperty } from './IRawProperty';

/** @hidden */
export class SkipAfterProperty<Ts> implements IProperty<Ts> {
export class SkipAfterProperty<Ts, IsAsync extends boolean> implements IRawProperty<Ts, IsAsync> {
private skipAfterTime: number;
constructor(
readonly property: IProperty<Ts>,
readonly property: IRawProperty<Ts, IsAsync>,
readonly getTime: () => number,
timeLimit: number,
readonly interruptExecution: boolean
Expand All @@ -17,7 +17,12 @@ export class SkipAfterProperty<Ts> implements IProperty<Ts> {
generate = (mrng: Random, runId?: number) => this.property.generate(mrng, runId);
run = (v: Ts) => {
if (this.getTime() >= this.skipAfterTime) {
return new PreconditionFailure(this.interruptExecution);
const preconditionFailure = new PreconditionFailure(this.interruptExecution);
if (this.isAsync()) {
return Promise.resolve(preconditionFailure) as any; // IsAsync => Promise<PreconditionFailure | string | null>
} else {
return preconditionFailure as any; // !IsAsync => PreconditionFailure | string | null
}
}
return this.property.run(v);
};
Expand Down
8 changes: 4 additions & 4 deletions src/check/property/TimeoutProperty.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Random } from '../../random/generator/Random';
import { Shrinkable } from '../arbitrary/definition/Shrinkable';
import { IProperty } from './IProperty';
import { IRawProperty } from './IRawProperty';

/** @hidden */
const timeoutAfter = (timeMs: number) => {
Expand All @@ -17,9 +17,9 @@ const timeoutAfter = (timeMs: number) => {
};

/** @hidden */
export class TimeoutProperty<Ts> implements IProperty<Ts> {
constructor(readonly property: IProperty<Ts>, readonly timeMs: number) {}
isAsync = () => true;
export class TimeoutProperty<Ts> implements IRawProperty<Ts, true> {
constructor(readonly property: IRawProperty<Ts>, readonly timeMs: number) {}
isAsync = () => true as const;
generate(mrng: Random, runId?: number): Shrinkable<Ts> {
return this.property.generate(mrng, runId);
}
Expand Down
6 changes: 3 additions & 3 deletions src/check/property/UnbiasedProperty.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Random } from '../../random/generator/Random';
import { IProperty } from './IProperty';
import { IRawProperty } from './IRawProperty';

/** @hidden */
export class UnbiasedProperty<Ts> implements IProperty<Ts> {
constructor(readonly property: IProperty<Ts>) {}
export class UnbiasedProperty<Ts, IsAsync extends boolean> implements IRawProperty<Ts, IsAsync> {
constructor(readonly property: IRawProperty<Ts, IsAsync>) {}
isAsync = () => this.property.isAsync();
generate = (mrng: Random, runId?: number) => this.property.generate(mrng);
run = (v: Ts) => this.property.run(v);
Expand Down
4 changes: 2 additions & 2 deletions src/check/runner/DecorateProperty.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IProperty } from '../property/IProperty';
import { IRawProperty } from '../property/IRawProperty';
import { SkipAfterProperty } from '../property/SkipAfterProperty';
import { TimeoutProperty } from '../property/TimeoutProperty';
import { UnbiasedProperty } from '../property/UnbiasedProperty';
Expand All @@ -11,7 +11,7 @@ type MinimalQualifiedParameters<Ts> = Pick<
>;

/** @hidden */
export function decorateProperty<Ts>(rawProperty: IProperty<Ts>, qParams: MinimalQualifiedParameters<Ts>) {
export function decorateProperty<Ts>(rawProperty: IRawProperty<Ts>, qParams: MinimalQualifiedParameters<Ts>) {
let prop = rawProperty;
if (rawProperty.isAsync() && qParams.timeout != null) prop = new TimeoutProperty(prop, qParams.timeout);
if (qParams.unbiased === true) prop = new UnbiasedProperty(prop);
Expand Down
26 changes: 13 additions & 13 deletions src/check/runner/Runner.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { stream } from '../../stream/Stream';
import { Shrinkable } from '../arbitrary/definition/Shrinkable';
import { PreconditionFailure } from '../precondition/PreconditionFailure';
import { AsyncProperty } from '../property/AsyncProperty';
import { IProperty } from '../property/IProperty';
import { Property } from '../property/Property';
import { IRawProperty } from '../property/IRawProperty';
import { readConfigureGlobal } from './configuration/GlobalParameters';
import { Parameters } from './configuration/Parameters';
import { QualifiedParameters } from './configuration/QualifiedParameters';
Expand All @@ -16,10 +14,12 @@ import { SourceValuesIterator } from './SourceValuesIterator';
import { toss } from './Tosser';
import { pathWalk } from './utils/PathWalker';
import { throwIfFailed } from './utils/RunDetailsFormatter';
import { IAsyncProperty } from '../property/AsyncProperty';
import { IProperty } from '../property/Property';

/** @hidden */
function runIt<Ts>(
property: IProperty<Ts>,
property: IRawProperty<Ts>,
sourceValues: SourceValuesIterator<Shrinkable<Ts>>,
verbose: VerbosityLevel,
interruptedAsFailure: boolean
Expand All @@ -34,7 +34,7 @@ function runIt<Ts>(

/** @hidden */
async function asyncRunIt<Ts>(
property: IProperty<Ts>,
property: IRawProperty<Ts>,
sourceValues: SourceValuesIterator<Shrinkable<Ts>>,
verbose: VerbosityLevel,
interruptedAsFailure: boolean
Expand Down Expand Up @@ -83,7 +83,7 @@ function buildInitialValues<Ts>(
*
* @returns Test status and other useful details
*/
function check<Ts>(property: AsyncProperty<Ts>, params?: Parameters<Ts>): Promise<RunDetails<Ts>>;
function check<Ts>(property: IAsyncProperty<Ts>, params?: Parameters<Ts>): Promise<RunDetails<Ts>>;
/**
* Run the property, do not throw contrary to {@link assert}
*
Expand All @@ -92,9 +92,9 @@ function check<Ts>(property: AsyncProperty<Ts>, params?: Parameters<Ts>): Promis
*
* @returns Test status and other useful details
*/
function check<Ts>(property: Property<Ts>, params?: Parameters<Ts>): RunDetails<Ts>;
function check<Ts>(property: IProperty<Ts>, params?: Parameters<Ts>): Promise<RunDetails<Ts>> | RunDetails<Ts>;
function check<Ts>(rawProperty: IProperty<Ts>, params?: Parameters<Ts>) {
function check<Ts>(property: IProperty<Ts>, params?: Parameters<Ts>): RunDetails<Ts>;
function check<Ts>(property: IRawProperty<Ts>, params?: Parameters<Ts>): Promise<RunDetails<Ts>> | RunDetails<Ts>;
function check<Ts>(rawProperty: IRawProperty<Ts>, params?: Parameters<Ts>) {
if (rawProperty == null || rawProperty.generate == null)
throw new Error('Invalid property encountered, please use a valid property');
if (rawProperty.run == null)
Expand Down Expand Up @@ -130,7 +130,7 @@ function check<Ts>(rawProperty: IProperty<Ts>, params?: Parameters<Ts>) {
* @param property Asynchronous property to be checked
* @param params Optional parameters to customize the execution
*/
function assert<Ts>(property: AsyncProperty<Ts>, params?: Parameters<Ts>): Promise<void>;
function assert<Ts>(property: IAsyncProperty<Ts>, params?: Parameters<Ts>): Promise<void>;
/**
* Run the property, throw in case of failure
*
Expand All @@ -140,9 +140,9 @@ function assert<Ts>(property: AsyncProperty<Ts>, params?: Parameters<Ts>): Promi
* @param property Synchronous property to be checked
* @param params Optional parameters to customize the execution
*/
function assert<Ts>(property: Property<Ts>, params?: Parameters<Ts>): void;
function assert<Ts>(property: IProperty<Ts>, params?: Parameters<Ts>): Promise<void> | void;
function assert<Ts>(property: IProperty<Ts>, params?: Parameters<Ts>) {
function assert<Ts>(property: IProperty<Ts>, params?: Parameters<Ts>): void;
function assert<Ts>(property: IRawProperty<Ts>, params?: Parameters<Ts>): Promise<void> | void;
function assert<Ts>(property: IRawProperty<Ts>, params?: Parameters<Ts>) {
const out = check(property, params);
if (property.isAsync()) return (out as Promise<RunDetails<Ts>>).then(throwIfFailed);
else throwIfFailed(out as RunDetails<Ts>);
Expand Down
17 changes: 10 additions & 7 deletions src/check/runner/Sampler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Stream, stream } from '../../stream/Stream';
import { ObjectEntries, StringPadEnd, StringPadStart } from '../../utils/polyfills';
import { Arbitrary } from '../arbitrary/definition/Arbitrary';
import { Shrinkable } from '../arbitrary/definition/Shrinkable';
import { IProperty } from '../property/IProperty';
import { Property } from '../property/Property';
import { IRawProperty } from '../property/IRawProperty';
import { Property } from '../property/Property.generic';
import { UnbiasedProperty } from '../property/UnbiasedProperty';
import { readConfigureGlobal } from './configuration/GlobalParameters';
import { Parameters } from './configuration/Parameters';
Expand All @@ -12,16 +12,19 @@ import { toss } from './Tosser';
import { pathWalk } from './utils/PathWalker';

/** @hidden */
function toProperty<Ts>(generator: IProperty<Ts> | Arbitrary<Ts>, qParams: QualifiedParameters<Ts>): IProperty<Ts> {
function toProperty<Ts>(
generator: IRawProperty<Ts> | Arbitrary<Ts>,
qParams: QualifiedParameters<Ts>
): IRawProperty<Ts> {
const prop = !Object.prototype.hasOwnProperty.call(generator, 'isAsync')
? new Property(generator as Arbitrary<Ts>, () => true)
: (generator as IProperty<Ts>);
: (generator as IRawProperty<Ts>);
return qParams.unbiased === true ? new UnbiasedProperty(prop) : prop;
}

/** @hidden */
function streamSample<Ts>(
generator: IProperty<Ts> | Arbitrary<Ts>,
generator: IRawProperty<Ts> | Arbitrary<Ts>,
params?: Parameters<Ts> | number
): IterableIterator<Ts> {
const extendedParams =
Expand Down Expand Up @@ -52,7 +55,7 @@ function streamSample<Ts>(
* @param generator {@link IProperty} or {@link Arbitrary} to extract the values from
* @param params Integer representing the number of values to generate or {@link Parameters} as in {@link assert}
*/
function sample<Ts>(generator: IProperty<Ts> | Arbitrary<Ts>, params?: Parameters<Ts> | number): Ts[] {
function sample<Ts>(generator: IRawProperty<Ts> | Arbitrary<Ts>, params?: Parameters<Ts> | number): Ts[] {
return [...streamSample(generator, params)];
}

Expand All @@ -78,7 +81,7 @@ function sample<Ts>(generator: IProperty<Ts> | Arbitrary<Ts>, params?: Parameter
* @param params Integer representing the number of values to generate or {@link Parameters} as in {@link assert}
*/
function statistics<Ts>(
generator: IProperty<Ts> | Arbitrary<Ts>,
generator: IRawProperty<Ts> | Arbitrary<Ts>,
classify: (v: Ts) => string | string[],
params?: Parameters<Ts> | number
): void {
Expand Down
6 changes: 3 additions & 3 deletions src/check/runner/Tosser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import * as prand from 'pure-rand';

import { Random } from '../../random/generator/Random';
import { Shrinkable } from '../arbitrary/definition/Shrinkable';
import { IProperty } from '../property/IProperty';
import { IRawProperty } from '../property/IRawProperty';

/** @hidden */
function lazyGenerate<Ts>(generator: IProperty<Ts>, rng: prand.RandomGenerator, idx: number): () => Shrinkable<Ts> {
function lazyGenerate<Ts>(generator: IRawProperty<Ts>, rng: prand.RandomGenerator, idx: number): () => Shrinkable<Ts> {
return () => generator.generate(new Random(rng), idx);
}

/** @hidden */
export function* toss<Ts>(
generator: IProperty<Ts>,
generator: IRawProperty<Ts>,
seed: number,
random: (seed: number) => prand.RandomGenerator,
examples: Ts[]
Expand Down
8 changes: 6 additions & 2 deletions src/fast-check-default.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { pre } from './check/precondition/Pre';
import { asyncProperty } from './check/property/AsyncProperty';
import { property } from './check/property/Property';
import { asyncProperty, IAsyncProperty } from './check/property/AsyncProperty';
import { property, IProperty } from './check/property/Property';
import { IRawProperty } from './check/property/IRawProperty';
import { Parameters } from './check/runner/configuration/Parameters';
import { RunDetails } from './check/runner/reporter/RunDetails';
import { assert, check } from './check/runner/Runner';
Expand Down Expand Up @@ -106,6 +107,9 @@ export {
// property definition
property,
asyncProperty,
IRawProperty,
IProperty,
IAsyncProperty,
// pre-built arbitraries
boolean,
float,
Expand Down
6 changes: 3 additions & 3 deletions test/unit/check/property/SkipAfterProperty.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IProperty } from '../../../../src/check/property/IProperty';
import { IRawProperty } from '../../../../src/check/property/IRawProperty';
import { SkipAfterProperty } from '../../../../src/check/property/SkipAfterProperty';

// Mocks
Expand All @@ -12,7 +12,7 @@ function buildProperty() {
generate: jest.fn(),
run: jest.fn()
};
return { mocks, property: mocks as IProperty<any> };
return { mocks, property: mocks as IRawProperty<any> };
}

function buildRandom() {
Expand Down Expand Up @@ -78,7 +78,7 @@ describe('SkipAfterProperty', () => {

expect(PreconditionFailure.isFailure(out)).toBe(true);
expect(timerMock.mock.calls.length).toBe(2);
expect(propertyMock.isAsync.mock.calls.length).toBe(0);
expect(propertyMock.isAsync.mock.calls.length).toBe(1); // check expected return type: return a resolved Promise if async, a value otherwise
expect(propertyMock.generate.mock.calls.length).toBe(0);
expect(propertyMock.run.mock.calls.length).toBe(0);
});
Expand Down
Loading

0 comments on commit 5f54476

Please sign in to comment.