* Fixes #2499

* This needs to be quoted

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
This commit is contained in:
Jarred Sumner
2023-03-29 05:37:15 -07:00
committed by GitHub
parent f0def8c770
commit 767fdfbb97
5 changed files with 462 additions and 320 deletions

632
src/api/schema.js generated
View File

@@ -1,90 +1,90 @@
const Loader = {
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
9: 9,
10: 10,
jsx: 1,
js: 2,
ts: 3,
tsx: 4,
css: 5,
file: 6,
json: 7,
toml: 8,
wasm: 9,
napi: 10,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"10": 10,
"jsx": 1,
"js": 2,
"ts": 3,
"tsx": 4,
"css": 5,
"file": 6,
"json": 7,
"toml": 8,
"wasm": 9,
"napi": 10,
};
const LoaderKeys = {
1: "jsx",
2: "js",
3: "ts",
4: "tsx",
5: "css",
6: "file",
7: "json",
8: "toml",
9: "wasm",
10: "napi",
jsx: "jsx",
js: "js",
ts: "ts",
tsx: "tsx",
css: "css",
file: "file",
json: "json",
toml: "toml",
wasm: "wasm",
napi: "napi",
"1": "jsx",
"2": "js",
"3": "ts",
"4": "tsx",
"5": "css",
"6": "file",
"7": "json",
"8": "toml",
"9": "wasm",
"10": "napi",
"jsx": "jsx",
"js": "js",
"ts": "ts",
"tsx": "tsx",
"css": "css",
"file": "file",
"json": "json",
"toml": "toml",
"wasm": "wasm",
"napi": "napi",
};
const FrameworkEntryPointType = {
1: 1,
2: 2,
3: 3,
client: 1,
server: 2,
fallback: 3,
"1": 1,
"2": 2,
"3": 3,
"client": 1,
"server": 2,
"fallback": 3,
};
const FrameworkEntryPointTypeKeys = {
1: "client",
2: "server",
3: "fallback",
client: "client",
server: "server",
fallback: "fallback",
"1": "client",
"2": "server",
"3": "fallback",
"client": "client",
"server": "server",
"fallback": "fallback",
};
const StackFrameScope = {
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
Eval: 1,
Module: 2,
Function: 3,
Global: 4,
Wasm: 5,
Constructor: 6,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"Eval": 1,
"Module": 2,
"Function": 3,
"Global": 4,
"Wasm": 5,
"Constructor": 6,
};
const StackFrameScopeKeys = {
1: "Eval",
2: "Module",
3: "Function",
4: "Global",
5: "Wasm",
6: "Constructor",
Eval: "Eval",
Module: "Module",
Function: "Function",
Global: "Global",
Wasm: "Wasm",
Constructor: "Constructor",
"1": "Eval",
"2": "Module",
"3": "Function",
"4": "Global",
"5": "Wasm",
"6": "Constructor",
"Eval": "Eval",
"Module": "Module",
"Function": "Function",
"Global": "Global",
"Wasm": "Wasm",
"Constructor": "Constructor",
};
function decodeStackFrame(bb) {
@@ -332,40 +332,40 @@ function encodeJSException(message, bb) {
bb.writeByte(0);
}
const FallbackStep = {
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
ssr_disabled: 1,
create_vm: 2,
configure_router: 3,
configure_defines: 4,
resolve_entry_point: 5,
load_entry_point: 6,
eval_entry_point: 7,
fetch_event_handler: 8,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"ssr_disabled": 1,
"create_vm": 2,
"configure_router": 3,
"configure_defines": 4,
"resolve_entry_point": 5,
"load_entry_point": 6,
"eval_entry_point": 7,
"fetch_event_handler": 8,
};
const FallbackStepKeys = {
1: "ssr_disabled",
2: "create_vm",
3: "configure_router",
4: "configure_defines",
5: "resolve_entry_point",
6: "load_entry_point",
7: "eval_entry_point",
8: "fetch_event_handler",
ssr_disabled: "ssr_disabled",
create_vm: "create_vm",
configure_router: "configure_router",
configure_defines: "configure_defines",
resolve_entry_point: "resolve_entry_point",
load_entry_point: "load_entry_point",
eval_entry_point: "eval_entry_point",
fetch_event_handler: "fetch_event_handler",
"1": "ssr_disabled",
"2": "create_vm",
"3": "configure_router",
"4": "configure_defines",
"5": "resolve_entry_point",
"6": "load_entry_point",
"7": "eval_entry_point",
"8": "fetch_event_handler",
"ssr_disabled": "ssr_disabled",
"create_vm": "create_vm",
"configure_router": "configure_router",
"configure_defines": "configure_defines",
"resolve_entry_point": "resolve_entry_point",
"load_entry_point": "load_entry_point",
"eval_entry_point": "eval_entry_point",
"fetch_event_handler": "fetch_event_handler",
};
function decodeProblems(bb) {
@@ -517,76 +517,76 @@ function encodeFallbackMessageContainer(message, bb) {
bb.writeByte(0);
}
const ResolveMode = {
1: 1,
2: 2,
3: 3,
4: 4,
disable: 1,
lazy: 2,
dev: 3,
bundle: 4,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"disable": 1,
"lazy": 2,
"dev": 3,
"bundle": 4,
};
const ResolveModeKeys = {
1: "disable",
2: "lazy",
3: "dev",
4: "bundle",
disable: "disable",
lazy: "lazy",
dev: "dev",
bundle: "bundle",
"1": "disable",
"2": "lazy",
"3": "dev",
"4": "bundle",
"disable": "disable",
"lazy": "lazy",
"dev": "dev",
"bundle": "bundle",
};
const Platform = {
1: 1,
2: 2,
3: 3,
4: 4,
browser: 1,
node: 2,
bun: 3,
bun_macro: 4,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"browser": 1,
"node": 2,
"bun": 3,
"bun_macro": 4,
};
const PlatformKeys = {
1: "browser",
2: "node",
3: "bun",
4: "bun_macro",
browser: "browser",
node: "node",
bun: "bun",
bun_macro: "bun_macro",
"1": "browser",
"2": "node",
"3": "bun",
"4": "bun_macro",
"browser": "browser",
"node": "node",
"bun": "bun",
"bun_macro": "bun_macro",
};
const CSSInJSBehavior = {
1: 1,
2: 2,
3: 3,
facade: 1,
facade_onimportcss: 2,
auto_onimportcss: 3,
"1": 1,
"2": 2,
"3": 3,
"facade": 1,
"facade_onimportcss": 2,
"auto_onimportcss": 3,
};
const CSSInJSBehaviorKeys = {
1: "facade",
2: "facade_onimportcss",
3: "auto_onimportcss",
facade: "facade",
facade_onimportcss: "facade_onimportcss",
auto_onimportcss: "auto_onimportcss",
"1": "facade",
"2": "facade_onimportcss",
"3": "auto_onimportcss",
"facade": "facade",
"facade_onimportcss": "facade_onimportcss",
"auto_onimportcss": "auto_onimportcss",
};
const JSXRuntime = {
1: 1,
2: 2,
3: 3,
automatic: 1,
classic: 2,
solid: 3,
"1": 1,
"2": 2,
"3": 3,
"automatic": 1,
"classic": 2,
"solid": 3,
};
const JSXRuntimeKeys = {
1: "automatic",
2: "classic",
3: "solid",
automatic: "automatic",
classic: "classic",
solid: "solid",
"1": "automatic",
"2": "classic",
"3": "solid",
"automatic": "automatic",
"classic": "classic",
"solid": "solid",
};
function decodeJSX(bb) {
@@ -914,28 +914,28 @@ function encodeJavascriptBundleContainer(message, bb) {
bb.writeByte(0);
}
const ScanDependencyMode = {
1: 1,
2: 2,
app: 1,
all: 2,
"1": 1,
"2": 2,
"app": 1,
"all": 2,
};
const ScanDependencyModeKeys = {
1: "app",
2: "all",
app: "app",
all: "all",
"1": "app",
"2": "all",
"app": "app",
"all": "all",
};
const ModuleImportType = {
1: 1,
2: 2,
import: 1,
require: 2,
"1": 1,
"2": 2,
"import": 1,
"require": 2,
};
const ModuleImportTypeKeys = {
1: "import",
2: "require",
import: "import",
require: "require",
"1": "import",
"2": "require",
"import": "import",
"require": "require",
};
function decodeModuleImportRecord(bb) {
@@ -1086,20 +1086,20 @@ function encodeLoaderMap(message, bb) {
}
}
const DotEnvBehavior = {
1: 1,
2: 2,
3: 3,
disable: 1,
prefix: 2,
load_all: 3,
"1": 1,
"2": 2,
"3": 3,
"disable": 1,
"prefix": 2,
"load_all": 3,
};
const DotEnvBehaviorKeys = {
1: "disable",
2: "prefix",
3: "load_all",
disable: "disable",
prefix: "prefix",
load_all: "load_all",
"1": "disable",
"2": "prefix",
"3": "load_all",
"disable": "disable",
"prefix": "prefix",
"load_all": "load_all",
};
function decodeEnvConfig(bb) {
@@ -1905,16 +1905,16 @@ function encodeTransformOptions(message, bb) {
bb.writeByte(0);
}
const SourceMapMode = {
1: 1,
2: 2,
inline_into_file: 1,
external: 2,
"1": 1,
"2": 2,
"inline_into_file": 1,
"external": 2,
};
const SourceMapModeKeys = {
1: "inline_into_file",
2: "external",
inline_into_file: "inline_into_file",
external: "external",
"1": "inline_into_file",
"2": "external",
"inline_into_file": "inline_into_file",
"external": "external",
};
function decodeFileHandle(bb) {
@@ -2133,52 +2133,52 @@ function encodeScannedImport(message, bb) {
}
}
const ImportKind = {
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
entry_point: 1,
stmt: 2,
require: 3,
dynamic: 4,
require_resolve: 5,
at: 6,
url: 7,
internal: 8,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"entry_point": 1,
"stmt": 2,
"require": 3,
"dynamic": 4,
"require_resolve": 5,
"at": 6,
"url": 7,
"internal": 8,
};
const ImportKindKeys = {
1: "entry_point",
2: "stmt",
3: "require",
4: "dynamic",
5: "require_resolve",
6: "at",
7: "url",
8: "internal",
entry_point: "entry_point",
stmt: "stmt",
require: "require",
dynamic: "dynamic",
require_resolve: "require_resolve",
at: "at",
url: "url",
internal: "internal",
"1": "entry_point",
"2": "stmt",
"3": "require",
"4": "dynamic",
"5": "require_resolve",
"6": "at",
"7": "url",
"8": "internal",
"entry_point": "entry_point",
"stmt": "stmt",
"require": "require",
"dynamic": "dynamic",
"require_resolve": "require_resolve",
"at": "at",
"url": "url",
"internal": "internal",
};
const TransformResponseStatus = {
1: 1,
2: 2,
success: 1,
fail: 2,
"1": 1,
"2": 2,
"success": 1,
"fail": 2,
};
const TransformResponseStatusKeys = {
1: "success",
2: "fail",
success: "success",
fail: "fail",
"1": "success",
"2": "fail",
"success": "success",
"fail": "fail",
};
function decodeOutputFile(bb) {
@@ -2256,28 +2256,28 @@ function encodeTransformResponse(message, bb) {
}
}
const MessageLevel = {
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
err: 1,
warn: 2,
note: 3,
info: 4,
debug: 5,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"err": 1,
"warn": 2,
"note": 3,
"info": 4,
"debug": 5,
};
const MessageLevelKeys = {
1: "err",
2: "warn",
3: "note",
4: "info",
5: "debug",
err: "err",
warn: "warn",
note: "note",
info: "info",
debug: "debug",
"1": "err",
"2": "warn",
"3": "note",
"4": "info",
"5": "debug",
"err": "err",
"warn": "warn",
"note": "note",
"info": "info",
"debug": "debug",
};
function decodeLocation(bb) {
@@ -2508,72 +2508,72 @@ function encodeLog(message, bb) {
}
}
const Reloader = {
1: 1,
2: 2,
3: 3,
disable: 1,
live: 2,
fast_refresh: 3,
"1": 1,
"2": 2,
"3": 3,
"disable": 1,
"live": 2,
"fast_refresh": 3,
};
const ReloaderKeys = {
1: "disable",
2: "live",
3: "fast_refresh",
disable: "disable",
live: "live",
fast_refresh: "fast_refresh",
"1": "disable",
"2": "live",
"3": "fast_refresh",
"disable": "disable",
"live": "live",
"fast_refresh": "fast_refresh",
};
const WebsocketMessageKind = {
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
welcome: 1,
file_change_notification: 2,
build_success: 3,
build_fail: 4,
manifest_success: 5,
manifest_fail: 6,
resolve_file: 7,
file_change_notification_with_hint: 8,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"welcome": 1,
"file_change_notification": 2,
"build_success": 3,
"build_fail": 4,
"manifest_success": 5,
"manifest_fail": 6,
"resolve_file": 7,
"file_change_notification_with_hint": 8,
};
const WebsocketMessageKindKeys = {
1: "welcome",
2: "file_change_notification",
3: "build_success",
4: "build_fail",
5: "manifest_success",
6: "manifest_fail",
7: "resolve_file",
8: "file_change_notification_with_hint",
welcome: "welcome",
file_change_notification: "file_change_notification",
build_success: "build_success",
build_fail: "build_fail",
manifest_success: "manifest_success",
manifest_fail: "manifest_fail",
resolve_file: "resolve_file",
file_change_notification_with_hint: "file_change_notification_with_hint",
"1": "welcome",
"2": "file_change_notification",
"3": "build_success",
"4": "build_fail",
"5": "manifest_success",
"6": "manifest_fail",
"7": "resolve_file",
"8": "file_change_notification_with_hint",
"welcome": "welcome",
"file_change_notification": "file_change_notification",
"build_success": "build_success",
"build_fail": "build_fail",
"manifest_success": "manifest_success",
"manifest_fail": "manifest_fail",
"resolve_file": "resolve_file",
"file_change_notification_with_hint": "file_change_notification_with_hint",
};
const WebsocketCommandKind = {
1: 1,
2: 2,
3: 3,
build: 1,
manifest: 2,
build_with_file_path: 3,
"1": 1,
"2": 2,
"3": 3,
"build": 1,
"manifest": 2,
"build_with_file_path": 3,
};
const WebsocketCommandKindKeys = {
1: "build",
2: "manifest",
3: "build_with_file_path",
build: "build",
manifest: "manifest",
build_with_file_path: "build_with_file_path",
"1": "build",
"2": "manifest",
"3": "build_with_file_path",
"build": "build",
"manifest": "manifest",
"build_with_file_path": "build_with_file_path",
};
function decodeWebsocketMessage(bb) {

View File

@@ -692,6 +692,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
pending_promises_for_abort: u8 = 0,
has_marked_complete: bool = false,
has_marked_pending: bool = false,
response_jsvalue: JSC.JSValue = JSC.JSValue.zero,
response_protected: bool = false,
response_ptr: ?*JSC.WebCore.Response = null,
@@ -835,7 +837,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
return;
}
if (!ctx.resp.hasResponded()) {
if (!ctx.resp.hasResponded() and !ctx.has_marked_pending) {
ctx.renderMissing();
return;
}
@@ -930,6 +932,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
return;
}
this.has_marked_pending = true;
this.response_buf_owned = std.ArrayListUnmanaged(u8){ .items = bb.items, .capacity = bb.capacity };
this.resp.onWritable(*RequestContext, onWritableCompleteResponseBuffer, this);
this.setAbortHandler();
@@ -948,11 +951,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
this.response_buf_owned.items.len,
this.shouldCloseConnection(),
)) {
this.has_marked_pending = true;
this.resp.onWritable(*RequestContext, onWritableCompleteResponseBuffer, this);
this.setAbortHandler();
return;
}
this.finalize();
}
@@ -1320,6 +1323,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
if (!this.sendfile.has_set_on_writable) {
this.sendfile.has_set_on_writable = true;
this.has_marked_pending = true;
this.resp.onWritable(*RequestContext, onWritableSendfile, this);
}
@@ -1352,6 +1356,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
this.finalize();
return true;
} else {
this.has_marked_pending = true;
this.resp.onWritable(*RequestContext, onWritableBytes, this);
return true;
}
@@ -1365,6 +1370,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
this.response_buf_owned.items.len = 0;
this.finalize();
} else {
this.has_marked_pending = true;
this.resp.onWritable(*RequestContext, onWritableCompleteResponseBuffer, this);
}
@@ -1885,7 +1891,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
}
// The user returned something that wasn't a promise or a promise with a response
if (!ctx.resp.hasResponded()) ctx.renderMissing();
if (!ctx.resp.hasResponded() and !ctx.has_marked_pending) ctx.renderMissing();
}
pub fn handleResolveStream(req: *RequestContext) void {
@@ -2139,6 +2145,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
} else {
// when it's the last one, we just want to know if it's done
if (stream.isDone()) {
this.has_marked_pending = true;
this.resp.onWritable(*RequestContext, onWritableResponseBuffer, this);
}
}
@@ -2394,6 +2401,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
bytes.len,
this.shouldCloseConnection(),
)) {
this.has_marked_pending = true;
this.resp.onWritable(*RequestContext, onWritableBytes, this);
// given a blob, we might not have set an abort handler yet
this.setAbortHandler();

View File

@@ -1025,11 +1025,15 @@ extern "C"
if (ssl)
{
uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res;
uwsRes->getHttpResponseData()->onWritable = nullptr;
uwsRes->onAborted(nullptr);
uwsRes->end(std::string_view(data, length), close_connection);
}
else
{
uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res;
uwsRes->getHttpResponseData()->onWritable = nullptr;
uwsRes->onAborted(nullptr);
uwsRes->end(std::string_view(data, length), close_connection);
}
}
@@ -1039,11 +1043,15 @@ extern "C"
if (ssl)
{
uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res;
uwsRes->getHttpResponseData()->onWritable = nullptr;
uwsRes->onAborted(nullptr);
uwsRes->endWithoutBody(std::nullopt, close_connection);
}
else
{
uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res;
uwsRes->getHttpResponseData()->onWritable = nullptr;
uwsRes->onAborted(nullptr);
uwsRes->endWithoutBody(std::nullopt, close_connection);
}
}
@@ -1514,12 +1522,22 @@ extern "C"
{
uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res;
auto pair = uwsRes->tryEnd(std::string_view(bytes, len), total_len, close);
if (pair.first) {
uwsRes->getHttpResponseData()->onWritable = nullptr;
uwsRes->onAborted(nullptr);
}
return pair.first;
}
else
{
uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res;
auto pair = uwsRes->tryEnd(std::string_view(bytes, len), total_len, close);
if (pair.first) {
uwsRes->getHttpResponseData()->onWritable = nullptr;
uwsRes->onAborted(nullptr);
}
return pair.first;
}
}
@@ -1551,4 +1569,4 @@ extern "C"
return uwsRes->getNativeHandle();
}
}
}
}

View File

@@ -0,0 +1,21 @@
const server = Bun.serve({
port: 0,
async fetch(req) {
console.log(await req.json());
return new Response();
},
});
console.log(
JSON.stringify({
hostname: server.hostname,
port: server.port,
}),
);
(async function () {
for await (let line of console) {
if (line === "--CLOSE--") {
process.exit(0);
}
}
})();

View File

@@ -0,0 +1,95 @@
import { expect, it } from "bun:test";
import { bunExe, bunEnv } from "../../harness.js";
import { mkdirSync, rmSync, writeFileSync, readFileSync, mkdtempSync } from "fs";
import { tmpdir } from "os";
import { dirname, join } from "path";
import { sleep, spawn, spawnSync, which } from "bun";
// https://github.com/oven-sh/bun/issues/2499
it("onAborted() and onWritable are not called after receiving an empty response body due to a promise rejection", async testDone => {
var timeout = AbortSignal.timeout(10_000);
timeout.onabort = e => {
testDone(new Error("Test timed out, which means it failed"));
};
const body = new FormData();
body.append("hey", "hi");
// We want to test that the server isn't keeping the connection open in a
// zombie-like state when an error occurs due to an unhandled rejected promise
//
// At the time of writing, this can only happen when:
// - development mode is enabled
// - the server didn't send the complete response body in one send()
// - renderMissing() is called
//
// In that case, it finalizes the response in the middle of an incomplete body
//
// On an M1, this reproduces 1 out of every 4 calls to this function
// It's inherently going to be flaky without simulating system calls or overriding libc
//
// So to make sure we catch it
// 1) Run this test 40 times
// 2) Set a timeout for this test of 10 seconds.
//
// In debug builds, this test should complete in 1-2 seconds.
for (let i = 0; i < 40; i++) {
let bunProcess;
try {
bunProcess = spawn({
cmd: [bunExe(), "run", join(import.meta.dir, "./02499-repro.ts")],
stdin: "pipe",
stderr: "ignore",
stdout: "pipe",
env: bunEnv,
});
const reader = bunProcess.stdout?.getReader();
let hostname, port;
{
const chunks = [];
var decoder = new TextDecoder();
while (!hostname && !port) {
// @ts-expect-error TODO
var { value, done } = await reader?.read();
if (done) break;
if (chunks.length > 0) {
chunks.push(value);
}
try {
if (chunks.length > 0) {
value = Buffer.concat(chunks);
}
({ hostname, port } = JSON.parse(decoder.decode(value).trim()));
} catch {
chunks.push(value);
}
}
}
try {
await fetch(`http://${hostname}:${port}/upload`, {
body,
keepalive: false,
method: "POST",
timeout: true,
signal: timeout,
});
} catch (e) {}
bunProcess.stdin?.write("--CLOSE--");
await bunProcess.stdin?.flush();
await bunProcess.stdin?.end();
expect(await bunProcess.exited).toBe(0);
} catch (e) {
timeout.onabort = () => {};
testDone(e);
throw e;
} finally {
bunProcess?.kill(9);
}
}
timeout.onabort = () => {};
testDone();
});