Repository: dubzzz/fast-check
Pull request #623 (2020-06-11 18:35:30)

✨ Report the configuration, that has been used, in RunDetails
## Why is this PR for?

Report the configuration, that has been used, in RunDetails

## In a nutshell

✔️ New feature
❌ Fix an issue
❌ Documentation improvement
❌ Other: *please explain*

(✔️: yes, ❌: no)

## Potential impacts

None.

codesandbox[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/dubzzz/fast-check/pr/623/builds/31853) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit bfe8b50def51677983f42be1189489d5518eb611:

|Sandbox| Source |
|--|--|
|[jovial-northcutt-kep1k](https://codesandbox.io/s/jovial-northcutt-kep1k)| Configuration |
|[mystifying-resonance-9ufhg](https://codesandbox.io/s/mystifying-resonance-9ufhg)| Configuration |

coveralls: 
[![Coverage Status](https://coveralls.io/builds/31397768/badge)](https://coveralls.io/builds/31397768)

Coverage decreased (-0.03%) to 95.971% when pulling **bfe8b50def51677983f42be1189489d5518eb611 on feat/run-configuration** into **e5bff9c2520b91e9d532d9e4e5cfc4138f81f693 on master**.


Patch:
diff --git a/documentation/1-Guides/Runners.md b/documentation/1-Guides/Runners.md
index 7223f7bc..b1f911e7 100644
--- a/documentation/1-Guides/Runners.md
+++ b/documentation/1-Guides/Runners.md
@@ -132,6 +132,7 @@ The details returned by `fc.check` are the following:
 ```typescript
 interface RunDetails<Ts> {
     failed: boolean;         // true in case of failure or too many skips, false otherwise
+    interrupted: boolean;    // true in case of interrupted run, false otherwise
     numRuns: number;         // number of runs (all runs if success, up and including the first failure if failed)
     numSkips: number;        // number of skipped entries due to failed pre-condition (before the first failure)
     numShrinks: number;      // number of shrinks (depth required to get the minimal failing example)
@@ -142,6 +143,10 @@ interface RunDetails<Ts> {
                                      // 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
+    failures: Ts[];          // verbose>=1 only: failures that have occurred during the run
+    executionSummary: ExecutionTree<Ts>[]; // verbose>=1 only: traces the origin of each value
+                                           // encountered during the test and its status
+    runConfiguration: Parameters<Ts>; // configuration of the run, it includes local and global paramaters
 }
 ```
 
diff --git a/src/check/runner/Runner.ts b/src/check/runner/Runner.ts
index 483bf409..76eac0a5 100644
--- a/src/check/runner/Runner.ts
+++ b/src/check/runner/Runner.ts
@@ -109,13 +109,14 @@ function check<Ts>(rawProperty: IRawProperty<Ts>, params?: Parameters<Ts>) {
   const sourceValues = new SourceValuesIterator(initialValues, maxInitialIterations, maxSkips);
   return property.isAsync()
     ? asyncRunIt(property, sourceValues, qParams.verbose, qParams.markInterruptAsFailure).then((e) =>
-        e.toRunDetails(qParams.seed, qParams.path, qParams.numRuns, maxSkips)
+        e.toRunDetails(qParams.seed, qParams.path, qParams.numRuns, maxSkips, qParams)
       )
     : runIt(property, sourceValues, qParams.verbose, qParams.markInterruptAsFailure).toRunDetails(
         qParams.seed,
         qParams.path,
         qParams.numRuns,
-        maxSkips
+        maxSkips,
+        qParams
       );
 }
 
diff --git a/src/check/runner/configuration/QualifiedParameters.ts b/src/check/runner/configuration/QualifiedParameters.ts
index 9a35e249..28d60fcb 100644
--- a/src/check/runner/configuration/QualifiedParameters.ts
+++ b/src/check/runner/configuration/QualifiedParameters.ts
@@ -46,6 +46,26 @@ export class QualifiedParameters<T> {
     this.endOnFailure = QualifiedParameters.readBoolean(p, 'endOnFailure');
   }
 
+  toParameters(): Parameters<T> {
+    const orUndefined = <V>(value: V | null) => (value !== null ? value : undefined);
+    return {
+      seed: this.seed,
+      randomType: this.randomType,
+      numRuns: this.numRuns,
+      maxSkipsPerRun: this.maxSkipsPerRun,
+      timeout: orUndefined(this.timeout),
+      skipAllAfterTimeLimit: orUndefined(this.skipAllAfterTimeLimit),
+      interruptAfterTimeLimit: orUndefined(this.interruptAfterTimeLimit),
+      markInterruptAsFailure: this.markInterruptAsFailure,
+      path: this.path,
+      logger: this.logger,
+      unbiased: this.unbiased,
+      verbose: this.verbose,
+      examples: this.examples,
+      endOnFailure: this.endOnFailure,
+    };
+  }
+
   private static readSeed = <T>(p: Parameters<T>): number => {
     // No seed specified
     if (p.seed == null) return Date.now() ^ (Math.random() * 0x100000000);
diff --git a/src/check/runner/reporter/RunDetails.ts b/src/check/runner/reporter/RunDetails.ts
index ca00ab45..4c085fa1 100644
--- a/src/check/runner/reporter/RunDetails.ts
+++ b/src/check/runner/reporter/RunDetails.ts
@@ -1,5 +1,6 @@
 import { VerbosityLevel } from '../configuration/VerbosityLevel';
 import { ExecutionTree } from './ExecutionTree';
+import { Parameters } from '../configuration/Parameters';
 
 /**
  * Post-run details produced by {@link check}
@@ -129,4 +130,11 @@ interface RunDetailsWithDoc<Ts> {
    * Verbosity level required by the user
    */
   verbose: VerbosityLevel;
+  /**
+   * Configuration of the run
+   *
+   * It includes both local parameters set on `fc.assert` or `fc.check`
+   * and global ones specified using `fc.configureGlobal`
+   */
+  runConfiguration: Parameters<Ts>;
 }
diff --git a/src/check/runner/reporter/RunExecution.ts b/src/check/runner/reporter/RunExecution.ts
index 92778f4e..1b5096e4 100644
--- a/src/check/runner/reporter/RunExecution.ts
+++ b/src/check/runner/reporter/RunExecution.ts
@@ -2,6 +2,7 @@ import { VerbosityLevel } from '../configuration/VerbosityLevel';
 import { ExecutionStatus } from './ExecutionStatus';
 import { ExecutionTree } from './ExecutionTree';
 import { RunDetails } from './RunDetails';
+import { QualifiedParameters } from '../configuration/QualifiedParameters';
 
 /**
  * @hidden
@@ -91,7 +92,13 @@ export class RunExecution<Ts> {
     return [...offsetItems.slice(0, offsetItems.length - 1), `${middle}`, ...remainingItems.slice(1)].join(':');
   };
 
-  toRunDetails(seed: number, basePath: string, numRuns: number, maxSkips: number): RunDetails<Ts> {
+  toRunDetails(
+    seed: number,
+    basePath: string,
+    numRuns: number,
+    maxSkips: number,
+    qParams: QualifiedParameters<Ts>
+  ): RunDetails<Ts> {
     if (!this.isSuccess()) {
       // encountered a property failure
       return {
@@ -114,6 +121,7 @@ export class RunExecution<Ts> {
         failures: this.extractFailures(),
         executionSummary: this.rootExecutionTrees,
         verbose: this.verbosity,
+        runConfiguration: qParams.toParameters(),
       };
     }
 
@@ -144,6 +152,7 @@ export class RunExecution<Ts> {
       failures: [],
       executionSummary: this.rootExecutionTrees,
       verbose: this.verbosity,
+      runConfiguration: qParams.toParameters(),
     };
   }
 }


Test patch:
diff --git a/test/unit/check/runner/reporter/RunExecution.spec.ts b/test/unit/check/runner/reporter/RunExecution.spec.ts
index fd9e7e65..7621c0bd 100644
--- a/test/unit/check/runner/reporter/RunExecution.spec.ts
+++ b/test/unit/check/runner/reporter/RunExecution.spec.ts
@@ -3,6 +3,7 @@ import * as fc from '../../../../../lib/fast-check';
 import { RunExecution } from '../../../../../src/check/runner/reporter/RunExecution';
 import { VerbosityLevel } from '../../../../../src/check/runner/configuration/VerbosityLevel';
 import { ExecutionStatus } from '../../../../../src/fast-check';
+import { QualifiedParameters } from '../../../../../src/check/runner/configuration/QualifiedParameters';
 
 describe('RunExecution', () => {
   it('Should expose data coming from the last failure', () =>
@@ -30,7 +31,7 @@ describe('RunExecution', () => {
           }
           // Assert the value
           const lastFailure = failuresDesc[failuresDesc.length - 1];
-          const details = run.toRunDetails(seed, '', 42, 10000);
+          const details = run.toRunDetails(seed, '', 42, 10000, QualifiedParameters.read({}));
           expect(details.failed).toBe(true);
           expect(details.interrupted).toBe(false);
           expect(details.counterexamplePath).not.toBe(null);
@@ -64,7 +65,9 @@ describe('RunExecution', () => {
           run.fail(42, failureId, 'Failed');
         }
         // Assert the value
-        expect(run.toRunDetails(seed, '', 42, 10000).counterexamplePath).toEqual(path.join(':'));
+        expect(run.toRunDetails(seed, '', 42, 10000, QualifiedParameters.read({})).counterexamplePath).toEqual(
+          path.join(':')
+        );
       })
     ));
   it('Should generate correct counterexamplePath given initial offset', () =>
@@ -86,9 +89,9 @@ describe('RunExecution', () => {
           const joinedPath = [...offsetPath, ...addedPath.slice(1)];
           joinedPath[offsetPath.length - 1] += addedPath[0];
           // Assert the value
-          expect(run.toRunDetails(seed, offsetPath.join(':'), 42, 10000).counterexamplePath).toEqual(
-            joinedPath.join(':')
-          );
+          expect(
+            run.toRunDetails(seed, offsetPath.join(':'), 42, 10000, QualifiedParameters.read({})).counterexamplePath
+          ).toEqual(joinedPath.join(':'));
         }
       )
     ));
@@ -119,7 +122,13 @@ describe('RunExecution', () => {
                 break;
             }
           }
-          const details = run.toRunDetails(0, '', executionStatuses.length + 1, executionStatuses.length + 1);
+          const details = run.toRunDetails(
+            0,
+            '',
+            executionStatuses.length + 1,
+            executionStatuses.length + 1,
+            QualifiedParameters.read({})
+          );
           let currentExecutionTrees = details.executionSummary;
           for (let idx = 0, idxInTrees = 0; idx !== executionStatuses.length; ++idx, ++idxInTrees) {
             // Ordered like execution: same value and status