Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸ“ Document core building blocks in new documentation #3745

Merged
merged 38 commits into from Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
56053e2
basic core-blocks beginning
dubzzz Apr 6, 2023
0e94fc2
adapt docs script
dubzzz Apr 6, 2023
953099f
add boolean
dubzzz Apr 6, 2023
ad3d844
add numeric
dubzzz Apr 6, 2023
814b72f
add numeric and fix it
dubzzz Apr 6, 2023
beefae6
format arb md
dubzzz Apr 6, 2023
57fad18
re-ident maxSafeNat
dubzzz Apr 6, 2023
4ce2191
add some strings
dubzzz Apr 6, 2023
00be7d5
put all strings
dubzzz Apr 6, 2023
a42a7c1
add string in geenrated stuff
dubzzz Apr 6, 2023
5f4e8f4
stop referencing depreciated
dubzzz Apr 6, 2023
5eff384
rename into number
dubzzz Apr 6, 2023
745d685
add missing description
dubzzz Apr 6, 2023
f612d2e
missing line
dubzzz Apr 6, 2023
4863622
add date
dubzzz Apr 7, 2023
ed95f4f
automatically discover md files of examples
dubzzz Apr 7, 2023
28d7594
Create an intermediate primitives directory
dubzzz Apr 7, 2023
9301809
spli into many categories
dubzzz Apr 7, 2023
6cd1625
add typed arrays
dubzzz Apr 10, 2023
6451fa2
add come more combiners
dubzzz Apr 10, 2023
137bd24
rename type-arrays into type-array
dubzzz Apr 10, 2023
b4d188c
add some more constants
dubzzz Apr 10, 2023
249d943
arrays
dubzzz Apr 10, 2023
eeb17a8
add iterable
dubzzz Apr 10, 2023
902b5dd
add object
dubzzz Apr 10, 2023
25da144
add fucntions
dubzzz Apr 10, 2023
b2e0a24
fix func
dubzzz Apr 10, 2023
ad2bf91
recursive structure
dubzzz Apr 10, 2023
3c63cdb
add last few ones
dubzzz Apr 10, 2023
11467bd
regenerate last few ones
dubzzz Apr 10, 2023
a372923
add json values
dubzzz Apr 10, 2023
f86c16d
add some more in combiners/any
dubzzz Apr 11, 2023
2bc0a19
add quick doc for runners
dubzzz Apr 11, 2023
f106150
describe more in runners
dubzzz Apr 11, 2023
61c5433
add tips
dubzzz Apr 11, 2023
7d66a77
minor typo
dubzzz Apr 11, 2023
04fcaa3
dedupe
dubzzz Apr 11, 2023
dbf09e1
versions
dubzzz Apr 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions .yarn/versions/696dee51.yml
@@ -0,0 +1,9 @@
releases:
fast-check: patch

declined:
- "@fast-check/ava"
- "@fast-check/jest"
- "@fast-check/vitest"
- "@fast-check/worker"
- website
1 change: 1 addition & 0 deletions packages/fast-check/package.json
Expand Up @@ -62,6 +62,7 @@
"@types/node": "^18.15.11",
"buffer": "^6.0.3",
"cross-env": "^7.0.3",
"glob": "^9.3.4",
"jest": "^29.5.0",
"replace-in-file": "^6.3.5",
"ts-jest": "^29.1.0",
Expand Down
24 changes: 20 additions & 4 deletions packages/fast-check/test/e2e/documentation/Docs.md.spec.ts
@@ -1,5 +1,6 @@
import * as fs from 'fs';
import fc from '../../../src/fast-check';
import { globSync } from 'glob';

// For ES Modules:
//import { dirname } from 'path';
Expand All @@ -14,9 +15,20 @@ const CommentForGeneratedValues = '// Examples of generated values:';
const CommentForArbitraryIndicator = '// Use the arbitrary:';
const CommentForStatistics = '// Computed statistics for 10k generated values:';

const allPathsFromWebsite = globSync(`../../website/docs/core-blocks/arbitraries/**/*.md`, {
withFileTypes: true,
nodir: true,
}).map((fileDescriptor) => ({
filePath: fileDescriptor.fullpath(),
shortName: fileDescriptor.name,
}));

describe('Docs.md', () => {
it('Should check code snippets validity and fix generated values', () => {
const originalFileContent = fs.readFileSync(`${__dirname}/../../../documentation/Arbitraries.md`).toString();
it.each([
{ filePath: `${__dirname}/../../../documentation/Arbitraries.md`, shortName: 'Arbitrary.md' },
...allPathsFromWebsite,
])('should check code snippets validity and fix generated values on $shortName', ({ filePath }) => {
const originalFileContent = fs.readFileSync(filePath).toString();
const { content: fileContent } = refreshContent(originalFileContent);

if (Number(process.versions.node.split('.')[0]) < 12) {
Expand All @@ -41,7 +53,7 @@ describe('Docs.md', () => {

if (fileContent !== originalFileContent && process.env.UPDATE_CODE_SNIPPETS) {
console.warn(`Updating code snippets defined in the documentation...`);
fs.writeFileSync(`${__dirname}/../../../documentation/Arbitraries.md`, fileContent);
fs.writeFileSync(filePath, fileContent);
}
if (!process.env.UPDATE_CODE_SNIPPETS) {
expect(fileContent).toEqual(originalFileContent);
Expand Down Expand Up @@ -133,7 +145,11 @@ function refreshContent(originalContent: string): { content: string; numExecuted
const indexArbitraryPart = refinedSnippet.indexOf(CommentForArbitraryIndicator);
const preparationPart = indexArbitraryPart !== -1 ? refinedSnippet.substring(0, indexArbitraryPart) : '';
const arbitraryPart = indexArbitraryPart !== -1 ? refinedSnippet.substring(indexArbitraryPart) : refinedSnippet;
const evalCode = `${preparationPart}\nfc.sample(${arbitraryPart}\n, { numRuns: ${numRuns}, seed: ${seed} }).map(v => fc.stringify(v))`;
const santitizeArbitraryPart = arbitraryPart
.trim()
.replace(/;$/, '')
.replace(/;\n\/\/.*$/m, '\n//');
const evalCode = `${preparationPart}\nfc.sample(${santitizeArbitraryPart}\n, { numRuns: ${numRuns}, seed: ${seed} }).map(v => fc.stringify(v))`;
try {
return eval(evalCode);
} catch (err) {
Expand Down
8 changes: 8 additions & 0 deletions website/docs/core-blocks/_category_.json
@@ -0,0 +1,8 @@
{
"label": "Core Blocks",
"position": 2,
"link": {
"type": "generated-index",
"description": "Deep dive into all the primitives provided by fast-check"
}
}
8 changes: 8 additions & 0 deletions website/docs/core-blocks/arbitraries/_category_.json
@@ -0,0 +1,8 @@
{
"label": "Arbitraries",
"position": 1,
"link": {
"type": "generated-index",
"description": "Full listing of all the arbitraries coming in fast-check"
}
}
@@ -0,0 +1,8 @@
{
"label": "Combiners",
"position": 3,
"link": {
"type": "generated-index",
"description": "Any arbitrary able to join together multiple other arbitraries to build more complex ones"
}
}
264 changes: 264 additions & 0 deletions website/docs/core-blocks/arbitraries/combiners/any.md
@@ -0,0 +1,264 @@
---
slug: /core-blocks/arbitraries/combiners/any
---

# Any

Combine and enhance any existing arbitraries.

## option

Randomly chooses between producing a value using the underlying arbitrary or returning nil

**Signatures:**

- `fc.option(arb)`
- `fc.option(arb, {freq?, nil?, depthSize?, maxDepth?, depthIdentifier?})`

**with:**

- `arb` β€” _arbitrary that will be called to generate normal values_
- `freq?` β€” default: `5` β€” _probability to build the nil value is of 1 / freq_
- `nil?` β€” default: `null` β€” _nil value_
- `depthSize?` β€” default: `undefined` [more](/docs/tips/larger-entries-by-default#depth-size-explained) β€” _how much we allow our recursive structures to be deep? The chance to select the nil value will increase as we go deeper in the structure_
- `maxDepth?` β€” default: `Number.POSITIVE_INFINITY` β€” _when reaching maxDepth, only nil could be produced_
- `depthIdentifier?` β€” default: `undefined` β€” _share the depth between instances using the same `depthIdentifier`_

**Usages:**

```js
fc.option(fc.nat());
// Examples of generated values: 28, 18, 2001121804, 2147483643, 12456933…

fc.option(fc.nat(), { freq: 2 });
// Examples of generated values: null, 1230277526, 10, 1854085942, 5…

fc.option(fc.nat(), { freq: 2, nil: Number.NaN });
// Examples of generated values: Number.NaN, 292454282, 990664982, 703789134, 278848986…

fc.option(fc.string(), { nil: undefined });
// Examples of generated values: "p:s", "", "ot(RM", "|", "2MyPDrq6"…

// fc.option fits very well with recursive stuctures built using fc.letrec.
// Examples of such recursive structures are available with fc.letrec.
```

Resources: [API reference](https://dubzzz.github.io/fast-check/api-reference/functions/option.html).
Available since 0.0.6.

## oneof

Generate one value based on one of the passed arbitraries

Randomly chooses an arbitrary at each new generation. Should be provided with at least one arbitrary. Probability to select a specific arbitrary is based on its weight: `weight(instance) / sumOf(weights)` (for depth=0). For higher depths, the probability to select the first arbitrary will increase as we go deeper in the tree so the formula is not applicable as-is. It preserves the shrinking capabilities of the underlying arbitrary. `fc.oneof` is able to shrink inside the failing arbitrary but not across arbitraries (contrary to `fc.constantFrom` when dealing with constant arbitraries) except if called with `withCrossShrink`.

:::warning First arbitrary, a privileged one
The first arbitrary specified on `oneof` will have a privileged position. Constraints like `withCrossShrink` or `depthSize` tend to favor it over others.
:::

**Signatures:**

- `fc.oneof(...arbitraries)`
- `fc.oneof({withCrossShrink?, maxDepth?, depthSize?, depthIdentifier?}, ...arbitraries)`

**with:**

- `...arbitraries` β€” _arbitraries that could be used to generate a value. The received instances can either be raw instances of arbitraries (meaning weight is 1) or objects containing the arbitrary and its associated weight (integer value β‰₯0)_
- `withCrossShrink?` β€” default: `false` β€” _in case of failure the shrinker will try to check if a failure can be found by using the first specified arbitrary. It may be pretty useful for recursive structures as it can easily help reducing their depth in case of failure_
- `maxDepth?` β€” default: `Number.POSITIVE_INFINITY` β€” _when reaching maxDepth, the first arbitrary will be used to generate the value_
- `depthSize?` β€” default: `undefined` [more](/docs/tips/larger-entries-by-default#depth-size-explained) β€” _how much we allow our recursive structures to be deep? The chance to select the first specified arbitrary will increase as we go deeper in the structure_
- `depthIdentifier?` β€” default: `undefined` β€” _share the depth between instances using the same `depthIdentifier`_

**Usages:**

```js
fc.oneof(fc.char(), fc.boolean());
// Note: Equivalent to:
// fc.oneof(
// { arbitrary: fc.char(), weight: 1 },
// { arbitrary: fc.boolean(), weight: 1 },
// )
// Examples of generated values: true, "p", " ", ",", "x"…

fc.oneof(fc.char(), fc.boolean(), fc.nat());
// Note: Equivalent to:
// fc.oneof(
// { arbitrary: fc.char(), weight: 1 },
// { arbitrary: fc.boolean(), weight: 1 },
// { arbitrary: fc.nat(), weight: 1 },
// )
// Examples of generated values: 12, true, 24, false, "N"…

fc.oneof({ arbitrary: fc.char(), weight: 5 }, { arbitrary: fc.boolean(), weight: 2 });
// Examples of generated values: false, true, "L", "b", "y"…

// fc.oneof fits very well with recursive stuctures built using fc.letrec.
// Examples of such recursive structures are available with fc.letrec.
```

Resources: [API reference](https://dubzzz.github.io/fast-check/api-reference/functions/oneof.html).
Available since 0.0.1.

## clone

Multiple identical values (they might not equal in terms of `===` or `==`).

Generate tuple containing multiple instances of the same value - values are independent from each others.

**Signatures:**

- `fc.clone(arb, numValues)`

**with:**

- `arb` β€” _arbitrary instance responsible to generate values_
- `numValues` β€” _number of clones (including itself)_

**Usages:**

```js
fc.clone(fc.nat(), 2);
// Examples of generated values: [1395148595,1395148595], [7,7], [1743838935,1743838935], [879259091,879259091], [2147483640,2147483640]…

fc.clone(fc.nat(), 3);
// Examples of generated values:
// β€’ [163289042,163289042,163289042]
// β€’ [287842615,287842615,287842615]
// β€’ [1845341787,1845341787,1845341787]
// β€’ [1127181441,1127181441,1127181441]
// β€’ [5,5,5]
// β€’ …
```

Resources: [API reference](https://dubzzz.github.io/fast-check/api-reference/functions/clone.html).
Available since 2.5.0.

## .filter

Filter an existing arbitrary.

**Signatures:**

- `.filter(predicate)`

**with:**

- `predicate` β€” _only keeps values such as `predicate(value) === true`_

**Usages:**

```js
fc.integer().filter((n) => n % 2 === 0);
// Note: Only produce even integer values
// Examples of generated values: -1582642274, 2147483644, 30, -902884124, -20…

fc.integer().filter((n) => n % 2 !== 0);
// Note: Only produce odd integer values
// Examples of generated values: 925226031, -1112273465, 29, -1459401265, 21…

fc.string().filter((s) => s[0] < s[1]);
// Note: Only produce strings with `s[0] < s[1]`
// Examples of generated values: "Aa]tp>", "apply", "?E%a$n x", "#l\"/L\"x&S{", "argument"…
```

Resources: [API reference](https://dubzzz.github.io/fast-check/api-reference/classes/Arbitrary.html#filter).
Available since 0.0.1.

## .map

Map an existing arbitrary.

**Signatures:**

- `.map(mapper)`

**with:**

- `mapper` β€” _transform the produced value into another one_

**Usages:**

```js
fc.nat(1024).map((n) => n * n);
// Note: Produce only square values
// Examples of generated values: 36, 24336, 49, 186624, 1038361…

fc.nat().map((n) => String(n));
// Note: Change the type of the produced value from number to string
// Examples of generated values: "2147483619", "12", "468194571", "14", "5"…

fc.tuple(fc.integer(), fc.integer()).map((t) => (t[0] < t[1] ? [t[0], t[1]] : [t[1], t[0]]));
// Note: Generate a range [min, max]
// Examples of generated values: [-1915878961,27], [-1997369034,-1], [-1489572084,-370560927], [-2133384365,28], [-1695373349,657254252]…

fc.string().map((s) => `[${s.length}] -> ${s}`);
// Examples of generated values: "[3] -> ref", "[8] -> xeE:81|z", "[9] -> B{1Z\\sxWa", "[3] -> key", "[1] -> _"…
```

Resources: [API reference](https://dubzzz.github.io/fast-check/api-reference/classes/Arbitrary.html#map).
Available since 0.0.1.

## .chain

Flat-Map an existing arbitrary.

:::warning Limited shrink
Be aware that the shrinker of such construct might not be able to shrink as much as possible (more details [here](https://github.com/dubzzz/fast-check/issues/650#issuecomment-648397230))
:::

**Signatures:**

- `.chain(fmapper)`

**with:**

- `fmapper` β€” _produce an arbitrary based on a generated value_

**Usages:**

```js
fc.nat().chain((min) => fc.tuple(fc.constant(min), fc.integer({ min, max: 0xffffffff })));
// Note: Produce a valid range
// Examples of generated values: [1211945858,4294967292], [1068058184,2981851306], [2147483626,2147483645], [1592081894,1592081914], [2147483623,2147483639]…
```

Resources: [API reference](https://dubzzz.github.io/fast-check/api-reference/classes/Arbitrary.html#chain).
Available since 1.2.0.

## .noBias

Drop bias from an existing arbitrary. Instead of being more likely to generate certain values the resulting arbitrary will be close to an equi-probable generator.

**Signatures:**

- `.noBias()`

**Usages:**

```js
fc.nat().noBias();
// Note: Compared to fc.nat() alone, the generated values are evenly distributed in
// the range 0 to 0x7fffffff making small values much more unlikely.
// Examples of generated values: 422394692, 1060515252, 383444404, 1509445429, 659009523…
```

Resources: [API reference](https://dubzzz.github.io/fast-check/api-reference/classes/Arbitrary.html#noBias).
Available since 1.1.0.

## .noShrink

Drop shrinking capabilities from an existing arbitrary.

**Signatures:**

- `.noShrink()`

**Usages:**

```js
fc.nat().noShrink();
// Examples of generated values: 3, 633829028, 2147483625, 1617246126, 25…
```

Resources: [API reference](https://dubzzz.github.io/fast-check/api-reference/classes/Arbitrary.html#noShrink).
Available since 0.0.9.