mirror of
https://github.com/denoland/deno.git
synced 2025-08-04 10:59:13 +00:00
This commit is contained in:
parent
cc0a372127
commit
7281775381
9 changed files with 204 additions and 239 deletions
|
@ -15,8 +15,10 @@ const {
|
|||
MapPrototypeGet,
|
||||
MapPrototypeHas,
|
||||
MapPrototypeSet,
|
||||
ObjectKeys,
|
||||
Promise,
|
||||
SafeArrayIterator,
|
||||
Set,
|
||||
SymbolToStringTag,
|
||||
TypeError,
|
||||
} = primordials;
|
||||
|
@ -340,6 +342,182 @@ function assertOps(fn) {
|
|||
};
|
||||
}
|
||||
|
||||
function prettyResourceNames(name) {
|
||||
switch (name) {
|
||||
case "fsFile":
|
||||
return ["A file", "opened", "closed"];
|
||||
case "fetchRequest":
|
||||
return ["A fetch request", "started", "finished"];
|
||||
case "fetchRequestBody":
|
||||
return ["A fetch request body", "created", "closed"];
|
||||
case "fetchResponse":
|
||||
return ["A fetch response body", "created", "consumed"];
|
||||
case "httpClient":
|
||||
return ["An HTTP client", "created", "closed"];
|
||||
case "dynamicLibrary":
|
||||
return ["A dynamic library", "loaded", "unloaded"];
|
||||
case "httpConn":
|
||||
return ["An inbound HTTP connection", "accepted", "closed"];
|
||||
case "httpStream":
|
||||
return ["An inbound HTTP request", "accepted", "closed"];
|
||||
case "tcpStream":
|
||||
return ["A TCP connection", "opened/accepted", "closed"];
|
||||
case "unixStream":
|
||||
return ["A Unix connection", "opened/accepted", "closed"];
|
||||
case "tlsStream":
|
||||
return ["A TLS connection", "opened/accepted", "closed"];
|
||||
case "tlsListener":
|
||||
return ["A TLS listener", "opened", "closed"];
|
||||
case "unixListener":
|
||||
return ["A Unix listener", "opened", "closed"];
|
||||
case "unixDatagram":
|
||||
return ["A Unix datagram", "opened", "closed"];
|
||||
case "tcpListener":
|
||||
return ["A TCP listener", "opened", "closed"];
|
||||
case "udpSocket":
|
||||
return ["A UDP socket", "opened", "closed"];
|
||||
case "timer":
|
||||
return ["A timer", "started", "fired/cleared"];
|
||||
case "textDecoder":
|
||||
return ["A text decoder", "created", "finished"];
|
||||
case "messagePort":
|
||||
return ["A message port", "created", "closed"];
|
||||
case "webSocketStream":
|
||||
return ["A WebSocket", "opened", "closed"];
|
||||
case "fsEvents":
|
||||
return ["A file system watcher", "created", "closed"];
|
||||
case "childStdin":
|
||||
return ["A child process stdin", "opened", "closed"];
|
||||
case "childStdout":
|
||||
return ["A child process stdout", "opened", "closed"];
|
||||
case "childStderr":
|
||||
return ["A child process stderr", "opened", "closed"];
|
||||
case "child":
|
||||
return ["A child process", "started", "closed"];
|
||||
case "signal":
|
||||
return ["A signal listener", "created", "fired/cleared"];
|
||||
case "stdin":
|
||||
return ["The stdin pipe", "opened", "closed"];
|
||||
case "stdout":
|
||||
return ["The stdout pipe", "opened", "closed"];
|
||||
case "stderr":
|
||||
return ["The stderr pipe", "opened", "closed"];
|
||||
case "compression":
|
||||
return ["A CompressionStream", "created", "closed"];
|
||||
default:
|
||||
return [`A "${name}" resource`, "created", "cleaned up"];
|
||||
}
|
||||
}
|
||||
|
||||
function resourceCloseHint(name) {
|
||||
switch (name) {
|
||||
case "fsFile":
|
||||
return "Close the file handle by calling `file.close()`.";
|
||||
case "fetchRequest":
|
||||
return "Await the promise returned from `fetch()` or abort the fetch with an abort signal.";
|
||||
case "fetchRequestBody":
|
||||
return "Terminate the request body `ReadableStream` by closing or erroring it.";
|
||||
case "fetchResponse":
|
||||
return "Consume or close the response body `ReadableStream`, e.g `await resp.text()` or `await resp.body.cancel()`.";
|
||||
case "httpClient":
|
||||
return "Close the HTTP client by calling `httpClient.close()`.";
|
||||
case "dynamicLibrary":
|
||||
return "Unload the dynamic library by calling `dynamicLibrary.close()`.";
|
||||
case "httpConn":
|
||||
return "Close the inbound HTTP connection by calling `httpConn.close()`.";
|
||||
case "httpStream":
|
||||
return "Close the inbound HTTP request by responding with `e.respondWith()` or closing the HTTP connection.";
|
||||
case "tcpStream":
|
||||
return "Close the TCP connection by calling `tcpConn.close()`.";
|
||||
case "unixStream":
|
||||
return "Close the Unix socket connection by calling `unixConn.close()`.";
|
||||
case "tlsStream":
|
||||
return "Close the TLS connection by calling `tlsConn.close()`.";
|
||||
case "tlsListener":
|
||||
return "Close the TLS listener by calling `tlsListener.close()`.";
|
||||
case "unixListener":
|
||||
return "Close the Unix socket listener by calling `unixListener.close()`.";
|
||||
case "unixDatagram":
|
||||
return "Close the Unix datagram socket by calling `unixDatagram.close()`.";
|
||||
case "tcpListener":
|
||||
return "Close the TCP listener by calling `tcpListener.close()`.";
|
||||
case "udpSocket":
|
||||
return "Close the UDP socket by calling `udpSocket.close()`.";
|
||||
case "timer":
|
||||
return "Clear the timer by calling `clearInterval` or `clearTimeout`.";
|
||||
case "textDecoder":
|
||||
return "Close the text decoder by calling `textDecoder.decode('')` or `await textDecoderStream.readable.cancel()`.";
|
||||
case "messagePort":
|
||||
return "Close the message port by calling `messagePort.close()`.";
|
||||
case "webSocketStream":
|
||||
return "Close the WebSocket by calling `webSocket.close()`.";
|
||||
case "fsEvents":
|
||||
return "Close the file system watcher by calling `watcher.close()`.";
|
||||
case "childStdin":
|
||||
return "Close the child process stdin by calling `proc.stdin.close()`.";
|
||||
case "childStdout":
|
||||
return "Close the child process stdout by calling `proc.stdout.close()` or `await child.stdout.cancel()`.";
|
||||
case "childStderr":
|
||||
return "Close the child process stderr by calling `proc.stderr.close()` or `await child.stderr.cancel()`.";
|
||||
case "child":
|
||||
return "Close the child process by calling `proc.kill()` or `proc.close()`.";
|
||||
case "signal":
|
||||
return "Clear the signal listener by calling `Deno.removeSignalListener`.";
|
||||
case "stdin":
|
||||
return "Close the stdin pipe by calling `Deno.stdin.close()`.";
|
||||
case "stdout":
|
||||
return "Close the stdout pipe by calling `Deno.stdout.close()`.";
|
||||
case "stderr":
|
||||
return "Close the stderr pipe by calling `Deno.stderr.close()`.";
|
||||
case "compression":
|
||||
return "Close the compression stream by calling `await stream.writable.close()`.";
|
||||
default:
|
||||
return "Close the resource before the end of the test.";
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap test function in additional assertion that makes sure
|
||||
// the test case does not "leak" resources - ie. resource table after
|
||||
// the test has exactly the same contents as before the test.
|
||||
function assertResources(fn) {
|
||||
/** @param desc {TestDescription | TestStepDescription} */
|
||||
return async function resourceSanitizer(desc) {
|
||||
const pre = core.resources();
|
||||
const innerResult = await fn(desc);
|
||||
if (innerResult) return innerResult;
|
||||
const post = core.resources();
|
||||
|
||||
const allResources = new Set([
|
||||
...new SafeArrayIterator(ObjectKeys(pre)),
|
||||
...new SafeArrayIterator(ObjectKeys(post)),
|
||||
]);
|
||||
|
||||
const details = [];
|
||||
for (const resource of allResources) {
|
||||
const preResource = pre[resource];
|
||||
const postResource = post[resource];
|
||||
if (preResource === postResource) continue;
|
||||
|
||||
if (preResource === undefined) {
|
||||
const [name, action1, action2] = prettyResourceNames(postResource);
|
||||
const hint = resourceCloseHint(postResource);
|
||||
const detail =
|
||||
`${name} (rid ${resource}) was ${action1} during the test, but not ${action2} during the test. ${hint}`;
|
||||
ArrayPrototypePush(details, detail);
|
||||
} else {
|
||||
const [name, action1, action2] = prettyResourceNames(preResource);
|
||||
const detail =
|
||||
`${name} (rid ${resource}) was ${action1} before the test started, but was ${action2} during the test. Do not close resources in a test that were not created during that test.`;
|
||||
ArrayPrototypePush(details, detail);
|
||||
}
|
||||
}
|
||||
if (details.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return { failed: { leakedResources: details } };
|
||||
};
|
||||
}
|
||||
|
||||
// Wrap test function in additional assertion that makes sure
|
||||
// that the test case does not accidentally exit prematurely.
|
||||
function assertExit(fn, isTest) {
|
||||
|
@ -542,8 +720,6 @@ function testInner(
|
|||
testDesc.name,
|
||||
testDesc.ignore,
|
||||
testDesc.only,
|
||||
false, /*testDesc.sanitizeOps*/
|
||||
testDesc.sanitizeResources,
|
||||
testDesc.location.fileName,
|
||||
testDesc.location.lineNumber,
|
||||
testDesc.location.columnNumber,
|
||||
|
@ -734,6 +910,9 @@ function wrapTest(desc) {
|
|||
if (desc.sanitizeOps) {
|
||||
testFn = assertOps(testFn);
|
||||
}
|
||||
if (desc.sanitizeResources) {
|
||||
testFn = assertResources(testFn);
|
||||
}
|
||||
if (desc.sanitizeExit) {
|
||||
testFn = assertExit(testFn, true);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue