@wtf/platform-suite 0.0.0-stage → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +47 -2
- package/dist/suites.d.ts +106 -0
- package/dist/suites.js +586 -0
- package/dist/suites.js.map +1 -0
- package/package.json +39 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mikael Karon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,48 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @wtf/platform-suite
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The test suites that every implementation of a `@wtf/platform` member passes, for Vitest. If you
|
|
4
|
+
write your own timers, `crypto`, `Path`, locks, channels, workers, `spawn` or a whole platform, run
|
|
5
|
+
these suites in your tests: they hold it to what Node's members, a page's, the memory platform's and
|
|
6
|
+
the test doubles do, so code that takes a `Platform` runs the same on yours. For a member from a
|
|
7
|
+
web standard, a suite covers the helpers over it and what they rely on from the standard; the Web
|
|
8
|
+
Platform Tests cover the rest.
|
|
9
|
+
|
|
10
|
+
Each suite registers its tests with Vitest's `describe` and `it` when you call it, in a test file
|
|
11
|
+
of your own. It takes a name for its tests, and what it tests, made fresh for each test:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { locksSuite, processSuite, suiteScript } from '@wtf/platform-suite/suites'
|
|
15
|
+
import { memoryLocks } from '@wtf/platform/locks'
|
|
16
|
+
import { memoryPlatform } from '@wtf/platform-memory/platform'
|
|
17
|
+
|
|
18
|
+
locksSuite('memoryLocks', memoryLocks)
|
|
19
|
+
processSuite('memoryPlatform', ({ env }) => memoryPlatform({ env, spawn: suiteScript }), [
|
|
20
|
+
'program'
|
|
21
|
+
])
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
| Suite | What it tests |
|
|
25
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| `timersSuite` | Timers and `performance`: `sleep`, an aborted sleep, a cleared timer, `wallTime` |
|
|
27
|
+
| `cryptoSuite` | A `crypto`: `randomInt`, `randomFloat`, `randomBytes` beyond the quota, `randomId`, a UUID |
|
|
28
|
+
| `pathSuite` | A `Path`: every call, on many paths and on each pair, returns what `node:path` returns |
|
|
29
|
+
| `locksSuite` | A `LockManager`: exclusive, shared, `ifAvailable` and aborted requests, as the Web Locks API defines them |
|
|
30
|
+
| `broadcastChannelSuite` | A `BroadcastChannel`: a message reaches every other channel with the same name, not the sender, as a copy |
|
|
31
|
+
| `workerSuite` | A `Worker`: a message to its script and back, as a copy, and a transferred buffer |
|
|
32
|
+
| `processSuite` | A `spawn`: output, input of bytes, streams, `env`, `cwd`, a signal, `kill`, a command not there, no throw |
|
|
33
|
+
| `platformSuite` | A whole `Platform`: its members, `dirs.cwd` and `navigator.storage.getDirectory()` |
|
|
34
|
+
|
|
35
|
+
`processSuite(name, make, program)` runs a program you name, which does what its next argument
|
|
36
|
+
says (`SuiteProgram`): `cat`, `print`, `env`, `cwd` or `wait`. To test the `spawn` of a real
|
|
37
|
+
platform, write a small script that does each, and name it with what runs it, as
|
|
38
|
+
`[process.execPath, 'program.mjs']`. To test one in memory, answer it with `suiteScript`, which does
|
|
39
|
+
each as a script does.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npm install --save-dev @wtf/platform-suite vitest
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The package publishes the built `dist`, depends on `@wtf/platform`, and takes your own Vitest, 5 or
|
|
48
|
+
later, as a peer, so its tests register with the run of yours.
|
package/dist/suites.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The suites every implementation of a member of `@wtf/platform` passes, each of one member,
|
|
3
|
+
* registered in a test's own run: {@link timersSuite}, {@link cryptoSuite}, {@link pathSuite},
|
|
4
|
+
* {@link locksSuite}, {@link broadcastChannelSuite}, {@link workerSuite} and, of a platform that
|
|
5
|
+
* starts processes, {@link processSuite}; and of a platform whole, {@link platformSuite}, which
|
|
6
|
+
* each of `nodePlatform`, `browserPlatform`, `memoryPlatform` and `testPlatform` passes. Where a
|
|
7
|
+
* member is a web standard's, its suite holds our helpers over it and what they rely on of the
|
|
8
|
+
* standard; the standard's own tests, the WPT, hold the rest. The tests of each package run them
|
|
9
|
+
* against its own implementations, so Node's, a page's, one in memory and a test's double behave
|
|
10
|
+
* alike.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
import type { Platform, ProcessPlatform } from '@wtf/platform';
|
|
15
|
+
import type { BroadcastChannelConstructor } from '@wtf/platform/channel';
|
|
16
|
+
import type { Env } from '@wtf/platform/env';
|
|
17
|
+
import type { LockManager } from '@wtf/platform/locks';
|
|
18
|
+
import type { ParsedPath, Path } from '@wtf/platform/path';
|
|
19
|
+
import { type Crypto } from '@wtf/platform/random';
|
|
20
|
+
import { type Performance, type Timers } from '@wtf/platform/timers';
|
|
21
|
+
import type { WorkerConstructor } from '@wtf/platform/worker';
|
|
22
|
+
/** Timers, and the `performance` of the same platform. */
|
|
23
|
+
export type TimersOf = Timers & {
|
|
24
|
+
readonly performance: Pick<Performance, 'now' | 'timeOrigin'>;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* That timers wait, and their `performance` reads the time: `advance` moves the timers of a test
|
|
28
|
+
* on, and is none of a platform's, whose waits are short.
|
|
29
|
+
*/
|
|
30
|
+
export declare const timersSuite: (name: string, make: () => TimersOf, advance?: (timers: TimersOf, ms: number) => Promise<void>) => void;
|
|
31
|
+
/** That a `crypto` draws what our helpers ask of it, and fails as the standard says. */
|
|
32
|
+
export declare const cryptoSuite: (name: string, make: () => Crypto) => void;
|
|
33
|
+
/** What a {@link pathSuite} compares a path of, as `node:path` has it. */
|
|
34
|
+
export type ReferencePath = Pick<Path, 'normalize' | 'resolve' | 'join' | 'isAbsolute' | 'relative' | 'dirname' | 'basename' | 'extname' | 'format'> & {
|
|
35
|
+
parse(path: string): ParsedPath;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* That a path does what `reference`, `node:path`'s `posix` or `win32`, does, of a working folder
|
|
39
|
+
* `cwd`: each call of each of many paths, and of each two. Where Node's differs of the base of a
|
|
40
|
+
* root of a suffix (`//`, `C:\`), and the extension of `/..` in `parse`, the suite
|
|
41
|
+
* keeps what the rest of Node's calls say.
|
|
42
|
+
*/
|
|
43
|
+
export declare const pathSuite: (name: string, path: Path, { reference, cwd, kind }: {
|
|
44
|
+
reference: ReferencePath;
|
|
45
|
+
cwd: string;
|
|
46
|
+
kind: "posix" | "win32";
|
|
47
|
+
}) => void;
|
|
48
|
+
/** That locks are held as the Web Locks API holds them. */
|
|
49
|
+
export declare const locksSuite: (name: string, make: () => LockManager) => void;
|
|
50
|
+
/** That channels of a name give a message to each other, not to the one that posted it. */
|
|
51
|
+
export declare const broadcastChannelSuite: (name: string, BroadcastChannel: BroadcastChannelConstructor) => void;
|
|
52
|
+
/**
|
|
53
|
+
* That workers of `url`, a script that posts back each message it is given, are given and post
|
|
54
|
+
* messages as a `Worker` is: a copy of each, its transferables moved, until it is terminated.
|
|
55
|
+
*/
|
|
56
|
+
export declare const workerSuite: (name: string, Worker: WorkerConstructor, url: URL | string) => void;
|
|
57
|
+
/**
|
|
58
|
+
* That a platform is one, whole: each member there, `navigator`'s `locks` and `storage` too; the
|
|
59
|
+
* folder it works in, `dirs.cwd`, an absolute path, which its `path` resolves a relative one
|
|
60
|
+
* against; and the root of its file system, `navigator.storage.getDirectory()`, a folder's handle,
|
|
61
|
+
* the same folder each call, from which a program walks down to a path.
|
|
62
|
+
*/
|
|
63
|
+
export declare const platformSuite: (name: string, make: () => Platform) => void;
|
|
64
|
+
/**
|
|
65
|
+
* The program a {@link processSuite} runs, as its command and the arguments before its own: what it
|
|
66
|
+
* does is its next argument.
|
|
67
|
+
*
|
|
68
|
+
* - `cat`: writes what it reads to its stdout, until its stdin ends;
|
|
69
|
+
* - `print <out> <err> <code>`: writes `out` to its stdout and `err` to its stderr, and exits with
|
|
70
|
+
* `code`;
|
|
71
|
+
* - `env <name>`: writes the variable `name` of its environment, or nothing, of none;
|
|
72
|
+
* - `cwd`: writes the folder it works in;
|
|
73
|
+
* - `wait`: runs until it is ended.
|
|
74
|
+
*/
|
|
75
|
+
export type SuiteProgram = readonly [command: string, ...args: string[]];
|
|
76
|
+
/**
|
|
77
|
+
* The {@link SuiteProgram} `program`, as a script of a platform in memory answers it, of the
|
|
78
|
+
* input, the folder and the environment it is run with: a command but `program` it does not
|
|
79
|
+
* answer, as one not there. Its `wait` ends at once, as a script cannot wait; the suite ends one
|
|
80
|
+
* by its signal before its stdin ends.
|
|
81
|
+
*/
|
|
82
|
+
export declare const suiteScript: (command: string, args: readonly string[], { input, cwd, env }: {
|
|
83
|
+
readonly input: string;
|
|
84
|
+
readonly cwd: string;
|
|
85
|
+
readonly env: Readonly<Record<string, string>>;
|
|
86
|
+
}) => {
|
|
87
|
+
stdout?: string;
|
|
88
|
+
stderr?: string;
|
|
89
|
+
code?: number;
|
|
90
|
+
} | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* That a platform's `spawn` starts processes as `./process` of `@wtf/platform` has them, by
|
|
93
|
+
* `program`, a {@link SuiteProgram}: what it writes, as text, and its code, of `exec`; its input on
|
|
94
|
+
* a stdin that ends, of bytes alone; its outputs and its status as streams and a promise; the
|
|
95
|
+
* environment given, whole, or the platform's; the folder given, or the platform's working folder;
|
|
96
|
+
* a process its signal aborts ended by `SIGTERM`, and one its `kill` sends a signal ended by it; and
|
|
97
|
+
* a command not there a `SystemError` of `NotFound`, of a `spawn` that never throws. `make`
|
|
98
|
+
* gives the platform of an environment. `realpath` gives the real path of a folder, its links
|
|
99
|
+
* followed, as a process reports its working folder: `/private/var/…` of macOS's `/var/…`. A
|
|
100
|
+
* platform of no links, as one in memory, needs none.
|
|
101
|
+
*/
|
|
102
|
+
export declare const processSuite: (name: string, make: (members: {
|
|
103
|
+
readonly env: Env;
|
|
104
|
+
}) => ProcessPlatform, program: SuiteProgram, { realpath }?: {
|
|
105
|
+
readonly realpath?: (folder: string) => string;
|
|
106
|
+
}) => void;
|
package/dist/suites.js
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
import { BadArgument } from '@wtf/platform/errors';
|
|
2
|
+
import { exec } from '@wtf/platform/process';
|
|
3
|
+
import { randomBytes, randomFloat, randomId, randomInt } from '@wtf/platform/random';
|
|
4
|
+
import { sleep, wallTime } from '@wtf/platform/timers';
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
/**
|
|
7
|
+
* That timers wait, and their `performance` reads the time: `advance` moves the timers of a test
|
|
8
|
+
* on, and is none of a platform's, whose waits are short.
|
|
9
|
+
*/
|
|
10
|
+
export const timersSuite = (name, make, advance = async () => { }) => {
|
|
11
|
+
describe(`${name}, timers`, () => {
|
|
12
|
+
it('read the wall’s time of the performance, in milliseconds', () => {
|
|
13
|
+
const timers = make();
|
|
14
|
+
expect(Number.isFinite(wallTime(timers))).toBe(true);
|
|
15
|
+
expect(wallTime(timers)).toBeGreaterThanOrEqual(timers.performance.timeOrigin);
|
|
16
|
+
});
|
|
17
|
+
it('sleep, and the time has moved on by it', async () => {
|
|
18
|
+
const timers = make();
|
|
19
|
+
const before = timers.performance.now();
|
|
20
|
+
const waited = sleep(5, timers);
|
|
21
|
+
await advance(timers, 5);
|
|
22
|
+
await waited;
|
|
23
|
+
expect(timers.performance.now() - before).toBeGreaterThanOrEqual(4);
|
|
24
|
+
});
|
|
25
|
+
it('reject a sleep aborted, before or during it, with the reason', async () => {
|
|
26
|
+
const timers = make();
|
|
27
|
+
const reason = new Error('stop');
|
|
28
|
+
await expect(sleep(5, timers, AbortSignal.abort(reason))).rejects.toBe(reason);
|
|
29
|
+
const stop = new AbortController();
|
|
30
|
+
const waiting = sleep(60_000, timers, stop.signal);
|
|
31
|
+
stop.abort(reason);
|
|
32
|
+
await expect(waiting).rejects.toBe(reason);
|
|
33
|
+
});
|
|
34
|
+
it('stop a timer cleared, and clear an id of none', async () => {
|
|
35
|
+
const timers = make();
|
|
36
|
+
let ran = false;
|
|
37
|
+
const id = timers.setTimeout(() => {
|
|
38
|
+
ran = true;
|
|
39
|
+
}, 1);
|
|
40
|
+
timers.clearTimeout(id);
|
|
41
|
+
timers.clearTimeout(undefined);
|
|
42
|
+
const after = sleep(5, timers);
|
|
43
|
+
await advance(timers, 5);
|
|
44
|
+
await after;
|
|
45
|
+
expect(ran).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
/** That a `crypto` draws what our helpers ask of it, and fails as the standard says. */
|
|
50
|
+
export const cryptoSuite = (name, make) => {
|
|
51
|
+
describe(`${name}, a crypto`, () => {
|
|
52
|
+
it('draws an integer of its bound, its end not included, and a number below 1', () => {
|
|
53
|
+
const source = { crypto: make() };
|
|
54
|
+
const integers = new Set(Array.from({ length: 300 }, () => randomInt(6, source)));
|
|
55
|
+
expect([...integers].toSorted()).toEqual([0, 1, 2, 3, 4, 5]);
|
|
56
|
+
const numbers = Array.from({ length: 200 }, () => randomFloat(source));
|
|
57
|
+
expect(numbers.every((each) => each >= 0 && each < 1)).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
it('draws bytes of the length asked, past a quota of a call', () => {
|
|
60
|
+
const source = { crypto: make() };
|
|
61
|
+
expect(randomBytes(70_000, source)).toHaveLength(70_000);
|
|
62
|
+
expect(randomBytes(0, source)).toEqual(new Uint8Array());
|
|
63
|
+
});
|
|
64
|
+
it('draws an id of its alphabet and size, and a UUID of version 4', () => {
|
|
65
|
+
const crypto = make();
|
|
66
|
+
expect(randomId({ crypto })).toMatch(/^[\w-]{21}$/);
|
|
67
|
+
expect(randomId({ crypto }, { size: 8, alphabet: '01' })).toMatch(/^[01]{8}$/);
|
|
68
|
+
expect(crypto.randomUUID()).toMatch(/^[\da-f]{8}-[\da-f]{4}-4[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/);
|
|
69
|
+
});
|
|
70
|
+
it('refuses a bound, a length or an alphabet that is none, as a BadArgument', () => {
|
|
71
|
+
const source = { crypto: make() };
|
|
72
|
+
expect(() => randomInt(0, source)).toThrow(BadArgument);
|
|
73
|
+
expect(() => randomBytes(-1, source)).toThrow(BadArgument);
|
|
74
|
+
expect(() => randomId(source, { alphabet: 'a' })).toThrow(BadArgument);
|
|
75
|
+
});
|
|
76
|
+
it('refuses to fill more than a quota, as a QuotaExceededError', () => {
|
|
77
|
+
expect(() => make().getRandomValues(new Uint8Array(65_537))).toThrow(expect.objectContaining({ name: 'QuotaExceededError' }));
|
|
78
|
+
});
|
|
79
|
+
it('digests bytes by SHA-1, SHA-256, SHA-384 and SHA-512, of a name of any case', async () => {
|
|
80
|
+
const { subtle } = make();
|
|
81
|
+
const hex = async (algorithm, text) => [...new Uint8Array(await subtle.digest(algorithm, new TextEncoder().encode(text)))]
|
|
82
|
+
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
83
|
+
.join('');
|
|
84
|
+
expect(await hex('SHA-1', 'abc')).toBe('a9993e364706816aba3e25717850c26c9cd0d89d');
|
|
85
|
+
expect(await hex({ name: 'sha-256' }, '')).toBe('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
|
|
86
|
+
expect(await hex('SHA-256', 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')).toBe('248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1');
|
|
87
|
+
expect(await hex('SHA-384', 'abc')).toBe('cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7');
|
|
88
|
+
expect(await hex('SHA-512', 'abc')).toBe('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f');
|
|
89
|
+
expect(await hex('SHA-256', 'a'.repeat(1000))).toBe('41edece42d63e8d9bf515a9ba6932e1c20cbc9f5a5d134645adb5db1b9737ea3');
|
|
90
|
+
await expect(subtle.digest('MD5', new Uint8Array())).rejects.toMatchObject({
|
|
91
|
+
name: 'NotSupportedError'
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
/** The paths a {@link pathSuite} compares, as each of its kind writes them. */
|
|
97
|
+
const POSIX = [
|
|
98
|
+
'',
|
|
99
|
+
'.',
|
|
100
|
+
'..',
|
|
101
|
+
'/',
|
|
102
|
+
'//',
|
|
103
|
+
'///a',
|
|
104
|
+
'a',
|
|
105
|
+
'a/',
|
|
106
|
+
'a//b',
|
|
107
|
+
'/a//b',
|
|
108
|
+
'/a/b/',
|
|
109
|
+
'a/b/../c',
|
|
110
|
+
'../a',
|
|
111
|
+
'../../a/..',
|
|
112
|
+
'./a/./b',
|
|
113
|
+
'/..',
|
|
114
|
+
'/../a',
|
|
115
|
+
'a/..',
|
|
116
|
+
'a/../',
|
|
117
|
+
'.a',
|
|
118
|
+
'..a',
|
|
119
|
+
'a.',
|
|
120
|
+
'.a.b',
|
|
121
|
+
'a.b.c',
|
|
122
|
+
'/a/.b',
|
|
123
|
+
'a/b.c/',
|
|
124
|
+
'...',
|
|
125
|
+
'a/...',
|
|
126
|
+
'file.tar.gz',
|
|
127
|
+
'/x/.',
|
|
128
|
+
'a/./',
|
|
129
|
+
'/a/b/c/..'
|
|
130
|
+
];
|
|
131
|
+
const WIN32 = [
|
|
132
|
+
'',
|
|
133
|
+
'.',
|
|
134
|
+
'..',
|
|
135
|
+
'\\',
|
|
136
|
+
'/',
|
|
137
|
+
'C:',
|
|
138
|
+
'C:\\',
|
|
139
|
+
'C:/',
|
|
140
|
+
'c:\\a',
|
|
141
|
+
'C:a',
|
|
142
|
+
'C:..\\a',
|
|
143
|
+
'D:\\x\\y',
|
|
144
|
+
'a',
|
|
145
|
+
'a\\',
|
|
146
|
+
'a/b',
|
|
147
|
+
'a\\\\b',
|
|
148
|
+
'\\a\\b\\',
|
|
149
|
+
'/a/b/../c',
|
|
150
|
+
'..\\a',
|
|
151
|
+
'\\\\server\\share',
|
|
152
|
+
'\\\\server\\share\\',
|
|
153
|
+
'\\\\server\\share\\a\\b',
|
|
154
|
+
'//server/share/a',
|
|
155
|
+
'a\\..',
|
|
156
|
+
'a\\..\\',
|
|
157
|
+
'C:\\a\\b.txt',
|
|
158
|
+
'.bashrc',
|
|
159
|
+
'C:\\..',
|
|
160
|
+
'C:\\a\\..\\..',
|
|
161
|
+
'a/./b',
|
|
162
|
+
'C:\\a\\B'
|
|
163
|
+
];
|
|
164
|
+
/**
|
|
165
|
+
* That a path does what `reference`, `node:path`'s `posix` or `win32`, does, of a working folder
|
|
166
|
+
* `cwd`: each call of each of many paths, and of each two. Where Node's differs of the base of a
|
|
167
|
+
* root of a suffix (`//`, `C:\`), and the extension of `/..` in `parse`, the suite
|
|
168
|
+
* keeps what the rest of Node's calls say.
|
|
169
|
+
*/
|
|
170
|
+
export const pathSuite = (name, path, { reference, cwd, kind }) => {
|
|
171
|
+
const inputs = kind === 'posix' ? POSIX : WIN32;
|
|
172
|
+
describe(`${name}, a Path of ${kind}`, () => {
|
|
173
|
+
it.each(inputs)('takes %j apart as the reference does', (input) => {
|
|
174
|
+
expect(path.normalize(input)).toBe(reference.normalize(input));
|
|
175
|
+
expect(path.dirname(input)).toBe(reference.dirname(input));
|
|
176
|
+
expect(path.basename(input)).toBe(reference.basename(input));
|
|
177
|
+
// Node's gives a root as a base of a suffix, and none without one: the suite takes none.
|
|
178
|
+
if (!['//', 'C:\\', 'C:/'].includes(input))
|
|
179
|
+
expect(path.basename(input, '.c')).toBe(reference.basename(input, '.c'));
|
|
180
|
+
expect(path.extname(input)).toBe(reference.extname(input));
|
|
181
|
+
expect(path.isAbsolute(input)).toBe(reference.isAbsolute(input));
|
|
182
|
+
if (input !== '/..') {
|
|
183
|
+
expect(path.parse(input)).toEqual({ ...reference.parse(input) });
|
|
184
|
+
expect(path.format(path.parse(input))).toBe(reference.format(reference.parse(input)));
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
it.each(inputs)('joins, resolves and relates %j to each as the reference does', (input) => {
|
|
188
|
+
for (const other of inputs) {
|
|
189
|
+
expect(path.join(input, other)).toBe(reference.join(input, other));
|
|
190
|
+
expect(path.resolve(input, other)).toBe(reference.resolve(cwd, input, other));
|
|
191
|
+
expect(path.relative(input, other)).toBe(reference.relative(reference.resolve(cwd, input), reference.resolve(cwd, other)));
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
it('gives the file URL of a path, and the path of it', () => {
|
|
195
|
+
const file = kind === 'posix' ? '/a b/c#d?e%f' : 'C:\\a b\\c#d?e%f';
|
|
196
|
+
const url = path.toFileUrl(file);
|
|
197
|
+
expect(url.protocol).toBe('file:');
|
|
198
|
+
expect(url.pathname).toMatch(/a%20b\/c%23d%3Fe%25f$/);
|
|
199
|
+
expect(path.fromFileUrl(url)).toBe(file);
|
|
200
|
+
});
|
|
201
|
+
it('refuses a URL that is no file URL, as a BadArgument', () => {
|
|
202
|
+
expect(() => path.fromFileUrl('https://example.com/a')).toThrow(BadArgument);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
/** A promise, and what resolves it. */
|
|
207
|
+
const deferred = () => Promise.withResolvers();
|
|
208
|
+
/** That locks are held as the Web Locks API holds them. */
|
|
209
|
+
export const locksSuite = (name, make) => {
|
|
210
|
+
describe(`${name}, Locks`, () => {
|
|
211
|
+
it('runs a request holding the lock, and gives what its callback gave', async () => {
|
|
212
|
+
const locks = make();
|
|
213
|
+
expect(await locks.request('a', (lock) => lock && { name: lock.name, mode: lock.mode })).toEqual({
|
|
214
|
+
name: 'a',
|
|
215
|
+
mode: 'exclusive'
|
|
216
|
+
});
|
|
217
|
+
expect(await locks.request('a', async () => 3)).toBe(3);
|
|
218
|
+
});
|
|
219
|
+
it('runs exclusive requests one at a time, in order', async () => {
|
|
220
|
+
const locks = make();
|
|
221
|
+
const order = [];
|
|
222
|
+
const first = deferred();
|
|
223
|
+
const one = locks.request('suite-a', async () => {
|
|
224
|
+
order.push('one');
|
|
225
|
+
await first.promise;
|
|
226
|
+
order.push('one done');
|
|
227
|
+
});
|
|
228
|
+
const two = locks.request('suite-a', () => order.push('two'));
|
|
229
|
+
await locks.request('suite-b', () => order.push('other'));
|
|
230
|
+
expect(order).toEqual(['one', 'other']);
|
|
231
|
+
first.resolve();
|
|
232
|
+
await Promise.all([one, two]);
|
|
233
|
+
expect(order).toEqual(['one', 'other', 'one done', 'two']);
|
|
234
|
+
});
|
|
235
|
+
it('holds a shared lock with others that share it, and an exclusive one waits', async () => {
|
|
236
|
+
const locks = make();
|
|
237
|
+
const held = deferred();
|
|
238
|
+
const order = [];
|
|
239
|
+
const a = locks.request('suite-s', { mode: 'shared' }, () => held.promise.then(() => order.push('a')));
|
|
240
|
+
await locks.request('suite-s', { mode: 'shared' }, () => order.push('b'));
|
|
241
|
+
const c = locks.request('suite-s', () => order.push('c'));
|
|
242
|
+
expect(order).toEqual(['b']);
|
|
243
|
+
held.resolve();
|
|
244
|
+
await Promise.all([a, c]);
|
|
245
|
+
expect(order).toEqual(['b', 'a', 'c']);
|
|
246
|
+
});
|
|
247
|
+
it('gives null at once, if it is not available', async () => {
|
|
248
|
+
const locks = make();
|
|
249
|
+
const held = deferred();
|
|
250
|
+
const holding = locks.request('suite-i', () => held.promise);
|
|
251
|
+
expect(await locks.request('suite-i', { ifAvailable: true }, (lock) => lock)).toBeNull();
|
|
252
|
+
held.resolve();
|
|
253
|
+
await holding;
|
|
254
|
+
});
|
|
255
|
+
it('says the locks held and asked for', async () => {
|
|
256
|
+
const locks = make();
|
|
257
|
+
const held = deferred();
|
|
258
|
+
const holding = locks.request('suite-q', () => held.promise);
|
|
259
|
+
const waiting = locks.request('suite-q', { mode: 'shared' }, () => 0);
|
|
260
|
+
const { held: holders = [], pending = [] } = await locks.query();
|
|
261
|
+
expect(holders.filter((each) => each.name === 'suite-q').map(({ mode }) => mode)).toEqual([
|
|
262
|
+
'exclusive'
|
|
263
|
+
]);
|
|
264
|
+
expect(pending.filter((each) => each.name === 'suite-q').map(({ mode }) => mode)).toEqual([
|
|
265
|
+
'shared'
|
|
266
|
+
]);
|
|
267
|
+
held.resolve();
|
|
268
|
+
await Promise.all([holding, waiting]);
|
|
269
|
+
});
|
|
270
|
+
it('refuse a name that starts with -, as a NotSupportedError', async () => {
|
|
271
|
+
await expect(make().request('-reserved', () => 0)).rejects.toMatchObject({
|
|
272
|
+
name: 'NotSupportedError'
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
it('rejects a wait aborted, and releases a lock its callback threw in', async () => {
|
|
276
|
+
const locks = make();
|
|
277
|
+
const reason = new Error('stop');
|
|
278
|
+
const held = deferred();
|
|
279
|
+
const holding = locks.request('suite-r', () => held.promise);
|
|
280
|
+
const stop = new AbortController();
|
|
281
|
+
const waiting = locks.request('suite-r', { signal: stop.signal }, () => 1);
|
|
282
|
+
stop.abort(reason);
|
|
283
|
+
await expect(waiting).rejects.toBe(reason);
|
|
284
|
+
held.resolve();
|
|
285
|
+
await holding;
|
|
286
|
+
await expect(locks.request('suite-r', () => {
|
|
287
|
+
throw reason;
|
|
288
|
+
})).rejects.toBe(reason);
|
|
289
|
+
expect(await locks.request('suite-r', () => 'free')).toBe('free');
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
/** The next message of a target. */
|
|
294
|
+
const nextOf = (target) => new Promise((resolve) => target.addEventListener('message', ({ data }) => resolve(data)));
|
|
295
|
+
/** That channels of a name give a message to each other, not to the one that posted it. */
|
|
296
|
+
export const broadcastChannelSuite = (name, BroadcastChannel) => {
|
|
297
|
+
describe(`${name}, a BroadcastChannel`, () => {
|
|
298
|
+
it('gives a message to each other channel of the name, and not to itself', async () => {
|
|
299
|
+
const [a, b, c] = [
|
|
300
|
+
new BroadcastChannel('suite'),
|
|
301
|
+
new BroadcastChannel('suite'),
|
|
302
|
+
new BroadcastChannel('suite')
|
|
303
|
+
];
|
|
304
|
+
const own = [];
|
|
305
|
+
a.addEventListener('message', ({ data }) => own.push(data));
|
|
306
|
+
const [toB, toC] = [nextOf(b), nextOf(c)];
|
|
307
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin
|
|
308
|
+
a.postMessage('hello');
|
|
309
|
+
expect(await Promise.all([toB, toC])).toEqual(['hello', 'hello']);
|
|
310
|
+
expect(own).toEqual([]);
|
|
311
|
+
for (const each of [a, b, c])
|
|
312
|
+
each.close();
|
|
313
|
+
});
|
|
314
|
+
it('gives nothing to a listener removed, nor to a channel of another name', async () => {
|
|
315
|
+
const [a, b, other] = [
|
|
316
|
+
new BroadcastChannel('suite-r'),
|
|
317
|
+
new BroadcastChannel('suite-r'),
|
|
318
|
+
new BroadcastChannel('suite-other')
|
|
319
|
+
];
|
|
320
|
+
const seen = [];
|
|
321
|
+
const listener = ({ data }) => seen.push(data);
|
|
322
|
+
b.addEventListener('message', listener);
|
|
323
|
+
b.removeEventListener('message', listener);
|
|
324
|
+
other.addEventListener('message', listener);
|
|
325
|
+
const last = nextOf(b);
|
|
326
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin
|
|
327
|
+
a.postMessage('one');
|
|
328
|
+
await last;
|
|
329
|
+
expect(seen).toEqual([]);
|
|
330
|
+
for (const each of [a, b, other])
|
|
331
|
+
each.close();
|
|
332
|
+
});
|
|
333
|
+
it('gives each a copy, by structuredClone, and refuses what cannot be copied', async () => {
|
|
334
|
+
const [a, b] = [new BroadcastChannel('suite-c'), new BroadcastChannel('suite-c')];
|
|
335
|
+
const sent = { list: [1, 2], when: new Date(0) };
|
|
336
|
+
const received = nextOf(b);
|
|
337
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin
|
|
338
|
+
a.postMessage(sent);
|
|
339
|
+
const copy = await received;
|
|
340
|
+
expect(copy).toEqual(sent);
|
|
341
|
+
expect(copy).not.toBe(sent);
|
|
342
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin
|
|
343
|
+
expect(() => a.postMessage(() => { })).toThrow(expect.objectContaining({ name: 'DataCloneError' }));
|
|
344
|
+
for (const each of [a, b])
|
|
345
|
+
each.close();
|
|
346
|
+
});
|
|
347
|
+
it('refuses a post to a channel closed, as an InvalidStateError', () => {
|
|
348
|
+
const a = new BroadcastChannel('suite-closed');
|
|
349
|
+
a.close();
|
|
350
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin
|
|
351
|
+
expect(() => a.postMessage('late')).toThrow(expect.objectContaining({ name: 'InvalidStateError' }));
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
/**
|
|
356
|
+
* That workers of `url`, a script that posts back each message it is given, are given and post
|
|
357
|
+
* messages as a `Worker` is: a copy of each, its transferables moved, until it is terminated.
|
|
358
|
+
*/
|
|
359
|
+
export const workerSuite = (name, Worker, url) => {
|
|
360
|
+
describe(`${name}, a Worker`, () => {
|
|
361
|
+
it('gives the script a copy of a message, and is given what it posts', async () => {
|
|
362
|
+
const worker = new Worker(url);
|
|
363
|
+
const sent = { list: [1, 2] };
|
|
364
|
+
const echoed = nextOf(worker);
|
|
365
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- a worker's post takes no origin
|
|
366
|
+
worker.postMessage(sent);
|
|
367
|
+
expect(await echoed).toEqual(sent);
|
|
368
|
+
worker.terminate();
|
|
369
|
+
});
|
|
370
|
+
it('moves what is transferred', async () => {
|
|
371
|
+
const worker = new Worker(url);
|
|
372
|
+
const buffer = new Uint8Array([1, 2, 3]).buffer;
|
|
373
|
+
const echoed = nextOf(worker);
|
|
374
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- a worker's post takes no origin
|
|
375
|
+
worker.postMessage(buffer, [buffer]);
|
|
376
|
+
expect(buffer.byteLength).toBe(0);
|
|
377
|
+
expect(new Uint8Array((await echoed))).toEqual(new Uint8Array([1, 2, 3]));
|
|
378
|
+
worker.terminate();
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
/** The members of a {@link Platform}, of the web standards and our own. */
|
|
383
|
+
const MEMBERS = [
|
|
384
|
+
'setTimeout',
|
|
385
|
+
'clearTimeout',
|
|
386
|
+
'performance',
|
|
387
|
+
'crypto',
|
|
388
|
+
'console',
|
|
389
|
+
'navigator',
|
|
390
|
+
'BroadcastChannel',
|
|
391
|
+
'MessageChannel',
|
|
392
|
+
'Worker',
|
|
393
|
+
'fetch',
|
|
394
|
+
'env',
|
|
395
|
+
'path',
|
|
396
|
+
'dirs',
|
|
397
|
+
'terminal',
|
|
398
|
+
'open'
|
|
399
|
+
];
|
|
400
|
+
/**
|
|
401
|
+
* That a platform is one, whole: each member there, `navigator`'s `locks` and `storage` too; the
|
|
402
|
+
* folder it works in, `dirs.cwd`, an absolute path, which its `path` resolves a relative one
|
|
403
|
+
* against; and the root of its file system, `navigator.storage.getDirectory()`, a folder's handle,
|
|
404
|
+
* the same folder each call, from which a program walks down to a path.
|
|
405
|
+
*/
|
|
406
|
+
export const platformSuite = (name, make) => {
|
|
407
|
+
describe(`${name}, a Platform`, () => {
|
|
408
|
+
it('has each member', () => {
|
|
409
|
+
const platform = make();
|
|
410
|
+
for (const member of MEMBERS)
|
|
411
|
+
expect(platform[member], member).toBeDefined();
|
|
412
|
+
expect(typeof platform.navigator.locks.request).toBe('function');
|
|
413
|
+
expect(typeof platform.navigator.storage.getDirectory).toBe('function');
|
|
414
|
+
});
|
|
415
|
+
it('works in the folder of an absolute path, which its paths resolve against', () => {
|
|
416
|
+
const { dirs, path } = make();
|
|
417
|
+
expect(path.isAbsolute(dirs.cwd)).toBe(true);
|
|
418
|
+
expect(path.resolve('a', 'b')).toBe(path.join(dirs.cwd, 'a', 'b'));
|
|
419
|
+
});
|
|
420
|
+
it('gives the root of its file system, a folder, the same each call', async () => {
|
|
421
|
+
const { storage } = make().navigator;
|
|
422
|
+
const root = await storage.getDirectory();
|
|
423
|
+
expect(root.kind).toBe('directory');
|
|
424
|
+
expect(await root.isSameEntry(await storage.getDirectory())).toBe(true);
|
|
425
|
+
expect(await root.resolve(root)).toEqual([]);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
};
|
|
429
|
+
/**
|
|
430
|
+
* The {@link SuiteProgram} `program`, as a script of a platform in memory answers it, of the
|
|
431
|
+
* input, the folder and the environment it is run with: a command but `program` it does not
|
|
432
|
+
* answer, as one not there. Its `wait` ends at once, as a script cannot wait; the suite ends one
|
|
433
|
+
* by its signal before its stdin ends.
|
|
434
|
+
*/
|
|
435
|
+
export const suiteScript = (command, args, { input, cwd, env }) => {
|
|
436
|
+
if (command !== 'program')
|
|
437
|
+
return undefined;
|
|
438
|
+
const [mode, first = '', second = '', third = '0'] = args;
|
|
439
|
+
if (mode === 'cat')
|
|
440
|
+
return { stdout: input };
|
|
441
|
+
if (mode === 'print')
|
|
442
|
+
return { stdout: first, stderr: second, code: Number(third) };
|
|
443
|
+
if (mode === 'env')
|
|
444
|
+
return { stdout: env[first] ?? '' };
|
|
445
|
+
if (mode === 'cwd')
|
|
446
|
+
return { stdout: cwd };
|
|
447
|
+
return {};
|
|
448
|
+
};
|
|
449
|
+
/** The text of a stream of bytes, to its end. */
|
|
450
|
+
const textOf = async (stream) => {
|
|
451
|
+
const reader = stream.getReader();
|
|
452
|
+
const decoder = new TextDecoder();
|
|
453
|
+
let text = '';
|
|
454
|
+
for (;;) {
|
|
455
|
+
// oxlint-disable-next-line no-await-in-loop -- a stream is read one chunk at a time
|
|
456
|
+
const { done, value } = await reader.read();
|
|
457
|
+
if (done)
|
|
458
|
+
return text + decoder.decode();
|
|
459
|
+
text += decoder.decode(value, { stream: true });
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
/**
|
|
463
|
+
* That a platform's `spawn` starts processes as `./process` of `@wtf/platform` has them, by
|
|
464
|
+
* `program`, a {@link SuiteProgram}: what it writes, as text, and its code, of `exec`; its input on
|
|
465
|
+
* a stdin that ends, of bytes alone; its outputs and its status as streams and a promise; the
|
|
466
|
+
* environment given, whole, or the platform's; the folder given, or the platform's working folder;
|
|
467
|
+
* a process its signal aborts ended by `SIGTERM`, and one its `kill` sends a signal ended by it; and
|
|
468
|
+
* a command not there a `SystemError` of `NotFound`, of a `spawn` that never throws. `make`
|
|
469
|
+
* gives the platform of an environment. `realpath` gives the real path of a folder, its links
|
|
470
|
+
* followed, as a process reports its working folder: `/private/var/…` of macOS's `/var/…`. A
|
|
471
|
+
* platform of no links, as one in memory, needs none.
|
|
472
|
+
*/
|
|
473
|
+
export const processSuite = (name, make, program, { realpath = (folder) => folder } = {}) => {
|
|
474
|
+
const [command, ...before] = program;
|
|
475
|
+
const run = (platform, args, options) => exec(platform.spawn, command, [...before, ...args], options);
|
|
476
|
+
describe(`${name}, processes`, () => {
|
|
477
|
+
it('runs a program to its end, and gives what it wrote, as text, and its code', async () => {
|
|
478
|
+
expect(await run(make({ env: {} }), ['print', 'out ü', 'err', '3'])).toEqual({
|
|
479
|
+
stdout: 'out ü',
|
|
480
|
+
stderr: 'err',
|
|
481
|
+
code: 3
|
|
482
|
+
});
|
|
483
|
+
expect(await run(make({ env: {} }), ['print', '', '', '0'])).toEqual({
|
|
484
|
+
stdout: '',
|
|
485
|
+
stderr: '',
|
|
486
|
+
code: 0
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
it('gives a program its input, and ends its stdin', async () => {
|
|
490
|
+
const platform = make({ env: {} });
|
|
491
|
+
expect((await run(platform, ['cat'], { input: 'one\ntwo ü' })).stdout).toBe('one\ntwo ü');
|
|
492
|
+
expect((await run(platform, ['cat'], { input: new Uint8Array([104, 105]) })).stdout).toBe('hi');
|
|
493
|
+
expect((await run(platform, ['cat'])).stdout).toBe('');
|
|
494
|
+
});
|
|
495
|
+
it('streams what a process reads and writes, and resolves how it ended', async () => {
|
|
496
|
+
const child = make({ env: {} }).spawn(command, [...before, 'cat']);
|
|
497
|
+
const writer = child.stdin.getWriter();
|
|
498
|
+
await writer.write(new TextEncoder().encode('streamed, '));
|
|
499
|
+
await writer.write(new TextEncoder().encode('in parts'));
|
|
500
|
+
await writer.close();
|
|
501
|
+
const [stdout, stderr, status] = await Promise.all([
|
|
502
|
+
textOf(child.stdout),
|
|
503
|
+
textOf(child.stderr),
|
|
504
|
+
child.status
|
|
505
|
+
]);
|
|
506
|
+
expect({ stdout, stderr, status }).toEqual({
|
|
507
|
+
stdout: 'streamed, in parts',
|
|
508
|
+
stderr: '',
|
|
509
|
+
status: { code: 0 }
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
it('gives a process the environment given, whole, or else the platform’s', async () => {
|
|
513
|
+
const platform = make({ env: { SUITE_PLATFORM: 'platform', SUITE_UNSET: undefined } });
|
|
514
|
+
expect((await run(platform, ['env', 'SUITE_PLATFORM'])).stdout).toBe('platform');
|
|
515
|
+
expect((await run(platform, ['env', 'SUITE_UNSET'])).stdout).toBe('');
|
|
516
|
+
const env = { SUITE_GIVEN: 'given' };
|
|
517
|
+
expect((await run(platform, ['env', 'SUITE_GIVEN'], { env })).stdout).toBe('given');
|
|
518
|
+
expect((await run(platform, ['env', 'SUITE_PLATFORM'], { env })).stdout).toBe('');
|
|
519
|
+
});
|
|
520
|
+
it('runs a process in the folder given, or else the platform’s working folder', async () => {
|
|
521
|
+
const platform = make({ env: {} });
|
|
522
|
+
expect((await run(platform, ['cwd'])).stdout).toBe(realpath(platform.dirs.cwd));
|
|
523
|
+
const cwd = platform.dirs.temp;
|
|
524
|
+
expect((await run(platform, ['cwd'], { cwd })).stdout).toBe(realpath(cwd));
|
|
525
|
+
});
|
|
526
|
+
it('ends a process its signal aborts, by SIGTERM, of the code a shell gives it', async () => {
|
|
527
|
+
const stop = new AbortController();
|
|
528
|
+
const child = make({ env: {} }).spawn(command, [...before, 'wait'], { signal: stop.signal });
|
|
529
|
+
stop.abort();
|
|
530
|
+
const [status] = await Promise.all([child.status, textOf(child.stdout), textOf(child.stderr)]);
|
|
531
|
+
expect(status).toEqual({ code: 143, signal: 'SIGTERM' });
|
|
532
|
+
const reason = new Error('stop');
|
|
533
|
+
await expect(run(make({ env: {} }), ['cat'], { signal: AbortSignal.abort(reason) })).rejects.toBe(reason);
|
|
534
|
+
});
|
|
535
|
+
it('sends a process a signal by kill, SIGTERM by default, and the status says it', async () => {
|
|
536
|
+
const platform = make({ env: {} });
|
|
537
|
+
const ended = async (signal) => {
|
|
538
|
+
const child = platform.spawn(command, [...before, 'wait']);
|
|
539
|
+
child.kill(signal);
|
|
540
|
+
const [status] = await Promise.all([
|
|
541
|
+
child.status,
|
|
542
|
+
textOf(child.stdout),
|
|
543
|
+
textOf(child.stderr)
|
|
544
|
+
]);
|
|
545
|
+
return status;
|
|
546
|
+
};
|
|
547
|
+
expect(await ended('SIGKILL')).toEqual({ code: 137, signal: 'SIGKILL' });
|
|
548
|
+
expect(await ended()).toEqual({ code: 143, signal: 'SIGTERM' });
|
|
549
|
+
});
|
|
550
|
+
it('sends a process that has ended nothing, and refuses a name of no signal', async () => {
|
|
551
|
+
const child = make({ env: {} }).spawn(command, [...before, 'print', 'out', '', '2']);
|
|
552
|
+
await child.stdin.close();
|
|
553
|
+
const [status] = await Promise.all([child.status, textOf(child.stdout), textOf(child.stderr)]);
|
|
554
|
+
child.kill('SIGKILL');
|
|
555
|
+
expect(await child.status).toEqual(status);
|
|
556
|
+
expect(status).toEqual({ code: 2 });
|
|
557
|
+
expect(() => child.kill('SIGNOPE')).toThrow(BadArgument);
|
|
558
|
+
});
|
|
559
|
+
it('refuses a chunk of stdin that is no bytes, and ends the process’s input', async () => {
|
|
560
|
+
const child = make({ env: {} }).spawn(command, [...before, 'cat']);
|
|
561
|
+
const writer = child.stdin.getWriter();
|
|
562
|
+
await writer.write(new TextEncoder().encode('before '));
|
|
563
|
+
await expect(writer.write('text')).rejects.toThrow(TypeError);
|
|
564
|
+
await expect(writer.closed).rejects.toThrow(TypeError);
|
|
565
|
+
const [stdout, status] = await Promise.all([
|
|
566
|
+
textOf(child.stdout),
|
|
567
|
+
child.status,
|
|
568
|
+
textOf(child.stderr)
|
|
569
|
+
]);
|
|
570
|
+
expect({ stdout, status }).toEqual({ stdout: 'before ', status: { code: 0 } });
|
|
571
|
+
});
|
|
572
|
+
it('rejects a command not there, as a SystemError of NotFound', async () => {
|
|
573
|
+
await expect(exec(make({ env: {} }).spawn, 'platform-suite-no-such-command', [])).rejects.toMatchObject({ _tag: 'SystemError', reason: 'NotFound', module: 'Process' });
|
|
574
|
+
});
|
|
575
|
+
it('gives a process that could not start, and never throws: its outputs end', async () => {
|
|
576
|
+
const child = make({ env: {} }).spawn('platform-suite-no-such-command', []);
|
|
577
|
+
// Its input ends, as exec ends it: of a process that never started, it may refuse to.
|
|
578
|
+
await child.stdin.close().catch(() => { });
|
|
579
|
+
expect(await textOf(child.stdout)).toBe('');
|
|
580
|
+
expect(await textOf(child.stderr)).toBe('');
|
|
581
|
+
await expect(child.status).rejects.toMatchObject({ _tag: 'SystemError', reason: 'NotFound' });
|
|
582
|
+
expect(() => child.kill()).not.toThrow();
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
};
|
|
586
|
+
//# sourceMappingURL=suites.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suites.js","sourceRoot":"","sources":["../src/suites.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The suites every implementation of a member of `@wtf/platform` passes, each of one member,\n * registered in a test's own run: {@link timersSuite}, {@link cryptoSuite}, {@link pathSuite},\n * {@link locksSuite}, {@link broadcastChannelSuite}, {@link workerSuite} and, of a platform that\n * starts processes, {@link processSuite}; and of a platform whole, {@link platformSuite}, which\n * each of `nodePlatform`, `browserPlatform`, `memoryPlatform` and `testPlatform` passes. Where a\n * member is a web standard's, its suite holds our helpers over it and what they rely on of the\n * standard; the standard's own tests, the WPT, hold the rest. The tests of each package run them\n * against its own implementations, so Node's, a page's, one in memory and a test's double behave\n * alike.\n *\n * @packageDocumentation\n */\nimport type { Platform, ProcessPlatform } from '@wtf/platform'\nimport type { BroadcastChannelConstructor } from '@wtf/platform/channel'\nimport type { Env } from '@wtf/platform/env'\nimport { BadArgument } from '@wtf/platform/errors'\nimport type { LockManager } from '@wtf/platform/locks'\nimport type { ParsedPath, Path } from '@wtf/platform/path'\nimport { exec, type ProcessSignal } from '@wtf/platform/process'\nimport { type Crypto, randomBytes, randomFloat, randomId, randomInt } from '@wtf/platform/random'\nimport { type Performance, sleep, type Timers, wallTime } from '@wtf/platform/timers'\nimport type { MessageEvent, WorkerConstructor } from '@wtf/platform/worker'\nimport { describe, expect, it } from 'vitest'\n\n/** Timers, and the `performance` of the same platform. */\nexport type TimersOf = Timers & { readonly performance: Pick<Performance, 'now' | 'timeOrigin'> }\n\n/**\n * That timers wait, and their `performance` reads the time: `advance` moves the timers of a test\n * on, and is none of a platform's, whose waits are short.\n */\nexport const timersSuite = (\n name: string,\n make: () => TimersOf,\n advance: (timers: TimersOf, ms: number) => Promise<void> = async () => {}\n): void => {\n describe(`${name}, timers`, () => {\n it('read the wall’s time of the performance, in milliseconds', () => {\n const timers = make()\n expect(Number.isFinite(wallTime(timers))).toBe(true)\n expect(wallTime(timers)).toBeGreaterThanOrEqual(timers.performance.timeOrigin)\n })\n\n it('sleep, and the time has moved on by it', async () => {\n const timers = make()\n const before = timers.performance.now()\n const waited = sleep(5, timers)\n await advance(timers, 5)\n await waited\n expect(timers.performance.now() - before).toBeGreaterThanOrEqual(4)\n })\n\n it('reject a sleep aborted, before or during it, with the reason', async () => {\n const timers = make()\n const reason = new Error('stop')\n await expect(sleep(5, timers, AbortSignal.abort(reason))).rejects.toBe(reason)\n const stop = new AbortController()\n const waiting = sleep(60_000, timers, stop.signal)\n stop.abort(reason)\n await expect(waiting).rejects.toBe(reason)\n })\n\n it('stop a timer cleared, and clear an id of none', async () => {\n const timers = make()\n let ran = false\n const id = timers.setTimeout(() => {\n ran = true\n }, 1)\n timers.clearTimeout(id)\n timers.clearTimeout(undefined)\n const after = sleep(5, timers)\n await advance(timers, 5)\n await after\n expect(ran).toBe(false)\n })\n })\n}\n\n/** That a `crypto` draws what our helpers ask of it, and fails as the standard says. */\nexport const cryptoSuite = (name: string, make: () => Crypto): void => {\n describe(`${name}, a crypto`, () => {\n it('draws an integer of its bound, its end not included, and a number below 1', () => {\n const source = { crypto: make() }\n const integers = new Set(Array.from({ length: 300 }, () => randomInt(6, source)))\n expect([...integers].toSorted()).toEqual([0, 1, 2, 3, 4, 5])\n const numbers = Array.from({ length: 200 }, () => randomFloat(source))\n expect(numbers.every((each) => each >= 0 && each < 1)).toBe(true)\n })\n\n it('draws bytes of the length asked, past a quota of a call', () => {\n const source = { crypto: make() }\n expect(randomBytes(70_000, source)).toHaveLength(70_000)\n expect(randomBytes(0, source)).toEqual(new Uint8Array())\n })\n\n it('draws an id of its alphabet and size, and a UUID of version 4', () => {\n const crypto = make()\n expect(randomId({ crypto })).toMatch(/^[\\w-]{21}$/)\n expect(randomId({ crypto }, { size: 8, alphabet: '01' })).toMatch(/^[01]{8}$/)\n expect(crypto.randomUUID()).toMatch(\n /^[\\da-f]{8}-[\\da-f]{4}-4[\\da-f]{3}-[89ab][\\da-f]{3}-[\\da-f]{12}$/\n )\n })\n\n it('refuses a bound, a length or an alphabet that is none, as a BadArgument', () => {\n const source = { crypto: make() }\n expect(() => randomInt(0, source)).toThrow(BadArgument)\n expect(() => randomBytes(-1, source)).toThrow(BadArgument)\n expect(() => randomId(source, { alphabet: 'a' })).toThrow(BadArgument)\n })\n\n it('refuses to fill more than a quota, as a QuotaExceededError', () => {\n expect(() => make().getRandomValues(new Uint8Array(65_537))).toThrow(\n expect.objectContaining({ name: 'QuotaExceededError' })\n )\n })\n\n it('digests bytes by SHA-1, SHA-256, SHA-384 and SHA-512, of a name of any case', async () => {\n const { subtle } = make()\n const hex = async (algorithm: string | { name: string }, text: string) =>\n [...new Uint8Array(await subtle.digest(algorithm, new TextEncoder().encode(text)))]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n expect(await hex('SHA-1', 'abc')).toBe('a9993e364706816aba3e25717850c26c9cd0d89d')\n expect(await hex({ name: 'sha-256' }, '')).toBe(\n 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'\n )\n expect(await hex('SHA-256', 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')).toBe(\n '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1'\n )\n expect(await hex('SHA-384', 'abc')).toBe(\n 'cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7'\n )\n expect(await hex('SHA-512', 'abc')).toBe(\n 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f'\n )\n expect(await hex('SHA-256', 'a'.repeat(1000))).toBe(\n '41edece42d63e8d9bf515a9ba6932e1c20cbc9f5a5d134645adb5db1b9737ea3'\n )\n await expect(subtle.digest('MD5', new Uint8Array())).rejects.toMatchObject({\n name: 'NotSupportedError'\n })\n })\n })\n}\n\n/** The paths a {@link pathSuite} compares, as each of its kind writes them. */\nconst POSIX = [\n '',\n '.',\n '..',\n '/',\n '//',\n '///a',\n 'a',\n 'a/',\n 'a//b',\n '/a//b',\n '/a/b/',\n 'a/b/../c',\n '../a',\n '../../a/..',\n './a/./b',\n '/..',\n '/../a',\n 'a/..',\n 'a/../',\n '.a',\n '..a',\n 'a.',\n '.a.b',\n 'a.b.c',\n '/a/.b',\n 'a/b.c/',\n '...',\n 'a/...',\n 'file.tar.gz',\n '/x/.',\n 'a/./',\n '/a/b/c/..'\n]\nconst WIN32 = [\n '',\n '.',\n '..',\n '\\\\',\n '/',\n 'C:',\n 'C:\\\\',\n 'C:/',\n 'c:\\\\a',\n 'C:a',\n 'C:..\\\\a',\n 'D:\\\\x\\\\y',\n 'a',\n 'a\\\\',\n 'a/b',\n 'a\\\\\\\\b',\n '\\\\a\\\\b\\\\',\n '/a/b/../c',\n '..\\\\a',\n '\\\\\\\\server\\\\share',\n '\\\\\\\\server\\\\share\\\\',\n '\\\\\\\\server\\\\share\\\\a\\\\b',\n '//server/share/a',\n 'a\\\\..',\n 'a\\\\..\\\\',\n 'C:\\\\a\\\\b.txt',\n '.bashrc',\n 'C:\\\\..',\n 'C:\\\\a\\\\..\\\\..',\n 'a/./b',\n 'C:\\\\a\\\\B'\n]\n\n/** What a {@link pathSuite} compares a path of, as `node:path` has it. */\nexport type ReferencePath = Pick<\n Path,\n | 'normalize'\n | 'resolve'\n | 'join'\n | 'isAbsolute'\n | 'relative'\n | 'dirname'\n | 'basename'\n | 'extname'\n | 'format'\n> & { parse(path: string): ParsedPath }\n\n/**\n * That a path does what `reference`, `node:path`'s `posix` or `win32`, does, of a working folder\n * `cwd`: each call of each of many paths, and of each two. Where Node's differs of the base of a\n * root of a suffix (`//`, `C:\\`), and the extension of `/..` in `parse`, the suite\n * keeps what the rest of Node's calls say.\n */\nexport const pathSuite = (\n name: string,\n path: Path,\n { reference, cwd, kind }: { reference: ReferencePath; cwd: string; kind: 'posix' | 'win32' }\n): void => {\n const inputs = kind === 'posix' ? POSIX : WIN32\n describe(`${name}, a Path of ${kind}`, () => {\n it.each(inputs)('takes %j apart as the reference does', (input) => {\n expect(path.normalize(input)).toBe(reference.normalize(input))\n expect(path.dirname(input)).toBe(reference.dirname(input))\n expect(path.basename(input)).toBe(reference.basename(input))\n // Node's gives a root as a base of a suffix, and none without one: the suite takes none.\n if (!['//', 'C:\\\\', 'C:/'].includes(input))\n expect(path.basename(input, '.c')).toBe(reference.basename(input, '.c'))\n expect(path.extname(input)).toBe(reference.extname(input))\n expect(path.isAbsolute(input)).toBe(reference.isAbsolute(input))\n if (input !== '/..') {\n expect(path.parse(input)).toEqual({ ...reference.parse(input) })\n expect(path.format(path.parse(input))).toBe(reference.format(reference.parse(input)))\n }\n })\n\n it.each(inputs)('joins, resolves and relates %j to each as the reference does', (input) => {\n for (const other of inputs) {\n expect(path.join(input, other)).toBe(reference.join(input, other))\n expect(path.resolve(input, other)).toBe(reference.resolve(cwd, input, other))\n expect(path.relative(input, other)).toBe(\n reference.relative(reference.resolve(cwd, input), reference.resolve(cwd, other))\n )\n }\n })\n\n it('gives the file URL of a path, and the path of it', () => {\n const file = kind === 'posix' ? '/a b/c#d?e%f' : 'C:\\\\a b\\\\c#d?e%f'\n const url = path.toFileUrl(file)\n expect(url.protocol).toBe('file:')\n expect(url.pathname).toMatch(/a%20b\\/c%23d%3Fe%25f$/)\n expect(path.fromFileUrl(url)).toBe(file)\n })\n\n it('refuses a URL that is no file URL, as a BadArgument', () => {\n expect(() => path.fromFileUrl('https://example.com/a')).toThrow(BadArgument)\n })\n })\n}\n\n/** A promise, and what resolves it. */\nconst deferred = () => Promise.withResolvers<void>()\n\n/** That locks are held as the Web Locks API holds them. */\nexport const locksSuite = (name: string, make: () => LockManager): void => {\n describe(`${name}, Locks`, () => {\n it('runs a request holding the lock, and gives what its callback gave', async () => {\n const locks = make()\n expect(\n await locks.request('a', (lock) => lock && { name: lock.name, mode: lock.mode })\n ).toEqual({\n name: 'a',\n mode: 'exclusive'\n })\n expect(await locks.request('a', async () => 3)).toBe(3)\n })\n\n it('runs exclusive requests one at a time, in order', async () => {\n const locks = make()\n const order: string[] = []\n const first = deferred()\n const one = locks.request('suite-a', async () => {\n order.push('one')\n await first.promise\n order.push('one done')\n })\n const two = locks.request('suite-a', () => order.push('two'))\n await locks.request('suite-b', () => order.push('other'))\n expect(order).toEqual(['one', 'other'])\n first.resolve()\n await Promise.all([one, two])\n expect(order).toEqual(['one', 'other', 'one done', 'two'])\n })\n\n it('holds a shared lock with others that share it, and an exclusive one waits', async () => {\n const locks = make()\n const held = deferred()\n const order: string[] = []\n const a = locks.request('suite-s', { mode: 'shared' }, () =>\n held.promise.then(() => order.push('a'))\n )\n await locks.request('suite-s', { mode: 'shared' }, () => order.push('b'))\n const c = locks.request('suite-s', () => order.push('c'))\n expect(order).toEqual(['b'])\n held.resolve()\n await Promise.all([a, c])\n expect(order).toEqual(['b', 'a', 'c'])\n })\n\n it('gives null at once, if it is not available', async () => {\n const locks = make()\n const held = deferred()\n const holding = locks.request('suite-i', () => held.promise)\n expect(await locks.request('suite-i', { ifAvailable: true }, (lock) => lock)).toBeNull()\n held.resolve()\n await holding\n })\n\n it('says the locks held and asked for', async () => {\n const locks = make()\n const held = deferred()\n const holding = locks.request('suite-q', () => held.promise)\n const waiting = locks.request('suite-q', { mode: 'shared' }, () => 0)\n const { held: holders = [], pending = [] } = await locks.query()\n expect(holders.filter((each) => each.name === 'suite-q').map(({ mode }) => mode)).toEqual([\n 'exclusive'\n ])\n expect(pending.filter((each) => each.name === 'suite-q').map(({ mode }) => mode)).toEqual([\n 'shared'\n ])\n held.resolve()\n await Promise.all([holding, waiting])\n })\n\n it('refuse a name that starts with -, as a NotSupportedError', async () => {\n await expect(make().request('-reserved', () => 0)).rejects.toMatchObject({\n name: 'NotSupportedError'\n })\n })\n\n it('rejects a wait aborted, and releases a lock its callback threw in', async () => {\n const locks = make()\n const reason = new Error('stop')\n const held = deferred()\n const holding = locks.request('suite-r', () => held.promise)\n const stop = new AbortController()\n const waiting = locks.request('suite-r', { signal: stop.signal }, () => 1)\n stop.abort(reason)\n await expect(waiting).rejects.toBe(reason)\n held.resolve()\n await holding\n await expect(\n locks.request('suite-r', () => {\n throw reason\n })\n ).rejects.toBe(reason)\n expect(await locks.request('suite-r', () => 'free')).toBe('free')\n })\n })\n}\n\n/** The next message of a target. */\nconst nextOf = (target: {\n addEventListener(type: 'message', listener: (event: MessageEvent) => void): void\n}) =>\n new Promise<unknown>((resolve) => target.addEventListener('message', ({ data }) => resolve(data)))\n\n/** That channels of a name give a message to each other, not to the one that posted it. */\nexport const broadcastChannelSuite = (\n name: string,\n BroadcastChannel: BroadcastChannelConstructor\n): void => {\n describe(`${name}, a BroadcastChannel`, () => {\n it('gives a message to each other channel of the name, and not to itself', async () => {\n const [a, b, c] = [\n new BroadcastChannel('suite'),\n new BroadcastChannel('suite'),\n new BroadcastChannel('suite')\n ]\n const own: unknown[] = []\n a.addEventListener('message', ({ data }) => own.push(data))\n const [toB, toC] = [nextOf(b), nextOf(c)]\n // oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin\n a.postMessage('hello')\n expect(await Promise.all([toB, toC])).toEqual(['hello', 'hello'])\n expect(own).toEqual([])\n for (const each of [a, b, c]) each.close()\n })\n\n it('gives nothing to a listener removed, nor to a channel of another name', async () => {\n const [a, b, other] = [\n new BroadcastChannel('suite-r'),\n new BroadcastChannel('suite-r'),\n new BroadcastChannel('suite-other')\n ]\n const seen: unknown[] = []\n const listener = ({ data }: MessageEvent) => seen.push(data)\n b.addEventListener('message', listener)\n b.removeEventListener('message', listener)\n other.addEventListener('message', listener)\n const last = nextOf(b)\n // oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin\n a.postMessage('one')\n await last\n expect(seen).toEqual([])\n for (const each of [a, b, other]) each.close()\n })\n\n it('gives each a copy, by structuredClone, and refuses what cannot be copied', async () => {\n const [a, b] = [new BroadcastChannel('suite-c'), new BroadcastChannel('suite-c')]\n const sent = { list: [1, 2], when: new Date(0) }\n const received = nextOf(b)\n // oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin\n a.postMessage(sent)\n const copy = await received\n expect(copy).toEqual(sent)\n expect(copy).not.toBe(sent)\n // oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin\n expect(() => a.postMessage(() => {})).toThrow(\n expect.objectContaining({ name: 'DataCloneError' })\n )\n for (const each of [a, b]) each.close()\n })\n\n it('refuses a post to a channel closed, as an InvalidStateError', () => {\n const a = new BroadcastChannel('suite-closed')\n a.close()\n // oxlint-disable-next-line unicorn/require-post-message-target-origin -- a channel's post takes no origin\n expect(() => a.postMessage('late')).toThrow(\n expect.objectContaining({ name: 'InvalidStateError' })\n )\n })\n })\n}\n\n/**\n * That workers of `url`, a script that posts back each message it is given, are given and post\n * messages as a `Worker` is: a copy of each, its transferables moved, until it is terminated.\n */\nexport const workerSuite = (name: string, Worker: WorkerConstructor, url: URL | string): void => {\n describe(`${name}, a Worker`, () => {\n it('gives the script a copy of a message, and is given what it posts', async () => {\n const worker = new Worker(url)\n const sent = { list: [1, 2] }\n const echoed = nextOf(worker)\n // oxlint-disable-next-line unicorn/require-post-message-target-origin -- a worker's post takes no origin\n worker.postMessage(sent)\n expect(await echoed).toEqual(sent)\n worker.terminate()\n })\n\n it('moves what is transferred', async () => {\n const worker = new Worker(url)\n const buffer = new Uint8Array([1, 2, 3]).buffer\n const echoed = nextOf(worker)\n // oxlint-disable-next-line unicorn/require-post-message-target-origin -- a worker's post takes no origin\n worker.postMessage(buffer, [buffer])\n expect(buffer.byteLength).toBe(0)\n expect(new Uint8Array((await echoed) as ArrayBuffer)).toEqual(new Uint8Array([1, 2, 3]))\n worker.terminate()\n })\n })\n}\n\n/** The members of a {@link Platform}, of the web standards and our own. */\nconst MEMBERS = [\n 'setTimeout',\n 'clearTimeout',\n 'performance',\n 'crypto',\n 'console',\n 'navigator',\n 'BroadcastChannel',\n 'MessageChannel',\n 'Worker',\n 'fetch',\n 'env',\n 'path',\n 'dirs',\n 'terminal',\n 'open'\n] as const satisfies readonly (keyof Platform)[]\n\n/**\n * That a platform is one, whole: each member there, `navigator`'s `locks` and `storage` too; the\n * folder it works in, `dirs.cwd`, an absolute path, which its `path` resolves a relative one\n * against; and the root of its file system, `navigator.storage.getDirectory()`, a folder's handle,\n * the same folder each call, from which a program walks down to a path.\n */\nexport const platformSuite = (name: string, make: () => Platform): void => {\n describe(`${name}, a Platform`, () => {\n it('has each member', () => {\n const platform = make()\n for (const member of MEMBERS) expect(platform[member], member).toBeDefined()\n expect(typeof platform.navigator.locks.request).toBe('function')\n expect(typeof platform.navigator.storage.getDirectory).toBe('function')\n })\n\n it('works in the folder of an absolute path, which its paths resolve against', () => {\n const { dirs, path } = make()\n expect(path.isAbsolute(dirs.cwd)).toBe(true)\n expect(path.resolve('a', 'b')).toBe(path.join(dirs.cwd, 'a', 'b'))\n })\n\n it('gives the root of its file system, a folder, the same each call', async () => {\n const { storage } = make().navigator\n const root = await storage.getDirectory()\n expect(root.kind).toBe('directory')\n expect(await root.isSameEntry(await storage.getDirectory())).toBe(true)\n expect(await root.resolve(root)).toEqual([])\n })\n })\n}\n\n/**\n * The program a {@link processSuite} runs, as its command and the arguments before its own: what it\n * does is its next argument.\n *\n * - `cat`: writes what it reads to its stdout, until its stdin ends;\n * - `print <out> <err> <code>`: writes `out` to its stdout and `err` to its stderr, and exits with\n * `code`;\n * - `env <name>`: writes the variable `name` of its environment, or nothing, of none;\n * - `cwd`: writes the folder it works in;\n * - `wait`: runs until it is ended.\n */\nexport type SuiteProgram = readonly [command: string, ...args: string[]]\n\n/**\n * The {@link SuiteProgram} `program`, as a script of a platform in memory answers it, of the\n * input, the folder and the environment it is run with: a command but `program` it does not\n * answer, as one not there. Its `wait` ends at once, as a script cannot wait; the suite ends one\n * by its signal before its stdin ends.\n */\nexport const suiteScript = (\n command: string,\n args: readonly string[],\n {\n input,\n cwd,\n env\n }: {\n readonly input: string\n readonly cwd: string\n readonly env: Readonly<Record<string, string>>\n }\n): { stdout?: string; stderr?: string; code?: number } | undefined => {\n if (command !== 'program') return undefined\n const [mode, first = '', second = '', third = '0'] = args\n if (mode === 'cat') return { stdout: input }\n if (mode === 'print') return { stdout: first, stderr: second, code: Number(third) }\n if (mode === 'env') return { stdout: env[first] ?? '' }\n if (mode === 'cwd') return { stdout: cwd }\n return {}\n}\n\n/** The text of a stream of bytes, to its end. */\nconst textOf = async (stream: ReadableStream<Uint8Array>): Promise<string> => {\n const reader = stream.getReader()\n const decoder = new TextDecoder()\n let text = ''\n for (;;) {\n // oxlint-disable-next-line no-await-in-loop -- a stream is read one chunk at a time\n const { done, value } = await reader.read()\n if (done) return text + decoder.decode()\n text += decoder.decode(value, { stream: true })\n }\n}\n\n/**\n * That a platform's `spawn` starts processes as `./process` of `@wtf/platform` has them, by\n * `program`, a {@link SuiteProgram}: what it writes, as text, and its code, of `exec`; its input on\n * a stdin that ends, of bytes alone; its outputs and its status as streams and a promise; the\n * environment given, whole, or the platform's; the folder given, or the platform's working folder;\n * a process its signal aborts ended by `SIGTERM`, and one its `kill` sends a signal ended by it; and\n * a command not there a `SystemError` of `NotFound`, of a `spawn` that never throws. `make`\n * gives the platform of an environment. `realpath` gives the real path of a folder, its links\n * followed, as a process reports its working folder: `/private/var/…` of macOS's `/var/…`. A\n * platform of no links, as one in memory, needs none.\n */\nexport const processSuite = (\n name: string,\n make: (members: { readonly env: Env }) => ProcessPlatform,\n program: SuiteProgram,\n { realpath = (folder: string) => folder }: { readonly realpath?: (folder: string) => string } = {}\n): void => {\n const [command, ...before] = program\n const run = (\n platform: ProcessPlatform,\n args: readonly string[],\n options?: Parameters<typeof exec>[3]\n ) => exec(platform.spawn, command, [...before, ...args], options)\n\n describe(`${name}, processes`, () => {\n it('runs a program to its end, and gives what it wrote, as text, and its code', async () => {\n expect(await run(make({ env: {} }), ['print', 'out ü', 'err', '3'])).toEqual({\n stdout: 'out ü',\n stderr: 'err',\n code: 3\n })\n expect(await run(make({ env: {} }), ['print', '', '', '0'])).toEqual({\n stdout: '',\n stderr: '',\n code: 0\n })\n })\n\n it('gives a program its input, and ends its stdin', async () => {\n const platform = make({ env: {} })\n expect((await run(platform, ['cat'], { input: 'one\\ntwo ü' })).stdout).toBe('one\\ntwo ü')\n expect((await run(platform, ['cat'], { input: new Uint8Array([104, 105]) })).stdout).toBe(\n 'hi'\n )\n expect((await run(platform, ['cat'])).stdout).toBe('')\n })\n\n it('streams what a process reads and writes, and resolves how it ended', async () => {\n const child = make({ env: {} }).spawn(command, [...before, 'cat'])\n const writer = child.stdin.getWriter()\n await writer.write(new TextEncoder().encode('streamed, '))\n await writer.write(new TextEncoder().encode('in parts'))\n await writer.close()\n const [stdout, stderr, status] = await Promise.all([\n textOf(child.stdout),\n textOf(child.stderr),\n child.status\n ])\n expect({ stdout, stderr, status }).toEqual({\n stdout: 'streamed, in parts',\n stderr: '',\n status: { code: 0 }\n })\n })\n\n it('gives a process the environment given, whole, or else the platform’s', async () => {\n const platform = make({ env: { SUITE_PLATFORM: 'platform', SUITE_UNSET: undefined } })\n expect((await run(platform, ['env', 'SUITE_PLATFORM'])).stdout).toBe('platform')\n expect((await run(platform, ['env', 'SUITE_UNSET'])).stdout).toBe('')\n const env = { SUITE_GIVEN: 'given' }\n expect((await run(platform, ['env', 'SUITE_GIVEN'], { env })).stdout).toBe('given')\n expect((await run(platform, ['env', 'SUITE_PLATFORM'], { env })).stdout).toBe('')\n })\n\n it('runs a process in the folder given, or else the platform’s working folder', async () => {\n const platform = make({ env: {} })\n expect((await run(platform, ['cwd'])).stdout).toBe(realpath(platform.dirs.cwd))\n const cwd = platform.dirs.temp\n expect((await run(platform, ['cwd'], { cwd })).stdout).toBe(realpath(cwd))\n })\n\n it('ends a process its signal aborts, by SIGTERM, of the code a shell gives it', async () => {\n const stop = new AbortController()\n const child = make({ env: {} }).spawn(command, [...before, 'wait'], { signal: stop.signal })\n stop.abort()\n const [status] = await Promise.all([child.status, textOf(child.stdout), textOf(child.stderr)])\n expect(status).toEqual({ code: 143, signal: 'SIGTERM' })\n const reason = new Error('stop')\n await expect(\n run(make({ env: {} }), ['cat'], { signal: AbortSignal.abort(reason) })\n ).rejects.toBe(reason)\n })\n\n it('sends a process a signal by kill, SIGTERM by default, and the status says it', async () => {\n const platform = make({ env: {} })\n const ended = async (signal?: ProcessSignal) => {\n const child = platform.spawn(command, [...before, 'wait'])\n child.kill(signal)\n const [status] = await Promise.all([\n child.status,\n textOf(child.stdout),\n textOf(child.stderr)\n ])\n return status\n }\n expect(await ended('SIGKILL')).toEqual({ code: 137, signal: 'SIGKILL' })\n expect(await ended()).toEqual({ code: 143, signal: 'SIGTERM' })\n })\n\n it('sends a process that has ended nothing, and refuses a name of no signal', async () => {\n const child = make({ env: {} }).spawn(command, [...before, 'print', 'out', '', '2'])\n await child.stdin.close()\n const [status] = await Promise.all([child.status, textOf(child.stdout), textOf(child.stderr)])\n child.kill('SIGKILL')\n expect(await child.status).toEqual(status)\n expect(status).toEqual({ code: 2 })\n expect(() => child.kill('SIGNOPE' as ProcessSignal)).toThrow(BadArgument)\n })\n\n it('refuses a chunk of stdin that is no bytes, and ends the process’s input', async () => {\n const child = make({ env: {} }).spawn(command, [...before, 'cat'])\n const writer = child.stdin.getWriter()\n await writer.write(new TextEncoder().encode('before '))\n await expect(writer.write('text' as unknown as Uint8Array)).rejects.toThrow(TypeError)\n await expect(writer.closed).rejects.toThrow(TypeError)\n const [stdout, status] = await Promise.all([\n textOf(child.stdout),\n child.status,\n textOf(child.stderr)\n ])\n expect({ stdout, status }).toEqual({ stdout: 'before ', status: { code: 0 } })\n })\n\n it('rejects a command not there, as a SystemError of NotFound', async () => {\n await expect(\n exec(make({ env: {} }).spawn, 'platform-suite-no-such-command', [])\n ).rejects.toMatchObject({ _tag: 'SystemError', reason: 'NotFound', module: 'Process' })\n })\n\n it('gives a process that could not start, and never throws: its outputs end', async () => {\n const child = make({ env: {} }).spawn('platform-suite-no-such-command', [])\n // Its input ends, as exec ends it: of a process that never started, it may refuse to.\n await child.stdin.close().catch(() => {})\n expect(await textOf(child.stdout)).toBe('')\n expect(await textOf(child.stderr)).toBe('')\n await expect(child.status).rejects.toMatchObject({ _tag: 'SystemError', reason: 'NotFound' })\n expect(() => child.kill()).not.toThrow()\n })\n })\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wtf/platform-suite",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "The test suites every implementation of a @wtf/platform member passes, for Vitest: hold your own timers, crypto, path, locks, channels, workers, spawn or whole platform to them, so it behaves as Node's, a page's, the memory platform and the test doubles do.",
|
|
5
|
+
"homepage": "https://gitlab.com/wetryfun/platform/platform/-/tree/main/packages/platform-suite",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://gitlab.com/wetryfun/platform/platform/-/issues"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"author": "Mikael Karon <mikael@karon.se>",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://gitlab.com/wetryfun/platform/platform.git",
|
|
14
|
+
"directory": "packages/platform-suite"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
"./suites": {
|
|
22
|
+
"types": "./dist/suites.d.ts",
|
|
23
|
+
"default": "./dist/suites.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -b"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@wtf/platform": "2.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"vitest": "5.1.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"vitest": "^5.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|