Skip to content

Commit 22c3c95

Browse files
committed
feat(internal): add src/lib-nodejs/ava-unhandled-rejection.js
1 parent 76167b6 commit 22c3c95

File tree

2 files changed

+96
-13
lines changed

2 files changed

+96
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// @ts-check
2+
import { spawn } from 'child_process';
3+
import { fileURLToPath } from 'url';
4+
5+
import engineGC from './engine-gc.js';
6+
import { makeGcAndFinalize } from './gc-and-finalize.js';
7+
8+
/**
9+
* @import {ExecutionContext, Macro} from 'ava';
10+
*/
11+
12+
export const AVA_EXPECT_UNHANDLED_REJECTIONS =
13+
'AGORIC_AVA_EXPECT_UNHANDLED_REJECTIONS';
14+
15+
export const SUBTEST_PREFIX = '(unhandled rejection subprocess): ';
16+
17+
/**
18+
* @template C
19+
* @param {string} importMetaUrl
20+
* @returns {Macro<
21+
* [
22+
* expectedUnhandled: number,
23+
* name: string,
24+
* testFn: (t: ExecutionContext<C>) => any,
25+
* ],
26+
* C
27+
* >}
28+
*/
29+
export const makeExpectUnhandledRejectionMacro = importMetaUrl => {
30+
const self = fileURLToPath(importMetaUrl);
31+
const gcAndFinalize = makeGcAndFinalize(engineGC);
32+
33+
if (process.env[AVA_EXPECT_UNHANDLED_REJECTIONS]) {
34+
return {
35+
title: (_, _expectedUnhandled, name, _testFn) => SUBTEST_PREFIX + name,
36+
exec: async (t, _expectedUnhandled, _name, testFn) => {
37+
await null;
38+
try {
39+
const result = await testFn(t);
40+
return result;
41+
} finally {
42+
await gcAndFinalize();
43+
}
44+
},
45+
};
46+
}
47+
48+
return {
49+
title: (_, _expectedUnhandled, name, _testFn) => name,
50+
exec: async (t, expectedUnhandled, name, _testFn) =>
51+
new Promise((resolve, reject) => {
52+
const ps = spawn('ava', [self, '-m', SUBTEST_PREFIX + name], {
53+
env: {
54+
...process.env,
55+
[AVA_EXPECT_UNHANDLED_REJECTIONS]: `${expectedUnhandled}`,
56+
},
57+
stdio: ['ignore', 'inherit', 'inherit', 'ignore'],
58+
});
59+
60+
ps.on('close', code => {
61+
t.is(code, 0, `got exit code ${code}, expected 0 for ${name}`);
62+
resolve();
63+
});
64+
ps.on('error', reject);
65+
}),
66+
};
67+
};

patches/ava+5.3.1.patch

+29-13
Original file line numberDiff line numberDiff line change
@@ -95,17 +95,20 @@ index 7630baa..78ced77 100644
9595
notifyOfPeerFailure() {
9696
send({type: 'peer-failed'});
9797
diff --git a/node_modules/ava/lib/reporters/default.js b/node_modules/ava/lib/reporters/default.js
98-
index 804e285..330164c 100644
98+
index 804e285..8daf5ee 100644
9999
--- a/node_modules/ava/lib/reporters/default.js
100100
+++ b/node_modules/ava/lib/reporters/default.js
101-
@@ -679,7 +679,9 @@ export default class Reporter {
101+
@@ -678,8 +678,11 @@ export default class Reporter {
102+
this.lineWriter.writeLine(colors.todo(`${this.stats.todoTests} ${plur('test', this.stats.todoTests)} todo`));
102103
}
103104

104-
if (this.stats.unhandledRejections > 0) {
105+
- if (this.stats.unhandledRejections > 0) {
105106
- this.lineWriter.writeLine(colors.error(`${this.stats.unhandledRejections} unhandled ${plur('rejection', this.stats.unhandledRejections)}`));
106-
+ const allowed = Number(process.env.AGORIC_AVA_ALLOW_UNHANDLED_REJECTIONS)
107-
+ const color = this.stats.unhandledRejections <= allowed ? 'skip' : 'error';
108-
+ this.lineWriter.writeLine(colors[color](`${this.stats.unhandledRejections} unhandled ${plur('rejection', this.stats.unhandledRejections)} (AGORIC_AVA_ALLOW_UNHANDLED_REJECTIONS=${allowed})`));
107+
+ // AGORIC: TODO(#11028): unhandled exceptions patch should be accomplished in userspace.
108+
+ if (this.stats.unhandledRejections > 0 || process.env.AGORIC_AVA_EXPECT_UNHANDLED_REJECTIONS) {
109+
+ const expected = Number(process.env.AGORIC_AVA_EXPECT_UNHANDLED_REJECTIONS || 0)
110+
+ const color = this.stats.unhandledRejections === expected ? 'skip' : 'error';
111+
+ this.lineWriter.writeLine(colors[color](`${this.stats.unhandledRejections} unhandled ${plur('rejection', this.stats.unhandledRejections)} (AGORIC_AVA_EXPECT_UNHANDLED_REJECTIONS=${expected})`));
109112
}
110113

111114
if (this.stats.uncaughtExceptions > 0) {
@@ -142,19 +145,32 @@ index b1989a4..fa1617f 100644
142145
error: evt.err ? dumpError(evt.err) : null,
143146
index: ++this.i,
144147
diff --git a/node_modules/ava/lib/run-status.js b/node_modules/ava/lib/run-status.js
145-
index 8471345..3110074 100644
148+
index 8471345..7433712 100644
146149
--- a/node_modules/ava/lib/run-status.js
147150
+++ b/node_modules/ava/lib/run-status.js
148-
@@ -230,6 +230,10 @@ export default class RunStatus extends Emittery {
151+
@@ -228,7 +228,7 @@ export default class RunStatus extends Emittery {
152+
|| this.stats.sharedWorkerErrors > 0
153+
|| this.stats.timeouts > 0
149154
|| this.stats.uncaughtExceptions > 0
150-
|| this.stats.unhandledRejections > 0
155+
- || this.stats.unhandledRejections > 0
156+
+ // AGORIC: `unhandledRejections` are processed below.
151157
) {
152-
+ const allowed = Number(process.env.AGORIC_AVA_ALLOW_UNHANDLED_REJECTIONS);
153-
+ if (this.stats.unhandledRejections <= allowed) {
154-
+ return 0;
155-
+ }
156158
return 1;
157159
}
160+
@@ -237,6 +237,14 @@ export default class RunStatus extends Emittery {
161+
return 1;
162+
}
163+
164+
+ // AGORIC: TODO(#11028): unhandled exceptions patch should be accomplished in userspace.
165+
+ if (this.stats.unhandledRejections > 0 || process.env.AGORIC_AVA_EXPECT_UNHANDLED_REJECTIONS) {
166+
+ const expected = Number(process.env.AGORIC_AVA_EXPECT_UNHANDLED_REJECTIONS || 0);
167+
+ if (this.stats.unhandledRejections !== expected) {
168+
+ return 1;
169+
+ }
170+
+ }
171+
+
172+
return 0;
173+
}
158174

159175
diff --git a/node_modules/ava/lib/worker/base.js b/node_modules/ava/lib/worker/base.js
160176
index cdd3c4a..4302a4a 100644

0 commit comments

Comments
 (0)