Repository: dubzzz/fast-check
Pull request #1385 (2021-01-21 20:50:33)

♻️ Extract Stream.of logic for re-use
<!-- Why is this PR for? -->
<!-- Describe the reason why you opened this PR or give a link towards the associated issue. -->

## In a nutshell

❌ New feature
❌ Fix an issue
❌ Documentation improvement
✔️ Other: *code cleaning*

(✔️: 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/1385/builds/94268) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 9ac6d5d870e684134406b4b980bdfe0003c719c0:

|Sandbox| Source |
|--|--|
|[Vanilla](https://codesandbox.io/s/vanilla-ylghe)| Configuration |
|[fast-check-examples](https://codesandbox.io/s/fast-check-examples-s0302)| Configuration |

codecov[bot]: # [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/1385?src=pr&el=h1) Report
> Merging [#1385](https://codecov.io/gh/dubzzz/fast-check/pull/1385?src=pr&el=desc) (9ac6d5d) into [master](https://codecov.io/gh/dubzzz/fast-check/commit/a0a6445412031890b1a9c47024be99833e56d1e0?el=desc) (a0a6445) will **decrease** coverage by `9.14%`.
> The diff coverage is `80.00%`.

[![Impacted file tree graph](https://codecov.io/gh/dubzzz/fast-check/pull/1385/graphs/tree.svg?width=650&height=150&src=pr&token=fRi2fJN730)](https://codecov.io/gh/dubzzz/fast-check/pull/1385?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1385      +/-   ##
==========================================
- Coverage   98.27%   89.13%   -9.15%     
==========================================
  Files          91       91              
  Lines        3078     3074       -4     
  Branches      600      600              
==========================================
- Hits         3025     2740     -285     
- Misses         50      326     +276     
- Partials        3        8       +5     
```

| Flag | Coverage Δ | |
|---|---|---|
| e2e-tests | `89.13% <80.00%> (+0.05%)` | :arrow_up: |
| e2e-tests-10.x-Linux | `?` | |
| e2e-tests-10.x-Windows | `?` | |
| e2e-tests-10.x-macOS | `?` | |
| e2e-tests-12.x-Linux | `89.13% <80.00%> (+0.11%)` | :arrow_up: |
| e2e-tests-12.x-Windows | `?` | |
| e2e-tests-12.x-macOS | `?` | |
| e2e-tests-14.x-Linux | `?` | |
| e2e-tests-14.x-Windows | `?` | |
| e2e-tests-14.x-macOS | `?` | |
| unit-tests | `?` | |
| unit-tests-10.x-Linux | `?` | |
| unit-tests-12.x-Linux | `?` | |
| unit-tests-14.x-Linux | `?` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dubzzz/fast-check/pull/1385?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/check/arbitrary/helpers/ArrayInt64Arbitrary.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL2FyYml0cmFyeS9oZWxwZXJzL0FycmF5SW50NjRBcmJpdHJhcnkudHM=) | `86.74% <0.00%> (-2.67%)` | :arrow_down: |
| [src/check/arbitrary/BigIntArbitrary.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL2FyYml0cmFyeS9CaWdJbnRBcmJpdHJhcnkudHM=) | `90.78% <100.00%> (-1.52%)` | :arrow_down: |
| [src/check/arbitrary/IntegerArbitrary.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL2FyYml0cmFyeS9JbnRlZ2VyQXJiaXRyYXJ5LnRz) | `94.59% <100.00%> (-2.78%)` | :arrow_down: |
| [src/stream/Stream.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL3N0cmVhbS9TdHJlYW0udHM=) | `78.94% <100.00%> (-21.06%)` | :arrow_down: |
| [src/check/property/TimeoutProperty.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL3Byb3BlcnR5L1RpbWVvdXRQcm9wZXJ0eS50cw==) | `11.76% <0.00%> (-88.24%)` | :arrow_down: |
| [src/check/model/commands/ScheduledCommand.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL21vZGVsL2NvbW1hbmRzL1NjaGVkdWxlZENvbW1hbmQudHM=) | `7.14% <0.00%> (-82.15%)` | :arrow_down: |
| [src/utils/hash.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL3V0aWxzL2hhc2gudHM=) | `29.62% <0.00%> (-70.38%)` | :arrow_down: |
| [...itrary/definition/ArbitraryWithContextualShrink.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL2FyYml0cmFyeS9kZWZpbml0aW9uL0FyYml0cmFyeVdpdGhDb250ZXh0dWFsU2hyaW5rLnRz) | `41.17% <0.00%> (-58.83%)` | :arrow_down: |
| [.../check/arbitrary/definition/ArbitraryWithShrink.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL2FyYml0cmFyeS9kZWZpbml0aW9uL0FyYml0cmFyeVdpdGhTaHJpbmsudHM=) | `60.00% <0.00%> (-40.00%)` | :arrow_down: |
| [src/stream/LazyIterableIterator.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree#diff-c3JjL3N0cmVhbS9MYXp5SXRlcmFibGVJdGVyYXRvci50cw==) | `63.63% <0.00%> (-36.37%)` | :arrow_down: |
| ... and [34 more](https://codecov.io/gh/dubzzz/fast-check/pull/1385/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/1385?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/1385?src=pr&el=footer). Last update [a0a6445...9ac6d5d](https://codecov.io/gh/dubzzz/fast-check/pull/1385?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

github-actions[bot]: Give a try to https://github.com/dubzzz/fast-check/pull/1385/commits/9ac6d5d870e684134406b4b980bdfe0003c719c0 with:

```bash
yarn add https://6009ea014cd34f22b31d4819--dazzling-goodall-4f0e38.netlify.app/fast-check.tgz
npm i https://6009ea014cd34f22b31d4819--dazzling-goodall-4f0e38.netlify.app/fast-check.tgz
```

Or have a look to the [generated documentation](https://6009ea014cd34f22b31d4819--dazzling-goodall-4f0e38.netlify.app).

Patch:
diff --git a/src/check/arbitrary/BigIntArbitrary.ts b/src/check/arbitrary/BigIntArbitrary.ts
index 296997df..d0a73df6 100644
--- a/src/check/arbitrary/BigIntArbitrary.ts
+++ b/src/check/arbitrary/BigIntArbitrary.ts
@@ -1,5 +1,5 @@
 import { Random } from '../../random/generator/Random';
-import { stream, Stream } from '../../stream/Stream';
+import { Stream } from '../../stream/Stream';
 import { Arbitrary } from './definition/Arbitrary';
 import { ArbitraryWithContextualShrink } from './definition/ArbitraryWithContextualShrink';
 import { biasWrapper } from './definition/BiasedArbitraryWrapper';
@@ -7,11 +7,6 @@ import { Shrinkable } from './definition/Shrinkable';
 import { biasNumeric, bigIntLogLike } from './helpers/BiasNumeric';
 import { shrinkBigInt } from './helpers/ShrinkBigInt';
 
-/** @internal */
-function* shrinkToExact(value: bigint): IterableIterator<[bigint, unknown]> {
-  yield [value, undefined];
-}
-
 /** @internal */
 class BigIntArbitrary extends ArbitraryWithContextualShrink<bigint> {
   private biasedBigIntArbitrary: Arbitrary<bigint> | null = null;
@@ -37,10 +32,10 @@ class BigIntArbitrary extends ArbitraryWithContextualShrink<bigint> {
 
     // Last chance try... (see IntegerArbitrary)
     if (current === context + BigInt(1) && current > this.min && current > 0) {
-      return stream(shrinkToExact(context)); // reset context in case of failure
+      return Stream.of([context, undefined]); // undefined reset context in case of failure
     }
     if (current === context - BigInt(1) && current < this.max && current < 0) {
-      return stream(shrinkToExact(context)); // reset context in case of failure
+      return Stream.of([context, undefined]); // undefined reset context in case of failure
     }
 
     // Normal shrink process
diff --git a/src/check/arbitrary/IntegerArbitrary.ts b/src/check/arbitrary/IntegerArbitrary.ts
index 86590f4e..3289db58 100644
--- a/src/check/arbitrary/IntegerArbitrary.ts
+++ b/src/check/arbitrary/IntegerArbitrary.ts
@@ -1,5 +1,5 @@
 import { Random } from '../../random/generator/Random';
-import { stream, Stream } from '../../stream/Stream';
+import { Stream } from '../../stream/Stream';
 import { Arbitrary } from './definition/Arbitrary';
 import { ArbitraryWithContextualShrink } from './definition/ArbitraryWithContextualShrink';
 import { biasWrapper } from './definition/BiasedArbitraryWrapper';
@@ -7,11 +7,6 @@ import { Shrinkable } from './definition/Shrinkable';
 import { biasNumeric, integerLogLike } from './helpers/BiasNumeric';
 import { shrinkInteger } from './helpers/ShrinkInteger';
 
-/** @internal */
-function* shrinkToExact(value: number): IterableIterator<[number, unknown]> {
-  yield [value, undefined];
-}
-
 /** @internal */
 class IntegerArbitrary extends ArbitraryWithContextualShrink<number> {
   static MIN_INT: number = 0x80000000 | 0;
@@ -44,10 +39,10 @@ class IntegerArbitrary extends ArbitraryWithContextualShrink<number> {
     // In order to check if they impacted us, we just try to move very close to our current value.
     // It is not ideal but it can help restart a shrinking process that stopped too early.
     if (current === context + 1 && current > this.min && current > 0) {
-      return stream(shrinkToExact(context)); // reset context in case of failure
+      return Stream.of([context, undefined]); // undefined resets context in case of failure
     }
     if (current === context - 1 && current < this.max && current < 0) {
-      return stream(shrinkToExact(context)); // reset context in case of failure
+      return Stream.of([context, undefined]); // undefined reset context in case of failure
     }
 
     // Normal shrink process
diff --git a/src/check/arbitrary/helpers/ArrayInt64Arbitrary.ts b/src/check/arbitrary/helpers/ArrayInt64Arbitrary.ts
index e92900cf..386987a3 100644
--- a/src/check/arbitrary/helpers/ArrayInt64Arbitrary.ts
+++ b/src/check/arbitrary/helpers/ArrayInt64Arbitrary.ts
@@ -19,11 +19,6 @@ import {
 } from './ArrayInt64';
 import { BiasedNumericArbitrary } from './BiasNumeric';
 
-/** @internal */
-function* shrinkToExact(value: ArrayInt64): IterableIterator<[ArrayInt64, unknown]> {
-  yield [value, undefined];
-}
-
 /** @internal */
 class ArrayInt64Arbitrary extends ArbitraryWithContextualShrink<ArrayInt64> {
   private biasedArrayInt64Arbitrary: Arbitrary<ArrayInt64> | null = null;
@@ -78,7 +73,7 @@ class ArrayInt64Arbitrary extends ArbitraryWithContextualShrink<ArrayInt64> {
       isEqual64(current, add64(context, Unit64)) &&
       isStrictlyPositive64(substract64(current, this.min))
     ) {
-      return stream(shrinkToExact(context)); // reset context in case of failure
+      return Stream.of([context, undefined]); // undefined reset context in case of failure
     }
     const currentIsStNeg = !currentIsZero && current.sign === -1;
     if (
@@ -86,7 +81,7 @@ class ArrayInt64Arbitrary extends ArbitraryWithContextualShrink<ArrayInt64> {
       isEqual64(current, substract64(context, Unit64)) &&
       isStrictlyNegative64(substract64(current, this.max))
     ) {
-      return stream(shrinkToExact(context)); // reset context in case of failure
+      return Stream.of([context, undefined]); // undefined reset context in case of failure
     }
     // Normal shrink process
     return this.shrinkValueTowards(current, context, false);
diff --git a/src/stream/Stream.ts b/src/stream/Stream.ts
index 1394440e..a683afda 100644
--- a/src/stream/Stream.ts
+++ b/src/stream/Stream.ts
@@ -14,6 +14,15 @@ export class Stream<T> implements IterableIterator<T> {
     return new Stream(nilHelper());
   }
 
+  /**
+   * Create a stream of T from a variable number of elements
+   *
+   * @param elements - Elements used to create the Stream
+   */
+  static of<T>(...elements: T[]): Stream<T> {
+    return new Stream(elements[Symbol.iterator]());
+  }
+
   // /*DEBUG*/ // no double iteration
   // /*DEBUG*/ private isLive: boolean;
 


Test patch:
diff --git a/test/unit/stream/Stream.spec.ts b/test/unit/stream/Stream.spec.ts
index 6b622d98..e099e07f 100644
--- a/test/unit/stream/Stream.spec.ts
+++ b/test/unit/stream/Stream.spec.ts
@@ -38,6 +38,24 @@ describe('Stream', () => {
       expect([...s]).toEqual([]);
     });
   });
+  describe('of', () => {
+    it('Should instantiate an empty stream given no elements', () => {
+      const s: Stream<number> = Stream.of();
+      expect([...s]).toEqual([]);
+    });
+    it('Should instantiate a stream containing a single entry given a single element', () => {
+      const s: Stream<number> = Stream.of(1);
+      expect([...s]).toEqual([1]);
+    });
+    it('Should instantiate a stream containing the same entries as passed elements with same ordering', () => {
+      const s: Stream<number> = Stream.of(1, 42, 69);
+      expect([...s]).toEqual([1, 42, 69]);
+    });
+    it('Should not consider elements of type Array differently from other ones', () => {
+      const s: Stream<number[]> = Stream.of([1, 42, 69]);
+      expect([...s]).toEqual([[1, 42, 69]]);
+    });
+  });
   describe('map', () => {
     it('Should apply on each element', () => {
       function* g() {