diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/tests/unit/command_test.ts | 40 | ||||
-rw-r--r-- | cli/tests/unit/files_test.ts | 27 | ||||
-rw-r--r-- | cli/tests/unit/fs_events_test.ts | 30 | ||||
-rw-r--r-- | cli/tests/unit/http_test.ts | 18 | ||||
-rw-r--r-- | cli/tests/unit/kv_test.ts | 27 | ||||
-rw-r--r-- | cli/tests/unit/net_test.ts | 31 | ||||
-rw-r--r-- | cli/tests/unit/serve_test.ts | 46 | ||||
-rw-r--r-- | cli/tsc/99_main_compiler.js | 6 | ||||
-rw-r--r-- | cli/tsc/dts/lib.deno.ns.d.ts | 16 | ||||
-rw-r--r-- | cli/tsc/dts/lib.deno.unstable.d.ts | 4 |
10 files changed, 237 insertions, 8 deletions
diff --git a/cli/tests/unit/command_test.ts b/cli/tests/unit/command_test.ts index 5f56a0c22..299c70b9b 100644 --- a/cli/tests/unit/command_test.ts +++ b/cli/tests/unit/command_test.ts @@ -258,6 +258,46 @@ Deno.test( Deno.test( { permissions: { run: true, read: true } }, + // deno lint bug, see https://github.com/denoland/deno_lint/issues/1206 + // deno-lint-ignore require-await + async function childProcessExplicitResourceManagement() { + let dead = undefined; + { + const command = new Deno.Command(Deno.execPath(), { + args: ["eval", "setTimeout(() => {}, 10000)"], + stdout: "null", + stderr: "null", + }); + await using child = command.spawn(); + child.status.then(({ signal }) => { + dead = signal; + }); + } + + if (Deno.build.os == "windows") { + assertEquals(dead, null); + } else { + assertEquals(dead, "SIGTERM"); + } + }, +); + +Deno.test( + { permissions: { run: true, read: true } }, + async function childProcessExplicitResourceManagementManualClose() { + const command = new Deno.Command(Deno.execPath(), { + args: ["eval", "setTimeout(() => {}, 10000)"], + stdout: "null", + stderr: "null", + }); + await using child = command.spawn(); + child.kill("SIGTERM"); + await child.status; + }, +); + +Deno.test( + { permissions: { run: true, read: true } }, async function commandKillFailed() { const command = new Deno.Command(Deno.execPath(), { args: ["eval", "setTimeout(() => {}, 5000)"], diff --git a/cli/tests/unit/files_test.ts b/cli/tests/unit/files_test.ts index 873c70c86..3e0390ae6 100644 --- a/cli/tests/unit/files_test.ts +++ b/cli/tests/unit/files_test.ts @@ -824,3 +824,30 @@ Deno.test( assertEquals(res, "hello \uFFFD"); }, ); + +Deno.test( + { permissions: { read: true } }, + async function fsFileExplicitResourceManagement() { + let file2: Deno.FsFile; + + { + using file = await Deno.open("cli/tests/testdata/assets/hello.txt"); + file2 = file; + + const stat = file.statSync(); + assert(stat.isFile); + } + + assertThrows(() => file2.statSync(), Deno.errors.BadResource); + }, +); + +Deno.test( + { permissions: { read: true } }, + async function fsFileExplicitResourceManagementManualClose() { + using file = await Deno.open("cli/tests/testdata/assets/hello.txt"); + file.close(); + assertThrows(() => file.statSync(), Deno.errors.BadResource); // definitely closed + // calling [Symbol.dispose] after manual close is a no-op + }, +); diff --git a/cli/tests/unit/fs_events_test.ts b/cli/tests/unit/fs_events_test.ts index 9330f2007..86adeb4d7 100644 --- a/cli/tests/unit/fs_events_test.ts +++ b/cli/tests/unit/fs_events_test.ts @@ -107,3 +107,33 @@ Deno.test( assertEquals(events, []); }, ); + +Deno.test( + { permissions: { read: true, write: true } }, + async function watchFsExplicitResourceManagement() { + let res; + { + const testDir = await makeTempDir(); + using iter = Deno.watchFs(testDir); + + res = iter[Symbol.asyncIterator]().next(); + } + + const { done } = await res; + assert(done); + }, +); + +Deno.test( + { permissions: { read: true, write: true } }, + async function watchFsExplicitResourceManagementManualClose() { + const testDir = await makeTempDir(); + using iter = Deno.watchFs(testDir); + + const res = iter[Symbol.asyncIterator]().next(); + + iter.close(); + const { done } = await res; + assert(done); + }, +); diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts index 10414cab3..8c7bf9974 100644 --- a/cli/tests/unit/http_test.ts +++ b/cli/tests/unit/http_test.ts @@ -2817,6 +2817,24 @@ Deno.test({ }, }); +Deno.test( + async function httpConnExplicitResourceManagement() { + let promise; + + { + const listen = Deno.listen({ port: listenPort }); + promise = fetch(`http://localhost:${listenPort}/`).catch(() => null); + const serverConn = await listen.accept(); + listen.close(); + + using _httpConn = Deno.serveHttp(serverConn); + } + + const response = await promise; + assertEquals(response, null); + }, +); + function chunkedBodyReader(h: Headers, r: BufReader): Deno.Reader { // Based on https://tools.ietf.org/html/rfc2616#section-19.4.6 const tp = new TextProtoReader(r); diff --git a/cli/tests/unit/kv_test.ts b/cli/tests/unit/kv_test.ts index 4e3ce5385..0bfc75481 100644 --- a/cli/tests/unit/kv_test.ts +++ b/cli/tests/unit/kv_test.ts @@ -2100,3 +2100,30 @@ Deno.test({ db.close(); }, }); + +Deno.test( + { permissions: { read: true } }, + async function kvExplicitResourceManagement() { + let kv2: Deno.Kv; + + { + using kv = await Deno.openKv(":memory:"); + kv2 = kv; + + const res = await kv.get(["a"]); + assertEquals(res.versionstamp, null); + } + + await assertRejects(() => kv2.get(["a"]), Deno.errors.BadResource); + }, +); + +Deno.test( + { permissions: { read: true } }, + async function kvExplicitResourceManagementManualClose() { + using kv = await Deno.openKv(":memory:"); + kv.close(); + await assertRejects(() => kv.get(["a"]), Deno.errors.BadResource); + // calling [Symbol.dispose] after manual close is a no-op + }, +); diff --git a/cli/tests/unit/net_test.ts b/cli/tests/unit/net_test.ts index 2a98b5e26..db99d2480 100644 --- a/cli/tests/unit/net_test.ts +++ b/cli/tests/unit/net_test.ts @@ -1242,3 +1242,34 @@ Deno.test({ const listener = Deno.listen({ hostname: "localhost", port: "0" }); listener.close(); }); + +Deno.test( + { permissions: { net: true } }, + async function listenerExplicitResourceManagement() { + let done: Promise<Deno.errors.BadResource>; + + { + using listener = Deno.listen({ port: listenPort }); + + done = assertRejects( + () => listener.accept(), + Deno.errors.BadResource, + ); + } + + await done; + }, +); + +Deno.test( + { permissions: { net: true } }, + async function listenerExplicitResourceManagementManualClose() { + using listener = Deno.listen({ port: listenPort }); + listener.close(); + await assertRejects( // definitely closed + () => listener.accept(), + Deno.errors.BadResource, + ); + // calling [Symbol.dispose] after manual close is a no-op + }, +); diff --git a/cli/tests/unit/serve_test.ts b/cli/tests/unit/serve_test.ts index 2e560af99..9f6bd4aa1 100644 --- a/cli/tests/unit/serve_test.ts +++ b/cli/tests/unit/serve_test.ts @@ -48,7 +48,12 @@ function onListen<T>( async function makeServer( handler: (req: Request) => Response | Promise<Response>, ): Promise< - { finished: Promise<void>; abort: () => void; shutdown: () => Promise<void> } + { + finished: Promise<void>; + abort: () => void; + shutdown: () => Promise<void>; + [Symbol.asyncDispose](): PromiseLike<void>; + } > { const ac = new AbortController(); const listeningPromise = deferred(); @@ -69,6 +74,9 @@ async function makeServer( async shutdown() { await server.shutdown(); }, + [Symbol.asyncDispose]() { + return server[Symbol.asyncDispose](); + }, }; } @@ -297,6 +305,42 @@ Deno.test( ); Deno.test( + { permissions: { net: true, write: true, read: true } }, + async function httpServerExplicitResourceManagement() { + let dataPromise; + + { + await using _server = await makeServer(async (_req) => { + return new Response((await makeTempFile(1024 * 1024)).readable); + }); + + const resp = await fetch(`http://localhost:${servePort}`); + dataPromise = resp.arrayBuffer(); + } + + assertEquals((await dataPromise).byteLength, 1048576); + }, +); + +Deno.test( + { permissions: { net: true, write: true, read: true } }, + async function httpServerExplicitResourceManagementManualClose() { + await using server = await makeServer(async (_req) => { + return new Response((await makeTempFile(1024 * 1024)).readable); + }); + + const resp = await fetch(`http://localhost:${servePort}`); + + const [_, data] = await Promise.all([ + server.shutdown(), + resp.arrayBuffer(), + ]); + + assertEquals(data.byteLength, 1048576); + }, +); + +Deno.test( { permissions: { read: true, run: true } }, async function httpServerUnref() { const [statusCode, _output] = await execCode(` diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index e246d5d0c..a2079c01e 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -1138,6 +1138,7 @@ delete Object.prototype.__proto__; `${ASSETS_URL_PREFIX}${specifier}`, ts.ScriptTarget.ESNext, ), + `failed to load '${ASSETS_URL_PREFIX}${specifier}'`, ); } // this helps ensure as much as possible is in memory that is re-usable @@ -1148,7 +1149,10 @@ delete Object.prototype.__proto__; options: SNAPSHOT_COMPILE_OPTIONS, host, }); - assert(ts.getPreEmitDiagnostics(TS_SNAPSHOT_PROGRAM).length === 0); + assert( + ts.getPreEmitDiagnostics(TS_SNAPSHOT_PROGRAM).length === 0, + "lib.d.ts files have errors", + ); // remove this now that we don't need it anymore for warming up tsc sourceFileCache.delete(buildSpecifier); diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index e51e9736a..08c8e9e9d 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -2,6 +2,7 @@ /// <reference no-default-lib="true" /> /// <reference lib="esnext" /> +/// <reference lib="esnext.disposable" /> /// <reference lib="deno.net" /> /** Deno provides extra properties on `import.meta`. These are included here @@ -2177,7 +2178,8 @@ declare namespace Deno { WriterSync, Seeker, SeekerSync, - Closer { + Closer, + Disposable { /** The resource ID associated with the file instance. The resource ID * should be considered an opaque reference to resource. */ readonly rid: number; @@ -2451,6 +2453,8 @@ declare namespace Deno { * ``` */ close(): void; + + [Symbol.dispose](): void; } /** @@ -3831,7 +3835,7 @@ declare namespace Deno { * * @category File System */ - export interface FsWatcher extends AsyncIterable<FsEvent> { + export interface FsWatcher extends AsyncIterable<FsEvent>, Disposable { /** The resource id. */ readonly rid: number; /** Stops watching the file system and closes the watcher resource. */ @@ -4284,7 +4288,7 @@ declare namespace Deno { * * @category Sub Process */ - export class ChildProcess { + export class ChildProcess implements Disposable { get stdin(): WritableStream<Uint8Array>; get stdout(): ReadableStream<Uint8Array>; get stderr(): ReadableStream<Uint8Array>; @@ -4307,6 +4311,8 @@ declare namespace Deno { /** Ensure that the status of the child process does not block the Deno * process from exiting. */ unref(): void; + + [Symbol.dispose](): void; } /** @@ -5258,7 +5264,7 @@ declare namespace Deno { * requests on the HTTP server connection. * * @category HTTP Server */ - export interface HttpConn extends AsyncIterable<RequestEvent> { + export interface HttpConn extends AsyncIterable<RequestEvent>, Disposable { /** The resource ID associated with this connection. Generally users do not * need to be aware of this identifier. */ readonly rid: number; @@ -5911,7 +5917,7 @@ declare namespace Deno { * * @category HTTP Server */ - export interface HttpServer { + export interface HttpServer extends AsyncDisposable { /** A promise that resolves once server finishes - eg. when aborted using * the signal passed to {@linkcode ServeOptions.signal}. */ diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index c758e620c..3bdba510b 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -1749,7 +1749,7 @@ declare namespace Deno { * * @category KV */ - export class Kv { + export class Kv implements Disposable { /** * Retrieve the value and versionstamp for the given key from the database * in the form of a {@linkcode Deno.KvEntryMaybe}. If no value exists for @@ -1945,6 +1945,8 @@ declare namespace Deno { * operations immediately. */ close(): void; + + [Symbol.dispose](): void; } /** **UNSTABLE**: New API, yet to be vetted. |