vitest-pool-assemblyscript 0.2.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/BINARYEN_VERSION +1 -0
- package/LICENSE +53 -0
- package/README.md +607 -0
- package/assembly/compare.ts +219 -0
- package/assembly/describe.ts +104 -0
- package/assembly/expect.ts +335 -0
- package/assembly/index.ts +14 -0
- package/assembly/options.ts +198 -0
- package/assembly/test.ts +147 -0
- package/assembly/tsconfig.json +6 -0
- package/binding.gyp +62 -0
- package/dist/ast-visitor-DC3SuTzs.mjs +310 -0
- package/dist/ast-visitor-DC3SuTzs.mjs.map +1 -0
- package/dist/compile-runner-8h0dBwG2.mjs +80 -0
- package/dist/compile-runner-8h0dBwG2.mjs.map +1 -0
- package/dist/compiler/transforms/strip-inline.d.mts +18 -0
- package/dist/compiler/transforms/strip-inline.d.mts.map +1 -0
- package/dist/compiler/transforms/strip-inline.mjs +38 -0
- package/dist/compiler/transforms/strip-inline.mjs.map +1 -0
- package/dist/compiler-CN6BRK_N.mjs +295 -0
- package/dist/compiler-CN6BRK_N.mjs.map +1 -0
- package/dist/config/index-v3.d.mts +111 -0
- package/dist/config/index-v3.d.mts.map +1 -0
- package/dist/config/index-v3.mjs +11 -0
- package/dist/config/index-v3.mjs.map +1 -0
- package/dist/config/index.d.mts +4 -0
- package/dist/config/index.mjs +8 -0
- package/dist/constants-CA50WBdr.mjs +130 -0
- package/dist/constants-CA50WBdr.mjs.map +1 -0
- package/dist/coverage-merge-0WqdC-dq.mjs +22 -0
- package/dist/coverage-merge-0WqdC-dq.mjs.map +1 -0
- package/dist/coverage-provider/index.d.mts +15 -0
- package/dist/coverage-provider/index.d.mts.map +1 -0
- package/dist/coverage-provider/index.mjs +535 -0
- package/dist/coverage-provider/index.mjs.map +1 -0
- package/dist/custom-provider-options-CF5C1kXb.d.mts +26 -0
- package/dist/custom-provider-options-CF5C1kXb.d.mts.map +1 -0
- package/dist/debug-IeEHsxy0.mjs +195 -0
- package/dist/debug-IeEHsxy0.mjs.map +1 -0
- package/dist/index-internal.d.mts +23 -0
- package/dist/index-internal.d.mts.map +1 -0
- package/dist/index-internal.mjs +4 -0
- package/dist/index-v3.d.mts +7 -0
- package/dist/index-v3.d.mts.map +1 -0
- package/dist/index-v3.mjs +206 -0
- package/dist/index-v3.mjs.map +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +8 -0
- package/dist/load-user-imports-J9eaAW0_.mjs +801 -0
- package/dist/load-user-imports-J9eaAW0_.mjs.map +1 -0
- package/dist/pool-runner-init-CEwLyNI3.d.mts +8 -0
- package/dist/pool-runner-init-CEwLyNI3.d.mts.map +1 -0
- package/dist/pool-runner-init-d5qScS41.mjs +400 -0
- package/dist/pool-runner-init-d5qScS41.mjs.map +1 -0
- package/dist/pool-thread/compile-worker-thread.d.mts +7 -0
- package/dist/pool-thread/compile-worker-thread.d.mts.map +1 -0
- package/dist/pool-thread/compile-worker-thread.mjs +42 -0
- package/dist/pool-thread/compile-worker-thread.mjs.map +1 -0
- package/dist/pool-thread/test-worker-thread.d.mts +7 -0
- package/dist/pool-thread/test-worker-thread.d.mts.map +1 -0
- package/dist/pool-thread/test-worker-thread.mjs +39 -0
- package/dist/pool-thread/test-worker-thread.mjs.map +1 -0
- package/dist/pool-thread/v3-tinypool-thread.d.mts +7 -0
- package/dist/pool-thread/v3-tinypool-thread.d.mts.map +1 -0
- package/dist/pool-thread/v3-tinypool-thread.mjs +57 -0
- package/dist/pool-thread/v3-tinypool-thread.mjs.map +1 -0
- package/dist/resolve-config-as1w-Qyz.mjs +65 -0
- package/dist/resolve-config-as1w-Qyz.mjs.map +1 -0
- package/dist/test-runner-B2BpyPNK.mjs +142 -0
- package/dist/test-runner-B2BpyPNK.mjs.map +1 -0
- package/dist/types-8KKo9Hbf.d.mts +228 -0
- package/dist/types-8KKo9Hbf.d.mts.map +1 -0
- package/dist/vitest-file-tasks-BUwzh375.mjs +61 -0
- package/dist/vitest-file-tasks-BUwzh375.mjs.map +1 -0
- package/dist/vitest-tasks-BKS7689f.mjs +319 -0
- package/dist/vitest-tasks-BKS7689f.mjs.map +1 -0
- package/dist/worker-rpc-channel-lbhK7Qz8.mjs +25 -0
- package/dist/worker-rpc-channel-lbhK7Qz8.mjs.map +1 -0
- package/package.json +112 -0
- package/prebuilds/linux-x64/vitest-pool-assemblyscript.glibc.node +0 -0
- package/scripts/install.js +91 -0
- package/scripts/setup-binaryen.js +179 -0
- package/src/native-instrumentation/addon.cpp +788 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
function arrayEquals<T extends ArrayLike<unknown>, U extends ArrayLike<unknown>>(actual: T, expected: U): bool {
|
|
2
|
+
if (actual.length != expected.length) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
for (let i = 0; i < expected.length; i++) {
|
|
7
|
+
if (!equals(actual[i], expected[i])) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function setEquals<T extends Set<V>, U extends Set<V>, V>(actual: T, expected: U): bool {
|
|
16
|
+
if (actual.size != expected.size) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const expectedValues: V[] = expected.values();
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < expectedValues.length; i++) {
|
|
23
|
+
if (!actual.has(expectedValues[i])) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function mapEquals<
|
|
32
|
+
T extends Map<V, W>,
|
|
33
|
+
U extends Map<V, W>,
|
|
34
|
+
V, W
|
|
35
|
+
>(actual: T, expected: U): bool {
|
|
36
|
+
if (actual.size != expected.size) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const expectedKeys: V[] = expected.keys();
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < expectedKeys.length; i++) {
|
|
43
|
+
const key: V = expectedKeys[i];
|
|
44
|
+
|
|
45
|
+
if (!actual.has(key)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!equals<W, W>(actual.get(key), expected.get(key))) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Generic primitive / reference equality comparison. Assumes comparable primitive types
|
|
59
|
+
* (or same reference type) for provided values.
|
|
60
|
+
*/
|
|
61
|
+
export function identical<T, U>(actual: T, expected: U): bool {
|
|
62
|
+
// both refs
|
|
63
|
+
if (isReference<T>() && isReference<U>()) {
|
|
64
|
+
const actualIsNullable = isNullable<T>();
|
|
65
|
+
const actualIsNull = actualIsNullable && actual == null;
|
|
66
|
+
const expectedIsNullable = isNullable<U>();
|
|
67
|
+
const expectedIsNull = expectedIsNullable && expected == null;
|
|
68
|
+
|
|
69
|
+
// null refs
|
|
70
|
+
if (actualIsNull && expectedIsNull) {
|
|
71
|
+
return true;
|
|
72
|
+
} else if ( (actualIsNull && !expectedIsNull) || (!actualIsNull && expectedIsNull) ) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// strings
|
|
77
|
+
if (isString<T>() && isString<U>()) {
|
|
78
|
+
return <string>actual == <string>expected;
|
|
79
|
+
} else {
|
|
80
|
+
// object refs
|
|
81
|
+
return changetype<usize>(actual) == changetype<usize>(expected);
|
|
82
|
+
}
|
|
83
|
+
} else if (isReference<T>() && !isReference<U>()) {
|
|
84
|
+
// actual is null ref, expected bare null
|
|
85
|
+
return changetype<usize>(actual) == usize(0) && expected == usize(0);
|
|
86
|
+
} else if (!isReference<T>() && isReference<U>()) {
|
|
87
|
+
// actual is bare null, expected is null ref
|
|
88
|
+
return actual == usize(0) && changetype<usize>(expected) == usize(0);
|
|
89
|
+
} else { // both primitives
|
|
90
|
+
if ( (isBoolean<T>() && !isBoolean<U>()) || (!isBoolean<T>() && isBoolean<U>())
|
|
91
|
+
) {
|
|
92
|
+
// when one is boolean and the other is not, they are not identical
|
|
93
|
+
// use toEqual for this comparison
|
|
94
|
+
return false;
|
|
95
|
+
} else if (isInteger<T>() && isInteger<U>()) {
|
|
96
|
+
if (isSigned<T>() && isSigned<U>()) {
|
|
97
|
+
return i64(actual) == i64(expected);
|
|
98
|
+
} else if (isSigned<T>() && !isSigned<U>()) {
|
|
99
|
+
if (i64(actual) < 0) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return u64(actual) == u64(expected);
|
|
103
|
+
} else if (!isSigned<T>() && isSigned<U>()) {
|
|
104
|
+
if (i64(expected) < 0) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return u64(actual) == u64(expected);
|
|
108
|
+
} else {
|
|
109
|
+
return u64(actual) == u64(expected);
|
|
110
|
+
}
|
|
111
|
+
} else if (isFloat<T>() && isFloat<U>()) {
|
|
112
|
+
return f64(actual) === f64(expected);
|
|
113
|
+
} else if ( (isFloat<T>() && isInteger<U>()) || (isInteger<T>() && isFloat<U>()) ) {
|
|
114
|
+
return f64(actual) === f64(expected);
|
|
115
|
+
} else if (isVector<T>() && isVector<U>()) {
|
|
116
|
+
return <v128>actual == <v128>expected;
|
|
117
|
+
} else {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function closeTo<T, U>(actual: T, expected: U, precision: i32 = 2): bool {
|
|
124
|
+
const match = equals(actual, expected);
|
|
125
|
+
if (match) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (isString<T>() && isString<U>()) {
|
|
130
|
+
return match;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if ( (isFloat<T>() || isInteger<T>()) && (isFloat<U>() || isInteger<U>()) ) {
|
|
134
|
+
const actualF64: f64 = f64(actual);
|
|
135
|
+
const expectedF64: f64 = f64(expected);
|
|
136
|
+
const expectedDiff: f64 = 10.0 ** -precision / 2.0;
|
|
137
|
+
const receivedDiff: f64 = Math.abs(expectedF64 - actualF64);
|
|
138
|
+
return receivedDiff < expectedDiff;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Generic value equality comparison. Assumes comparable types for both values.
|
|
146
|
+
* Does not yet support user-defined types.
|
|
147
|
+
*/
|
|
148
|
+
export function equals<T, U>(actual: T, expected: U): bool {
|
|
149
|
+
let exactMatch: bool = false;
|
|
150
|
+
|
|
151
|
+
// allow boolean-to-number comparisons here
|
|
152
|
+
if (isBoolean<T>() && !isBoolean<U>()) {
|
|
153
|
+
exactMatch = identical(u8(actual), expected);
|
|
154
|
+
} else if (!isBoolean<T>() && isBoolean<U>()) {
|
|
155
|
+
exactMatch = identical(actual, u8(expected));
|
|
156
|
+
} else {
|
|
157
|
+
exactMatch = identical(actual, expected);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!isReference<T>() || isString<T>() || isVector<T>()) {
|
|
161
|
+
// non-bool primitives or strings: return result of comparing
|
|
162
|
+
return exactMatch;
|
|
163
|
+
} else if (exactMatch) {
|
|
164
|
+
// primitive / reference comparison passed already
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (isNullable<T>()) {
|
|
169
|
+
if (actual == null && expected == null) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if ( (actual == null && expected != null) || (actual != null && expected == null) ) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (isArrayLike<T>(actual) && isArrayLike<U>(expected)) {
|
|
179
|
+
return arrayEquals(actual, expected);
|
|
180
|
+
}
|
|
181
|
+
if (actual instanceof Set && expected instanceof Set) {
|
|
182
|
+
return setEquals(actual, expected);
|
|
183
|
+
}
|
|
184
|
+
if (actual instanceof Map && expected instanceof Map) {
|
|
185
|
+
return mapEquals(actual, expected);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// TODO value compare
|
|
189
|
+
throw new Error("Comparison of user-defined object types not yet implemented");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function truthyOrFalsey<T>(actual: T, expected: bool): bool {
|
|
193
|
+
return !!actual == expected;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function isNull<T>(value: T): bool {
|
|
197
|
+
if (isReference<T>()) {
|
|
198
|
+
if (isNullable<T>()) {
|
|
199
|
+
return value == null;
|
|
200
|
+
} else {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
if (isBoolean<T>()) {
|
|
205
|
+
return false;
|
|
206
|
+
} else {
|
|
207
|
+
return nameof<T>(value) == 'usize' && value == 0;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function nan<T>(value: T): bool {
|
|
213
|
+
if (isFloat<T>()) {
|
|
214
|
+
// @ts-ignore
|
|
215
|
+
return isNaN<T>(value);
|
|
216
|
+
} else {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { TestCallback } from './test';
|
|
2
|
+
import { TestOptions, DEFAULT_TEST_OPTIONS } from './options';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* @external functions are imported to the
|
|
6
|
+
* WASM execution environment from pool executor
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// @ts-ignore: top level decorators are supported in AssemblyScript
|
|
10
|
+
@external("__as_pool_env__", "__begin_register_suite")
|
|
11
|
+
declare function __begin_register_suite(
|
|
12
|
+
name: string,
|
|
13
|
+
timeout: i32,
|
|
14
|
+
retry: i32,
|
|
15
|
+
skip: i32,
|
|
16
|
+
only: i32,
|
|
17
|
+
fails: i32
|
|
18
|
+
): void;
|
|
19
|
+
|
|
20
|
+
// @ts-ignore: top level decorators are supported in AssemblyScript
|
|
21
|
+
@external("__as_pool_env__", "__end_register_suite")
|
|
22
|
+
declare function __end_register_suite(name: string): void;
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register a test suite (a collection of tests and suites).
|
|
27
|
+
*/
|
|
28
|
+
export function describe<T = TestCallback, U = TestOptions>(
|
|
29
|
+
name: string,
|
|
30
|
+
optionsOrFn: T,
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
fnOrOptions: U = DEFAULT_TEST_OPTIONS // defaults all undefined here, merged with config in JS
|
|
33
|
+
): void {
|
|
34
|
+
let fn: TestCallback;
|
|
35
|
+
let options: TestOptions;
|
|
36
|
+
|
|
37
|
+
if (isFunction(optionsOrFn) && fnOrOptions instanceof TestOptions) {
|
|
38
|
+
fn = optionsOrFn;
|
|
39
|
+
options = fnOrOptions;
|
|
40
|
+
} else if (optionsOrFn instanceof TestOptions && isFunction(fnOrOptions)) {
|
|
41
|
+
fn = fnOrOptions;
|
|
42
|
+
options = optionsOrFn;
|
|
43
|
+
} else {
|
|
44
|
+
throw new Error("Invalid describe() arguments");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
__begin_register_suite(
|
|
48
|
+
name,
|
|
49
|
+
options._valueOfTimeout,
|
|
50
|
+
options._valueOfRetry,
|
|
51
|
+
options._valueOfSkip,
|
|
52
|
+
options._valueOfOnly,
|
|
53
|
+
options._valueOfFails
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
fn();
|
|
57
|
+
|
|
58
|
+
__end_register_suite(name);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function describeWithMergedOption<T = TestCallback, U = TestOptions>(
|
|
62
|
+
name: string,
|
|
63
|
+
optionToMerge: TestOptions,
|
|
64
|
+
optionsOrFn: T,
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
fnOrOptions: U = DEFAULT_TEST_OPTIONS
|
|
67
|
+
): void {
|
|
68
|
+
let fn: TestCallback;
|
|
69
|
+
let options: TestOptions;
|
|
70
|
+
|
|
71
|
+
if (isFunction(optionsOrFn) && fnOrOptions instanceof TestOptions) {
|
|
72
|
+
fn = optionsOrFn;
|
|
73
|
+
options = fnOrOptions;
|
|
74
|
+
} else if (optionsOrFn instanceof TestOptions && isFunction(fnOrOptions)) {
|
|
75
|
+
fn = fnOrOptions;
|
|
76
|
+
options = optionsOrFn;
|
|
77
|
+
} else {
|
|
78
|
+
throw new Error("Invalid describe() arguments");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const merged = options.__merge(optionToMerge);
|
|
82
|
+
|
|
83
|
+
return describe(name, merged, fn);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export namespace describe {
|
|
87
|
+
export function skip<T = TestCallback, U = TestOptions>(
|
|
88
|
+
name: string,
|
|
89
|
+
optionsOrFn: T,
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
fnOrOptions: U = DEFAULT_TEST_OPTIONS
|
|
92
|
+
): void {
|
|
93
|
+
return describeWithMergedOption(name, TestOptions.skip(), optionsOrFn, fnOrOptions);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function only<T = TestCallback, U = TestOptions>(
|
|
97
|
+
name: string,
|
|
98
|
+
optionsOrFn: T,
|
|
99
|
+
// @ts-ignore
|
|
100
|
+
fnOrOptions: U = DEFAULT_TEST_OPTIONS
|
|
101
|
+
): void {
|
|
102
|
+
return describeWithMergedOption(name, TestOptions.only(), optionsOrFn, fnOrOptions);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import {
|
|
2
|
+
closeTo,
|
|
3
|
+
equals,
|
|
4
|
+
identical,
|
|
5
|
+
isNull,
|
|
6
|
+
nan,
|
|
7
|
+
truthyOrFalsey
|
|
8
|
+
} from './compare';
|
|
9
|
+
|
|
10
|
+
// @external functions are imported to the WASM execution environment from pool code
|
|
11
|
+
|
|
12
|
+
// @ts-ignore: top level decorators are supported in AssemblyScript
|
|
13
|
+
@external("__as_pool_env__", "__assertion_pass")
|
|
14
|
+
declare function __assertion_pass(): void;
|
|
15
|
+
|
|
16
|
+
// @ts-ignore: top level decorators are supported in AssemblyScript
|
|
17
|
+
@external("__as_pool_env__", "__assertion_fail")
|
|
18
|
+
declare function __assertion_fail<T>(
|
|
19
|
+
msg: string,
|
|
20
|
+
typeName: string,
|
|
21
|
+
valuesProvided: bool,
|
|
22
|
+
actual?: T,
|
|
23
|
+
expected?: T
|
|
24
|
+
): void;
|
|
25
|
+
|
|
26
|
+
// @ts-ignore: top level decorators are supported in AssemblyScript
|
|
27
|
+
@external("__as_pool_env__", "__expect_throw")
|
|
28
|
+
declare function __expect_throw(fnPtr: usize, errorMsg?: string): void;
|
|
29
|
+
|
|
30
|
+
// @ts-ignore: top level decorators are supported in AssemblyScript
|
|
31
|
+
@external("__as_pool_env__", "__end_expect_throw")
|
|
32
|
+
declare function __end_expect_throw(): void;
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
function itemMessageString<T>(item: T): string {
|
|
36
|
+
if (isNull(item)) return "<null>";
|
|
37
|
+
if (nan(item)) return "NaN";
|
|
38
|
+
|
|
39
|
+
if (isReference<T>()) {
|
|
40
|
+
if (isString<T>()) return `"${item}"`;
|
|
41
|
+
else if (item instanceof ArrayBuffer) return `ArrayBuffer[${item.byteLength}]`;
|
|
42
|
+
else if (isArrayLike<T>(item)) return arrayMessageString(item);
|
|
43
|
+
else if (item instanceof Set || item instanceof Map) return item.toString();
|
|
44
|
+
else return nameof<T>(item);
|
|
45
|
+
} else if (isBoolean<T>()){
|
|
46
|
+
return bool(item).toString();
|
|
47
|
+
} else if (isInteger<T>(item) || isFloat<T>(item)) {
|
|
48
|
+
return item.toString();
|
|
49
|
+
} else {
|
|
50
|
+
return nameof<T>(item);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function arrayMessageString<T extends ArrayLike<unknown>>(array: T): string {
|
|
55
|
+
if (isNullable<T>(array) && array == null) {
|
|
56
|
+
return "<null>";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let str = "[";
|
|
60
|
+
for (let i = 0; i < array.length; i++) {
|
|
61
|
+
str += itemMessageString(array[i]);
|
|
62
|
+
|
|
63
|
+
if (i < array.length - 1) {
|
|
64
|
+
str += ","
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
str += "]";
|
|
68
|
+
|
|
69
|
+
return str;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Expect matcher
|
|
74
|
+
*/
|
|
75
|
+
abstract class BaseExpectMatcher<T> {
|
|
76
|
+
protected isInverted: bool = false;
|
|
77
|
+
protected isSoft: bool = false;
|
|
78
|
+
protected actual: T;
|
|
79
|
+
|
|
80
|
+
constructor(val: T) {
|
|
81
|
+
this.actual = val;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Inverts the matcher that follows. Can be chained. */
|
|
85
|
+
get not(): InvertedExpectMatcher<T> {
|
|
86
|
+
return new InvertedExpectMatcher(this.actual, !this.isInverted);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// get soft(): this {
|
|
90
|
+
// this.isSoft = true;
|
|
91
|
+
// return this;
|
|
92
|
+
// }
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Checks that a value is what you expect. Primitives and strings are compared directly,
|
|
96
|
+
* and references are checked for reference equality only (including objects, arrays, etc).
|
|
97
|
+
* Don't use `toBe` with floating-point numbers - see `toBeCloseTo` instead.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* expect(1 + 1).toBe(2);
|
|
101
|
+
* expect("hello").toBe("hello");
|
|
102
|
+
*/
|
|
103
|
+
toBe<U>(val: U): void {
|
|
104
|
+
this.assertComparison(identical(this.actual, val), this.actual, val, "to be", true);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Checks if a value is close to what you expect. Using exact equality with floating point
|
|
109
|
+
* numbers often doesn't work correctly, because small internal rounding occurs to be able
|
|
110
|
+
* to represent floats in binary. This rounding means intuitive comparisons will often fail.
|
|
111
|
+
*
|
|
112
|
+
* Comparing strings, integers, or references will fall back to using a `toBe` comparison.
|
|
113
|
+
*
|
|
114
|
+
* @param precision - Specify an integer representing the number of decimal places
|
|
115
|
+
* that must match for values to be considered close. Defaults to 2 digits, meaning effectively
|
|
116
|
+
* that values must be within 0.005 of each other.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* expect(0.1 + 0.2).toBeCloseTo(0.3);
|
|
120
|
+
* expect(1.005).toBeCloseTo(1.0, 1);
|
|
121
|
+
*/
|
|
122
|
+
toBeCloseTo<U>(val: U, precision: i32 = 2): void {
|
|
123
|
+
this.assertComparison(closeTo(this.actual, val, precision), this.actual, val, "to be close to", true);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
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
|
|
131
|
+
* `toBe()` rules.
|
|
132
|
+
*
|
|
133
|
+
* Note: Does not yet support user-defined object deep equality checking.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* expect([1, 2, 3]).toEqual([1, 2, 3]);
|
|
137
|
+
* expect(["one", "two", "three"]).toEqual(["one", "two", "three"]);
|
|
138
|
+
*
|
|
139
|
+
* // objects use reference equality (deep equality not yet supported)
|
|
140
|
+
* const a: MyObject = new MyObject();
|
|
141
|
+
* const b: MyObject = new MyObject();
|
|
142
|
+
* expect([a, b]).toEqual([a, b]);
|
|
143
|
+
*/
|
|
144
|
+
toEqual<U>(val: U): void {
|
|
145
|
+
this.assertComparison(equals(this.actual, val), this.actual, val, "to deeply equal", true);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Alias for `toEqual`. Currently no differences in AssemblyScript.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* expect([1, 2]).toStrictEqual([1, 2]);
|
|
153
|
+
*/
|
|
154
|
+
toStrictEqual<U>(val: U): void {
|
|
155
|
+
this.assertComparison(equals(this.actual, val), this.actual, val, "to strictly equal", true);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Checks that a value is truthy (not `0`, `false`, `""`, or `null`).
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* expect(1).toBeTruthy();
|
|
163
|
+
* expect("hello").toBeTruthy();
|
|
164
|
+
*/
|
|
165
|
+
toBeTruthy(): void {
|
|
166
|
+
this.assertComparison(truthyOrFalsey(this.actual, true), this.actual, true, "to be truthy", false);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Checks that a value is falsey (`0`, `false`, `""`, or `null`).
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* expect(0).toBeFalsey();
|
|
174
|
+
* expect("").toBeFalsey();
|
|
175
|
+
*/
|
|
176
|
+
toBeFalsey(): void {
|
|
177
|
+
this.assertComparison(truthyOrFalsey(this.actual, false), this.actual, false, "to be falsey", false);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Checks that a value is null (`usize(0)` in AssemblyScript).
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* const val: string | null = null;
|
|
185
|
+
* expect(val).toBeNull();
|
|
186
|
+
* expect("hello").not.toBeNull();
|
|
187
|
+
* expect(0).not.toBeNull();
|
|
188
|
+
* expect(false).not.toBeNull();
|
|
189
|
+
*/
|
|
190
|
+
toBeNull(): void {
|
|
191
|
+
this.assertComparison(isNull(this.actual), this.actual, null, "to be null", false);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Checks that the type of the value is nullable (can hold `null`). This is a type-level
|
|
196
|
+
* check, not a value check — a bare `null` (which is `usize(0)`) is not itself a nullable type.
|
|
197
|
+
* Use `toBeNull()` to check if a value is null.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* const val: string | null = null;
|
|
201
|
+
* expect(val).toBeNullable();
|
|
202
|
+
* expect("hello").not.toBeNullable();
|
|
203
|
+
*/
|
|
204
|
+
toBeNullable(): void {
|
|
205
|
+
if (isReference<T>()) {
|
|
206
|
+
this.assertComparison(isNullable<T>(), this.actual, null, "to be nullable", false, false);
|
|
207
|
+
} else {
|
|
208
|
+
this.assertComparison(false, this.actual, null, "to be nullable", false, false);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Checks that a floating point value is `NaN`.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* expect(NaN).toBeNaN();
|
|
217
|
+
* expect(1.0).not.toBeNaN();
|
|
218
|
+
*/
|
|
219
|
+
toBeNaN(): void {
|
|
220
|
+
this.assertComparison(nan(this.actual), this.actual, NaN, "to be NaN", false);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Checks that an array or array-like value has the expected length.
|
|
225
|
+
* Uses `toBeCloseTo` semantics when the expected length is a float.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* expect([1, 2, 3]).toHaveLength(3);
|
|
229
|
+
* expect([]).toHaveLength(0);
|
|
230
|
+
* expect("hello world").toHaveLength(11);
|
|
231
|
+
*/
|
|
232
|
+
toHaveLength<U extends number>(length: U): void {
|
|
233
|
+
const actualIsNull = isNull(this.actual);
|
|
234
|
+
|
|
235
|
+
if (actualIsNull) {
|
|
236
|
+
this.assertComparison(false, null, length, "to have length", true);
|
|
237
|
+
} else if (isReference<T>()) {
|
|
238
|
+
if (isArray<T>() || isArrayLike<T>()) {
|
|
239
|
+
const nonNullActual = <NonNullable<T>>this.actual;
|
|
240
|
+
|
|
241
|
+
if (isFloat<U>()) {
|
|
242
|
+
// @ts-ignore
|
|
243
|
+
this.assertComparison<number, U>(closeTo<number, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
|
|
244
|
+
} else {
|
|
245
|
+
// @ts-ignore
|
|
246
|
+
this.assertComparison<number, U>(identical<number, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
this.assertComparison(false, this.actual, length, "to have length", true);
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
this.assertComparison(false, this.actual, length, "to have length", true);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
protected abortTest(message: string): void {
|
|
257
|
+
if (!this.isSoft) {
|
|
258
|
+
abort(message);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
protected assertComparison<U, V>(rawCondition: bool, actual: U, expected: V, methodStr: string, printExpected: bool, provideDiff: bool = true): void {
|
|
263
|
+
const condition = this.isInverted ? !rawCondition : rawCondition;
|
|
264
|
+
|
|
265
|
+
if (condition) {
|
|
266
|
+
__assertion_pass();
|
|
267
|
+
} else {
|
|
268
|
+
const notStr = this.isInverted ? "not " : "";
|
|
269
|
+
const actualStr = itemMessageString(actual);
|
|
270
|
+
const expectedStr = itemMessageString(expected);
|
|
271
|
+
const msg = `expected ${actualStr} ${notStr}${methodStr}${printExpected ? ` ${expectedStr}` : ""}`;
|
|
272
|
+
|
|
273
|
+
__assertion_fail<string>(msg, nameof<U>() + " " + nameof<V>(), provideDiff, actualStr, expectedStr);
|
|
274
|
+
|
|
275
|
+
// Abort on failure - terminates WASM execution - must be called from WASM.
|
|
276
|
+
// Imported abort handler will handle this and mark the test as failed.
|
|
277
|
+
this.abortTest(msg);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
class StandardExpectMatcher<T> extends BaseExpectMatcher<T> {
|
|
283
|
+
constructor(val: T) {
|
|
284
|
+
super(val);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Checks that a function throws an error when called. Optionally checks that the
|
|
289
|
+
* error message matches the provided string. The callback traps/aborts WASM execution,
|
|
290
|
+
* which is caught and evaluated by the pool. Also available as `toThrow()`.
|
|
291
|
+
*
|
|
292
|
+
* Note: Requires a void callback passed to `expect()`. Does not support `.not` inversion.
|
|
293
|
+
*
|
|
294
|
+
* @param errorMsg - Optional expected error message to match against.
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* expect(() => { throw new Error("boom"); }).toThrowError();
|
|
298
|
+
* expect(() => { throw new Error("boom"); }).toThrowError("boom");
|
|
299
|
+
*/
|
|
300
|
+
toThrowError(errorMsg: string | null = null): void {
|
|
301
|
+
if (isFunction<T>()) {
|
|
302
|
+
// @ts-ignore
|
|
303
|
+
const fnIndex = this.actual.index;
|
|
304
|
+
|
|
305
|
+
if (errorMsg == null) {
|
|
306
|
+
__expect_throw(fnIndex);
|
|
307
|
+
} else {
|
|
308
|
+
__expect_throw(fnIndex, errorMsg);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// if we get here it didn't throw, so this handles the missing error
|
|
312
|
+
__end_expect_throw();
|
|
313
|
+
} else {
|
|
314
|
+
throw new Error("expect() requires a callback function when used with toThrowError() matcher");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** Alias for `toThrowError` */
|
|
319
|
+
toThrow(errorMsg: string | null = null): void {
|
|
320
|
+
this.toThrowError(errorMsg);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
class InvertedExpectMatcher<T> extends BaseExpectMatcher<T> {
|
|
325
|
+
constructor(val: T, isInverted: bool) {
|
|
326
|
+
super(val);
|
|
327
|
+
|
|
328
|
+
// allow chaining multiple nots if desired
|
|
329
|
+
this.isInverted = isInverted;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function expect<T>(value: T): StandardExpectMatcher<T> {
|
|
334
|
+
return new StandardExpectMatcher<T>(value);
|
|
335
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test framework with per-test crash isolation support
|
|
3
|
+
*
|
|
4
|
+
* Execution flow:
|
|
5
|
+
* 1. Instantiation: Pool creates WASM instance with import callbacks
|
|
6
|
+
* 2. Registration: _start() runs, top-level test() calls invoke __register_test callback
|
|
7
|
+
* 3. Discovery: Pool receives test names + function indices via callbacks
|
|
8
|
+
* 4. Execution: Pool calls table.get(fnIndex)() directly via exported function table
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export * from './describe';
|
|
12
|
+
export * from './expect';
|
|
13
|
+
export * from './options';
|
|
14
|
+
export * from './test';
|