Repository: dubzzz/fast-check
Pull request #1451 (2021-02-06 18:05:24)

✨ Add support for sparse arrays in `fc.object`
<!-- 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: *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/1451/builds/99172) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 19fc718fc7ef9893716e1b2d0b677125115eb5d1:

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

codecov[bot]: # [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/1451?src=pr&el=h1) Report
> Merging [#1451](https://codecov.io/gh/dubzzz/fast-check/pull/1451?src=pr&el=desc) (19fc718) into [master](https://codecov.io/gh/dubzzz/fast-check/commit/e9ad01fd9a1ca7ff0ffb83ee0ed836e73b342e7a?el=desc) (e9ad01f) will **decrease** coverage by `9.51%`.
> The diff coverage is `100.00%`.

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

```diff
@@            Coverage Diff             @@
##           master    #1451      +/-   ##
==========================================
- Coverage   98.31%   88.79%   -9.52%     
==========================================
  Files          92       92              
  Lines        3138     3141       +3     
  Branches      610      611       +1     
==========================================
- Hits         3085     2789     -296     
- Misses         50      344     +294     
- Partials        3        8       +5     
```

| Flag | Coverage Δ | |
|---|---|---|
| e2e-tests | `88.79% <100.00%> (+0.01%)` | :arrow_up: |
| e2e-tests-10.x-Linux | `?` | |
| e2e-tests-10.x-Windows | `?` | |
| e2e-tests-10.x-macOS | `?` | |
| e2e-tests-12.x-Linux | `88.79% <100.00%> (+0.01%)` | :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/1451?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/check/arbitrary/ObjectArbitrary.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1451/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL2FyYml0cmFyeS9PYmplY3RBcmJpdHJhcnkudHM=) | `99.11% <100.00%> (+0.02%)` | :arrow_up: |
| [src/check/property/TimeoutProperty.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1451/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/1451/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/1451/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/1451/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL2FyYml0cmFyeS9kZWZpbml0aW9uL0FyYml0cmFyeVdpdGhDb250ZXh0dWFsU2hyaW5rLnRz) | `46.66% <0.00%> (-53.34%)` | :arrow_down: |
| [.../check/arbitrary/definition/ArbitraryWithShrink.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1451/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/1451/diff?src=pr&el=tree#diff-c3JjL3N0cmVhbS9MYXp5SXRlcmFibGVJdGVyYXRvci50cw==) | `63.63% <0.00%> (-36.37%)` | :arrow_down: |
| [src/check/property/AsyncProperty.generic.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1451/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL3Byb3BlcnR5L0FzeW5jUHJvcGVydHkuZ2VuZXJpYy50cw==) | `66.66% <0.00%> (-33.34%)` | :arrow_down: |
| [src/check/model/ModelRunner.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1451/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL21vZGVsL01vZGVsUnVubmVyLnRz) | `58.33% <0.00%> (-31.26%)` | :arrow_down: |
| [src/utils/stringify.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1451/diff?src=pr&el=tree#diff-c3JjL3V0aWxzL3N0cmluZ2lmeS50cw==) | `67.85% <0.00%> (-31.25%)` | :arrow_down: |
| ... and [32 more](https://codecov.io/gh/dubzzz/fast-check/pull/1451/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/1451?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/1451?src=pr&el=footer). Last update [e9ad01f...19fc718](https://codecov.io/gh/dubzzz/fast-check/pull/1451?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/documentation/Arbitraries.md b/documentation/Arbitraries.md
index 1e4650f9..7e4a4b58 100644
--- a/documentation/Arbitraries.md
+++ b/documentation/Arbitraries.md
@@ -2913,6 +2913,7 @@ fc.unicodeJsonObject({maxDepth: 1})
 - `withObjectString?` — default: `false` — _enable strings looking as string representations of JavaScript instances - eg.: `"{}"`, `"new Set([1])"`_
 - `withSet?` — default: `false` — _enable `Set` - eg.: `new Set([1, 2, 3])`_
 - `withTypedArray?` — default: `false` — _enable typed arrays for ints, uints and floats - eg.: `Int8Array.from([1, 2, 3])`_
+- `withSparseArray?` — default: `false` — _enable sparse arrays - eg.: `[1,,,3]`_
 - `values?` — default: _booleans, numbers, strings, null and undefined_ — _array of arbitraries producing the root* values - *non-object ones_
 
 *&#8195;Usages*
@@ -2959,13 +2960,14 @@ fc.anything({
   withObjectString: true,
   withSet: true,
   withTypedArray: true,
+  withSparseArray: true,
 })
 // Examples of generated values:
-// • 50262693997624561656566658494844280329167601007133105101730712090471350904016n
-// • Uint16Array.from([64924,45869,14750,25035,2761,46076])
-// • Float32Array.from([0.08553320169448853,-1.6264161412677633e+31,-6.835593778612435e+24,1.664170987467859e+26,4.2968819535433035e+34,-3.622660163541219e-33])
-// • Uint16Array.from([3344,28462])
-// • Int8Array.from([71,-1])
+// • Object.assign(Object.create(null),{"WG":new Boolean(false)})
+// • Uint16Array.from([29101,11])
+// • Uint8Array.from([147])
+// • Uint8ClampedArray.from([])
+// • Object.assign(Object.create(null),{"! ":Int16Array.from([29906])})
 // • …
 ```
 </details>
diff --git a/src/check/arbitrary/ObjectArbitrary.ts b/src/check/arbitrary/ObjectArbitrary.ts
index 620f6a44..b3ed8c60 100644
--- a/src/check/arbitrary/ObjectArbitrary.ts
+++ b/src/check/arbitrary/ObjectArbitrary.ts
@@ -26,6 +26,7 @@ import {
   uint8Array,
   uint8ClampedArray,
 } from './TypedArrayArbitrary';
+import { sparseArray } from './SparseArrayArbitrary';
 
 /**
  * Constraints for {@link anything} and {@link object}
@@ -84,6 +85,8 @@ export interface ObjectConstraints {
    * Remark: no typed arrays made of bigint
    */
   withTypedArray?: boolean;
+  /** Also generate sparse arrays (arrays with holes) */
+  withSparseArray?: boolean;
 }
 
 /**
@@ -131,7 +134,8 @@ class QualifiedObjectConstraints {
     readonly withNullPrototype: boolean,
     readonly withBigInt: boolean,
     readonly withDate: boolean,
-    readonly withTypedArray: boolean
+    readonly withTypedArray: boolean,
+    readonly withSparseArray: boolean
   ) {}
 
   /**
@@ -173,7 +177,8 @@ class QualifiedObjectConstraints {
       orDefault(settings.withNullPrototype, false),
       orDefault(settings.withBigInt, false),
       orDefault(settings.withDate, false),
-      orDefault(settings.withTypedArray, false)
+      orDefault(settings.withTypedArray, false),
+      orDefault(settings.withSparseArray, false)
     );
   }
 }
@@ -244,7 +249,8 @@ const anythingInternal = (constraints: QualifiedObjectConstraints): Arbitrary<un
             float32Array(),
             float64Array(),
           ]
-        : [])
+        : []),
+      ...(constraints.withSparseArray ? [sparseArray(anythingArb())] : [])
     );
   });
 


Test patch:
diff --git a/test/e2e/GenerateAllValues.spec.ts b/test/e2e/GenerateAllValues.spec.ts
index 1316defa..d12e32fc 100644
--- a/test/e2e/GenerateAllValues.spec.ts
+++ b/test/e2e/GenerateAllValues.spec.ts
@@ -72,6 +72,7 @@ describe(`Generate all values (seed: ${seed})`, () => {
           withNullPrototype: true,
           withDate: true,
           withTypedArray: true,
+          withSparseArray: true,
           ...(typeof BigInt !== 'undefined' ? { withBigInt: true } : {}),
         });
         while (++numTries <= 10000) {
diff --git a/test/unit/check/arbitrary/ObjectArbitrary.spec.ts b/test/unit/check/arbitrary/ObjectArbitrary.spec.ts
index ff9a9010..63818d96 100644
--- a/test/unit/check/arbitrary/ObjectArbitrary.spec.ts
+++ b/test/unit/check/arbitrary/ObjectArbitrary.spec.ts
@@ -194,6 +194,12 @@ describe('ObjectArbitrary', () => {
           v instanceof Float64Array
         );
       }));
+    // Rq: For the moment typed arrays require the depth to be at least of 1
+    it('Should be able to produce sparse arrays', () =>
+      checkProduce(
+        { values: [constant(0)], maxDepth: 1, withSparseArray: true },
+        (v) => Array.isArray(v) && v.length !== Object.keys(v).length
+      ));
     // Rq: For the moment stringified representations require the depth to be at least of 1
     it('Should be able to produce stringified representations of objects', () =>
       checkProduce({ values: [constant(0)], maxDepth: 1, withObjectString: true }, (v) => {
diff --git a/test/unit/utils/stringify.spec.ts b/test/unit/utils/stringify.spec.ts
index 03502779..0dcec550 100644
--- a/test/unit/utils/stringify.spec.ts
+++ b/test/unit/utils/stringify.spec.ts
@@ -38,6 +38,7 @@ const anythingEnableAll = {
   withNullPrototype: true,
   withDate: true,
   withTypedArray: true,
+  withSparseArray: true,
   ...(typeof BigInt !== 'undefined' ? { withBigInt: true } : {}),
 };