vitest-pool-assemblyscript 0.2.4 → 0.3.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/README.md +111 -192
- package/assembly/compare.ts +37 -7
- package/assembly/expect.ts +56 -18
- package/dist/{compile-runner-C9TJrfCl.mjs → compile-runner-C2eh_xLp.mjs} +2 -2
- package/dist/{compile-runner-C9TJrfCl.mjs.map → compile-runner-C2eh_xLp.mjs.map} +1 -1
- package/dist/{load-user-imports-CcIdE4_0.mjs → load-user-imports-eGZuxeNp.mjs} +6 -6
- package/dist/load-user-imports-eGZuxeNp.mjs.map +1 -0
- package/dist/pool-thread/compile-worker-thread.mjs +2 -2
- package/dist/pool-thread/test-worker-thread.mjs +2 -2
- package/dist/pool-thread/v3-tinypool-thread.mjs +3 -3
- package/dist/{test-runner-DZd3SxEc.mjs → test-runner-vGpTcXsw.mjs} +2 -2
- package/dist/{test-runner-DZd3SxEc.mjs.map → test-runner-vGpTcXsw.mjs.map} +1 -1
- package/package.json +1 -1
- package/dist/load-user-imports-CcIdE4_0.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,26 +1,52 @@
|
|
|
1
1
|
# vitest-pool-assemblyscript
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="docs/images/as-icon.svg" height="50" align="middle">
|
|
5
|
+
➕
|
|
6
|
+
<img src="docs/images/vitest-dark.svg" height="30" align="middle">
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
AssemblyScript unit testing for your Vitest workflow: Simple, fast, familiar, AS-native.
|
|
11
|
+
<br/>
|
|
12
|
+
<br/>
|
|
13
|
+
<a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat"/></a>
|
|
14
|
+
<a href="https://github.com/themattspiral/vitest-pool-assemblyscript/actions/workflows/release.yml"><img alt="Release Pipeline" src="https://github.com/themattspiral/vitest-pool-assemblyscript/actions/workflows/release.yml/badge.svg"/></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/vitest-pool-assemblyscript"><img alt="npm package version" src="https://img.shields.io/npm/v/vitest-pool-assemblyscript.svg?style=flat&logo=npm"/></a>
|
|
16
|
+
</p>
|
|
4
17
|
|
|
5
|
-
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<br/>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
This <a href="https://vitest.dev/guide/advanced/pool.html">custom pool</a> plugs into <a href="https://vitest.dev">Vitest</a>, giving it the ability to compile AssemblyScript, run isolated WASM tests, and report with Vitest's reporters. It co-exists alongside your existing JavaScript/TypeScript tests and coverage reports, and is designed for simple incremental adoption.
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<a href="#status">Status</a> | <a href="#quick-start">Quick Start</a> | <a href="#features">Features</a> | <a href="#compatibility">Compatibility</a> | <a href="#performance">Performance</a> | <a href="#prior-work">Prior Work</a> | <a href="#license">License</a>
|
|
28
|
+
<br/>
|
|
29
|
+
<a href="#writing-tests">Writing Tests</a> | <a href="docs/matchers-api.md">Matchers API</a> | <a href="docs/configuration-guide.md">Configuration Guide</a> | <a href="docs/providing-wasm-imports.md">Providing WASM Imports</a>
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
<br/>
|
|
35
|
+
<p align="center">
|
|
36
|
+
<img src="docs/images/example-small.gif" alt="vitest-pool-assemblyscript demo">
|
|
37
|
+
</p>
|
|
6
38
|
|
|
7
|
-
|
|
8
|
-
- [Compatibility](#compatibility)
|
|
9
|
-
- [Features](#features)
|
|
10
|
-
- [Configuration Guide](docs/configuration-guide.md)
|
|
11
|
-
- [Providing WASM Imports](docs/providing-wasm-imports.md)
|
|
12
|
-
- [Writing Tests Guide](#writing-tests-guide)
|
|
13
|
-
- [Matcher API](#matcher-api)
|
|
14
|
-
- [Project Status & Expectations](#project-status--expectations)
|
|
15
|
-
- [License](#license)
|
|
39
|
+
## Status
|
|
16
40
|
|
|
17
|
-
|
|
18
|
-
- All features listed in the [Features](#features) section are stable and assumed to be bug-free
|
|
19
|
-
- Native instrumentation prebuilds are available cross-platform
|
|
20
|
-
- Expect matchers are stable (except where noted below), with more coming soon
|
|
21
|
-
- See [Project Status & Expectations](#project-status--expectations) to see what's still planned!
|
|
41
|
+
This project is relatively new to the scene, but is being improved every day. Please give it a try!
|
|
22
42
|
|
|
23
|
-
|
|
43
|
+
- All [listed features](#features) are working and assumed to be bug-free ([report](https://github.com/themattspiral/vitest-pool-assemblyscript/issues/new))
|
|
44
|
+
- [`describe()` and `test()` definition APIs](#writing-tests) are stable and not expected to change
|
|
45
|
+
- [`expect()` matchers API](docs/matchers-api.md) is stable and not expected to change. More are coming soon
|
|
46
|
+
- Native instrumentation prebuilds are available [across many platforms](#compatibility)
|
|
47
|
+
- See [Current Limitations & Roadmap](#current-limitations--roadmap) for important limitations to be aware of, and to see what's still planned.
|
|
48
|
+
|
|
49
|
+
Please [report a bug or request a feature](https://github.com/themattspiral/vitest-pool-assemblyscript/issues/new) if you have something you'd like to share.
|
|
24
50
|
|
|
25
51
|
---
|
|
26
52
|
|
|
@@ -95,25 +121,6 @@ npx vitest run
|
|
|
95
121
|
|
|
96
122
|
---
|
|
97
123
|
|
|
98
|
-
## Compatibility
|
|
99
|
-
|
|
100
|
-
| Dependency | Supported Versions |
|
|
101
|
-
|---|---|
|
|
102
|
-
| Node.js | 20, 22, 24+ |
|
|
103
|
-
| Vitest | 3.2.x, 4.x |
|
|
104
|
-
| AssemblyScript | 0.28+ |
|
|
105
|
-
|
|
106
|
-
**Platforms with prebuilt native binaries:**
|
|
107
|
-
|
|
108
|
-
| | x64 | arm64 |
|
|
109
|
-
|---|---|---|
|
|
110
|
-
| Linux (glibc) | ✓ | ✓ |
|
|
111
|
-
| Linux (musl/Alpine) | ✓ | |
|
|
112
|
-
| macOS | ✓ | ✓ |
|
|
113
|
-
| Windows | ✓ | ✓ |
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
124
|
## Features
|
|
118
125
|
|
|
119
126
|
### Vitest Integration
|
|
@@ -133,33 +140,53 @@ npx vitest run
|
|
|
133
140
|
### Familiar Developer Experience
|
|
134
141
|
- Suite and test definition using `describe()` and `test()` in AssemblyScript
|
|
135
142
|
- Inline test option configuration for common vitest options: `timeout`, `retry`, `skip`, `only`, `fails`
|
|
136
|
-
- Assertion matching API based on vitest/jest `expect()` API
|
|
143
|
+
- Assertion matching API based on vitest/jest `expect()` API
|
|
144
|
+
- `.not`, `toBe`, `toBeCloseTo`, `toEqual` (with caveats*), `toStrictEqual`, `toHaveLength`, `toThrowError`, `toBeTruthy`, `toBeFalsy`, `toBeNull`, `toBeNullable`, `toBeNaN` - See [Matchers API](docs/matchers-api.md) for details and differences from JavaScript
|
|
137
145
|
- Highlighted diffs for assertion and runtime failures, which point to source code
|
|
138
|
-
- Source-mapped WASM error stack traces (accurate source `function file:line:column`)
|
|
146
|
+
- Source-mapped WASM error stack traces (accurate AssemblyScript source `function file:line:column`)
|
|
139
147
|
- AssemblyScript console output captured and provided to vitest for display
|
|
140
|
-
-
|
|
148
|
+
- AssemblyScript compiler errors output plainly to the console for debugging
|
|
149
|
+
- AssemblyScript source code coverage based on WASM execution, including uncovered source
|
|
150
|
+
- No AssemblyScript boilerplate patterns for: `run()`, `endTest()`, `fs.readFile`, `WebAssembly.Instance`, etc
|
|
141
151
|
|
|
142
152
|
### Performance & Customization
|
|
143
|
-
- Parallel execution thread
|
|
144
|
-
- Lightweight coverage instrumentation using separate memory
|
|
153
|
+
- Parallel execution thread pools
|
|
145
154
|
- In-memory binaries and source maps for minimal file I/O
|
|
155
|
+
- Lightweight coverage instrumentation using separate WASM memory (no intermediate JS boundary crossing, no user memory conflicts)
|
|
146
156
|
- Coverage for inlined (`@inline`) code
|
|
147
157
|
- Enforced hard timeouts for long-running WASM via thread termination, with intelligent resume
|
|
148
158
|
- Configurable AssemblyScript compiler options
|
|
149
159
|
- Configurable test memory size
|
|
150
|
-
-
|
|
160
|
+
- User-provided WASM imports with access to test memory
|
|
151
161
|
|
|
152
162
|
---
|
|
153
163
|
|
|
154
|
-
|
|
164
|
+
## Compatibility
|
|
165
|
+
|
|
166
|
+
| Dependency | Supported Versions |
|
|
167
|
+
|---|---|
|
|
168
|
+
| Node.js | 20, 22, 24+ |
|
|
169
|
+
| Vitest | 3.2.x, 4.x |
|
|
170
|
+
| AssemblyScript | 0.28+ |
|
|
171
|
+
|
|
172
|
+
**Platforms with prebuilt native binaries:**
|
|
173
|
+
|
|
174
|
+
| | x64 | arm64 |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| Linux (glibc) | ✓ | ✓ |
|
|
177
|
+
| Linux (musl/Alpine) | ✓ | |
|
|
178
|
+
| macOS | ✓ | ✓ |
|
|
179
|
+
| Windows | ✓ | ✓ |
|
|
155
180
|
|
|
156
181
|
---
|
|
157
182
|
|
|
158
|
-
## Writing Tests
|
|
183
|
+
## Writing Tests
|
|
159
184
|
|
|
160
|
-
Import `test`, `describe`, `expect`
|
|
185
|
+
Import `test`, `describe`, `expect` from `vitest-pool-assemblyscript/assembly`. These functions are designed to follow the vitest API as closely as possible, so that everything is familiar and easy to reason about.
|
|
161
186
|
|
|
162
|
-
`it` is available as an alias for `test
|
|
187
|
+
- `it` is available as an alias for `test`
|
|
188
|
+
- `describe` and `test` have inline modifiers to quickly change their status (see below)
|
|
189
|
+
- `TestOptions` allows per-test inline configuration of additional options
|
|
163
190
|
|
|
164
191
|
```typescript
|
|
165
192
|
import { test, it, describe, expect, TestOptions } from "vitest-pool-assemblyscript/assembly";
|
|
@@ -183,7 +210,7 @@ describe("a suite of math operations", () => {
|
|
|
183
210
|
});
|
|
184
211
|
```
|
|
185
212
|
|
|
186
|
-
### Modifiers: `.skip`, `.only`, `.fails`
|
|
213
|
+
### Inline Modifiers: `.skip`, `.only`, `.fails`
|
|
187
214
|
|
|
188
215
|
```typescript
|
|
189
216
|
test.skip("not ready yet", () => { /* ... */ });
|
|
@@ -201,7 +228,9 @@ describe.only("only this suite runs", () => { /* ... */ });
|
|
|
201
228
|
|
|
202
229
|
### Inline Test Options
|
|
203
230
|
|
|
204
|
-
`TestOptions` provides chainable configuration for `timeout`, `retry`, `skip`, `only`, and `fails`.
|
|
231
|
+
`TestOptions` provides chainable configuration for `timeout`, `retry`, `skip`, `only`, and `fails`.
|
|
232
|
+
- Options can be placed before or after the callback
|
|
233
|
+
- Suite options are inherited by nested tests and suites
|
|
205
234
|
|
|
206
235
|
```typescript
|
|
207
236
|
// options before callback
|
|
@@ -227,152 +256,23 @@ test.fails("expected failure with retry", TestOptions.retry(3), () => {
|
|
|
227
256
|
});
|
|
228
257
|
```
|
|
229
258
|
|
|
259
|
+
See the [Matchers API documentation](docs/matchers-api.md) for details on the available `expect()` methods you can use within your tests.
|
|
260
|
+
|
|
230
261
|
### Lifecycle Hooks (Setup & Teardown)
|
|
231
262
|
|
|
232
263
|
Coming Soon!
|
|
233
264
|
|
|
234
265
|
---
|
|
235
266
|
|
|
236
|
-
##
|
|
237
|
-
|
|
238
|
-
This project aims to follow the [vitest/jest `expect()` API](https://vitest.dev/api/expect.html) as closely as possible, with some necessary differences given AssemblyScript's static-typing.
|
|
239
|
-
|
|
240
|
-
The following subset of vitest/jest expect matchers are currently supported:
|
|
241
|
-
|
|
242
|
-
### `.not`
|
|
243
|
-
Inverts the matcher that follows. Can be chained.
|
|
244
|
-
```typescript
|
|
245
|
-
expect(1).not.toBe(2);
|
|
246
|
-
expect("hello").not.toBeNull();
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### `toBe()`
|
|
250
|
-
Checks that a value is what you expect. Primitives and strings are compared directly, and references are checked for reference equality only (including objects and arrays). These comparisons are done using `==`, although it is forgiving of numeric type differences.
|
|
251
|
-
|
|
252
|
-
Don't use `toBe` with floating-point numbers - see `toBeCloseTo()` instead.
|
|
253
|
-
```typescript
|
|
254
|
-
expect(1 + 1).toBe(2);
|
|
255
|
-
expect("hello").toBe("hello");
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
> ⚠️ IMPORTANT: this matcher is currently too permissive with type comparison - it deems some numeric values of different types to be the same when they shouldn't actually be. The `toEqual()` matcher is more appropriate for these permissive sementics. Fix coming soon!
|
|
267
|
+
## Current Limitations & Roadmap
|
|
259
268
|
|
|
260
|
-
###
|
|
261
|
-
Checks if a floating point value is close to what you expect. Using exact equality with floating point numbers often doesn't work correctly, because small internal rounding occurs to be able to represent floats in binary. This rounding means intuitive comparisons will often fail.
|
|
269
|
+
### Limitations
|
|
262
270
|
|
|
263
|
-
|
|
271
|
+
These are known limitations which are currently being worked on.
|
|
264
272
|
|
|
265
|
-
Accepts an additional `precision: i32` argument - Number of decimal places that must match for values to be considered close. Defaults to 2 digits, meaning effectively that values must be within 0.005 of each other.
|
|
266
|
-
```typescript
|
|
267
|
-
expect(0.1 + 0.2).toBeCloseTo(0.3);
|
|
268
|
-
expect(1.005).toBeCloseTo(1.0, 1);
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
### `toEqual()`
|
|
272
|
-
Checks that two values have the same value (deep equality). Currently supports checking equality of Arrays, Sets, Maps, and nulls. Values inside arrays are compared using `toEqual()` also, while Maps and Sets use their respective rules for membership.
|
|
273
|
-
|
|
274
|
-
Primitives, strings, and other object references are compared with `toBe()` rules.
|
|
275
|
-
|
|
276
|
-
> ⚠️ IMPORTANT: Does not yet support user-defined object deep equality checking. Coming soon!
|
|
277
|
-
|
|
278
|
-
```typescript
|
|
279
|
-
expect([1, 2, 3]).toEqual([1, 2, 3]);
|
|
280
|
-
expect(["one", "two", "three"]).toEqual(["one", "two", "three"]);
|
|
281
|
-
|
|
282
|
-
// objects use reference equality (deep equality not yet supported)
|
|
283
|
-
const a: MyObject = new MyObject();
|
|
284
|
-
const b: MyObject = new MyObject();
|
|
285
|
-
expect([a, b]).toEqual([a, b]);
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### `toStrictEqual()`
|
|
289
|
-
Alias for `toEqual()`. Currently no differences in AssemblyScript.
|
|
290
|
-
|
|
291
|
-
### `toBeTruthy()` & `toBeFalsey()`
|
|
292
|
-
Check that a value is truthy or falsey. Falsey values are `0`, `false`, `""`, and `null`.
|
|
293
|
-
|
|
294
|
-
```typescript
|
|
295
|
-
expect(1).toBeTruthy();
|
|
296
|
-
expect("hello").toBeTruthy();
|
|
297
|
-
expect("").toBeTruthy(); // <-- Empty String is TRUTHY in AS!
|
|
298
|
-
|
|
299
|
-
expect(0).toBeFalsey();
|
|
300
|
-
expect(NaN).toBeFalsey();
|
|
301
|
-
expect(null).toBeFalsey();
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
>⚠️ AS Quirk: Unlike in JavaScript, empty string is truthy in AssemblyScript because it is an object reference, not a primitive. An empty string is still an allocated object with a non-zero address, so it evaluates as truthy!
|
|
305
|
-
|
|
306
|
-
### `toBeNull()`
|
|
307
|
-
Checks that a value is null (`usize(0)` in AssemblyScript).
|
|
308
|
-
|
|
309
|
-
```typescript
|
|
310
|
-
const val: string | null = null;
|
|
311
|
-
expect(val).toBeNull();
|
|
312
|
-
expect("hello").not.toBeNull();
|
|
313
|
-
expect(0).not.toBeNull();
|
|
314
|
-
expect(false).not.toBeNull();
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### `toBeNullable()`
|
|
318
|
-
Checks that the type of the value is nullable (can hold `null`). This is a type-level check, not a value check — use `toBeNull()` to check if a value *is* null.
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
const val: string | null = null;
|
|
322
|
-
expect(val).toBeNullable();
|
|
323
|
-
expect("hello").not.toBeNullable();
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### `toBeNaN()`
|
|
327
|
-
Checks that a floating point value is `NaN`.
|
|
328
|
-
|
|
329
|
-
```typescript
|
|
330
|
-
expect(NaN).toBeNaN();
|
|
331
|
-
expect(1.0).not.toBeNaN();
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### `toHaveLength()`
|
|
335
|
-
Checks that an array or array-like value has the expected length.
|
|
336
|
-
|
|
337
|
-
```typescript
|
|
338
|
-
expect([1, 2, 3]).toHaveLength(3);
|
|
339
|
-
expect([]).toHaveLength(0);
|
|
340
|
-
expect("hello world").toHaveLength(11);
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
### `toThrowError()`
|
|
344
|
-
Checks that a function throws an error when called. Optionally checks that the error message matches the provided string. Also available as `toThrow()`.
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
expect(() => { throw new Error("boom"); }).toThrowError();
|
|
348
|
-
expect(() => { throw new Error("boom"); }).toThrowError("boom");
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
>⚠️ AS Quirk: You must provide a callback with a non-inferred type to expect() when using `toThrowError()`, e.g. `expect(() => { returnsNumber(); })` (definitely void) or `expect((): i32 => returnsNumber())` (explicit type)
|
|
352
|
-
|
|
353
|
-
>ℹ️ Note: `toThrowError()` does not accept inversion using `expect().not.toThrowError()`
|
|
354
|
-
|
|
355
|
-
### Planned Matchers
|
|
356
|
-
`toBeDefined`, `toBeUndefined`, `toBeGreaterThan`, `toBeGreaterThanOrEqual`, `toBeLessThan`, `toBeLessThanOrEqual`, `toContain`, `toContainEqual`
|
|
357
|
-
|
|
358
|
-
### Likely Matchers
|
|
359
|
-
`toBeOneOf`, `toBeTypeOf`, `toBeInstanceOf`, `toHaveProperty`, `toMatch`
|
|
360
|
-
|
|
361
|
-
---
|
|
362
|
-
|
|
363
|
-
## Project Status & Expectations
|
|
364
|
-
|
|
365
|
-
**This is an early-stage project** being developed in the open by an interested individual with a career of experience shipping production code.
|
|
366
|
-
- All features listed in the [Features](#features) section are stable and assumed to be bug-free
|
|
367
|
-
- Native instrumentation prebuilds are available cross-platform
|
|
368
|
-
- Expect matchers are stable (except where noted above), with more coming soon
|
|
369
|
-
|
|
370
|
-
Please [report a bug / request a feature](https://github.com/themattspiral/vitest-pool-assemblyscript/issues/new) if you encounter something you'd like to share!
|
|
371
|
-
|
|
372
|
-
**⚠️ Known Limitations - Coming Soon:**
|
|
373
273
|
- **Function-level coverage only**: No statement, branch, or line coverage yet
|
|
374
274
|
- **No lifecycle hooks**: No setup/teardown hooks yet
|
|
375
|
-
- **Watch mode specs only**: Re-runs test files when they are directly changed, but not yet based on changed source files
|
|
275
|
+
- **Watch mode handles specs only**: Re-runs test files when they are directly changed, but not yet based on changed source files
|
|
376
276
|
- **`toEqual` doesn't reflect**: Doesn't yet support deep inspection of user-defined objects
|
|
377
277
|
|
|
378
278
|
### Near Future Roadmap
|
|
@@ -384,18 +284,18 @@ Please [report a bug / request a feature](https://github.com/themattspiral/vites
|
|
|
384
284
|
|
|
385
285
|
**Epic: Testing DX**
|
|
386
286
|
- Lifecycle hooks (`beforeEach`, `afterEach`, `beforeAll`, `afterAll`)
|
|
387
|
-
- Watch mode
|
|
388
|
-
- `toEqual` reflection
|
|
389
|
-
- expect.soft
|
|
390
|
-
- Allow delegating JS/TS to istanbul coverage provider
|
|
391
|
-
- Per-file compilation setting override
|
|
287
|
+
- Watch mode: re-run applicable tests on source file changes
|
|
288
|
+
- `toEqual` reflection for deep equality inspection of user objects
|
|
289
|
+
- expect.soft to prevent fail-fast behavior
|
|
290
|
+
- Allow delegating JS/TS to istanbul coverage provider in addition to v8
|
|
291
|
+
- Maybe: Per-file compilation setting override
|
|
392
292
|
|
|
393
293
|
**Epic: Expand expect matcher API**
|
|
394
294
|
- Planned: `toBeDefined`, `toBeUndefined`, `toBeGreaterThan`, `toBeGreaterThanOrEqual`, `toBeLessThan`, `toBeLessThanOrEqual`, `toContain`, `toContainEqual`
|
|
395
|
-
-
|
|
295
|
+
- Likely: `toBeOneOf`, `toBeTypeOf`, `toBeInstanceOf`, `toHaveProperty`, `toMatch`
|
|
396
296
|
|
|
397
297
|
**Epic: Spy and Mock**
|
|
398
|
-
-
|
|
298
|
+
- Intend to support
|
|
399
299
|
|
|
400
300
|
**✖️ Out of Scope (Currently):**
|
|
401
301
|
- Generic JS-harness testing of any precompiled WASM binary
|
|
@@ -406,11 +306,30 @@ Please [report a bug / request a feature](https://github.com/themattspiral/vites
|
|
|
406
306
|
|
|
407
307
|
---
|
|
408
308
|
|
|
309
|
+
## Performance
|
|
310
|
+
|
|
311
|
+
Efforts have been made to compile and run tests as quickly as possible:
|
|
312
|
+
- Separate compile and test execution threads for quicker startup and respawn
|
|
313
|
+
- Compile threads tuned to take advantage of significant Node V8 engine warmup time savings on consecutive AssemblyScript compilations
|
|
314
|
+
- In-memory compiled files and source maps to eliminate intermediate disk I/O
|
|
315
|
+
- Enforced hard timeouts for long-running WASM via thread termination, with intelligent resume
|
|
316
|
+
|
|
317
|
+
As such, it is capable of compiling dozens of test files comprising hundreds of tests in a few seconds.
|
|
318
|
+
|
|
319
|
+
<p align="center">
|
|
320
|
+
<img src="docs/images/example-fixtures-suite.gif" alt="vitest-pool-assemblyscript demo">
|
|
321
|
+
</p>
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
409
325
|
## Prior Work
|
|
410
326
|
|
|
411
|
-
There are
|
|
327
|
+
There are several core pieces of software without which this project would not be possible.
|
|
412
328
|
|
|
413
|
-
|
|
329
|
+
- This project makes direct use of the [AssemblyScript language](https://www.assemblyscript.org) and its fantastic [compiler](https://www.assemblyscript.org/compiler.html)
|
|
330
|
+
- Thanks to the [Vitest team](https://github.com/vitest-dev) for creating the framework in the first place and making it pluggable for different runtimes. Their internal pools were used as extensive reference in early phase development.
|
|
331
|
+
- The key component that allows us to perform WASM instrumentation is [Binaryen](https://github.com/WebAssembly/binaryen), a C++ toolchain infrastructure library for WebAssembly.
|
|
332
|
+
- Particular gratitude is owed to [assemblyscript-unittest-framework](https://github.com/wasm-ecosystem/assemblyscript-unittest-framework) for inspiring parts of our test discovery and instrumentation walking approaches.
|
|
414
333
|
|
|
415
334
|
---
|
|
416
335
|
|
package/assembly/compare.ts
CHANGED
|
@@ -90,7 +90,6 @@ export function identical<T, U>(actual: T, expected: U): bool {
|
|
|
90
90
|
if ( (isBoolean<T>() && !isBoolean<U>()) || (!isBoolean<T>() && isBoolean<U>())
|
|
91
91
|
) {
|
|
92
92
|
// when one is boolean and the other is not, they are not identical
|
|
93
|
-
// use toEqual for this comparison
|
|
94
93
|
return false;
|
|
95
94
|
} else if (isInteger<T>() && isInteger<U>()) {
|
|
96
95
|
if (isSigned<T>() && isSigned<U>()) {
|
|
@@ -111,6 +110,28 @@ export function identical<T, U>(actual: T, expected: U): bool {
|
|
|
111
110
|
} else if (isFloat<T>() && isFloat<U>()) {
|
|
112
111
|
return f64(actual) === f64(expected);
|
|
113
112
|
} else if ( (isFloat<T>() && isInteger<U>()) || (isInteger<T>() && isFloat<U>()) ) {
|
|
113
|
+
// Reject combinations where the float's mantissa cannot losslessly represent
|
|
114
|
+
// the integer type's full range. This mirrors AssemblyScript's own == operator,
|
|
115
|
+
// which rejects these same combinations at compile time (e.g. f32 == i32, f64 == i64).
|
|
116
|
+
if (isFloat<T>() && isInteger<U>()) {
|
|
117
|
+
if (sizeof<U>() >= sizeof<T>()) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
"Cannot compare " + nameof<T>() + " with " + nameof<U>()
|
|
120
|
+
+ ": float precision is insufficient for the integer type's range."
|
|
121
|
+
+ " Cast both values to f64 before comparing, e.g. expect(f64(a)).toBe(f64(b))."
|
|
122
|
+
+ " Note: large integer values may lose precision when cast to f64, which could cause false positives."
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
if (sizeof<T>() >= sizeof<U>()) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
"Cannot compare " + nameof<T>() + " with " + nameof<U>()
|
|
129
|
+
+ ": float precision is insufficient for the integer type's range."
|
|
130
|
+
+ " Cast both values to f64 before comparing, e.g. expect(f64(a)).toBe(f64(b))."
|
|
131
|
+
+ " Note: large integer values may lose precision when cast to f64, which could cause false positives."
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
114
135
|
return f64(actual) === f64(expected);
|
|
115
136
|
} else if (isVector<T>() && isVector<U>()) {
|
|
116
137
|
return <v128>actual == <v128>expected;
|
|
@@ -121,18 +142,24 @@ export function identical<T, U>(actual: T, expected: U): bool {
|
|
|
121
142
|
}
|
|
122
143
|
|
|
123
144
|
export function closeTo<T, U>(actual: T, expected: U, precision: i32 = 2): bool {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
// Note: closeTo intentionally does NOT delegate to equals()/identical() for its
|
|
146
|
+
// initial exact-match check, because identical() now throws for float/integer
|
|
147
|
+
// combinations the language rejects (e.g. f32 vs i32). closeTo handles these
|
|
148
|
+
// via its own f64 promotion, which is appropriate for approximate comparison.
|
|
128
149
|
|
|
129
150
|
if (isString<T>() && isString<U>()) {
|
|
130
|
-
return
|
|
151
|
+
return <string>actual == <string>expected;
|
|
131
152
|
}
|
|
132
153
|
|
|
133
154
|
if ( (isFloat<T>() || isInteger<T>()) && (isFloat<U>() || isInteger<U>()) ) {
|
|
134
155
|
const actualF64: f64 = f64(actual);
|
|
135
156
|
const expectedF64: f64 = f64(expected);
|
|
157
|
+
|
|
158
|
+
// exact match shortcut (also handles ±Infinity)
|
|
159
|
+
if (actualF64 === expectedF64) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
136
163
|
const expectedDiff: f64 = 10.0 ** -precision / 2.0;
|
|
137
164
|
const receivedDiff: f64 = Math.abs(expectedF64 - actualF64);
|
|
138
165
|
return receivedDiff < expectedDiff;
|
|
@@ -186,7 +213,10 @@ export function equals<T, U>(actual: T, expected: U): bool {
|
|
|
186
213
|
}
|
|
187
214
|
|
|
188
215
|
// TODO value compare
|
|
189
|
-
throw new Error("
|
|
216
|
+
throw new Error("Deep equality comparison of user-defined reference types"
|
|
217
|
+
+ " is not yet implemented, and these references are not identical."
|
|
218
|
+
+ " Use toBe() for reference equality."
|
|
219
|
+
);
|
|
190
220
|
}
|
|
191
221
|
|
|
192
222
|
export function truthyOrFalsey<T>(actual: T, expected: bool): bool {
|
package/assembly/expect.ts
CHANGED
|
@@ -92,13 +92,30 @@ abstract class BaseExpectMatcher<T> {
|
|
|
92
92
|
// }
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* Checks that a value is what you expect. Primitives and strings
|
|
96
|
-
* and references are checked for reference equality only (including
|
|
97
|
-
*
|
|
95
|
+
* Checks that a value is what you expect using identity comparison. Primitives and strings
|
|
96
|
+
* are compared by value, and references are checked for reference equality only (including
|
|
97
|
+
* objects, arrays, etc).
|
|
98
|
+
*
|
|
99
|
+
* Cross-type numeric comparisons are allowed where AssemblyScript's own `==` operator
|
|
100
|
+
* permits them (e.g. `f64` vs `i32`). Combinations where the float type lacks sufficient
|
|
101
|
+
* mantissa precision for the integer type's range are rejected with an error, mirroring
|
|
102
|
+
* the AS compiler's behavior (e.g. `f32` vs `i32`, `f64` vs `i64`).
|
|
103
|
+
*
|
|
104
|
+
* Don't use `toBe` to compare two floating-point numbers — see `toBeCloseTo` instead.
|
|
98
105
|
*
|
|
99
106
|
* @example
|
|
100
107
|
* expect(1 + 1).toBe(2);
|
|
101
108
|
* expect("hello").toBe("hello");
|
|
109
|
+
*
|
|
110
|
+
* // cross-type integer comparisons
|
|
111
|
+
* expect(i64(42)).toBe(u8(42));
|
|
112
|
+
*
|
|
113
|
+
* // supported float/integer comparisons (small integer types)
|
|
114
|
+
* expect(f64(42.0)).toBe(i32(42));
|
|
115
|
+
*
|
|
116
|
+
* // unsupported float/integer comparisons throw an error
|
|
117
|
+
* // expect(f32(42.0)).toBe(i32(42)); // Error: float precision insufficient
|
|
118
|
+
* // expect(f64(42.0)).toBe(i64(42)); // Error: float precision insufficient
|
|
102
119
|
*/
|
|
103
120
|
toBe<U>(val: U): void {
|
|
104
121
|
this.assertComparison(identical(this.actual, val), this.actual, val, "to be", true);
|
|
@@ -108,8 +125,8 @@ abstract class BaseExpectMatcher<T> {
|
|
|
108
125
|
* Checks if a value is close to what you expect. Using exact equality with floating point
|
|
109
126
|
* numbers often doesn't work correctly, because small internal rounding occurs to be able
|
|
110
127
|
* to represent floats in binary. This rounding means intuitive comparisons will often fail.
|
|
111
|
-
*
|
|
112
|
-
*
|
|
128
|
+
*
|
|
129
|
+
* Strings are compared by value equality. Non-numeric, non-string types return false.
|
|
113
130
|
*
|
|
114
131
|
* @param precision - Specify an integer representing the number of decimal places
|
|
115
132
|
* that must match for values to be considered close. Defaults to 2 digits, meaning effectively
|
|
@@ -124,12 +141,17 @@ abstract class BaseExpectMatcher<T> {
|
|
|
124
141
|
}
|
|
125
142
|
|
|
126
143
|
/**
|
|
127
|
-
* Checks that two values have the same value (deep equality). Currently supports
|
|
128
|
-
* checking equality of Arrays, Sets, Maps, and nulls. Values inside arrays are
|
|
129
|
-
* compared using `toEqual()` also, while Maps and Sets use their respective rules
|
|
130
|
-
* for membership. Primitives, strings, and other object references are compared with
|
|
144
|
+
* Checks that two values have the same value (deep equality). Currently supports
|
|
145
|
+
* checking equality of Arrays, Sets, Maps, and nulls. Values inside arrays are
|
|
146
|
+
* compared using `toEqual()` also, while Maps and Sets use their respective rules
|
|
147
|
+
* for membership. Primitives, strings, and other object references are compared with
|
|
131
148
|
* `toBe()` rules.
|
|
132
149
|
*
|
|
150
|
+
* Like `toBe`, cross-type numeric comparisons follow AssemblyScript's own `==` operator
|
|
151
|
+
* restrictions. Combinations where the float type lacks sufficient mantissa precision
|
|
152
|
+
* for the integer type's range are rejected with an error (e.g. `f32` vs `i32`,
|
|
153
|
+
* `f64` vs `i64`).
|
|
154
|
+
*
|
|
133
155
|
* Note: Does not yet support user-defined object deep equality checking.
|
|
134
156
|
*
|
|
135
157
|
* @example
|
|
@@ -156,27 +178,43 @@ abstract class BaseExpectMatcher<T> {
|
|
|
156
178
|
}
|
|
157
179
|
|
|
158
180
|
/**
|
|
159
|
-
* Checks that a value is truthy (not `0`, `false`, `
|
|
181
|
+
* Checks that a value is truthy (not `0`, `false`, `NaN`, or `null`).
|
|
182
|
+
*
|
|
183
|
+
* Unlike in JavaScript, empty string (`""`) is truthy in AssemblyScript because it is
|
|
184
|
+
* an object reference, not a primitive. An empty string is still an allocated object
|
|
185
|
+
* with a non-zero address, so it evaluates as truthy.
|
|
160
186
|
*
|
|
161
187
|
* @example
|
|
162
188
|
* expect(1).toBeTruthy();
|
|
163
189
|
* expect("hello").toBeTruthy();
|
|
190
|
+
* expect("").toBeTruthy(); // truthy in AS (unlike JS)
|
|
164
191
|
*/
|
|
165
192
|
toBeTruthy(): void {
|
|
166
193
|
this.assertComparison(truthyOrFalsey(this.actual, true), this.actual, true, "to be truthy", false);
|
|
167
194
|
}
|
|
168
195
|
|
|
169
196
|
/**
|
|
170
|
-
* Checks that a value is
|
|
197
|
+
* Checks that a value is falsy (`0`, `false`, `NaN`, or `null`).
|
|
198
|
+
*
|
|
199
|
+
* Unlike in JavaScript, empty string (`""`) is NOT falsy in AssemblyScript because it is
|
|
200
|
+
* an object reference, not a primitive. An empty string is still an allocated object
|
|
201
|
+
* with a non-zero address, so it evaluates as truthy.
|
|
171
202
|
*
|
|
172
203
|
* @example
|
|
173
|
-
* expect(0).
|
|
174
|
-
* expect(
|
|
204
|
+
* expect(0).toBeFalsy();
|
|
205
|
+
* expect(NaN).toBeFalsy();
|
|
206
|
+
* expect(null).toBeFalsy();
|
|
207
|
+
* expect("").not.toBeFalsy(); // not falsy in AS (unlike JS)
|
|
175
208
|
*/
|
|
176
|
-
|
|
209
|
+
toBeFalsy(): void {
|
|
177
210
|
this.assertComparison(truthyOrFalsey(this.actual, false), this.actual, false, "to be falsey", false);
|
|
178
211
|
}
|
|
179
212
|
|
|
213
|
+
/** @deprecated Use `toBeFalsy()` instead. */
|
|
214
|
+
toBeFalsey(): void {
|
|
215
|
+
this.toBeFalsy();
|
|
216
|
+
}
|
|
217
|
+
|
|
180
218
|
/**
|
|
181
219
|
* Checks that a value is null (`usize(0)` in AssemblyScript).
|
|
182
220
|
*
|
|
@@ -239,11 +277,11 @@ abstract class BaseExpectMatcher<T> {
|
|
|
239
277
|
const nonNullActual = <NonNullable<T>>this.actual;
|
|
240
278
|
|
|
241
279
|
if (isFloat<U>()) {
|
|
242
|
-
// @ts-ignore
|
|
243
|
-
this.assertComparison<
|
|
280
|
+
// @ts-ignore: .length is i32; use closeTo for float comparison
|
|
281
|
+
this.assertComparison<i32, U>(closeTo<i32, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
|
|
244
282
|
} else {
|
|
245
|
-
// @ts-ignore
|
|
246
|
-
this.assertComparison<
|
|
283
|
+
// @ts-ignore: .length is i32; compare as integers
|
|
284
|
+
this.assertComparison<i32, U>(identical<i32, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
|
|
247
285
|
}
|
|
248
286
|
} else {
|
|
249
287
|
this.assertComparison(false, this.actual, length, "to have length", true);
|
|
@@ -2,7 +2,7 @@ import { POOL_ERROR_NAMES, POOL_INTERNAL_PATHS } from "./constants-DX9yo-el.mjs"
|
|
|
2
2
|
import { createPoolErrorFromAnyError, debug, getTestErrorFromPoolError } from "./debug-Cf2jt1kg.mjs";
|
|
3
3
|
import { failFile, getFullTaskHierarchy, prepareFileTaskForCollection } from "./vitest-file-tasks-BUwzh375.mjs";
|
|
4
4
|
import { getTaskLogLabel, getTaskLogPrefix } from "./vitest-tasks-Cbri6MWZ.mjs";
|
|
5
|
-
import { executeWASMDiscovery, flushRpcUpdates, reportFileCollected, reportFileError, reportFileQueued, reportUserConsoleLogs } from "./load-user-imports-
|
|
5
|
+
import { executeWASMDiscovery, flushRpcUpdates, reportFileCollected, reportFileError, reportFileQueued, reportUserConsoleLogs } from "./load-user-imports-eGZuxeNp.mjs";
|
|
6
6
|
import { compileAssemblyScript } from "./compiler-BaNECXMW.mjs";
|
|
7
7
|
import { basename, relative } from "node:path";
|
|
8
8
|
|
|
@@ -77,4 +77,4 @@ async function runCompileAndDiscover(file, logModule, rpc, poolOptions, projectR
|
|
|
77
77
|
|
|
78
78
|
//#endregion
|
|
79
79
|
export { runCompileAndDiscover };
|
|
80
|
-
//# sourceMappingURL=compile-runner-
|
|
80
|
+
//# sourceMappingURL=compile-runner-C2eh_xLp.mjs.map
|