From 30702e2678200b6e21ba142347d2d213b86e9c6d Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 20 May 2020 17:52:51 -0400 Subject: move js unit tests to cli/tests (#5678) --- cli/js/tests/README.md | 80 --- cli/js/tests/abort_controller_test.ts | 56 -- cli/js/tests/blob_test.ts | 92 --- cli/js/tests/body_test.ts | 74 -- cli/js/tests/buffer_test.ts | 294 -------- cli/js/tests/build_test.ts | 10 - cli/js/tests/chmod_test.ts | 149 ---- cli/js/tests/chown_test.ts | 147 ---- cli/js/tests/console_test.ts | 1139 ------------------------------ cli/js/tests/copy_file_test.ts | 176 ----- cli/js/tests/custom_event_test.ts | 27 - cli/js/tests/dir_test.ts | 60 -- cli/js/tests/dispatch_json_test.ts | 32 - cli/js/tests/dispatch_minimal_test.ts | 43 -- cli/js/tests/dom_exception_test.ts | 9 - cli/js/tests/dom_iterable_test.ts | 87 --- cli/js/tests/error_stack_test.ts | 108 --- cli/js/tests/event_target_test.ts | 231 ------ cli/js/tests/event_test.ts | 94 --- cli/js/tests/fetch_test.ts | 534 -------------- cli/js/tests/file_test.ts | 105 --- cli/js/tests/files_test.ts | 571 --------------- cli/js/tests/form_data_test.ts | 203 ------ cli/js/tests/format_error_test.ts | 33 - cli/js/tests/fs_events_test.ts | 71 -- cli/js/tests/get_random_values_test.ts | 51 -- cli/js/tests/globals_test.ts | 112 --- cli/js/tests/headers_test.ts | 420 ----------- cli/js/tests/internals_test.ts | 10 - cli/js/tests/io_test.ts | 73 -- cli/js/tests/link_test.ts | 147 ---- cli/js/tests/make_temp_test.ts | 178 ----- cli/js/tests/metrics_test.ts | 58 -- cli/js/tests/mkdir_test.ts | 206 ------ cli/js/tests/net_test.ts | 527 -------------- cli/js/tests/os_test.ts | 338 --------- cli/js/tests/performance_test.ts | 15 - cli/js/tests/permissions_test.ts | 27 - cli/js/tests/process_test.ts | 386 ---------- cli/js/tests/read_dir_test.ts | 82 --- cli/js/tests/read_file_test.ts | 65 -- cli/js/tests/read_link_test.ts | 73 -- cli/js/tests/read_text_file_test.ts | 61 -- cli/js/tests/real_path_test.ts | 108 --- cli/js/tests/remove_test.ts | 506 ------------- cli/js/tests/rename_test.ts | 216 ------ cli/js/tests/request_test.ts | 49 -- cli/js/tests/resources_test.ts | 61 -- cli/js/tests/signal_test.ts | 195 ----- cli/js/tests/stat_test.ts | 238 ------- cli/js/tests/streams_piping_test.ts | 131 ---- cli/js/tests/streams_transform_test.ts | 562 --------------- cli/js/tests/streams_writable_test.ts | 253 ------- cli/js/tests/symlink_test.ts | 44 -- cli/js/tests/test_util.ts | 364 ---------- cli/js/tests/testing_test.ts | 27 - cli/js/tests/text_encoding_test.ts | 206 ------ cli/js/tests/timers_test.ts | 368 ---------- cli/js/tests/tls_test.ts | 262 ------- cli/js/tests/truncate_test.ts | 80 --- cli/js/tests/tty_test.ts | 23 - cli/js/tests/umask_test.ts | 15 - cli/js/tests/unit_test_runner.ts | 309 -------- cli/js/tests/unit_tests.ts | 72 -- cli/js/tests/url_search_params_test.ts | 275 -------- cli/js/tests/url_test.ts | 407 ----------- cli/js/tests/utime_test.ts | 230 ------ cli/js/tests/version_test.ts | 8 - cli/js/tests/write_file_test.ts | 228 ------ cli/js/tests/write_text_file_test.ts | 79 --- cli/tests/integration_tests.rs | 2 +- cli/tests/unit/README.md | 80 +++ cli/tests/unit/abort_controller_test.ts | 56 ++ cli/tests/unit/blob_test.ts | 92 +++ cli/tests/unit/body_test.ts | 74 ++ cli/tests/unit/buffer_test.ts | 294 ++++++++ cli/tests/unit/build_test.ts | 10 + cli/tests/unit/chmod_test.ts | 149 ++++ cli/tests/unit/chown_test.ts | 147 ++++ cli/tests/unit/console_test.ts | 1139 ++++++++++++++++++++++++++++++ cli/tests/unit/copy_file_test.ts | 176 +++++ cli/tests/unit/custom_event_test.ts | 27 + cli/tests/unit/dir_test.ts | 60 ++ cli/tests/unit/dispatch_json_test.ts | 32 + cli/tests/unit/dispatch_minimal_test.ts | 43 ++ cli/tests/unit/dom_exception_test.ts | 9 + cli/tests/unit/dom_iterable_test.ts | 87 +++ cli/tests/unit/error_stack_test.ts | 108 +++ cli/tests/unit/event_target_test.ts | 231 ++++++ cli/tests/unit/event_test.ts | 94 +++ cli/tests/unit/fetch_test.ts | 534 ++++++++++++++ cli/tests/unit/file_test.ts | 105 +++ cli/tests/unit/files_test.ts | 571 +++++++++++++++ cli/tests/unit/form_data_test.ts | 203 ++++++ cli/tests/unit/format_error_test.ts | 33 + cli/tests/unit/fs_events_test.ts | 71 ++ cli/tests/unit/get_random_values_test.ts | 51 ++ cli/tests/unit/globals_test.ts | 112 +++ cli/tests/unit/headers_test.ts | 420 +++++++++++ cli/tests/unit/internals_test.ts | 10 + cli/tests/unit/io_test.ts | 73 ++ cli/tests/unit/link_test.ts | 147 ++++ cli/tests/unit/make_temp_test.ts | 178 +++++ cli/tests/unit/metrics_test.ts | 58 ++ cli/tests/unit/mkdir_test.ts | 206 ++++++ cli/tests/unit/net_test.ts | 527 ++++++++++++++ cli/tests/unit/os_test.ts | 338 +++++++++ cli/tests/unit/performance_test.ts | 15 + cli/tests/unit/permissions_test.ts | 27 + cli/tests/unit/process_test.ts | 386 ++++++++++ cli/tests/unit/read_dir_test.ts | 82 +++ cli/tests/unit/read_file_test.ts | 65 ++ cli/tests/unit/read_link_test.ts | 73 ++ cli/tests/unit/read_text_file_test.ts | 61 ++ cli/tests/unit/real_path_test.ts | 108 +++ cli/tests/unit/remove_test.ts | 506 +++++++++++++ cli/tests/unit/rename_test.ts | 216 ++++++ cli/tests/unit/request_test.ts | 49 ++ cli/tests/unit/resources_test.ts | 61 ++ cli/tests/unit/signal_test.ts | 195 +++++ cli/tests/unit/stat_test.ts | 238 +++++++ cli/tests/unit/streams_piping_test.ts | 131 ++++ cli/tests/unit/streams_transform_test.ts | 562 +++++++++++++++ cli/tests/unit/streams_writable_test.ts | 253 +++++++ cli/tests/unit/symlink_test.ts | 44 ++ cli/tests/unit/test_util.ts | 364 ++++++++++ cli/tests/unit/testing_test.ts | 27 + cli/tests/unit/text_encoding_test.ts | 206 ++++++ cli/tests/unit/timers_test.ts | 368 ++++++++++ cli/tests/unit/tls_test.ts | 262 +++++++ cli/tests/unit/truncate_test.ts | 80 +++ cli/tests/unit/tty_test.ts | 23 + cli/tests/unit/umask_test.ts | 15 + cli/tests/unit/unit_test_runner.ts | 309 ++++++++ cli/tests/unit/unit_tests.ts | 72 ++ cli/tests/unit/url_search_params_test.ts | 275 ++++++++ cli/tests/unit/url_test.ts | 407 +++++++++++ cli/tests/unit/utime_test.ts | 230 ++++++ cli/tests/unit/version_test.ts | 8 + cli/tests/unit/write_file_test.ts | 228 ++++++ cli/tests/unit/write_text_file_test.ts | 79 +++ 141 files changed, 12571 insertions(+), 12571 deletions(-) delete mode 100644 cli/js/tests/README.md delete mode 100644 cli/js/tests/abort_controller_test.ts delete mode 100644 cli/js/tests/blob_test.ts delete mode 100644 cli/js/tests/body_test.ts delete mode 100644 cli/js/tests/buffer_test.ts delete mode 100644 cli/js/tests/build_test.ts delete mode 100644 cli/js/tests/chmod_test.ts delete mode 100644 cli/js/tests/chown_test.ts delete mode 100644 cli/js/tests/console_test.ts delete mode 100644 cli/js/tests/copy_file_test.ts delete mode 100644 cli/js/tests/custom_event_test.ts delete mode 100644 cli/js/tests/dir_test.ts delete mode 100644 cli/js/tests/dispatch_json_test.ts delete mode 100644 cli/js/tests/dispatch_minimal_test.ts delete mode 100644 cli/js/tests/dom_exception_test.ts delete mode 100644 cli/js/tests/dom_iterable_test.ts delete mode 100644 cli/js/tests/error_stack_test.ts delete mode 100644 cli/js/tests/event_target_test.ts delete mode 100644 cli/js/tests/event_test.ts delete mode 100644 cli/js/tests/fetch_test.ts delete mode 100644 cli/js/tests/file_test.ts delete mode 100644 cli/js/tests/files_test.ts delete mode 100644 cli/js/tests/form_data_test.ts delete mode 100644 cli/js/tests/format_error_test.ts delete mode 100644 cli/js/tests/fs_events_test.ts delete mode 100644 cli/js/tests/get_random_values_test.ts delete mode 100644 cli/js/tests/globals_test.ts delete mode 100644 cli/js/tests/headers_test.ts delete mode 100644 cli/js/tests/internals_test.ts delete mode 100644 cli/js/tests/io_test.ts delete mode 100644 cli/js/tests/link_test.ts delete mode 100644 cli/js/tests/make_temp_test.ts delete mode 100644 cli/js/tests/metrics_test.ts delete mode 100644 cli/js/tests/mkdir_test.ts delete mode 100644 cli/js/tests/net_test.ts delete mode 100644 cli/js/tests/os_test.ts delete mode 100644 cli/js/tests/performance_test.ts delete mode 100644 cli/js/tests/permissions_test.ts delete mode 100644 cli/js/tests/process_test.ts delete mode 100644 cli/js/tests/read_dir_test.ts delete mode 100644 cli/js/tests/read_file_test.ts delete mode 100644 cli/js/tests/read_link_test.ts delete mode 100644 cli/js/tests/read_text_file_test.ts delete mode 100644 cli/js/tests/real_path_test.ts delete mode 100644 cli/js/tests/remove_test.ts delete mode 100644 cli/js/tests/rename_test.ts delete mode 100644 cli/js/tests/request_test.ts delete mode 100644 cli/js/tests/resources_test.ts delete mode 100644 cli/js/tests/signal_test.ts delete mode 100644 cli/js/tests/stat_test.ts delete mode 100644 cli/js/tests/streams_piping_test.ts delete mode 100644 cli/js/tests/streams_transform_test.ts delete mode 100644 cli/js/tests/streams_writable_test.ts delete mode 100644 cli/js/tests/symlink_test.ts delete mode 100644 cli/js/tests/test_util.ts delete mode 100644 cli/js/tests/testing_test.ts delete mode 100644 cli/js/tests/text_encoding_test.ts delete mode 100644 cli/js/tests/timers_test.ts delete mode 100644 cli/js/tests/tls_test.ts delete mode 100644 cli/js/tests/truncate_test.ts delete mode 100644 cli/js/tests/tty_test.ts delete mode 100644 cli/js/tests/umask_test.ts delete mode 100755 cli/js/tests/unit_test_runner.ts delete mode 100644 cli/js/tests/unit_tests.ts delete mode 100644 cli/js/tests/url_search_params_test.ts delete mode 100644 cli/js/tests/url_test.ts delete mode 100644 cli/js/tests/utime_test.ts delete mode 100644 cli/js/tests/version_test.ts delete mode 100644 cli/js/tests/write_file_test.ts delete mode 100644 cli/js/tests/write_text_file_test.ts create mode 100644 cli/tests/unit/README.md create mode 100644 cli/tests/unit/abort_controller_test.ts create mode 100644 cli/tests/unit/blob_test.ts create mode 100644 cli/tests/unit/body_test.ts create mode 100644 cli/tests/unit/buffer_test.ts create mode 100644 cli/tests/unit/build_test.ts create mode 100644 cli/tests/unit/chmod_test.ts create mode 100644 cli/tests/unit/chown_test.ts create mode 100644 cli/tests/unit/console_test.ts create mode 100644 cli/tests/unit/copy_file_test.ts create mode 100644 cli/tests/unit/custom_event_test.ts create mode 100644 cli/tests/unit/dir_test.ts create mode 100644 cli/tests/unit/dispatch_json_test.ts create mode 100644 cli/tests/unit/dispatch_minimal_test.ts create mode 100644 cli/tests/unit/dom_exception_test.ts create mode 100644 cli/tests/unit/dom_iterable_test.ts create mode 100644 cli/tests/unit/error_stack_test.ts create mode 100644 cli/tests/unit/event_target_test.ts create mode 100644 cli/tests/unit/event_test.ts create mode 100644 cli/tests/unit/fetch_test.ts create mode 100644 cli/tests/unit/file_test.ts create mode 100644 cli/tests/unit/files_test.ts create mode 100644 cli/tests/unit/form_data_test.ts create mode 100644 cli/tests/unit/format_error_test.ts create mode 100644 cli/tests/unit/fs_events_test.ts create mode 100644 cli/tests/unit/get_random_values_test.ts create mode 100644 cli/tests/unit/globals_test.ts create mode 100644 cli/tests/unit/headers_test.ts create mode 100644 cli/tests/unit/internals_test.ts create mode 100644 cli/tests/unit/io_test.ts create mode 100644 cli/tests/unit/link_test.ts create mode 100644 cli/tests/unit/make_temp_test.ts create mode 100644 cli/tests/unit/metrics_test.ts create mode 100644 cli/tests/unit/mkdir_test.ts create mode 100644 cli/tests/unit/net_test.ts create mode 100644 cli/tests/unit/os_test.ts create mode 100644 cli/tests/unit/performance_test.ts create mode 100644 cli/tests/unit/permissions_test.ts create mode 100644 cli/tests/unit/process_test.ts create mode 100644 cli/tests/unit/read_dir_test.ts create mode 100644 cli/tests/unit/read_file_test.ts create mode 100644 cli/tests/unit/read_link_test.ts create mode 100644 cli/tests/unit/read_text_file_test.ts create mode 100644 cli/tests/unit/real_path_test.ts create mode 100644 cli/tests/unit/remove_test.ts create mode 100644 cli/tests/unit/rename_test.ts create mode 100644 cli/tests/unit/request_test.ts create mode 100644 cli/tests/unit/resources_test.ts create mode 100644 cli/tests/unit/signal_test.ts create mode 100644 cli/tests/unit/stat_test.ts create mode 100644 cli/tests/unit/streams_piping_test.ts create mode 100644 cli/tests/unit/streams_transform_test.ts create mode 100644 cli/tests/unit/streams_writable_test.ts create mode 100644 cli/tests/unit/symlink_test.ts create mode 100644 cli/tests/unit/test_util.ts create mode 100644 cli/tests/unit/testing_test.ts create mode 100644 cli/tests/unit/text_encoding_test.ts create mode 100644 cli/tests/unit/timers_test.ts create mode 100644 cli/tests/unit/tls_test.ts create mode 100644 cli/tests/unit/truncate_test.ts create mode 100644 cli/tests/unit/tty_test.ts create mode 100644 cli/tests/unit/umask_test.ts create mode 100755 cli/tests/unit/unit_test_runner.ts create mode 100644 cli/tests/unit/unit_tests.ts create mode 100644 cli/tests/unit/url_search_params_test.ts create mode 100644 cli/tests/unit/url_test.ts create mode 100644 cli/tests/unit/utime_test.ts create mode 100644 cli/tests/unit/version_test.ts create mode 100644 cli/tests/unit/write_file_test.ts create mode 100644 cli/tests/unit/write_text_file_test.ts (limited to 'cli') diff --git a/cli/js/tests/README.md b/cli/js/tests/README.md deleted file mode 100644 index ea52f1242..000000000 --- a/cli/js/tests/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# Deno runtime tests - -Files in this directory are unit tests for Deno runtime. - -They are run under compiled Deno binary as opposed to files in `cli/js/` which -are bundled and snapshotted using `deno_typescript` crate. - -Testing Deno runtime code requires checking API under different runtime -permissions (ie. running with different `--allow-*` flags). To accomplish this -all tests exercised are created using `unitTest()` function. - -``` -import { unitTest } from "./test_util.ts"; - -unitTest(function simpleTestFn(): void { - // test code here -}); - -unitTest({ - ignore: Deno.build.os === "windows", - perms: { read: true, write: true }, - }, - function complexTestFn(): void { - // test code here - } -); -``` - -`unitTest` is is a wrapper function that enhances `Deno.test()` API in several -ways: - -- ability to conditionally skip tests using `UnitTestOptions.skip` -- ability to register required set of permissions for given test case using - `UnitTestOptions.perms` -- sanitization of resources - ensuring that tests close all opened resources - preventing interference between tests -- sanitization of async ops - ensuring that tests don't leak async ops by - ensuring that all started async ops are done before test finishes - -## Running tests - -`unit_test_runner.ts` is the main script used to run unit tests. - -Runner discovers required permissions combinations by loading -`cli/js/tests/unit_tests.ts` and going through all registered instances of -`unitTest`. - -There are three ways to run `unit_test_runner.ts`: - -``` -# Run all tests. Spawns worker processes for each discovered permission -# combination: -target/debug/deno run -A cli/js/tests/unit_test_runner.ts --master - -# By default all output of worker processes is discarded; for debug purposes -# the --verbose flag preserves output from the worker -target/debug/deno run -A cli/js/tests/unit_test_runner.ts --master --verbose - -# Run subset of tests that don't require any permissions -target/debug/deno run --unstable cli/js/tests/unit_test_runner.ts - -# Run subset tests that require "net" and "read" permissions -target/debug/deno run --unstable --allow-net --allow-read cli/js/tests/unit_test_runner.ts - -# "worker" mode communicates with parent using TCP socket on provided address; -# after initial setup drops permissions to specified set. It shouldn't be used -# directly, only be "master" process. -target/debug/deno run -A cli/js/tests/unit_test_runner.ts --worker --addr=127.0.0.1:4500 --perms=net,write,run - -# Run specific tests -target/debug/deno run --unstable --allow-net cli/js/tests/unit_test_runner.ts -- netTcpListenClose - -RUST_BACKTRACE=1 cargo run -- run --unstable --allow-read --allow-write cli/js/tests/unit_test_runner.ts -- netUnixDialListen -``` - -### Http server - -`tools/http_server.py` is required to run when one's running unit tests. During -CI it's spawned automatically, but if you want to run tests manually make sure -that server is spawned otherwise there'll be cascade of test failures. diff --git a/cli/js/tests/abort_controller_test.ts b/cli/js/tests/abort_controller_test.ts deleted file mode 100644 index ecc1abb88..000000000 --- a/cli/js/tests/abort_controller_test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest(function basicAbortController() { - const controller = new AbortController(); - assert(controller); - const { signal } = controller; - assert(signal); - assertEquals(signal.aborted, false); - controller.abort(); - assertEquals(signal.aborted, true); -}); - -unitTest(function signalCallsOnabort() { - const controller = new AbortController(); - const { signal } = controller; - let called = false; - signal.onabort = (evt): void => { - assert(evt); - assertEquals(evt.type, "abort"); - called = true; - }; - controller.abort(); - assert(called); -}); - -unitTest(function signalEventListener() { - const controller = new AbortController(); - const { signal } = controller; - let called = false; - signal.addEventListener("abort", function (ev) { - assert(this === signal); - assertEquals(ev.type, "abort"); - called = true; - }); - controller.abort(); - assert(called); -}); - -unitTest(function onlyAbortsOnce() { - const controller = new AbortController(); - const { signal } = controller; - let called = 0; - signal.addEventListener("abort", () => called++); - signal.onabort = (): void => { - called++; - }; - controller.abort(); - assertEquals(called, 2); - controller.abort(); - assertEquals(called, 2); -}); - -unitTest(function controllerHasProperToString() { - const actual = Object.prototype.toString.call(new AbortController()); - assertEquals(actual, "[object AbortController]"); -}); diff --git a/cli/js/tests/blob_test.ts b/cli/js/tests/blob_test.ts deleted file mode 100644 index 70f8fb3d5..000000000 --- a/cli/js/tests/blob_test.ts +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; -import { concat } from "../../../std/bytes/mod.ts"; -import { decode } from "../../../std/encoding/utf8.ts"; - -unitTest(function blobString(): void { - const b1 = new Blob(["Hello World"]); - const str = "Test"; - const b2 = new Blob([b1, str]); - assertEquals(b2.size, b1.size + str.length); -}); - -unitTest(function blobBuffer(): void { - const buffer = new ArrayBuffer(12); - const u8 = new Uint8Array(buffer); - const f1 = new Float32Array(buffer); - const b1 = new Blob([buffer, u8]); - assertEquals(b1.size, 2 * u8.length); - const b2 = new Blob([b1, f1]); - assertEquals(b2.size, 3 * u8.length); -}); - -unitTest(function blobSlice(): void { - const blob = new Blob(["Deno", "Foo"]); - const b1 = blob.slice(0, 3, "Text/HTML"); - assert(b1 instanceof Blob); - assertEquals(b1.size, 3); - assertEquals(b1.type, "text/html"); - const b2 = blob.slice(-1, 3); - assertEquals(b2.size, 0); - const b3 = blob.slice(100, 3); - assertEquals(b3.size, 0); - const b4 = blob.slice(0, 10); - assertEquals(b4.size, blob.size); -}); - -unitTest(function blobInvalidType(): void { - const blob = new Blob(["foo"], { - type: "\u0521", - }); - - assertEquals(blob.type, ""); -}); - -unitTest(function blobShouldNotThrowError(): void { - let hasThrown = false; - - try { - const options1: object = { - ending: "utf8", - hasOwnProperty: "hasOwnProperty", - }; - const options2: object = Object.create(null); - new Blob(["Hello World"], options1); - new Blob(["Hello World"], options2); - } catch { - hasThrown = true; - } - - assertEquals(hasThrown, false); -}); - -unitTest(function nativeEndLine(): void { - const options: object = { - ending: "native", - }; - const blob = new Blob(["Hello\nWorld"], options); - - assertEquals(blob.size, Deno.build.os === "windows" ? 12 : 11); -}); - -unitTest(async function blobText(): Promise { - const blob = new Blob(["Hello World"]); - assertEquals(await blob.text(), "Hello World"); -}); - -unitTest(async function blobStream(): Promise { - const blob = new Blob(["Hello World"]); - const stream = blob.stream(); - assert(stream instanceof ReadableStream); - const reader = stream.getReader(); - let bytes = new Uint8Array(); - const read = async (): Promise => { - const { done, value } = await reader.read(); - if (!done && value) { - bytes = concat(bytes, value); - return read(); - } - }; - await read(); - assertEquals(decode(bytes), "Hello World"); -}); diff --git a/cli/js/tests/body_test.ts b/cli/js/tests/body_test.ts deleted file mode 100644 index c8f783e04..000000000 --- a/cli/js/tests/body_test.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals, assert } from "./test_util.ts"; - -// just a hack to get a body object -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function buildBody(body: any): Body { - const stub = new Request("", { - body: body, - }); - return stub as Body; -} - -const intArrays = [ - Int8Array, - Int16Array, - Int32Array, - Uint8Array, - Uint16Array, - Uint32Array, - Uint8ClampedArray, - Float32Array, - Float64Array, -]; -unitTest(async function arrayBufferFromByteArrays(): Promise { - const buffer = new TextEncoder().encode("ahoyhoy8").buffer; - - for (const type of intArrays) { - const body = buildBody(new type(buffer)); - const text = new TextDecoder("utf-8").decode(await body.arrayBuffer()); - assertEquals(text, "ahoyhoy8"); - } -}); - -//FormData -unitTest( - { perms: { net: true } }, - async function bodyMultipartFormData(): Promise { - const response = await fetch( - "http://localhost:4545/cli/tests/subdir/multipart_form_data.txt" - ); - const text = await response.text(); - - const body = buildBody(text); - - // @ts-ignore - body.contentType = "multipart/form-data;boundary=boundary"; - - const formData = await body.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1")!.toString(), "value_1 \r\n"); - assert(formData.has("field_2")); - } -); - -unitTest( - { perms: { net: true } }, - async function bodyURLEncodedFormData(): Promise { - const response = await fetch( - "http://localhost:4545/cli/tests/subdir/form_urlencoded.txt" - ); - const text = await response.text(); - - const body = buildBody(text); - - // @ts-ignore - body.contentType = "application/x-www-form-urlencoded"; - - const formData = await body.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1")!.toString(), "Hi"); - assert(formData.has("field_2")); - assertEquals(formData.get("field_2")!.toString(), ""); - } -); diff --git a/cli/js/tests/buffer_test.ts b/cli/js/tests/buffer_test.ts deleted file mode 100644 index ac2d7db7b..000000000 --- a/cli/js/tests/buffer_test.ts +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -// This code has been ported almost directly from Go's src/bytes/buffer_test.go -// Copyright 2009 The Go Authors. All rights reserved. BSD license. -// https://github.com/golang/go/blob/master/LICENSE -import { - assertEquals, - assert, - assertStrContains, - unitTest, -} from "./test_util.ts"; - -const { Buffer, readAll, readAllSync, writeAll, writeAllSync } = Deno; -type Buffer = Deno.Buffer; - -// N controls how many iterations of certain checks are performed. -const N = 100; -let testBytes: Uint8Array | null; -let testString: string | null; - -function init(): void { - if (testBytes == null) { - testBytes = new Uint8Array(N); - for (let i = 0; i < N; i++) { - testBytes[i] = "a".charCodeAt(0) + (i % 26); - } - const decoder = new TextDecoder(); - testString = decoder.decode(testBytes); - } -} - -function check(buf: Deno.Buffer, s: string): void { - const bytes = buf.bytes(); - assertEquals(buf.length, bytes.byteLength); - const decoder = new TextDecoder(); - const bytesStr = decoder.decode(bytes); - assertEquals(bytesStr, s); - assertEquals(buf.length, s.length); -} - -// Fill buf through n writes of byte slice fub. -// The initial contents of buf corresponds to the string s; -// the result is the final contents of buf returned as a string. -async function fillBytes( - buf: Buffer, - s: string, - n: number, - fub: Uint8Array -): Promise { - check(buf, s); - for (; n > 0; n--) { - const m = await buf.write(fub); - assertEquals(m, fub.byteLength); - const decoder = new TextDecoder(); - s += decoder.decode(fub); - check(buf, s); - } - return s; -} - -// Empty buf through repeated reads into fub. -// The initial contents of buf corresponds to the string s. -async function empty(buf: Buffer, s: string, fub: Uint8Array): Promise { - check(buf, s); - while (true) { - const r = await buf.read(fub); - if (r === null) { - break; - } - s = s.slice(r); - check(buf, s); - } - check(buf, ""); -} - -function repeat(c: string, bytes: number): Uint8Array { - assertEquals(c.length, 1); - const ui8 = new Uint8Array(bytes); - ui8.fill(c.charCodeAt(0)); - return ui8; -} - -unitTest(function bufferNewBuffer(): void { - init(); - assert(testBytes); - assert(testString); - const buf = new Buffer(testBytes.buffer as ArrayBuffer); - check(buf, testString); -}); - -unitTest(async function bufferBasicOperations(): Promise { - init(); - assert(testBytes); - assert(testString); - const buf = new Buffer(); - for (let i = 0; i < 5; i++) { - check(buf, ""); - - buf.reset(); - check(buf, ""); - - buf.truncate(0); - check(buf, ""); - - let n = await buf.write(testBytes.subarray(0, 1)); - assertEquals(n, 1); - check(buf, "a"); - - n = await buf.write(testBytes.subarray(1, 2)); - assertEquals(n, 1); - check(buf, "ab"); - - n = await buf.write(testBytes.subarray(2, 26)); - assertEquals(n, 24); - check(buf, testString.slice(0, 26)); - - buf.truncate(26); - check(buf, testString.slice(0, 26)); - - buf.truncate(20); - check(buf, testString.slice(0, 20)); - - await empty(buf, testString.slice(0, 20), new Uint8Array(5)); - await empty(buf, "", new Uint8Array(100)); - - // TODO buf.writeByte() - // TODO buf.readByte() - } -}); - -unitTest(async function bufferReadEmptyAtEOF(): Promise { - // check that EOF of 'buf' is not reached (even though it's empty) if - // results are written to buffer that has 0 length (ie. it can't store any data) - const buf = new Buffer(); - const zeroLengthTmp = new Uint8Array(0); - const result = await buf.read(zeroLengthTmp); - assertEquals(result, 0); -}); - -unitTest(async function bufferLargeByteWrites(): Promise { - init(); - const buf = new Buffer(); - const limit = 9; - for (let i = 3; i < limit; i += 3) { - const s = await fillBytes(buf, "", 5, testBytes!); - await empty(buf, s, new Uint8Array(Math.floor(testString!.length / i))); - } - check(buf, ""); -}); - -unitTest(async function bufferTooLargeByteWrites(): Promise { - init(); - const tmp = new Uint8Array(72); - const growLen = Number.MAX_VALUE; - const xBytes = repeat("x", 0); - const buf = new Buffer(xBytes.buffer as ArrayBuffer); - await buf.read(tmp); - - let err; - try { - buf.grow(growLen); - } catch (e) { - err = e; - } - - assert(err instanceof Error); - assertStrContains(err.message, "grown beyond the maximum size"); -}); - -unitTest(async function bufferLargeByteReads(): Promise { - init(); - assert(testBytes); - assert(testString); - const buf = new Buffer(); - for (let i = 3; i < 30; i += 3) { - const n = Math.floor(testBytes.byteLength / i); - const s = await fillBytes(buf, "", 5, testBytes.subarray(0, n)); - await empty(buf, s, new Uint8Array(testString.length)); - } - check(buf, ""); -}); - -unitTest(function bufferCapWithPreallocatedSlice(): void { - const buf = new Buffer(new ArrayBuffer(10)); - assertEquals(buf.capacity, 10); -}); - -unitTest(async function bufferReadFrom(): Promise { - init(); - assert(testBytes); - assert(testString); - const buf = new Buffer(); - for (let i = 3; i < 30; i += 3) { - const s = await fillBytes( - buf, - "", - 5, - testBytes.subarray(0, Math.floor(testBytes.byteLength / i)) - ); - const b = new Buffer(); - await b.readFrom(buf); - const fub = new Uint8Array(testString.length); - await empty(b, s, fub); - } -}); - -unitTest(async function bufferReadFromSync(): Promise { - init(); - assert(testBytes); - assert(testString); - const buf = new Buffer(); - for (let i = 3; i < 30; i += 3) { - const s = await fillBytes( - buf, - "", - 5, - testBytes.subarray(0, Math.floor(testBytes.byteLength / i)) - ); - const b = new Buffer(); - b.readFromSync(buf); - const fub = new Uint8Array(testString.length); - await empty(b, s, fub); - } -}); - -unitTest(async function bufferTestGrow(): Promise { - const tmp = new Uint8Array(72); - for (const startLen of [0, 100, 1000, 10000, 100000]) { - const xBytes = repeat("x", startLen); - for (const growLen of [0, 100, 1000, 10000, 100000]) { - const buf = new Buffer(xBytes.buffer as ArrayBuffer); - // If we read, this affects buf.off, which is good to test. - const nread = (await buf.read(tmp)) ?? 0; - buf.grow(growLen); - const yBytes = repeat("y", growLen); - await buf.write(yBytes); - // Check that buffer has correct data. - assertEquals( - buf.bytes().subarray(0, startLen - nread), - xBytes.subarray(nread) - ); - assertEquals( - buf.bytes().subarray(startLen - nread, startLen - nread + growLen), - yBytes - ); - } - } -}); - -unitTest(async function testReadAll(): Promise { - init(); - assert(testBytes); - const reader = new Buffer(testBytes.buffer as ArrayBuffer); - const actualBytes = await readAll(reader); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - -unitTest(function testReadAllSync(): void { - init(); - assert(testBytes); - const reader = new Buffer(testBytes.buffer as ArrayBuffer); - const actualBytes = readAllSync(reader); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - -unitTest(async function testWriteAll(): Promise { - init(); - assert(testBytes); - const writer = new Buffer(); - await writeAll(writer, testBytes); - const actualBytes = writer.bytes(); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - -unitTest(function testWriteAllSync(): void { - init(); - assert(testBytes); - const writer = new Buffer(); - writeAllSync(writer, testBytes); - const actualBytes = writer.bytes(); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); diff --git a/cli/js/tests/build_test.ts b/cli/js/tests/build_test.ts deleted file mode 100644 index 987ed8d39..000000000 --- a/cli/js/tests/build_test.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -unitTest(function buildInfo(): void { - // Deno.build is injected by rollup at compile time. Here - // we check it has been properly transformed. - const { arch, os } = Deno.build; - assert(arch.length > 0); - assert(os === "darwin" || os === "windows" || os === "linux"); -}); diff --git a/cli/js/tests/chmod_test.ts b/cli/js/tests/chmod_test.ts deleted file mode 100644 index d0d17629d..000000000 --- a/cli/js/tests/chmod_test.ts +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - function chmodSyncSuccess(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - - Deno.chmodSync(filename, 0o777); - - const fileInfo = Deno.statSync(filename); - assert(fileInfo.mode); - assertEquals(fileInfo.mode & 0o777, 0o777); - } -); - -// Check symlink when not on windows -unitTest( - { - ignore: Deno.build.os === "windows", - perms: { read: true, write: true }, - }, - function chmodSyncSymlinkSuccess(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - - const filename = tempDir + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - const symlinkName = tempDir + "/test_symlink.txt"; - Deno.symlinkSync(filename, symlinkName); - - let symlinkInfo = Deno.lstatSync(symlinkName); - assert(symlinkInfo.mode); - const symlinkMode = symlinkInfo.mode & 0o777; // platform dependent - - Deno.chmodSync(symlinkName, 0o777); - - // Change actual file mode, not symlink - const fileInfo = Deno.statSync(filename); - assert(fileInfo.mode); - assertEquals(fileInfo.mode & 0o777, 0o777); - symlinkInfo = Deno.lstatSync(symlinkName); - assert(symlinkInfo.mode); - assertEquals(symlinkInfo.mode & 0o777, symlinkMode); - } -); - -unitTest({ perms: { write: true } }, function chmodSyncFailure(): void { - let err; - try { - const filename = "/badfile.txt"; - Deno.chmodSync(filename, 0o777); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); -}); - -unitTest({ perms: { write: false } }, function chmodSyncPerm(): void { - let err; - try { - Deno.chmodSync("/somefile.txt", 0o777); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - async function chmodSuccess(): Promise { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - - await Deno.chmod(filename, 0o777); - - const fileInfo = Deno.statSync(filename); - assert(fileInfo.mode); - assertEquals(fileInfo.mode & 0o777, 0o777); - } -); - -// Check symlink when not on windows - -unitTest( - { - ignore: Deno.build.os === "windows", - perms: { read: true, write: true }, - }, - async function chmodSymlinkSuccess(): Promise { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - - const filename = tempDir + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - const symlinkName = tempDir + "/test_symlink.txt"; - Deno.symlinkSync(filename, symlinkName); - - let symlinkInfo = Deno.lstatSync(symlinkName); - assert(symlinkInfo.mode); - const symlinkMode = symlinkInfo.mode & 0o777; // platform dependent - - await Deno.chmod(symlinkName, 0o777); - - // Just change actual file mode, not symlink - const fileInfo = Deno.statSync(filename); - assert(fileInfo.mode); - assertEquals(fileInfo.mode & 0o777, 0o777); - symlinkInfo = Deno.lstatSync(symlinkName); - assert(symlinkInfo.mode); - assertEquals(symlinkInfo.mode & 0o777, symlinkMode); - } -); - -unitTest({ perms: { write: true } }, async function chmodFailure(): Promise< - void -> { - let err; - try { - const filename = "/badfile.txt"; - await Deno.chmod(filename, 0o777); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); -}); - -unitTest({ perms: { write: false } }, async function chmodPerm(): Promise< - void -> { - let err; - try { - await Deno.chmod("/somefile.txt", 0o777); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); diff --git a/cli/js/tests/chown_test.ts b/cli/js/tests/chown_test.ts deleted file mode 100644 index 724ea5a21..000000000 --- a/cli/js/tests/chown_test.ts +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals, assert } from "./test_util.ts"; - -// chown on Windows is noop for now, so ignore its testing on Windows -if (Deno.build.os !== "windows") { - async function getUidAndGid(): Promise<{ uid: number; gid: number }> { - // get the user ID and group ID of the current process - const uidProc = Deno.run({ - stdout: "piped", - cmd: ["python", "-c", "import os; print(os.getuid())"], - }); - const gidProc = Deno.run({ - stdout: "piped", - cmd: ["python", "-c", "import os; print(os.getgid())"], - }); - - assertEquals((await uidProc.status()).code, 0); - assertEquals((await gidProc.status()).code, 0); - const uid = parseInt( - new TextDecoder("utf-8").decode(await uidProc.output()) - ); - uidProc.close(); - const gid = parseInt( - new TextDecoder("utf-8").decode(await gidProc.output()) - ); - gidProc.close(); - - return { uid, gid }; - } - - unitTest(async function chownNoWritePermission(): Promise { - const filePath = "chown_test_file.txt"; - try { - await Deno.chown(filePath, 1000, 1000); - } catch (e) { - assert(e instanceof Deno.errors.PermissionDenied); - } - }); - - unitTest( - { perms: { run: true, write: true } }, - async function chownSyncFileNotExist(): Promise { - const { uid, gid } = await getUidAndGid(); - const filePath = Deno.makeTempDirSync() + "/chown_test_file.txt"; - - try { - Deno.chownSync(filePath, uid, gid); - } catch (e) { - assert(e instanceof Deno.errors.NotFound); - } - } - ); - - unitTest( - { perms: { run: true, write: true } }, - async function chownFileNotExist(): Promise { - const { uid, gid } = await getUidAndGid(); - const filePath = (await Deno.makeTempDir()) + "/chown_test_file.txt"; - - try { - await Deno.chown(filePath, uid, gid); - } catch (e) { - assert(e instanceof Deno.errors.NotFound); - } - } - ); - - unitTest( - { perms: { write: true } }, - function chownSyncPermissionDenied(): void { - const enc = new TextEncoder(); - const dirPath = Deno.makeTempDirSync(); - const filePath = dirPath + "/chown_test_file.txt"; - const fileData = enc.encode("Hello"); - Deno.writeFileSync(filePath, fileData); - - try { - // try changing the file's owner to root - Deno.chownSync(filePath, 0, 0); - } catch (e) { - assert(e instanceof Deno.errors.PermissionDenied); - } - Deno.removeSync(dirPath, { recursive: true }); - } - ); - - unitTest( - { perms: { write: true } }, - async function chownPermissionDenied(): Promise { - const enc = new TextEncoder(); - const dirPath = await Deno.makeTempDir(); - const filePath = dirPath + "/chown_test_file.txt"; - const fileData = enc.encode("Hello"); - await Deno.writeFile(filePath, fileData); - - try { - // try changing the file's owner to root - await Deno.chown(filePath, 0, 0); - } catch (e) { - assert(e instanceof Deno.errors.PermissionDenied); - } - await Deno.remove(dirPath, { recursive: true }); - } - ); - - unitTest( - { perms: { run: true, write: true } }, - async function chownSyncSucceed(): Promise { - // TODO: when a file's owner is actually being changed, - // chown only succeeds if run under priviledged user (root) - // The test script has no such privilege, so need to find a better way to test this case - const { uid, gid } = await getUidAndGid(); - - const enc = new TextEncoder(); - const dirPath = Deno.makeTempDirSync(); - const filePath = dirPath + "/chown_test_file.txt"; - const fileData = enc.encode("Hello"); - Deno.writeFileSync(filePath, fileData); - - // the test script creates this file with the same uid and gid, - // here chown is a noop so it succeeds under non-priviledged user - Deno.chownSync(filePath, uid, gid); - - Deno.removeSync(dirPath, { recursive: true }); - } - ); - - unitTest( - { perms: { run: true, write: true } }, - async function chownSucceed(): Promise { - // TODO: same as chownSyncSucceed - const { uid, gid } = await getUidAndGid(); - - const enc = new TextEncoder(); - const dirPath = await Deno.makeTempDir(); - const filePath = dirPath + "/chown_test_file.txt"; - const fileData = enc.encode("Hello"); - await Deno.writeFile(filePath, fileData); - - // the test script creates this file with the same uid and gid, - // here chown is a noop so it succeeds under non-priviledged user - await Deno.chown(filePath, uid, gid); - - Deno.removeSync(dirPath, { recursive: true }); - } - ); -} diff --git a/cli/js/tests/console_test.ts b/cli/js/tests/console_test.ts deleted file mode 100644 index 02d71e4ca..000000000 --- a/cli/js/tests/console_test.ts +++ /dev/null @@ -1,1139 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -// TODO(ry) The unit test functions in this module are too coarse. They should -// be broken up into smaller bits. - -// TODO(ry) These tests currentl strip all the ANSI colors out. We don't have a -// good way to control whether we produce color output or not since -// std/fmt/colors auto determines whether to put colors in or not. We need -// better infrastructure here so we can properly test the colors. - -import { assert, assertEquals, unitTest } from "./test_util.ts"; -import { stripColor } from "../../../std/fmt/colors.ts"; - -// Some of these APIs aren't exposed in the types and so we have to cast to any -// in order to "trick" TypeScript. -const { - inspect, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -} = Deno as any; - -const customInspect = Deno.customInspect; -const { - Console, - stringifyArgs, - // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol -} = Deno[Deno.internal]; - -function stringify(...args: unknown[]): string { - return stripColor(stringifyArgs(args).replace(/\n$/, "")); -} - -// test cases from web-platform-tests -// via https://github.com/web-platform-tests/wpt/blob/master/console/console-is-a-namespace.any.js -unitTest(function consoleShouldBeANamespace(): void { - const prototype1 = Object.getPrototypeOf(console); - const prototype2 = Object.getPrototypeOf(prototype1); - - assertEquals(Object.getOwnPropertyNames(prototype1).length, 0); - assertEquals(prototype2, Object.prototype); -}); - -unitTest(function consoleHasRightInstance(): void { - assert(console instanceof Console); - assertEquals({} instanceof Console, false); -}); - -unitTest(function consoleTestAssertShouldNotThrowError(): void { - mockConsole((console) => { - console.assert(true); - let hasThrown = undefined; - try { - console.assert(false); - hasThrown = false; - } catch { - hasThrown = true; - } - assertEquals(hasThrown, false); - }); -}); - -unitTest(function consoleTestStringifyComplexObjects(): void { - assertEquals(stringify("foo"), "foo"); - assertEquals(stringify(["foo", "bar"]), `[ "foo", "bar" ]`); - assertEquals(stringify({ foo: "bar" }), `{ foo: "bar" }`); -}); - -unitTest(function consoleTestStringifyLongStrings(): void { - const veryLongString = "a".repeat(200); - // If we stringify an object containing the long string, it gets abbreviated. - let actual = stringify({ veryLongString }); - assert(actual.includes("...")); - assert(actual.length < 200); - // However if we stringify the string itself, we get it exactly. - actual = stringify(veryLongString); - assertEquals(actual, veryLongString); -}); - -/* eslint-disable @typescript-eslint/explicit-function-return-type */ -unitTest(function consoleTestStringifyCircular(): void { - class Base { - a = 1; - m1() {} - } - - class Extended extends Base { - b = 2; - m2() {} - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const nestedObj: any = { - num: 1, - bool: true, - str: "a", - method() {}, - async asyncMethod() {}, - *generatorMethod() {}, - un: undefined, - nu: null, - arrowFunc: () => {}, - extendedClass: new Extended(), - nFunc: new Function(), - extendedCstr: Extended, - }; - - const circularObj = { - num: 2, - bool: false, - str: "b", - method() {}, - un: undefined, - nu: null, - nested: nestedObj, - emptyObj: {}, - arr: [1, "s", false, null, nestedObj], - baseClass: new Base(), - }; - - nestedObj.o = circularObj; - const nestedObjExpected = `{ - num: 1, - bool: true, - str: "a", - method: [Function: method], - asyncMethod: [AsyncFunction: asyncMethod], - generatorMethod: [GeneratorFunction: generatorMethod], - un: undefined, - nu: null, - arrowFunc: [Function: arrowFunc], - extendedClass: Extended { a: 1, b: 2 }, - nFunc: [Function], - extendedCstr: [Function: Extended], - o: { - num: 2, - bool: false, - str: "b", - method: [Function: method], - un: undefined, - nu: null, - nested: [Circular], - emptyObj: {}, - arr: [ 1, "s", false, null, [Circular] ], - baseClass: Base { a: 1 } - } -}`; - - assertEquals(stringify(1), "1"); - assertEquals(stringify(-0), "-0"); - assertEquals(stringify(1n), "1n"); - assertEquals(stringify("s"), "s"); - assertEquals(stringify(false), "false"); - assertEquals(stringify(new Number(1)), "[Number: 1]"); - assertEquals(stringify(new Boolean(true)), "[Boolean: true]"); - assertEquals(stringify(new String("deno")), `[String: "deno"]`); - assertEquals(stringify(/[0-9]*/), "/[0-9]*/"); - assertEquals( - stringify(new Date("2018-12-10T02:26:59.002Z")), - "2018-12-10T02:26:59.002Z" - ); - assertEquals(stringify(new Set([1, 2, 3])), "Set { 1, 2, 3 }"); - assertEquals( - stringify( - new Map([ - [1, "one"], - [2, "two"], - ]) - ), - `Map { 1 => "one", 2 => "two" }` - ); - assertEquals(stringify(new WeakSet()), "WeakSet { [items unknown] }"); - assertEquals(stringify(new WeakMap()), "WeakMap { [items unknown] }"); - assertEquals(stringify(Symbol(1)), "Symbol(1)"); - assertEquals(stringify(null), "null"); - assertEquals(stringify(undefined), "undefined"); - assertEquals(stringify(new Extended()), "Extended { a: 1, b: 2 }"); - assertEquals( - stringify(function f(): void {}), - "[Function: f]" - ); - assertEquals( - stringify(async function af(): Promise {}), - "[AsyncFunction: af]" - ); - assertEquals( - stringify(function* gf() {}), - "[GeneratorFunction: gf]" - ); - assertEquals( - stringify(async function* agf() {}), - "[AsyncGeneratorFunction: agf]" - ); - assertEquals( - stringify(new Uint8Array([1, 2, 3])), - "Uint8Array(3) [ 1, 2, 3 ]" - ); - assertEquals(stringify(Uint8Array.prototype), "TypedArray {}"); - assertEquals( - stringify({ a: { b: { c: { d: new Set([1]) } } } }), - "{ a: { b: { c: { d: [Set] } } } }" - ); - assertEquals(stringify(nestedObj), nestedObjExpected); - assertEquals(stringify(JSON), 'JSON { Symbol(Symbol.toStringTag): "JSON" }'); - assertEquals( - stringify(console), - `{ - log: [Function], - debug: [Function], - info: [Function], - dir: [Function], - dirxml: [Function], - warn: [Function], - error: [Function], - assert: [Function], - count: [Function], - countReset: [Function], - table: [Function], - time: [Function], - timeLog: [Function], - timeEnd: [Function], - group: [Function], - groupCollapsed: [Function], - groupEnd: [Function], - clear: [Function], - trace: [Function], - indentLevel: 0, - Symbol(isConsoleInstance): true -}` - ); - assertEquals( - stringify({ str: 1, [Symbol.for("sym")]: 2, [Symbol.toStringTag]: "TAG" }), - 'TAG { str: 1, Symbol(sym): 2, Symbol(Symbol.toStringTag): "TAG" }' - ); - // test inspect is working the same - assertEquals(stripColor(inspect(nestedObj)), nestedObjExpected); -}); -/* eslint-enable @typescript-eslint/explicit-function-return-type */ - -unitTest(function consoleTestStringifyWithDepth(): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const nestedObj: any = { a: { b: { c: { d: { e: { f: 42 } } } } } }; - assertEquals( - stripColor(stringifyArgs([nestedObj], { depth: 3 })), - "{ a: { b: { c: [Object] } } }" - ); - assertEquals( - stripColor(stringifyArgs([nestedObj], { depth: 4 })), - "{ a: { b: { c: { d: [Object] } } } }" - ); - assertEquals( - stripColor(stringifyArgs([nestedObj], { depth: 0 })), - "[Object]" - ); - assertEquals( - stripColor(stringifyArgs([nestedObj])), - "{ a: { b: { c: { d: [Object] } } } }" - ); - // test inspect is working the same way - assertEquals( - stripColor(inspect(nestedObj, { depth: 4 })), - "{ a: { b: { c: { d: [Object] } } } }" - ); -}); - -unitTest(function consoleTestStringifyLargeObject(): void { - const obj = { - a: 2, - o: { - a: "1", - b: "2", - c: "3", - d: "4", - e: "5", - f: "6", - g: 10, - asd: 2, - asda: 3, - x: { a: "asd", x: 3 }, - }, - }; - assertEquals( - stringify(obj), - `{ - a: 2, - o: { - a: "1", - b: "2", - c: "3", - d: "4", - e: "5", - f: "6", - g: 10, - asd: 2, - asda: 3, - x: { a: "asd", x: 3 } - } -}` - ); -}); - -unitTest(function consoleTestStringifyIterable() { - const shortArray = [1, 2, 3, 4, 5]; - assertEquals(stringify(shortArray), "[ 1, 2, 3, 4, 5 ]"); - - const longArray = new Array(200).fill(0); - assertEquals( - stringify(longArray), - `[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ... 100 more items -]` - ); - - const obj = { a: "a", longArray }; - assertEquals( - stringify(obj), - `{ - a: "a", - longArray: [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ... 100 more items - ] -}` - ); - - const shortMap = new Map([ - ["a", 0], - ["b", 1], - ]); - assertEquals(stringify(shortMap), `Map { "a" => 0, "b" => 1 }`); - - const longMap = new Map(); - for (const key of Array(200).keys()) { - longMap.set(`${key}`, key); - } - assertEquals( - stringify(longMap), - `Map { - "0" => 0, - "1" => 1, - "2" => 2, - "3" => 3, - "4" => 4, - "5" => 5, - "6" => 6, - "7" => 7, - "8" => 8, - "9" => 9, - "10" => 10, - "11" => 11, - "12" => 12, - "13" => 13, - "14" => 14, - "15" => 15, - "16" => 16, - "17" => 17, - "18" => 18, - "19" => 19, - "20" => 20, - "21" => 21, - "22" => 22, - "23" => 23, - "24" => 24, - "25" => 25, - "26" => 26, - "27" => 27, - "28" => 28, - "29" => 29, - "30" => 30, - "31" => 31, - "32" => 32, - "33" => 33, - "34" => 34, - "35" => 35, - "36" => 36, - "37" => 37, - "38" => 38, - "39" => 39, - "40" => 40, - "41" => 41, - "42" => 42, - "43" => 43, - "44" => 44, - "45" => 45, - "46" => 46, - "47" => 47, - "48" => 48, - "49" => 49, - "50" => 50, - "51" => 51, - "52" => 52, - "53" => 53, - "54" => 54, - "55" => 55, - "56" => 56, - "57" => 57, - "58" => 58, - "59" => 59, - "60" => 60, - "61" => 61, - "62" => 62, - "63" => 63, - "64" => 64, - "65" => 65, - "66" => 66, - "67" => 67, - "68" => 68, - "69" => 69, - "70" => 70, - "71" => 71, - "72" => 72, - "73" => 73, - "74" => 74, - "75" => 75, - "76" => 76, - "77" => 77, - "78" => 78, - "79" => 79, - "80" => 80, - "81" => 81, - "82" => 82, - "83" => 83, - "84" => 84, - "85" => 85, - "86" => 86, - "87" => 87, - "88" => 88, - "89" => 89, - "90" => 90, - "91" => 91, - "92" => 92, - "93" => 93, - "94" => 94, - "95" => 95, - "96" => 96, - "97" => 97, - "98" => 98, - "99" => 99, - ... 100 more items -}` - ); - - const shortSet = new Set([1, 2, 3]); - assertEquals(stringify(shortSet), `Set { 1, 2, 3 }`); - const longSet = new Set(); - for (const key of Array(200).keys()) { - longSet.add(key); - } - assertEquals( - stringify(longSet), - `Set { - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - ... 100 more items -}` - ); - - const withEmptyEl = Array(10); - withEmptyEl.fill(0, 4, 6); - assertEquals( - stringify(withEmptyEl), - `[ <4 empty items>, 0, 0, <4 empty items> ]` - ); - - /* TODO(ry) Fix this test - const lWithEmptyEl = Array(200); - lWithEmptyEl.fill(0, 50, 80); - assertEquals( - stringify(lWithEmptyEl), - `[ - <50 empty items>, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, <120 empty items> -]` - ); -*/ -}); - -unitTest(async function consoleTestStringifyPromises(): Promise { - const pendingPromise = new Promise((_res, _rej) => {}); - assertEquals(stringify(pendingPromise), "Promise { }"); - - const resolvedPromise = new Promise((res, _rej) => { - res("Resolved!"); - }); - assertEquals(stringify(resolvedPromise), `Promise { "Resolved!" }`); - - let rejectedPromise; - try { - rejectedPromise = new Promise((_, rej) => { - rej(Error("Whoops")); - }); - await rejectedPromise; - } catch (err) {} - const strLines = stringify(rejectedPromise).split("\n"); - assertEquals(strLines[0], "Promise {"); - assertEquals(strLines[1], " Error: Whoops"); -}); - -unitTest(function consoleTestWithCustomInspector(): void { - class A { - [customInspect](): string { - return "b"; - } - } - - assertEquals(stringify(new A()), "b"); -}); - -unitTest(function consoleTestWithCustomInspectorError(): void { - class A { - [customInspect](): string { - throw new Error("BOOM"); - return "b"; - } - } - - assertEquals(stringify(new A()), "A {}"); - - class B { - constructor(public field: { a: string }) {} - [customInspect](): string { - return this.field.a; - } - } - - assertEquals(stringify(new B({ a: "a" })), "a"); - assertEquals( - stringify(B.prototype), - "{ Symbol(Deno.symbols.customInspect): [Function: [Deno.symbols.customInspect]] }" - ); -}); - -unitTest(function consoleTestWithIntegerFormatSpecifier(): void { - assertEquals(stringify("%i"), "%i"); - assertEquals(stringify("%i", 42.0), "42"); - assertEquals(stringify("%i", 42), "42"); - assertEquals(stringify("%i", "42"), "42"); - assertEquals(stringify("%i", "42.0"), "42"); - assertEquals(stringify("%i", 1.5), "1"); - assertEquals(stringify("%i", -0.5), "0"); - assertEquals(stringify("%i", ""), "NaN"); - assertEquals(stringify("%i", Symbol()), "NaN"); - assertEquals(stringify("%i %d", 42, 43), "42 43"); - assertEquals(stringify("%d %i", 42), "42 %i"); - assertEquals(stringify("%d", 12345678901234567890123), "1"); - assertEquals( - stringify("%i", 12345678901234567890123n), - "12345678901234567890123n" - ); -}); - -unitTest(function consoleTestWithFloatFormatSpecifier(): void { - assertEquals(stringify("%f"), "%f"); - assertEquals(stringify("%f", 42.0), "42"); - assertEquals(stringify("%f", 42), "42"); - assertEquals(stringify("%f", "42"), "42"); - assertEquals(stringify("%f", "42.0"), "42"); - assertEquals(stringify("%f", 1.5), "1.5"); - assertEquals(stringify("%f", -0.5), "-0.5"); - assertEquals(stringify("%f", Math.PI), "3.141592653589793"); - assertEquals(stringify("%f", ""), "NaN"); - assertEquals(stringify("%f", Symbol("foo")), "NaN"); - assertEquals(stringify("%f", 5n), "5"); - assertEquals(stringify("%f %f", 42, 43), "42 43"); - assertEquals(stringify("%f %f", 42), "42 %f"); -}); - -unitTest(function consoleTestWithStringFormatSpecifier(): void { - assertEquals(stringify("%s"), "%s"); - assertEquals(stringify("%s", undefined), "undefined"); - assertEquals(stringify("%s", "foo"), "foo"); - assertEquals(stringify("%s", 42), "42"); - assertEquals(stringify("%s", "42"), "42"); - assertEquals(stringify("%s %s", 42, 43), "42 43"); - assertEquals(stringify("%s %s", 42), "42 %s"); - assertEquals(stringify("%s", Symbol("foo")), "Symbol(foo)"); -}); - -unitTest(function consoleTestWithObjectFormatSpecifier(): void { - assertEquals(stringify("%o"), "%o"); - assertEquals(stringify("%o", 42), "42"); - assertEquals(stringify("%o", "foo"), "foo"); - assertEquals(stringify("o: %o, a: %O", {}, []), "o: {}, a: []"); - assertEquals(stringify("%o", { a: 42 }), "{ a: 42 }"); - assertEquals( - stringify("%o", { a: { b: { c: { d: new Set([1]) } } } }), - "{ a: { b: { c: { d: [Set] } } } }" - ); -}); - -unitTest(function consoleTestWithVariousOrInvalidFormatSpecifier(): void { - assertEquals(stringify("%s:%s"), "%s:%s"); - assertEquals(stringify("%i:%i"), "%i:%i"); - assertEquals(stringify("%d:%d"), "%d:%d"); - assertEquals(stringify("%%s%s", "foo"), "%sfoo"); - assertEquals(stringify("%s:%s", undefined), "undefined:%s"); - assertEquals(stringify("%s:%s", "foo", "bar"), "foo:bar"); - assertEquals(stringify("%s:%s", "foo", "bar", "baz"), "foo:bar baz"); - assertEquals(stringify("%%%s%%", "hi"), "%hi%"); - assertEquals(stringify("%d:%d", 12), "12:%d"); - assertEquals(stringify("%i:%i", 12), "12:%i"); - assertEquals(stringify("%f:%f", 12), "12:%f"); - assertEquals(stringify("o: %o, a: %o", {}), "o: {}, a: %o"); - assertEquals(stringify("abc%", 1), "abc% 1"); -}); - -unitTest(function consoleTestCallToStringOnLabel(): void { - const methods = ["count", "countReset", "time", "timeLog", "timeEnd"]; - mockConsole((console) => { - for (const method of methods) { - let hasCalled = false; - // @ts-ignore - console[method]({ - toString(): void { - hasCalled = true; - }, - }); - assertEquals(hasCalled, true); - } - }); -}); - -unitTest(function consoleTestError(): void { - class MyError extends Error { - constructor(errStr: string) { - super(errStr); - this.name = "MyError"; - } - } - try { - throw new MyError("This is an error"); - } catch (e) { - assert( - stringify(e) - .split("\n")[0] // error has been caught - .includes("MyError: This is an error") - ); - } -}); - -unitTest(function consoleTestClear(): void { - mockConsole((console, out) => { - console.clear(); - assertEquals(out.toString(), "\x1b[1;1H" + "\x1b[0J"); - }); -}); - -// Test bound this issue -unitTest(function consoleDetachedLog(): void { - mockConsole((console) => { - const log = console.log; - const dir = console.dir; - const dirxml = console.dirxml; - const debug = console.debug; - const info = console.info; - const warn = console.warn; - const error = console.error; - const consoleAssert = console.assert; - const consoleCount = console.count; - const consoleCountReset = console.countReset; - const consoleTable = console.table; - const consoleTime = console.time; - const consoleTimeLog = console.timeLog; - const consoleTimeEnd = console.timeEnd; - const consoleGroup = console.group; - const consoleGroupEnd = console.groupEnd; - const consoleClear = console.clear; - log("Hello world"); - dir("Hello world"); - dirxml("Hello world"); - debug("Hello world"); - info("Hello world"); - warn("Hello world"); - error("Hello world"); - consoleAssert(true); - consoleCount("Hello world"); - consoleCountReset("Hello world"); - consoleTable({ test: "Hello world" }); - consoleTime("Hello world"); - consoleTimeLog("Hello world"); - consoleTimeEnd("Hello world"); - consoleGroup("Hello world"); - consoleGroupEnd(); - consoleClear(); - }); -}); - -class StringBuffer { - chunks: string[] = []; - add(x: string): void { - this.chunks.push(x); - } - toString(): string { - return this.chunks.join(""); - } -} - -type ConsoleExamineFunc = ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - csl: any, - out: StringBuffer, - err?: StringBuffer, - both?: StringBuffer -) => void; - -function mockConsole(f: ConsoleExamineFunc): void { - const out = new StringBuffer(); - const err = new StringBuffer(); - const both = new StringBuffer(); - const csl = new Console( - (x: string, isErr: boolean, printsNewLine: boolean): void => { - const content = x + (printsNewLine ? "\n" : ""); - const buf = isErr ? err : out; - buf.add(content); - both.add(content); - } - ); - f(csl, out, err, both); -} - -// console.group test -unitTest(function consoleGroup(): void { - mockConsole((console, out): void => { - console.group("1"); - console.log("2"); - console.group("3"); - console.log("4"); - console.groupEnd(); - console.groupEnd(); - console.log("5"); - console.log("6"); - - assertEquals( - out.toString(), - `1 - 2 - 3 - 4 -5 -6 -` - ); - }); -}); - -// console.group with console.warn test -unitTest(function consoleGroupWarn(): void { - mockConsole((console, _out, _err, both): void => { - assert(both); - console.warn("1"); - console.group(); - console.warn("2"); - console.group(); - console.warn("3"); - console.groupEnd(); - console.warn("4"); - console.groupEnd(); - console.warn("5"); - - console.warn("6"); - console.warn("7"); - assertEquals( - both.toString(), - `1 - 2 - 3 - 4 -5 -6 -7 -` - ); - }); -}); - -// console.table test -unitTest(function consoleTable(): void { - mockConsole((console, out): void => { - console.table({ a: "test", b: 1 }); - assertEquals( - stripColor(out.toString()), - `┌───────┬────────┐ -│ (idx) │ Values │ -├───────┼────────┤ -│ a │ "test" │ -│ b │ 1 │ -└───────┴────────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table({ a: { b: 10 }, b: { b: 20, c: 30 } }, ["c"]); - assertEquals( - stripColor(out.toString()), - `┌───────┬────┐ -│ (idx) │ c │ -├───────┼────┤ -│ a │ │ -│ b │ 30 │ -└───────┴────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table([1, 2, [3, [4]], [5, 6], [[7], [8]]]); - assertEquals( - stripColor(out.toString()), - `┌───────┬───────┬───────┬────────┐ -│ (idx) │ 0 │ 1 │ Values │ -├───────┼───────┼───────┼────────┤ -│ 0 │ │ │ 1 │ -│ 1 │ │ │ 2 │ -│ 2 │ 3 │ [ 4 ] │ │ -│ 3 │ 5 │ 6 │ │ -│ 4 │ [ 7 ] │ [ 8 ] │ │ -└───────┴───────┴───────┴────────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table(new Set([1, 2, 3, "test"])); - assertEquals( - stripColor(out.toString()), - `┌────────────┬────────┐ -│ (iter idx) │ Values │ -├────────────┼────────┤ -│ 0 │ 1 │ -│ 1 │ 2 │ -│ 2 │ 3 │ -│ 3 │ "test" │ -└────────────┴────────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table( - new Map([ - [1, "one"], - [2, "two"], - ]) - ); - assertEquals( - stripColor(out.toString()), - `┌────────────┬─────┬────────┐ -│ (iter idx) │ Key │ Values │ -├────────────┼─────┼────────┤ -│ 0 │ 1 │ "one" │ -│ 1 │ 2 │ "two" │ -└────────────┴─────┴────────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table({ - a: true, - b: { c: { d: 10 }, e: [1, 2, [5, 6]] }, - f: "test", - g: new Set([1, 2, 3, "test"]), - h: new Map([[1, "one"]]), - }); - assertEquals( - stripColor(out.toString()), - `┌───────┬───────────┬───────────────────┬────────┐ -│ (idx) │ c │ e │ Values │ -├───────┼───────────┼───────────────────┼────────┤ -│ a │ │ │ true │ -│ b │ { d: 10 } │ [ 1, 2, [Array] ] │ │ -│ f │ │ │ "test" │ -│ g │ │ │ │ -│ h │ │ │ │ -└───────┴───────────┴───────────────────┴────────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table([ - 1, - "test", - false, - { a: 10 }, - ["test", { b: 20, c: "test" }], - ]); - assertEquals( - stripColor(out.toString()), - `┌───────┬────────┬──────────────────────┬────┬────────┐ -│ (idx) │ 0 │ 1 │ a │ Values │ -├───────┼────────┼──────────────────────┼────┼────────┤ -│ 0 │ │ │ │ 1 │ -│ 1 │ │ │ │ "test" │ -│ 2 │ │ │ │ false │ -│ 3 │ │ │ 10 │ │ -│ 4 │ "test" │ { b: 20, c: "test" } │ │ │ -└───────┴────────┴──────────────────────┴────┴────────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table([]); - assertEquals( - stripColor(out.toString()), - `┌───────┐ -│ (idx) │ -├───────┤ -└───────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table({}); - assertEquals( - stripColor(out.toString()), - `┌───────┐ -│ (idx) │ -├───────┤ -└───────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table(new Set()); - assertEquals( - stripColor(out.toString()), - `┌────────────┐ -│ (iter idx) │ -├────────────┤ -└────────────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table(new Map()); - assertEquals( - stripColor(out.toString()), - `┌────────────┐ -│ (iter idx) │ -├────────────┤ -└────────────┘ -` - ); - }); - mockConsole((console, out): void => { - console.table("test"); - assertEquals(out.toString(), "test\n"); - }); - mockConsole((console, out): void => { - console.table(["Hello", "你好", "Amapá"]); - assertEquals( - stripColor(out.toString()), - `┌───────┬─────────┐ -│ (idx) │ Values │ -├───────┼─────────┤ -│ 0 │ "Hello" │ -│ 1 │ "你好" │ -│ 2 │ "Amapá" │ -└───────┴─────────┘ -` - ); - }); -}); - -// console.log(Error) test -unitTest(function consoleLogShouldNotThrowError(): void { - mockConsole((console) => { - let result = 0; - try { - console.log(new Error("foo")); - result = 1; - } catch (e) { - result = 2; - } - assertEquals(result, 1); - }); - - // output errors to the console should not include "Uncaught" - mockConsole((console, out): void => { - console.log(new Error("foo")); - assertEquals(out.toString().includes("Uncaught"), false); - }); -}); - -// console.log(Invalid Date) test -unitTest(function consoleLogShoultNotThrowErrorWhenInvalidDateIsPassed(): void { - mockConsole((console, out) => { - const invalidDate = new Date("test"); - console.log(invalidDate); - assertEquals(stripColor(out.toString()), "Invalid Date\n"); - }); -}); - -// console.dir test -unitTest(function consoleDir(): void { - mockConsole((console, out): void => { - console.dir("DIR"); - assertEquals(out.toString(), "DIR\n"); - }); - mockConsole((console, out): void => { - console.dir("DIR", { indentLevel: 2 }); - assertEquals(out.toString(), " DIR\n"); - }); -}); - -// console.dir test -unitTest(function consoleDirXml(): void { - mockConsole((console, out): void => { - console.dirxml("DIRXML"); - assertEquals(out.toString(), "DIRXML\n"); - }); - mockConsole((console, out): void => { - console.dirxml("DIRXML", { indentLevel: 2 }); - assertEquals(out.toString(), " DIRXML\n"); - }); -}); - -// console.trace test -unitTest(function consoleTrace(): void { - mockConsole((console, _out, err): void => { - console.trace("%s", "custom message"); - assert(err); - assert(err.toString().includes("Trace: custom message")); - }); -}); diff --git a/cli/js/tests/copy_file_test.ts b/cli/js/tests/copy_file_test.ts deleted file mode 100644 index 986bab53b..000000000 --- a/cli/js/tests/copy_file_test.ts +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -function readFileString(filename: string): string { - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - return dec.decode(dataRead); -} - -function writeFileString(filename: string, s: string): void { - const enc = new TextEncoder(); - const data = enc.encode(s); - Deno.writeFileSync(filename, data, { mode: 0o666 }); -} - -function assertSameContent(filename1: string, filename2: string): void { - const data1 = Deno.readFileSync(filename1); - const data2 = Deno.readFileSync(filename2); - assertEquals(data1, data2); -} - -unitTest( - { perms: { read: true, write: true } }, - function copyFileSyncSuccess(): void { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - writeFileString(fromFilename, "Hello world!"); - Deno.copyFileSync(fromFilename, toFilename); - // No change to original file - assertEquals(readFileString(fromFilename), "Hello world!"); - // Original == Dest - assertSameContent(fromFilename, toFilename); - } -); - -unitTest( - { perms: { write: true, read: true } }, - function copyFileSyncFailure(): void { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - // We skip initial writing here, from.txt does not exist - let err; - try { - Deno.copyFileSync(fromFilename, toFilename); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: false } }, - function copyFileSyncPerm1(): void { - let caughtError = false; - try { - Deno.copyFileSync("/from.txt", "/to.txt"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); - } -); - -unitTest( - { perms: { write: false, read: true } }, - function copyFileSyncPerm2(): void { - let caughtError = false; - try { - Deno.copyFileSync("/from.txt", "/to.txt"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function copyFileSyncOverwrite(): void { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - writeFileString(fromFilename, "Hello world!"); - // Make Dest exist and have different content - writeFileString(toFilename, "Goodbye!"); - Deno.copyFileSync(fromFilename, toFilename); - // No change to original file - assertEquals(readFileString(fromFilename), "Hello world!"); - // Original == Dest - assertSameContent(fromFilename, toFilename); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function copyFileSuccess(): Promise { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - writeFileString(fromFilename, "Hello world!"); - await Deno.copyFile(fromFilename, toFilename); - // No change to original file - assertEquals(readFileString(fromFilename), "Hello world!"); - // Original == Dest - assertSameContent(fromFilename, toFilename); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function copyFileFailure(): Promise { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - // We skip initial writing here, from.txt does not exist - let err; - try { - await Deno.copyFile(fromFilename, toFilename); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function copyFileOverwrite(): Promise { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - writeFileString(fromFilename, "Hello world!"); - // Make Dest exist and have different content - writeFileString(toFilename, "Goodbye!"); - await Deno.copyFile(fromFilename, toFilename); - // No change to original file - assertEquals(readFileString(fromFilename), "Hello world!"); - // Original == Dest - assertSameContent(fromFilename, toFilename); - } -); - -unitTest( - { perms: { read: false, write: true } }, - async function copyFilePerm1(): Promise { - let caughtError = false; - try { - await Deno.copyFile("/from.txt", "/to.txt"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); - } -); - -unitTest( - { perms: { read: true, write: false } }, - async function copyFilePerm2(): Promise { - let caughtError = false; - try { - await Deno.copyFile("/from.txt", "/to.txt"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); - } -); diff --git a/cli/js/tests/custom_event_test.ts b/cli/js/tests/custom_event_test.ts deleted file mode 100644 index a8b2fcf88..000000000 --- a/cli/js/tests/custom_event_test.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals } from "./test_util.ts"; - -unitTest(function customEventInitializedWithDetail(): void { - const type = "touchstart"; - const detail = { message: "hello" }; - const customEventInit = { - bubbles: true, - cancelable: true, - detail, - } as CustomEventInit; - const event = new CustomEvent(type, customEventInit); - - assertEquals(event.bubbles, true); - assertEquals(event.cancelable, true); - assertEquals(event.currentTarget, null); - assertEquals(event.detail, detail); - assertEquals(event.isTrusted, false); - assertEquals(event.target, null); - assertEquals(event.type, type); -}); - -unitTest(function toStringShouldBeWebCompatibility(): void { - const type = "touchstart"; - const event = new CustomEvent(type, {}); - assertEquals(event.toString(), "[object CustomEvent]"); -}); diff --git a/cli/js/tests/dir_test.ts b/cli/js/tests/dir_test.ts deleted file mode 100644 index dc05d9564..000000000 --- a/cli/js/tests/dir_test.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest({ perms: { read: true } }, function dirCwdNotNull(): void { - assert(Deno.cwd() != null); -}); - -unitTest( - { perms: { read: true, write: true } }, - function dirCwdChdirSuccess(): void { - const initialdir = Deno.cwd(); - const path = Deno.makeTempDirSync(); - Deno.chdir(path); - const current = Deno.cwd(); - if (Deno.build.os === "darwin") { - assertEquals(current, "/private" + path); - } else { - assertEquals(current, path); - } - Deno.chdir(initialdir); - } -); - -unitTest({ perms: { read: true, write: true } }, function dirCwdError(): void { - // excluding windows since it throws resource busy, while removeSync - if (["linux", "darwin"].includes(Deno.build.os)) { - const initialdir = Deno.cwd(); - const path = Deno.makeTempDirSync(); - Deno.chdir(path); - Deno.removeSync(path); - try { - Deno.cwd(); - throw Error("current directory removed, should throw error"); - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - assert(err.name === "NotFound"); - } else { - throw Error("raised different exception"); - } - } - Deno.chdir(initialdir); - } -}); - -unitTest( - { perms: { read: true, write: true } }, - function dirChdirError(): void { - const path = Deno.makeTempDirSync() + "test"; - try { - Deno.chdir(path); - throw Error("directory not available, should throw error"); - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - assert(err.name === "NotFound"); - } else { - throw Error("raised different exception"); - } - } - } -); diff --git a/cli/js/tests/dispatch_json_test.ts b/cli/js/tests/dispatch_json_test.ts deleted file mode 100644 index 4e95b86a2..000000000 --- a/cli/js/tests/dispatch_json_test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { assert, unitTest, assertMatch, unreachable } from "./test_util.ts"; - -const openErrorStackPattern = new RegExp( - `^.* - at unwrapResponse \\(.*dispatch_json\\.ts:.*\\) - at Object.sendAsync \\(.*dispatch_json\\.ts:.*\\) - at async Object\\.open \\(.*files\\.ts:.*\\).*$`, - "ms" -); - -unitTest( - { perms: { read: true } }, - async function sendAsyncStackTrace(): Promise { - await Deno.open("nonexistent.txt") - .then(unreachable) - .catch((error): void => { - assertMatch(error.stack, openErrorStackPattern); - }); - } -); - -unitTest(function malformedJsonControlBuffer(): void { - // @ts-ignore - const opId = Deno.core.ops()["op_open"]; - // @ts-ignore - const res = Deno.core.send(opId, new Uint8Array([1, 2, 3, 4, 5])); - const resText = new TextDecoder().decode(res); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const resJson = JSON.parse(resText) as any; - assert(!resJson.ok); - assert(resJson.err); -}); diff --git a/cli/js/tests/dispatch_minimal_test.ts b/cli/js/tests/dispatch_minimal_test.ts deleted file mode 100644 index afc17f4fb..000000000 --- a/cli/js/tests/dispatch_minimal_test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - assert, - assertEquals, - assertMatch, - unitTest, - unreachable, -} from "./test_util.ts"; - -const readErrorStackPattern = new RegExp( - `^.* - at unwrapResponse \\(.*dispatch_minimal\\.ts:.*\\) - at Object.sendAsyncMinimal \\(.*dispatch_minimal\\.ts:.*\\) - at async Object\\.read \\(.*io\\.ts:.*\\).*$`, - "ms" -); - -unitTest(async function sendAsyncStackTrace(): Promise { - const buf = new Uint8Array(10); - const rid = 10; - try { - await Deno.read(rid, buf); - unreachable(); - } catch (error) { - assertMatch(error.stack, readErrorStackPattern); - } -}); - -unitTest(function malformedMinimalControlBuffer(): void { - // @ts-ignore - const readOpId = Deno.core.ops()["op_read"]; - // @ts-ignore - const res = Deno.core.send(readOpId, new Uint8Array([1, 2, 3, 4, 5])); - const header = res.slice(0, 12); - const buf32 = new Int32Array( - header.buffer, - header.byteOffset, - header.byteLength / 4 - ); - const arg = buf32[1]; - const message = new TextDecoder().decode(res.slice(12)).trim(); - assert(arg < 0); - assertEquals(message, "Unparsable control buffer"); -}); diff --git a/cli/js/tests/dom_exception_test.ts b/cli/js/tests/dom_exception_test.ts deleted file mode 100644 index 2eb7633e1..000000000 --- a/cli/js/tests/dom_exception_test.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals, assert } from "./test_util.ts"; - -unitTest(function testDomError() { - const de = new DOMException("foo", "bar"); - assert(de); - assertEquals(de.message, "foo"); - assertEquals(de.name, "bar"); -}); diff --git a/cli/js/tests/dom_iterable_test.ts b/cli/js/tests/dom_iterable_test.ts deleted file mode 100644 index b9435b3bc..000000000 --- a/cli/js/tests/dom_iterable_test.ts +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -function setup() { - const dataSymbol = Symbol("data symbol"); - class Base { - [dataSymbol] = new Map(); - - constructor( - data: Array<[string, number]> | IterableIterator<[string, number]> - ) { - for (const [key, value] of data) { - this[dataSymbol].set(key, value); - } - } - } - - return { - Base, - // This is using an internal API we don't want published as types, so having - // to cast to any to "trick" TypeScript - // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol - DomIterable: Deno[Deno.internal].DomIterableMixin(Base, dataSymbol), - }; -} - -unitTest(function testDomIterable(): void { - const { DomIterable, Base } = setup(); - - const fixture: Array<[string, number]> = [ - ["foo", 1], - ["bar", 2], - ]; - - const domIterable = new DomIterable(fixture); - - assertEquals(Array.from(domIterable.entries()), fixture); - assertEquals(Array.from(domIterable.values()), [1, 2]); - assertEquals(Array.from(domIterable.keys()), ["foo", "bar"]); - - let result: Array<[string, number]> = []; - for (const [key, value] of domIterable) { - assert(key != null); - assert(value != null); - result.push([key, value]); - } - assertEquals(fixture, result); - - result = []; - const scope = {}; - function callback( - this: typeof scope, - value: number, - key: string, - parent: typeof domIterable - ): void { - assertEquals(parent, domIterable); - assert(key != null); - assert(value != null); - assert(this === scope); - result.push([key, value]); - } - domIterable.forEach(callback, scope); - assertEquals(fixture, result); - - assertEquals(DomIterable.name, Base.name); -}); - -unitTest(function testDomIterableScope(): void { - const { DomIterable } = setup(); - - const domIterable = new DomIterable([["foo", 1]]); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - function checkScope(thisArg: any, expected: any): void { - function callback(this: typeof thisArg): void { - assertEquals(this, expected); - } - domIterable.forEach(callback, thisArg); - } - - checkScope(0, Object(0)); - checkScope("", Object("")); - checkScope(null, window); - checkScope(undefined, window); -}); diff --git a/cli/js/tests/error_stack_test.ts b/cli/js/tests/error_stack_test.ts deleted file mode 100644 index e5cedfcf5..000000000 --- a/cli/js/tests/error_stack_test.ts +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -// @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol -const { setPrepareStackTrace } = Deno[Deno.internal]; - -interface CallSite { - getThis(): unknown; - getTypeName(): string | null; - getFunction(): Function | null; - getFunctionName(): string | null; - getMethodName(): string | null; - getFileName(): string | null; - getLineNumber(): number | null; - getColumnNumber(): number | null; - getEvalOrigin(): string | null; - isToplevel(): boolean | null; - isEval(): boolean; - isNative(): boolean; - isConstructor(): boolean; - isAsync(): boolean; - isPromiseAll(): boolean; - getPromiseIndex(): number | null; -} - -function getMockCallSite( - fileName: string, - lineNumber: number | null, - columnNumber: number | null -): CallSite { - return { - getThis(): unknown { - return undefined; - }, - getTypeName(): string { - return ""; - }, - getFunction(): Function { - return (): void => {}; - }, - getFunctionName(): string { - return ""; - }, - getMethodName(): string { - return ""; - }, - getFileName(): string { - return fileName; - }, - getLineNumber(): number | null { - return lineNumber; - }, - getColumnNumber(): number | null { - return columnNumber; - }, - getEvalOrigin(): null { - return null; - }, - isToplevel(): false { - return false; - }, - isEval(): false { - return false; - }, - isNative(): false { - return false; - }, - isConstructor(): false { - return false; - }, - isAsync(): false { - return false; - }, - isPromiseAll(): false { - return false; - }, - getPromiseIndex(): null { - return null; - }, - }; -} - -unitTest(function prepareStackTrace(): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const MockError = {} as any; - setPrepareStackTrace(MockError); - assert(typeof MockError.prepareStackTrace === "function"); - const prepareStackTrace: ( - error: Error, - structuredStackTrace: CallSite[] - ) => string = MockError.prepareStackTrace; - const result = prepareStackTrace(new Error("foo"), [ - getMockCallSite("CLI_SNAPSHOT.js", 23, 0), - ]); - assert(result.startsWith("Error: foo\n")); - assert(result.includes(".ts:"), "should remap to something in 'js/'"); -}); - -unitTest(function applySourceMap(): void { - const result = Deno.applySourceMap({ - fileName: "CLI_SNAPSHOT.js", - lineNumber: 23, - columnNumber: 0, - }); - assert(result.fileName.endsWith(".ts")); - assert(result.lineNumber != null); - assert(result.columnNumber != null); -}); diff --git a/cli/js/tests/event_target_test.ts b/cli/js/tests/event_target_test.ts deleted file mode 100644 index 0c4eb4d0d..000000000 --- a/cli/js/tests/event_target_test.ts +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals } from "./test_util.ts"; - -unitTest(function addEventListenerTest(): void { - const document = new EventTarget(); - - // @ts-ignore tests ignoring the type system for resilience - assertEquals(document.addEventListener("x", null, false), undefined); - // @ts-ignore - assertEquals(document.addEventListener("x", null, true), undefined); - // @ts-ignore - assertEquals(document.addEventListener("x", null), undefined); -}); - -unitTest(function constructedEventTargetCanBeUsedAsExpected(): void { - const target = new EventTarget(); - const event = new Event("foo", { bubbles: true, cancelable: false }); - let callCount = 0; - - const listener = (e: Event): void => { - assertEquals(e, event); - ++callCount; - }; - - target.addEventListener("foo", listener); - - target.dispatchEvent(event); - assertEquals(callCount, 1); - - target.dispatchEvent(event); - assertEquals(callCount, 2); - - target.removeEventListener("foo", listener); - target.dispatchEvent(event); - assertEquals(callCount, 2); -}); - -unitTest(function anEventTargetCanBeSubclassed(): void { - class NicerEventTarget extends EventTarget { - on( - type: string, - callback: ((e: Event) => void) | null, - options?: AddEventListenerOptions - ): void { - this.addEventListener(type, callback, options); - } - - off( - type: string, - callback: ((e: Event) => void) | null, - options?: EventListenerOptions - ): void { - this.removeEventListener(type, callback, options); - } - } - - const target = new NicerEventTarget(); - new Event("foo", { bubbles: true, cancelable: false }); - let callCount = 0; - - const listener = (): void => { - ++callCount; - }; - - target.on("foo", listener); - assertEquals(callCount, 0); - - target.off("foo", listener); - assertEquals(callCount, 0); -}); - -unitTest(function removingNullEventListenerShouldSucceed(): void { - const document = new EventTarget(); - // @ts-ignore - assertEquals(document.removeEventListener("x", null, false), undefined); - // @ts-ignore - assertEquals(document.removeEventListener("x", null, true), undefined); - // @ts-ignore - assertEquals(document.removeEventListener("x", null), undefined); -}); - -unitTest(function constructedEventTargetUseObjectPrototype(): void { - const target = new EventTarget(); - const event = new Event("toString", { bubbles: true, cancelable: false }); - let callCount = 0; - - const listener = (e: Event): void => { - assertEquals(e, event); - ++callCount; - }; - - target.addEventListener("toString", listener); - - target.dispatchEvent(event); - assertEquals(callCount, 1); - - target.dispatchEvent(event); - assertEquals(callCount, 2); - - target.removeEventListener("toString", listener); - target.dispatchEvent(event); - assertEquals(callCount, 2); -}); - -unitTest(function toStringShouldBeWebCompatible(): void { - const target = new EventTarget(); - assertEquals(target.toString(), "[object EventTarget]"); -}); - -unitTest(function dispatchEventShouldNotThrowError(): void { - let hasThrown = false; - - try { - const target = new EventTarget(); - const event = new Event("hasOwnProperty", { - bubbles: true, - cancelable: false, - }); - const listener = (): void => {}; - target.addEventListener("hasOwnProperty", listener); - target.dispatchEvent(event); - } catch { - hasThrown = true; - } - - assertEquals(hasThrown, false); -}); - -unitTest(function eventTargetThisShouldDefaultToWindow(): void { - const { - addEventListener, - dispatchEvent, - removeEventListener, - } = EventTarget.prototype; - let n = 1; - const event = new Event("hello"); - const listener = (): void => { - n = 2; - }; - - addEventListener("hello", listener); - window.dispatchEvent(event); - assertEquals(n, 2); - n = 1; - removeEventListener("hello", listener); - window.dispatchEvent(event); - assertEquals(n, 1); - - window.addEventListener("hello", listener); - dispatchEvent(event); - assertEquals(n, 2); - n = 1; - window.removeEventListener("hello", listener); - dispatchEvent(event); - assertEquals(n, 1); -}); - -unitTest(function eventTargetShouldAcceptEventListenerObject(): void { - const target = new EventTarget(); - const event = new Event("foo", { bubbles: true, cancelable: false }); - let callCount = 0; - - const listener = { - handleEvent(e: Event): void { - assertEquals(e, event); - ++callCount; - }, - }; - - target.addEventListener("foo", listener); - - target.dispatchEvent(event); - assertEquals(callCount, 1); - - target.dispatchEvent(event); - assertEquals(callCount, 2); - - target.removeEventListener("foo", listener); - target.dispatchEvent(event); - assertEquals(callCount, 2); -}); - -unitTest(function eventTargetShouldAcceptAsyncFunction(): void { - const target = new EventTarget(); - const event = new Event("foo", { bubbles: true, cancelable: false }); - let callCount = 0; - - const listener = (e: Event): void => { - assertEquals(e, event); - ++callCount; - }; - - target.addEventListener("foo", listener); - - target.dispatchEvent(event); - assertEquals(callCount, 1); - - target.dispatchEvent(event); - assertEquals(callCount, 2); - - target.removeEventListener("foo", listener); - target.dispatchEvent(event); - assertEquals(callCount, 2); -}); - -unitTest( - function eventTargetShouldAcceptAsyncFunctionForEventListenerObject(): void { - const target = new EventTarget(); - const event = new Event("foo", { bubbles: true, cancelable: false }); - let callCount = 0; - - const listener = { - handleEvent(e: Event): void { - assertEquals(e, event); - ++callCount; - }, - }; - - target.addEventListener("foo", listener); - - target.dispatchEvent(event); - assertEquals(callCount, 1); - - target.dispatchEvent(event); - assertEquals(callCount, 2); - - target.removeEventListener("foo", listener); - target.dispatchEvent(event); - assertEquals(callCount, 2); - } -); diff --git a/cli/js/tests/event_test.ts b/cli/js/tests/event_test.ts deleted file mode 100644 index ce3076e58..000000000 --- a/cli/js/tests/event_test.ts +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals, assert } from "./test_util.ts"; - -unitTest(function eventInitializedWithType(): void { - const type = "click"; - const event = new Event(type); - - assertEquals(event.isTrusted, false); - assertEquals(event.target, null); - assertEquals(event.currentTarget, null); - assertEquals(event.type, "click"); - assertEquals(event.bubbles, false); - assertEquals(event.cancelable, false); -}); - -unitTest(function eventInitializedWithTypeAndDict(): void { - const init = "submit"; - const eventInit = { bubbles: true, cancelable: true } as EventInit; - const event = new Event(init, eventInit); - - assertEquals(event.isTrusted, false); - assertEquals(event.target, null); - assertEquals(event.currentTarget, null); - assertEquals(event.type, "submit"); - assertEquals(event.bubbles, true); - assertEquals(event.cancelable, true); -}); - -unitTest(function eventComposedPathSuccess(): void { - const type = "click"; - const event = new Event(type); - const composedPath = event.composedPath(); - - assertEquals(composedPath, []); -}); - -unitTest(function eventStopPropagationSuccess(): void { - const type = "click"; - const event = new Event(type); - - assertEquals(event.cancelBubble, false); - event.stopPropagation(); - assertEquals(event.cancelBubble, true); -}); - -unitTest(function eventStopImmediatePropagationSuccess(): void { - const type = "click"; - const event = new Event(type); - - assertEquals(event.cancelBubble, false); - event.stopImmediatePropagation(); - assertEquals(event.cancelBubble, true); -}); - -unitTest(function eventPreventDefaultSuccess(): void { - const type = "click"; - const event = new Event(type); - - assertEquals(event.defaultPrevented, false); - event.preventDefault(); - assertEquals(event.defaultPrevented, false); - - const eventInit = { bubbles: true, cancelable: true } as EventInit; - const cancelableEvent = new Event(type, eventInit); - assertEquals(cancelableEvent.defaultPrevented, false); - cancelableEvent.preventDefault(); - assertEquals(cancelableEvent.defaultPrevented, true); -}); - -unitTest(function eventInitializedWithNonStringType(): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const type: any = undefined; - const event = new Event(type); - - assertEquals(event.isTrusted, false); - assertEquals(event.target, null); - assertEquals(event.currentTarget, null); - assertEquals(event.type, "undefined"); - assertEquals(event.bubbles, false); - assertEquals(event.cancelable, false); -}); - -// ref https://github.com/web-platform-tests/wpt/blob/master/dom/events/Event-isTrusted.any.js -unitTest(function eventIsTrusted(): void { - const desc1 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted"); - assert(desc1); - assertEquals(typeof desc1.get, "function"); - - const desc2 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted"); - assert(desc2); - assertEquals(typeof desc2!.get, "function"); - - assertEquals(desc1!.get, desc2!.get); -}); diff --git a/cli/js/tests/fetch_test.ts b/cli/js/tests/fetch_test.ts deleted file mode 100644 index 37fca2112..000000000 --- a/cli/js/tests/fetch_test.ts +++ /dev/null @@ -1,534 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - unitTest, - assert, - assertEquals, - assertStrContains, - assertThrows, - fail, -} from "./test_util.ts"; - -unitTest({ perms: { net: true } }, async function fetchProtocolError(): Promise< - void -> { - let err; - try { - await fetch("file:///"); - } catch (err_) { - err = err_; - } - assert(err instanceof TypeError); - assertStrContains(err.message, "not supported"); -}); - -unitTest( - { perms: { net: true } }, - async function fetchConnectionError(): Promise { - let err; - try { - await fetch("http://localhost:4000"); - } catch (err_) { - err = err_; - } - assert(err instanceof Deno.errors.Http); - assertStrContains(err.message, "error trying to connect"); - } -); - -unitTest({ perms: { net: true } }, async function fetchJsonSuccess(): Promise< - void -> { - const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); - const json = await response.json(); - assertEquals(json.name, "deno"); -}); - -unitTest(async function fetchPerm(): Promise { - let err; - try { - await fetch("http://localhost:4545/cli/tests/fixture.json"); - } catch (err_) { - err = err_; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest({ perms: { net: true } }, async function fetchUrl(): Promise { - const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); - assertEquals(response.url, "http://localhost:4545/cli/tests/fixture.json"); - const _json = await response.json(); -}); - -unitTest({ perms: { net: true } }, async function fetchURL(): Promise { - const response = await fetch( - new URL("http://localhost:4545/cli/tests/fixture.json") - ); - assertEquals(response.url, "http://localhost:4545/cli/tests/fixture.json"); - const _json = await response.json(); -}); - -unitTest({ perms: { net: true } }, async function fetchHeaders(): Promise< - void -> { - const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); - const headers = response.headers; - assertEquals(headers.get("Content-Type"), "application/json"); - assert(headers.get("Server")!.startsWith("SimpleHTTP")); - const _json = await response.json(); -}); - -unitTest({ perms: { net: true } }, async function fetchBlob(): Promise { - const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); - const headers = response.headers; - const blob = await response.blob(); - assertEquals(blob.type, headers.get("Content-Type")); - assertEquals(blob.size, Number(headers.get("Content-Length"))); -}); - -unitTest({ perms: { net: true } }, async function fetchBodyUsed(): Promise< - void -> { - const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); - assertEquals(response.bodyUsed, false); - assertThrows((): void => { - // Assigning to read-only property throws in the strict mode. - // @ts-ignore - response.bodyUsed = true; - }); - await response.blob(); - assertEquals(response.bodyUsed, true); -}); - -// TODO(ry) response.body shouldn't be iterable. Instead we should use -// response.body.getReader(). -/* -unitTest({ perms: { net: true } }, async function fetchAsyncIterator(): Promise< - void -> { - const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); - const headers = response.headers; - let total = 0; - for await (const chunk of response.body) { - total += chunk.length; - } - - assertEquals(total, Number(headers.get("Content-Length"))); - const _json = await response.json(); -}); -*/ - -unitTest({ perms: { net: true } }, async function responseClone(): Promise< - void -> { - const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); - const response1 = response.clone(); - assert(response !== response1); - assertEquals(response.status, response1.status); - assertEquals(response.statusText, response1.statusText); - const u8a = new Uint8Array(await response.arrayBuffer()); - const u8a1 = new Uint8Array(await response1.arrayBuffer()); - for (let i = 0; i < u8a.byteLength; i++) { - assertEquals(u8a[i], u8a1[i]); - } -}); - -unitTest({ perms: { net: true } }, async function fetchEmptyInvalid(): Promise< - void -> { - let err; - try { - await fetch(""); - } catch (err_) { - err = err_; - } - assert(err instanceof URIError); -}); - -unitTest( - { perms: { net: true } }, - async function fetchMultipartFormDataSuccess(): Promise { - const response = await fetch( - "http://localhost:4545/cli/tests/subdir/multipart_form_data.txt" - ); - const formData = await response.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1")!.toString(), "value_1 \r\n"); - assert(formData.has("field_2")); - /* TODO(ry) Re-enable this test once we bring back the global File type. - const file = formData.get("field_2") as File; - assertEquals(file.name, "file.js"); - */ - // Currently we cannot read from file... - } -); - -unitTest( - { perms: { net: true } }, - async function fetchURLEncodedFormDataSuccess(): Promise { - const response = await fetch( - "http://localhost:4545/cli/tests/subdir/form_urlencoded.txt" - ); - const formData = await response.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1")!.toString(), "Hi"); - assert(formData.has("field_2")); - assertEquals(formData.get("field_2")!.toString(), ""); - } -); - -unitTest( - { - perms: { net: true }, - }, - async function fetchWithRedirection(): Promise { - const response = await fetch("http://localhost:4546/"); // will redirect to http://localhost:4545/ - assertEquals(response.status, 200); - assertEquals(response.statusText, "OK"); - assertEquals(response.url, "http://localhost:4545/"); - const body = await response.text(); - assert(body.includes("Directory listing for /")); - } -); - -unitTest( - { - perms: { net: true }, - }, - async function fetchWithRelativeRedirection(): Promise { - const response = await fetch("http://localhost:4545/cli/tests"); // will redirect to /cli/tests/ - assertEquals(response.status, 200); - assertEquals(response.statusText, "OK"); - const body = await response.text(); - assert(body.includes("Directory listing for /cli/tests/")); - } -); - -unitTest( - { - // FIXME(bartlomieju): - // The feature below is not implemented, but the test should work after implementation - ignore: true, - perms: { net: true }, - }, - async function fetchWithInfRedirection(): Promise { - const response = await fetch("http://localhost:4549/cli/tests"); // will redirect to the same place - assertEquals(response.status, 0); // network error - } -); - -unitTest( - { perms: { net: true } }, - async function fetchInitStringBody(): Promise { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: data, - }); - const text = await response.text(); - assertEquals(text, data); - assert(response.headers.get("content-type")!.startsWith("text/plain")); - } -); - -unitTest( - { perms: { net: true } }, - async function fetchRequestInitStringBody(): Promise { - const data = "Hello World"; - const req = new Request("http://localhost:4545/echo_server", { - method: "POST", - body: data, - }); - const response = await fetch(req); - const text = await response.text(); - assertEquals(text, data); - } -); - -unitTest( - { perms: { net: true } }, - async function fetchInitTypedArrayBody(): Promise { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: new TextEncoder().encode(data), - }); - const text = await response.text(); - assertEquals(text, data); - } -); - -unitTest( - { perms: { net: true } }, - async function fetchInitURLSearchParamsBody(): Promise { - const data = "param1=value1¶m2=value2"; - const params = new URLSearchParams(data); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: params, - }); - const text = await response.text(); - assertEquals(text, data); - assert( - response.headers - .get("content-type")! - .startsWith("application/x-www-form-urlencoded") - ); - } -); - -unitTest({ perms: { net: true } }, async function fetchInitBlobBody(): Promise< - void -> { - const data = "const a = 1"; - const blob = new Blob([data], { - type: "text/javascript", - }); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: blob, - }); - const text = await response.text(); - assertEquals(text, data); - assert(response.headers.get("content-type")!.startsWith("text/javascript")); -}); - -unitTest( - { perms: { net: true } }, - async function fetchInitFormDataBody(): Promise { - const form = new FormData(); - form.append("field", "value"); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: form, - }); - const resultForm = await response.formData(); - assertEquals(form.get("field"), resultForm.get("field")); - } -); - -unitTest({ perms: { net: true } }, async function fetchUserAgent(): Promise< - void -> { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: new TextEncoder().encode(data), - }); - assertEquals(response.headers.get("user-agent"), `Deno/${Deno.version.deno}`); - await response.text(); -}); - -// TODO(ry) The following tests work but are flaky. There's a race condition -// somewhere. Here is what one of these flaky failures looks like: -// -// unitTest fetchPostBodyString_permW0N1E0R0 -// assertEquals failed. actual = expected = POST /blah HTTP/1.1 -// hello: World -// foo: Bar -// host: 127.0.0.1:4502 -// content-length: 11 -// hello world -// Error: actual: expected: POST /blah HTTP/1.1 -// hello: World -// foo: Bar -// host: 127.0.0.1:4502 -// content-length: 11 -// hello world -// at Object.assertEquals (file:///C:/deno/js/testing/util.ts:29:11) -// at fetchPostBodyString (file - -function bufferServer(addr: string): Deno.Buffer { - const [hostname, port] = addr.split(":"); - const listener = Deno.listen({ - hostname, - port: Number(port), - }) as Deno.Listener; - const buf = new Deno.Buffer(); - listener.accept().then(async (conn: Deno.Conn) => { - const p1 = buf.readFrom(conn); - const p2 = conn.write( - new TextEncoder().encode( - "HTTP/1.0 404 Not Found\r\nContent-Length: 2\r\n\r\nNF" - ) - ); - // Wait for both an EOF on the read side of the socket and for the write to - // complete before closing it. Due to keep-alive, the EOF won't be sent - // until the Connection close (HTTP/1.0) response, so readFrom() can't - // proceed write. Conversely, if readFrom() is async, waiting for the - // write() to complete is not a guarantee that we've read the incoming - // request. - await Promise.all([p1, p2]); - conn.close(); - listener.close(); - }); - return buf; -} - -unitTest( - { - // FIXME(bartlomieju) - ignore: true, - perms: { net: true }, - }, - async function fetchRequest(): Promise { - const addr = "127.0.0.1:4501"; - const buf = bufferServer(addr); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Hello", "World"], - ["Foo", "Bar"], - ], - }); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode(buf.bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - `host: ${addr}\r\n\r\n`, - ].join(""); - assertEquals(actual, expected); - } -); - -unitTest( - { - // FIXME(bartlomieju) - ignore: true, - perms: { net: true }, - }, - async function fetchPostBodyString(): Promise { - const addr = "127.0.0.1:4502"; - const buf = bufferServer(addr); - const body = "hello world"; - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Hello", "World"], - ["Foo", "Bar"], - ], - body, - }); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode(buf.bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - `host: ${addr}\r\n`, - `content-length: ${body.length}\r\n\r\n`, - body, - ].join(""); - assertEquals(actual, expected); - } -); - -unitTest( - { - // FIXME(bartlomieju) - ignore: true, - perms: { net: true }, - }, - async function fetchPostBodyTypedArray(): Promise { - const addr = "127.0.0.1:4503"; - const buf = bufferServer(addr); - const bodyStr = "hello world"; - const body = new TextEncoder().encode(bodyStr); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Hello", "World"], - ["Foo", "Bar"], - ], - body, - }); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode(buf.bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - `host: ${addr}\r\n`, - `content-length: ${body.byteLength}\r\n\r\n`, - bodyStr, - ].join(""); - assertEquals(actual, expected); - } -); - -unitTest( - { - perms: { net: true }, - }, - async function fetchWithManualRedirection(): Promise { - const response = await fetch("http://localhost:4546/", { - redirect: "manual", - }); // will redirect to http://localhost:4545/ - assertEquals(response.status, 0); - assertEquals(response.statusText, ""); - assertEquals(response.url, ""); - assertEquals(response.type, "opaqueredirect"); - try { - await response.text(); - fail( - "Reponse.text() didn't throw on a filtered response without a body (type opaqueredirect)" - ); - } catch (e) { - return; - } - } -); - -unitTest( - { - perms: { net: true }, - }, - async function fetchWithErrorRedirection(): Promise { - const response = await fetch("http://localhost:4546/", { - redirect: "error", - }); // will redirect to http://localhost:4545/ - assertEquals(response.status, 0); - assertEquals(response.statusText, ""); - assertEquals(response.url, ""); - assertEquals(response.type, "error"); - try { - await response.text(); - fail( - "Reponse.text() didn't throw on a filtered response without a body (type error)" - ); - } catch (e) { - return; - } - } -); - -unitTest(function responseRedirect(): void { - const redir = Response.redirect("example.com/newLocation", 301); - assertEquals(redir.status, 301); - assertEquals(redir.statusText, ""); - assertEquals(redir.url, ""); - assertEquals(redir.headers.get("Location"), "example.com/newLocation"); - assertEquals(redir.type, "default"); -}); - -unitTest(function responseConstructionHeaderRemoval(): void { - const res = new Response( - "example.com", - 200, - "OK", - [["Set-Cookie", "mysessionid"]], - -1, - false, - "basic", - null - ); - assert(res.headers.get("Set-Cookie") != "mysessionid"); -}); diff --git a/cli/js/tests/file_test.ts b/cli/js/tests/file_test.ts deleted file mode 100644 index 4941554ad..000000000 --- a/cli/js/tests/file_test.ts +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function testFirstArgument(arg1: any[], expectedSize: number): void { - const file = new File(arg1, "name"); - assert(file instanceof File); - assertEquals(file.name, "name"); - assertEquals(file.size, expectedSize); - assertEquals(file.type, ""); -} - -unitTest(function fileEmptyFileBits(): void { - testFirstArgument([], 0); -}); - -unitTest(function fileStringFileBits(): void { - testFirstArgument(["bits"], 4); -}); - -unitTest(function fileUnicodeStringFileBits(): void { - testFirstArgument(["𝓽𝓮𝔁𝓽"], 16); -}); - -unitTest(function fileStringObjectFileBits(): void { - testFirstArgument([new String("string object")], 13); -}); - -unitTest(function fileEmptyBlobFileBits(): void { - testFirstArgument([new Blob()], 0); -}); - -unitTest(function fileBlobFileBits(): void { - testFirstArgument([new Blob(["bits"])], 4); -}); - -unitTest(function fileEmptyFileFileBits(): void { - testFirstArgument([new File([], "world.txt")], 0); -}); - -unitTest(function fileFileFileBits(): void { - testFirstArgument([new File(["bits"], "world.txt")], 4); -}); - -unitTest(function fileArrayBufferFileBits(): void { - testFirstArgument([new ArrayBuffer(8)], 8); -}); - -unitTest(function fileTypedArrayFileBits(): void { - testFirstArgument([new Uint8Array([0x50, 0x41, 0x53, 0x53])], 4); -}); - -unitTest(function fileVariousFileBits(): void { - testFirstArgument( - [ - "bits", - new Blob(["bits"]), - new Blob(), - new Uint8Array([0x50, 0x41]), - new Uint16Array([0x5353]), - new Uint32Array([0x53534150]), - ], - 16 - ); -}); - -unitTest(function fileNumberInFileBits(): void { - testFirstArgument([12], 2); -}); - -unitTest(function fileArrayInFileBits(): void { - testFirstArgument([[1, 2, 3]], 5); -}); - -unitTest(function fileObjectInFileBits(): void { - // "[object Object]" - testFirstArgument([{}], 15); -}); - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function testSecondArgument(arg2: any, expectedFileName: string): void { - const file = new File(["bits"], arg2); - assert(file instanceof File); - assertEquals(file.name, expectedFileName); -} - -unitTest(function fileUsingFileName(): void { - testSecondArgument("dummy", "dummy"); -}); - -unitTest(function fileUsingSpecialCharacterInFileName(): void { - testSecondArgument("dummy/foo", "dummy:foo"); -}); - -unitTest(function fileUsingNullFileName(): void { - testSecondArgument(null, "null"); -}); - -unitTest(function fileUsingNumberFileName(): void { - testSecondArgument(1, "1"); -}); - -unitTest(function fileUsingEmptyStringFileName(): void { - testSecondArgument("", ""); -}); diff --git a/cli/js/tests/files_test.ts b/cli/js/tests/files_test.ts deleted file mode 100644 index a035c7074..000000000 --- a/cli/js/tests/files_test.ts +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - unitTest, - assert, - assertEquals, - assertStrContains, -} from "./test_util.ts"; - -unitTest(function filesStdioFileDescriptors(): void { - assertEquals(Deno.stdin.rid, 0); - assertEquals(Deno.stdout.rid, 1); - assertEquals(Deno.stderr.rid, 2); -}); - -unitTest({ perms: { read: true } }, async function filesCopyToStdout(): Promise< - void -> { - const filename = "cli/tests/fixture.json"; - const file = await Deno.open(filename); - assert(file.rid > 2); - const bytesWritten = await Deno.copy(file, Deno.stdout); - const fileSize = Deno.statSync(filename).size; - assertEquals(bytesWritten, fileSize); - console.log("bytes written", bytesWritten); - file.close(); -}); - -unitTest({ perms: { read: true } }, async function filesIter(): Promise { - const filename = "cli/tests/hello.txt"; - const file = await Deno.open(filename); - - let totalSize = 0; - for await (const buf of Deno.iter(file)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); - file.close(); -}); - -unitTest( - { perms: { read: true } }, - async function filesIterCustomBufSize(): Promise { - const filename = "cli/tests/hello.txt"; - const file = await Deno.open(filename); - - let totalSize = 0; - let iterations = 0; - for await (const buf of Deno.iter(file, { bufSize: 6 })) { - totalSize += buf.byteLength; - iterations += 1; - } - - assertEquals(totalSize, 12); - assertEquals(iterations, 2); - file.close(); - } -); - -unitTest({ perms: { read: true } }, function filesIterSync(): void { - const filename = "cli/tests/hello.txt"; - const file = Deno.openSync(filename); - - let totalSize = 0; - for (const buf of Deno.iterSync(file)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); - file.close(); -}); - -unitTest( - { perms: { read: true } }, - function filesIterSyncCustomBufSize(): void { - const filename = "cli/tests/hello.txt"; - const file = Deno.openSync(filename); - - let totalSize = 0; - let iterations = 0; - for (const buf of Deno.iterSync(file, { bufSize: 6 })) { - totalSize += buf.byteLength; - iterations += 1; - } - - assertEquals(totalSize, 12); - assertEquals(iterations, 2); - file.close(); - } -); - -unitTest(async function readerIter(): Promise { - // ref: https://github.com/denoland/deno/issues/2330 - const encoder = new TextEncoder(); - - class TestReader implements Deno.Reader { - #offset = 0; - #buf: Uint8Array; - - constructor(s: string) { - this.#buf = new Uint8Array(encoder.encode(s)); - } - - read(p: Uint8Array): Promise { - const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset); - p.set(this.#buf.slice(this.#offset, this.#offset + n)); - this.#offset += n; - - if (n === 0) { - return Promise.resolve(null); - } - - return Promise.resolve(n); - } - } - - const reader = new TestReader("hello world!"); - - let totalSize = 0; - for await (const buf of Deno.iter(reader)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); -}); - -unitTest(async function readerIterSync(): Promise { - // ref: https://github.com/denoland/deno/issues/2330 - const encoder = new TextEncoder(); - - class TestReader implements Deno.ReaderSync { - #offset = 0; - #buf: Uint8Array; - - constructor(s: string) { - this.#buf = new Uint8Array(encoder.encode(s)); - } - - readSync(p: Uint8Array): number | null { - const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset); - p.set(this.#buf.slice(this.#offset, this.#offset + n)); - this.#offset += n; - - if (n === 0) { - return null; - } - - return n; - } - } - - const reader = new TestReader("hello world!"); - - let totalSize = 0; - for await (const buf of Deno.iterSync(reader)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); -}); - -unitTest( - { - perms: { read: true, write: true }, - }, - function openSyncMode(): void { - const path = Deno.makeTempDirSync() + "/test_openSync.txt"; - const file = Deno.openSync(path, { - write: true, - createNew: true, - mode: 0o626, - }); - file.close(); - const pathInfo = Deno.statSync(path); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o626 & ~Deno.umask()); - } - } -); - -unitTest( - { - perms: { read: true, write: true }, - }, - async function openMode(): Promise { - const path = (await Deno.makeTempDir()) + "/test_open.txt"; - const file = await Deno.open(path, { - write: true, - createNew: true, - mode: 0o626, - }); - file.close(); - const pathInfo = Deno.statSync(path); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o626 & ~Deno.umask()); - } - } -); - -unitTest( - { perms: { write: false } }, - async function writePermFailure(): Promise { - const filename = "tests/hello.txt"; - const openOptions: Deno.OpenOptions[] = [{ write: true }, { append: true }]; - for (const options of openOptions) { - let err; - try { - await Deno.open(filename, options); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } - } -); - -unitTest(async function openOptions(): Promise { - const filename = "cli/tests/fixture.json"; - let err; - try { - await Deno.open(filename, { write: false }); - } catch (e) { - err = e; - } - assert(!!err); - assertStrContains( - err.message, - "OpenOptions requires at least one option to be true" - ); - - try { - await Deno.open(filename, { truncate: true, write: false }); - } catch (e) { - err = e; - } - assert(!!err); - assertStrContains(err.message, "'truncate' option requires 'write' option"); - - try { - await Deno.open(filename, { create: true, write: false }); - } catch (e) { - err = e; - } - assert(!!err); - assertStrContains( - err.message, - "'create' or 'createNew' options require 'write' or 'append' option" - ); - - try { - await Deno.open(filename, { createNew: true, append: false }); - } catch (e) { - err = e; - } - assert(!!err); - assertStrContains( - err.message, - "'create' or 'createNew' options require 'write' or 'append' option" - ); -}); - -unitTest({ perms: { read: false } }, async function readPermFailure(): Promise< - void -> { - let caughtError = false; - try { - await Deno.open("package.json", { read: true }); - await Deno.open("cli/tests/fixture.json", { read: true }); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest( - { perms: { write: true } }, - async function writeNullBufferFailure(): Promise { - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "hello.txt"; - const w = { - write: true, - truncate: true, - create: true, - }; - const file = await Deno.open(filename, w); - - // writing null should throw an error - let err; - try { - // @ts-ignore - await file.write(null); - } catch (e) { - err = e; - } - // TODO: Check error kind when dispatch_minimal pipes errors properly - assert(!!err); - - file.close(); - await Deno.remove(tempDir, { recursive: true }); - } -); - -unitTest( - { perms: { write: true, read: true } }, - async function readNullBufferFailure(): Promise { - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "hello.txt"; - const file = await Deno.open(filename, { - read: true, - write: true, - truncate: true, - create: true, - }); - - // reading into an empty buffer should return 0 immediately - const bytesRead = await file.read(new Uint8Array(0)); - assert(bytesRead === 0); - - // reading file into null buffer should throw an error - let err; - try { - // @ts-ignore - await file.read(null); - } catch (e) { - err = e; - } - // TODO: Check error kind when dispatch_minimal pipes errors properly - assert(!!err); - - file.close(); - await Deno.remove(tempDir, { recursive: true }); - } -); - -unitTest( - { perms: { write: false, read: false } }, - async function readWritePermFailure(): Promise { - const filename = "tests/hello.txt"; - let err; - try { - await Deno.open(filename, { read: true }); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function createFile(): Promise { - const tempDir = await Deno.makeTempDir(); - const filename = tempDir + "/test.txt"; - const f = await Deno.create(filename); - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); - assert(fileInfo.size === 0); - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - await f.write(data); - fileInfo = Deno.statSync(filename); - assert(fileInfo.size === 5); - f.close(); - - // TODO: test different modes - await Deno.remove(tempDir, { recursive: true }); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function openModeWrite(): Promise { - const tempDir = Deno.makeTempDirSync(); - const encoder = new TextEncoder(); - const filename = tempDir + "hello.txt"; - const data = encoder.encode("Hello world!\n"); - let file = await Deno.open(filename, { - create: true, - write: true, - truncate: true, - }); - // assert file was created - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); - assertEquals(fileInfo.size, 0); - // write some data - await file.write(data); - fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.size, 13); - // assert we can't read from file - let thrown = false; - try { - const buf = new Uint8Array(20); - await file.read(buf); - } catch (e) { - thrown = true; - } finally { - assert(thrown, "'w' mode shouldn't allow to read file"); - } - file.close(); - // assert that existing file is truncated on open - file = await Deno.open(filename, { - write: true, - truncate: true, - }); - file.close(); - const fileSize = Deno.statSync(filename).size; - assertEquals(fileSize, 0); - await Deno.remove(tempDir, { recursive: true }); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function openModeWriteRead(): Promise { - const tempDir = Deno.makeTempDirSync(); - const encoder = new TextEncoder(); - const filename = tempDir + "hello.txt"; - const data = encoder.encode("Hello world!\n"); - - const file = await Deno.open(filename, { - write: true, - truncate: true, - create: true, - read: true, - }); - const seekPosition = 0; - // assert file was created - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); - assertEquals(fileInfo.size, 0); - // write some data - await file.write(data); - fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.size, 13); - - const buf = new Uint8Array(20); - // seeking from beginning of a file - const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Start); - assertEquals(seekPosition, cursorPosition); - const result = await file.read(buf); - assertEquals(result, 13); - file.close(); - - await Deno.remove(tempDir, { recursive: true }); - } -); - -unitTest({ perms: { read: true } }, async function seekStart(): Promise { - const filename = "cli/tests/hello.txt"; - const file = await Deno.open(filename); - const seekPosition = 6; - // Deliberately move 1 step forward - await file.read(new Uint8Array(1)); // "H" - // Skipping "Hello " - // seeking from beginning of a file plus seekPosition - const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Start); - assertEquals(seekPosition, cursorPosition); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); - file.close(); -}); - -unitTest({ perms: { read: true } }, function seekSyncStart(): void { - const filename = "cli/tests/hello.txt"; - const file = Deno.openSync(filename); - const seekPosition = 6; - // Deliberately move 1 step forward - file.readSync(new Uint8Array(1)); // "H" - // Skipping "Hello " - // seeking from beginning of a file plus seekPosition - const cursorPosition = file.seekSync(seekPosition, Deno.SeekMode.Start); - assertEquals(seekPosition, cursorPosition); - const buf = new Uint8Array(6); - file.readSync(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); - file.close(); -}); - -unitTest({ perms: { read: true } }, async function seekCurrent(): Promise< - void -> { - const filename = "cli/tests/hello.txt"; - const file = await Deno.open(filename); - // Deliberately move 1 step forward - await file.read(new Uint8Array(1)); // "H" - // Skipping "ello " - const seekPosition = 5; - // seekPosition is relative to current cursor position after read - const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Current); - assertEquals(seekPosition + 1, cursorPosition); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); - file.close(); -}); - -unitTest({ perms: { read: true } }, function seekSyncCurrent(): void { - const filename = "cli/tests/hello.txt"; - const file = Deno.openSync(filename); - // Deliberately move 1 step forward - file.readSync(new Uint8Array(1)); // "H" - // Skipping "ello " - const seekPosition = 5; - // seekPosition is relative to current cursor position after read - const cursorPosition = file.seekSync(seekPosition, Deno.SeekMode.Current); - assertEquals(seekPosition + 1, cursorPosition); - const buf = new Uint8Array(6); - file.readSync(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); - file.close(); -}); - -unitTest({ perms: { read: true } }, async function seekEnd(): Promise { - const filename = "cli/tests/hello.txt"; - const file = await Deno.open(filename); - const seekPosition = -6; - // seek from end of file that has 12 chars, 12 - 6 = 6 - const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.End); - assertEquals(6, cursorPosition); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); - file.close(); -}); - -unitTest({ perms: { read: true } }, function seekSyncEnd(): void { - const filename = "cli/tests/hello.txt"; - const file = Deno.openSync(filename); - const seekPosition = -6; - // seek from end of file that has 12 chars, 12 - 6 = 6 - const cursorPosition = file.seekSync(seekPosition, Deno.SeekMode.End); - assertEquals(6, cursorPosition); - const buf = new Uint8Array(6); - file.readSync(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); - file.close(); -}); - -unitTest({ perms: { read: true } }, async function seekMode(): Promise { - const filename = "cli/tests/hello.txt"; - const file = await Deno.open(filename); - let err; - try { - await file.seek(1, -1); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof TypeError); - assertStrContains(err.message, "Invalid seek mode"); - - // We should still be able to read the file - // since it is still open. - const buf = new Uint8Array(1); - await file.read(buf); // "H" - assertEquals(new TextDecoder().decode(buf), "H"); - file.close(); -}); diff --git a/cli/js/tests/form_data_test.ts b/cli/js/tests/form_data_test.ts deleted file mode 100644 index 10cbd30a7..000000000 --- a/cli/js/tests/form_data_test.ts +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - unitTest, - assert, - assertEquals, - assertStrContains, -} from "./test_util.ts"; - -unitTest({ ignore: true }, function formDataHasCorrectNameProp(): void { - assertEquals(FormData.name, "FormData"); -}); - -unitTest(function formDataParamsAppendSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - assertEquals(formData.get("a"), "true"); -}); - -unitTest(function formDataParamsDeleteSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - assertEquals(formData.get("b"), "false"); - formData.delete("b"); - assertEquals(formData.get("a"), "true"); - assertEquals(formData.get("b"), null); -}); - -unitTest(function formDataParamsGetAllSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - formData.append("a", "null"); - assertEquals(formData.getAll("a"), ["true", "null"]); - assertEquals(formData.getAll("b"), ["false"]); - assertEquals(formData.getAll("c"), []); -}); - -unitTest(function formDataParamsGetSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - formData.append("a", "null"); - // @ts-ignore - formData.append("d", undefined); - // @ts-ignore - formData.append("e", null); - assertEquals(formData.get("a"), "true"); - assertEquals(formData.get("b"), "false"); - assertEquals(formData.get("c"), null); - assertEquals(formData.get("d"), "undefined"); - assertEquals(formData.get("e"), "null"); -}); - -unitTest(function formDataParamsHasSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - assert(formData.has("a")); - assert(formData.has("b")); - assert(!formData.has("c")); -}); - -unitTest(function formDataParamsSetSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - formData.append("a", "null"); - assertEquals(formData.getAll("a"), ["true", "null"]); - assertEquals(formData.getAll("b"), ["false"]); - formData.set("a", "false"); - assertEquals(formData.getAll("a"), ["false"]); - // @ts-ignore - formData.set("d", undefined); - assertEquals(formData.get("d"), "undefined"); - // @ts-ignore - formData.set("e", null); - assertEquals(formData.get("e"), "null"); -}); - -unitTest(function fromDataUseDomFile(): void { - const formData = new FormData(); - const file = new File(["foo"], "bar", { - type: "text/plain", - }); - formData.append("file", file); - assertEquals(formData.get("file"), file); -}); - -unitTest(function formDataSetEmptyBlobSuccess(): void { - const formData = new FormData(); - formData.set("a", new Blob([]), "blank.txt"); - formData.get("a"); - /* TODO Fix this test. - assert(file instanceof File); - if (typeof file !== "string") { - assertEquals(file.name, "blank.txt"); - } - */ -}); - -unitTest(function formDataParamsForEachSuccess(): void { - const init = [ - ["a", "54"], - ["b", "true"], - ]; - const formData = new FormData(); - for (const [name, value] of init) { - formData.append(name, value); - } - let callNum = 0; - formData.forEach((value, key, parent): void => { - assertEquals(formData, parent); - assertEquals(value, init[callNum][1]); - assertEquals(key, init[callNum][0]); - callNum++; - }); - assertEquals(callNum, init.length); -}); - -unitTest(function formDataParamsArgumentsCheck(): void { - const methodRequireOneParam = [ - "delete", - "getAll", - "get", - "has", - "forEach", - ] as const; - - const methodRequireTwoParams = ["append", "set"] as const; - - methodRequireOneParam.forEach((method): void => { - const formData = new FormData(); - let hasThrown = 0; - let errMsg = ""; - try { - // @ts-ignore - formData[method](); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertStrContains( - errMsg, - `${method} requires at least 1 argument, but only 0 present` - ); - }); - - methodRequireTwoParams.forEach((method: string): void => { - const formData = new FormData(); - let hasThrown = 0; - let errMsg = ""; - - try { - // @ts-ignore - formData[method](); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertStrContains( - errMsg, - `${method} requires at least 2 arguments, but only 0 present` - ); - - hasThrown = 0; - errMsg = ""; - try { - // @ts-ignore - formData[method]("foo"); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertStrContains( - errMsg, - `${method} requires at least 2 arguments, but only 1 present` - ); - }); -}); - -unitTest(function toStringShouldBeWebCompatibility(): void { - const formData = new FormData(); - assertEquals(formData.toString(), "[object FormData]"); -}); diff --git a/cli/js/tests/format_error_test.ts b/cli/js/tests/format_error_test.ts deleted file mode 100644 index ae7559c82..000000000 --- a/cli/js/tests/format_error_test.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { assert, unitTest } from "./test_util.ts"; - -unitTest(function formatDiagnosticBasic() { - const fixture: Deno.DiagnosticItem[] = [ - { - message: "Example error", - category: Deno.DiagnosticCategory.Error, - sourceLine: "abcdefghijklmnopqrstuv", - lineNumber: 1000, - scriptResourceName: "foo.ts", - startColumn: 1, - endColumn: 2, - code: 4000, - }, - ]; - const out = Deno.formatDiagnostics(fixture); - assert(out.includes("Example error")); - assert(out.includes("foo.ts")); -}); - -unitTest(function formatDiagnosticError() { - let thrown = false; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const bad = ([{ hello: 123 }] as any) as Deno.DiagnosticItem[]; - try { - Deno.formatDiagnostics(bad); - } catch (e) { - assert(e instanceof Deno.errors.InvalidData); - thrown = true; - } - assert(thrown); -}); diff --git a/cli/js/tests/fs_events_test.ts b/cli/js/tests/fs_events_test.ts deleted file mode 100644 index ad8ba8502..000000000 --- a/cli/js/tests/fs_events_test.ts +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -// TODO(ry) Add more tests to specify format. - -unitTest({ perms: { read: false } }, function watchFsPermissions() { - let thrown = false; - try { - Deno.watchFs("."); - } catch (err) { - assert(err instanceof Deno.errors.PermissionDenied); - thrown = true; - } - assert(thrown); -}); - -unitTest({ perms: { read: true } }, function watchFsInvalidPath() { - let thrown = false; - try { - Deno.watchFs("non-existant.file"); - } catch (err) { - console.error(err); - if (Deno.build.os === "windows") { - assert( - err.message.includes( - "Input watch path is neither a file nor a directory" - ) - ); - } else { - assert(err instanceof Deno.errors.NotFound); - } - thrown = true; - } - assert(thrown); -}); - -async function getTwoEvents( - iter: AsyncIterableIterator -): Promise { - const events = []; - for await (const event of iter) { - events.push(event); - if (events.length > 2) break; - } - return events; -} - -unitTest( - { perms: { read: true, write: true } }, - async function watchFsBasic(): Promise { - const testDir = await Deno.makeTempDir(); - const iter = Deno.watchFs(testDir); - - // Asynchornously capture two fs events. - const eventsPromise = getTwoEvents(iter); - - // Make some random file system activity. - const file1 = testDir + "/file1.txt"; - const file2 = testDir + "/file2.txt"; - Deno.writeFileSync(file1, new Uint8Array([0, 1, 2])); - Deno.writeFileSync(file2, new Uint8Array([0, 1, 2])); - - // We should have gotten two fs events. - const events = await eventsPromise; - assert(events.length >= 2); - assert(events[0].kind == "create"); - assert(events[0].paths[0].includes(testDir)); - assert(events[1].kind == "create" || events[1].kind == "modify"); - assert(events[1].paths[0].includes(testDir)); - } -); diff --git a/cli/js/tests/get_random_values_test.ts b/cli/js/tests/get_random_values_test.ts deleted file mode 100644 index 76fa732ea..000000000 --- a/cli/js/tests/get_random_values_test.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertNotEquals, assertStrictEq } from "./test_util.ts"; - -unitTest(function getRandomValuesInt8Array(): void { - const arr = new Int8Array(32); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Int8Array(32)); -}); - -unitTest(function getRandomValuesUint8Array(): void { - const arr = new Uint8Array(32); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint8Array(32)); -}); - -unitTest(function getRandomValuesUint8ClampedArray(): void { - const arr = new Uint8ClampedArray(32); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint8ClampedArray(32)); -}); - -unitTest(function getRandomValuesInt16Array(): void { - const arr = new Int16Array(4); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Int16Array(4)); -}); - -unitTest(function getRandomValuesUint16Array(): void { - const arr = new Uint16Array(4); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint16Array(4)); -}); - -unitTest(function getRandomValuesInt32Array(): void { - const arr = new Int32Array(8); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Int32Array(8)); -}); - -unitTest(function getRandomValuesUint32Array(): void { - const arr = new Uint32Array(8); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint32Array(8)); -}); - -unitTest(function getRandomValuesReturnValue(): void { - const arr = new Uint32Array(8); - const rtn = crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint32Array(8)); - assertStrictEq(rtn, arr); -}); diff --git a/cli/js/tests/globals_test.ts b/cli/js/tests/globals_test.ts deleted file mode 100644 index aa8b4f46e..000000000 --- a/cli/js/tests/globals_test.ts +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -unitTest(function globalThisExists(): void { - assert(globalThis != null); -}); - -unitTest(function windowExists(): void { - assert(window != null); -}); - -unitTest(function selfExists(): void { - assert(self != null); -}); - -unitTest(function windowWindowExists(): void { - assert(window.window === window); -}); - -unitTest(function windowSelfExists(): void { - assert(window.self === window); -}); - -unitTest(function globalThisEqualsWindow(): void { - assert(globalThis === window); -}); - -unitTest(function globalThisEqualsSelf(): void { - assert(globalThis === self); -}); - -unitTest(function DenoNamespaceExists(): void { - assert(Deno != null); -}); - -unitTest(function DenoNamespaceEqualsWindowDeno(): void { - assert(Deno === window.Deno); -}); - -unitTest(function DenoNamespaceIsFrozen(): void { - assert(Object.isFrozen(Deno)); -}); - -unitTest(function webAssemblyExists(): void { - assert(typeof WebAssembly.compile === "function"); -}); - -unitTest(function DenoNamespaceImmutable(): void { - const denoCopy = window.Deno; - try { - // @ts-ignore - Deno = 1; - } catch {} - assert(denoCopy === Deno); - try { - // @ts-ignore - window.Deno = 1; - } catch {} - assert(denoCopy === Deno); - try { - delete window.Deno; - } catch {} - assert(denoCopy === Deno); - - const { readFile } = Deno; - try { - // @ts-ignore - Deno.readFile = 1; - } catch {} - assert(readFile === Deno.readFile); - try { - delete window.Deno.readFile; - } catch {} - assert(readFile === Deno.readFile); - - // @ts-ignore - const { print } = Deno.core; - try { - // @ts-ignore - Deno.core.print = 1; - } catch {} - // @ts-ignore - assert(print === Deno.core.print); - try { - // @ts-ignore - delete Deno.core.print; - } catch {} - // @ts-ignore - assert(print === Deno.core.print); -}); - -unitTest(async function windowQueueMicrotask(): Promise { - let resolve1: () => void | undefined; - let resolve2: () => void | undefined; - let microtaskDone = false; - const p1 = new Promise((res): void => { - resolve1 = (): void => { - microtaskDone = true; - res(); - }; - }); - const p2 = new Promise((res): void => { - resolve2 = (): void => { - assert(microtaskDone); - res(); - }; - }); - window.queueMicrotask(resolve1!); - setTimeout(resolve2!, 0); - await p1; - await p2; -}); diff --git a/cli/js/tests/headers_test.ts b/cli/js/tests/headers_test.ts deleted file mode 100644 index aaa829837..000000000 --- a/cli/js/tests/headers_test.ts +++ /dev/null @@ -1,420 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - unitTest, - assert, - assertEquals, - assertStrContains, -} from "./test_util.ts"; -const { - stringifyArgs, - // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol -} = Deno[Deno.internal]; - -// Logic heavily copied from web-platform-tests, make -// sure pass mostly header basic test -// ref: https://github.com/web-platform-tests/wpt/blob/7c50c216081d6ea3c9afe553ee7b64534020a1b2/fetch/api/headers/headers-basic.html -unitTest(function newHeaderTest(): void { - new Headers(); - new Headers(undefined); - new Headers({}); - try { - // @ts-ignore - new Headers(null); - } catch (e) { - assertEquals( - e.message, - "Failed to construct 'Headers'; The provided value was not valid" - ); - } -}); - -const headerDict: Record = { - name1: "value1", - name2: "value2", - name3: "value3", - // @ts-ignore - name4: undefined, - "Content-Type": "value4", -}; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const headerSeq: any[] = []; -for (const name in headerDict) { - headerSeq.push([name, headerDict[name]]); -} - -unitTest(function newHeaderWithSequence(): void { - const headers = new Headers(headerSeq); - for (const name in headerDict) { - assertEquals(headers.get(name), String(headerDict[name])); - } - assertEquals(headers.get("length"), null); -}); - -unitTest(function newHeaderWithRecord(): void { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assertEquals(headers.get(name), String(headerDict[name])); - } -}); - -unitTest(function newHeaderWithHeadersInstance(): void { - const headers = new Headers(headerDict); - const headers2 = new Headers(headers); - for (const name in headerDict) { - assertEquals(headers2.get(name), String(headerDict[name])); - } -}); - -unitTest(function headerAppendSuccess(): void { - const headers = new Headers(); - for (const name in headerDict) { - headers.append(name, headerDict[name]); - assertEquals(headers.get(name), String(headerDict[name])); - } -}); - -unitTest(function headerSetSuccess(): void { - const headers = new Headers(); - for (const name in headerDict) { - headers.set(name, headerDict[name]); - assertEquals(headers.get(name), String(headerDict[name])); - } -}); - -unitTest(function headerHasSuccess(): void { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assert(headers.has(name), "headers has name " + name); - assert( - !headers.has("nameNotInHeaders"), - "headers do not have header: nameNotInHeaders" - ); - } -}); - -unitTest(function headerDeleteSuccess(): void { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assert(headers.has(name), "headers have a header: " + name); - headers.delete(name); - assert(!headers.has(name), "headers do not have anymore a header: " + name); - } -}); - -unitTest(function headerGetSuccess(): void { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assertEquals(headers.get(name), String(headerDict[name])); - assertEquals(headers.get("nameNotInHeaders"), null); - } -}); - -unitTest(function headerEntriesSuccess(): void { - const headers = new Headers(headerDict); - const iterators = headers.entries(); - for (const it of iterators) { - const key = it[0]; - const value = it[1]; - assert(headers.has(key)); - assertEquals(value, headers.get(key)); - } -}); - -unitTest(function headerKeysSuccess(): void { - const headers = new Headers(headerDict); - const iterators = headers.keys(); - for (const it of iterators) { - assert(headers.has(it)); - } -}); - -unitTest(function headerValuesSuccess(): void { - const headers = new Headers(headerDict); - const iterators = headers.values(); - const entries = headers.entries(); - const values = []; - for (const pair of entries) { - values.push(pair[1]); - } - for (const it of iterators) { - assert(values.includes(it)); - } -}); - -const headerEntriesDict: Record = { - name1: "value1", - Name2: "value2", - name: "value3", - "content-Type": "value4", - "Content-Typ": "value5", - "Content-Types": "value6", -}; - -unitTest(function headerForEachSuccess(): void { - const headers = new Headers(headerEntriesDict); - const keys = Object.keys(headerEntriesDict); - keys.forEach((key): void => { - const value = headerEntriesDict[key]; - const newkey = key.toLowerCase(); - headerEntriesDict[newkey] = value; - }); - let callNum = 0; - headers.forEach((value, key, container): void => { - assertEquals(headers, container); - assertEquals(value, headerEntriesDict[key]); - callNum++; - }); - assertEquals(callNum, keys.length); -}); - -unitTest(function headerSymbolIteratorSuccess(): void { - assert(Symbol.iterator in Headers.prototype); - const headers = new Headers(headerEntriesDict); - for (const header of headers) { - const key = header[0]; - const value = header[1]; - assert(headers.has(key)); - assertEquals(value, headers.get(key)); - } -}); - -unitTest(function headerTypesAvailable(): void { - function newHeaders(): Headers { - return new Headers(); - } - const headers = newHeaders(); - assert(headers instanceof Headers); -}); - -// Modified from https://github.com/bitinn/node-fetch/blob/7d3293200a91ad52b5ca7962f9d6fd1c04983edb/test/test.js#L2001-L2014 -// Copyright (c) 2016 David Frank. MIT License. -unitTest(function headerIllegalReject(): void { - let errorCount = 0; - try { - new Headers({ "He y": "ok" }); - } catch (e) { - errorCount++; - } - try { - new Headers({ "Hé-y": "ok" }); - } catch (e) { - errorCount++; - } - try { - new Headers({ "He-y": "ăk" }); - } catch (e) { - errorCount++; - } - const headers = new Headers(); - try { - headers.append("Hé-y", "ok"); - } catch (e) { - errorCount++; - } - try { - headers.delete("Hé-y"); - } catch (e) { - errorCount++; - } - try { - headers.get("Hé-y"); - } catch (e) { - errorCount++; - } - try { - headers.has("Hé-y"); - } catch (e) { - errorCount++; - } - try { - headers.set("Hé-y", "ok"); - } catch (e) { - errorCount++; - } - try { - headers.set("", "ok"); - } catch (e) { - errorCount++; - } - assertEquals(errorCount, 9); - // 'o k' is valid value but invalid name - new Headers({ "He-y": "o k" }); -}); - -// If pair does not contain exactly two items,then throw a TypeError. -unitTest(function headerParamsShouldThrowTypeError(): void { - let hasThrown = 0; - - try { - new Headers(([["1"]] as unknown) as Array<[string, string]>); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - - assertEquals(hasThrown, 2); -}); - -unitTest(function headerParamsArgumentsCheck(): void { - const methodRequireOneParam = ["delete", "get", "has", "forEach"]; - - const methodRequireTwoParams = ["append", "set"]; - - methodRequireOneParam.forEach((method): void => { - const headers = new Headers(); - let hasThrown = 0; - let errMsg = ""; - try { - // @ts-ignore - headers[method](); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertStrContains( - errMsg, - `${method} requires at least 1 argument, but only 0 present` - ); - }); - - methodRequireTwoParams.forEach((method): void => { - const headers = new Headers(); - let hasThrown = 0; - let errMsg = ""; - - try { - // @ts-ignore - headers[method](); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertStrContains( - errMsg, - `${method} requires at least 2 arguments, but only 0 present` - ); - - hasThrown = 0; - errMsg = ""; - try { - // @ts-ignore - headers[method]("foo"); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertStrContains( - errMsg, - `${method} requires at least 2 arguments, but only 1 present` - ); - }); -}); - -unitTest(function headersInitMultiple(): void { - const headers = new Headers([ - ["Set-Cookie", "foo=bar"], - ["Set-Cookie", "bar=baz"], - ["X-Deno", "foo"], - ["X-Deno", "bar"], - ]); - const actual = [...headers]; - assertEquals(actual, [ - ["set-cookie", "foo=bar"], - ["set-cookie", "bar=baz"], - ["x-deno", "foo, bar"], - ]); -}); - -unitTest(function headersAppendMultiple(): void { - const headers = new Headers([ - ["Set-Cookie", "foo=bar"], - ["X-Deno", "foo"], - ]); - headers.append("set-Cookie", "bar=baz"); - headers.append("x-Deno", "bar"); - const actual = [...headers]; - assertEquals(actual, [ - ["set-cookie", "foo=bar"], - ["x-deno", "foo, bar"], - ["set-cookie", "bar=baz"], - ]); -}); - -unitTest(function headersAppendDuplicateSetCookieKey(): void { - const headers = new Headers([["Set-Cookie", "foo=bar"]]); - headers.append("set-Cookie", "foo=baz"); - headers.append("Set-cookie", "baz=bar"); - const actual = [...headers]; - assertEquals(actual, [ - ["set-cookie", "foo=baz"], - ["set-cookie", "baz=bar"], - ]); -}); - -unitTest(function headersSetDuplicateCookieKey(): void { - const headers = new Headers([["Set-Cookie", "foo=bar"]]); - headers.set("set-Cookie", "foo=baz"); - headers.set("set-cookie", "bar=qat"); - const actual = [...headers]; - assertEquals(actual, [ - ["set-cookie", "foo=baz"], - ["set-cookie", "bar=qat"], - ]); -}); - -unitTest(function headersGetSetCookie(): void { - const headers = new Headers([ - ["Set-Cookie", "foo=bar"], - ["set-Cookie", "bar=qat"], - ]); - assertEquals(headers.get("SET-COOKIE"), "foo=bar, bar=qat"); -}); - -unitTest(function toStringShouldBeWebCompatibility(): void { - const headers = new Headers(); - assertEquals(headers.toString(), "[object Headers]"); -}); - -function stringify(...args: unknown[]): string { - return stringifyArgs(args).replace(/\n$/, ""); -} - -unitTest(function customInspectReturnsCorrectHeadersFormat(): void { - const blankHeaders = new Headers(); - assertEquals(stringify(blankHeaders), "Headers {}"); - const singleHeader = new Headers([["Content-Type", "application/json"]]); - assertEquals( - stringify(singleHeader), - "Headers { content-type: application/json }" - ); - const multiParamHeader = new Headers([ - ["Content-Type", "application/json"], - ["Content-Length", "1337"], - ]); - assertEquals( - stringify(multiParamHeader), - "Headers { content-type: application/json, content-length: 1337 }" - ); -}); diff --git a/cli/js/tests/internals_test.ts b/cli/js/tests/internals_test.ts deleted file mode 100644 index abd4c94c3..000000000 --- a/cli/js/tests/internals_test.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -unitTest(function internalsExists(): void { - const { - stringifyArgs, - // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol - } = Deno[Deno.internal]; - assert(!!stringifyArgs); -}); diff --git a/cli/js/tests/io_test.ts b/cli/js/tests/io_test.ts deleted file mode 100644 index 0ccd83ea2..000000000 --- a/cli/js/tests/io_test.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals } from "./test_util.ts"; - -const DEFAULT_BUF_SIZE = 32 * 1024; - -type Spy = { calls: number }; - -function repeat(c: string, bytes: number): Uint8Array { - assertEquals(c.length, 1); - const ui8 = new Uint8Array(bytes); - ui8.fill(c.charCodeAt(0)); - return ui8; -} - -function spyRead(obj: Deno.Buffer): Spy { - const spy: Spy = { - calls: 0, - }; - - const orig = obj.read.bind(obj); - - obj.read = (p: Uint8Array): Promise => { - spy.calls++; - return orig(p); - }; - - return spy; -} - -unitTest(async function copyWithDefaultBufferSize() { - const xBytes = repeat("b", DEFAULT_BUF_SIZE); - const reader = new Deno.Buffer(xBytes.buffer as ArrayBuffer); - const write = new Deno.Buffer(); - - const readSpy = spyRead(reader); - - const n = await Deno.copy(reader, write); - - assertEquals(n, xBytes.length); - assertEquals(write.length, xBytes.length); - assertEquals(readSpy.calls, 2); // read with DEFAULT_BUF_SIZE bytes + read with 0 bytes -}); - -unitTest(async function copyWithCustomBufferSize() { - const bufSize = 1024; - const xBytes = repeat("b", DEFAULT_BUF_SIZE); - const reader = new Deno.Buffer(xBytes.buffer as ArrayBuffer); - const write = new Deno.Buffer(); - - const readSpy = spyRead(reader); - - const n = await Deno.copy(reader, write, { bufSize }); - - assertEquals(n, xBytes.length); - assertEquals(write.length, xBytes.length); - assertEquals(readSpy.calls, DEFAULT_BUF_SIZE / bufSize + 1); -}); - -unitTest({ perms: { write: true } }, async function copyBufferToFile() { - const filePath = "test-file.txt"; - // bigger than max File possible buffer 16kb - const bufSize = 32 * 1024; - const xBytes = repeat("b", bufSize); - const reader = new Deno.Buffer(xBytes.buffer as ArrayBuffer); - const write = await Deno.open(filePath, { write: true, create: true }); - - const n = await Deno.copy(reader, write, { bufSize }); - - assertEquals(n, xBytes.length); - - write.close(); - await Deno.remove(filePath); -}); diff --git a/cli/js/tests/link_test.ts b/cli/js/tests/link_test.ts deleted file mode 100644 index c6ea4901e..000000000 --- a/cli/js/tests/link_test.ts +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest( - { perms: { read: true, write: true } }, - function linkSyncSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const oldData = "Hardlink"; - const oldName = testDir + "/oldname"; - const newName = testDir + "/newname"; - Deno.writeFileSync(oldName, new TextEncoder().encode(oldData)); - // Create the hard link. - Deno.linkSync(oldName, newName); - // We should expect reading the same content. - const newData = new TextDecoder().decode(Deno.readFileSync(newName)); - assertEquals(oldData, newData); - // Writing to newname also affects oldname. - const newData2 = "Modified"; - Deno.writeFileSync(newName, new TextEncoder().encode(newData2)); - assertEquals( - newData2, - new TextDecoder().decode(Deno.readFileSync(oldName)) - ); - // Writing to oldname also affects newname. - const newData3 = "ModifiedAgain"; - Deno.writeFileSync(oldName, new TextEncoder().encode(newData3)); - assertEquals( - newData3, - new TextDecoder().decode(Deno.readFileSync(newName)) - ); - // Remove oldname. File still accessible through newname. - Deno.removeSync(oldName); - const newNameStat = Deno.statSync(newName); - assert(newNameStat.isFile); - assert(!newNameStat.isSymlink); // Not a symlink. - assertEquals( - newData3, - new TextDecoder().decode(Deno.readFileSync(newName)) - ); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function linkSyncExists(): void { - const testDir = Deno.makeTempDirSync(); - const oldName = testDir + "/oldname"; - const newName = testDir + "/newname"; - Deno.writeFileSync(oldName, new TextEncoder().encode("oldName")); - // newname is already created. - Deno.writeFileSync(newName, new TextEncoder().encode("newName")); - - let err; - try { - Deno.linkSync(oldName, newName); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Deno.errors.AlreadyExists); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function linkSyncNotFound(): void { - const testDir = Deno.makeTempDirSync(); - const oldName = testDir + "/oldname"; - const newName = testDir + "/newname"; - - let err; - try { - Deno.linkSync(oldName, newName); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { read: false, write: true } }, - function linkSyncReadPerm(): void { - let err; - try { - Deno.linkSync("oldbaddir", "newbaddir"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } -); - -unitTest( - { perms: { read: true, write: false } }, - function linkSyncWritePerm(): void { - let err; - try { - Deno.linkSync("oldbaddir", "newbaddir"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function linkSuccess(): Promise { - const testDir = Deno.makeTempDirSync(); - const oldData = "Hardlink"; - const oldName = testDir + "/oldname"; - const newName = testDir + "/newname"; - Deno.writeFileSync(oldName, new TextEncoder().encode(oldData)); - // Create the hard link. - await Deno.link(oldName, newName); - // We should expect reading the same content. - const newData = new TextDecoder().decode(Deno.readFileSync(newName)); - assertEquals(oldData, newData); - // Writing to newname also affects oldname. - const newData2 = "Modified"; - Deno.writeFileSync(newName, new TextEncoder().encode(newData2)); - assertEquals( - newData2, - new TextDecoder().decode(Deno.readFileSync(oldName)) - ); - // Writing to oldname also affects newname. - const newData3 = "ModifiedAgain"; - Deno.writeFileSync(oldName, new TextEncoder().encode(newData3)); - assertEquals( - newData3, - new TextDecoder().decode(Deno.readFileSync(newName)) - ); - // Remove oldname. File still accessible through newname. - Deno.removeSync(oldName); - const newNameStat = Deno.statSync(newName); - assert(newNameStat.isFile); - assert(!newNameStat.isSymlink); // Not a symlink. - assertEquals( - newData3, - new TextDecoder().decode(Deno.readFileSync(newName)) - ); - } -); diff --git a/cli/js/tests/make_temp_test.ts b/cli/js/tests/make_temp_test.ts deleted file mode 100644 index 59fe8c5f5..000000000 --- a/cli/js/tests/make_temp_test.ts +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest({ perms: { write: true } }, function makeTempDirSyncSuccess(): void { - const dir1 = Deno.makeTempDirSync({ prefix: "hello", suffix: "world" }); - const dir2 = Deno.makeTempDirSync({ prefix: "hello", suffix: "world" }); - // Check that both dirs are different. - assert(dir1 !== dir2); - for (const dir of [dir1, dir2]) { - // Check that the prefix and suffix are applied. - const lastPart = dir.replace(/^.*[\\\/]/, ""); - assert(lastPart.startsWith("hello")); - assert(lastPart.endsWith("world")); - } - // Check that the `dir` option works. - const dir3 = Deno.makeTempDirSync({ dir: dir1 }); - assert(dir3.startsWith(dir1)); - assert(/^[\\\/]/.test(dir3.slice(dir1.length))); - // Check that creating a temp dir inside a nonexisting directory fails. - let err; - try { - Deno.makeTempDirSync({ dir: "/baddir" }); - } catch (err_) { - err = err_; - } - assert(err instanceof Deno.errors.NotFound); -}); - -unitTest( - { perms: { read: true, write: true } }, - function makeTempDirSyncMode(): void { - const path = Deno.makeTempDirSync(); - const pathInfo = Deno.statSync(path); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o700 & ~Deno.umask()); - } - } -); - -unitTest(function makeTempDirSyncPerm(): void { - // makeTempDirSync should require write permissions (for now). - let err; - try { - Deno.makeTempDirSync({ dir: "/baddir" }); - } catch (err_) { - err = err_; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest( - { perms: { write: true } }, - async function makeTempDirSuccess(): Promise { - const dir1 = await Deno.makeTempDir({ prefix: "hello", suffix: "world" }); - const dir2 = await Deno.makeTempDir({ prefix: "hello", suffix: "world" }); - // Check that both dirs are different. - assert(dir1 !== dir2); - for (const dir of [dir1, dir2]) { - // Check that the prefix and suffix are applied. - const lastPart = dir.replace(/^.*[\\\/]/, ""); - assert(lastPart.startsWith("hello")); - assert(lastPart.endsWith("world")); - } - // Check that the `dir` option works. - const dir3 = await Deno.makeTempDir({ dir: dir1 }); - assert(dir3.startsWith(dir1)); - assert(/^[\\\/]/.test(dir3.slice(dir1.length))); - // Check that creating a temp dir inside a nonexisting directory fails. - let err; - try { - await Deno.makeTempDir({ dir: "/baddir" }); - } catch (err_) { - err = err_; - } - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function makeTempDirMode(): Promise { - const path = await Deno.makeTempDir(); - const pathInfo = Deno.statSync(path); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o700 & ~Deno.umask()); - } - } -); - -unitTest({ perms: { write: true } }, function makeTempFileSyncSuccess(): void { - const file1 = Deno.makeTempFileSync({ prefix: "hello", suffix: "world" }); - const file2 = Deno.makeTempFileSync({ prefix: "hello", suffix: "world" }); - // Check that both dirs are different. - assert(file1 !== file2); - for (const dir of [file1, file2]) { - // Check that the prefix and suffix are applied. - const lastPart = dir.replace(/^.*[\\\/]/, ""); - assert(lastPart.startsWith("hello")); - assert(lastPart.endsWith("world")); - } - // Check that the `dir` option works. - const dir = Deno.makeTempDirSync({ prefix: "tempdir" }); - const file3 = Deno.makeTempFileSync({ dir }); - assert(file3.startsWith(dir)); - assert(/^[\\\/]/.test(file3.slice(dir.length))); - // Check that creating a temp file inside a nonexisting directory fails. - let err; - try { - Deno.makeTempFileSync({ dir: "/baddir" }); - } catch (err_) { - err = err_; - } - assert(err instanceof Deno.errors.NotFound); -}); - -unitTest( - { perms: { read: true, write: true } }, - function makeTempFileSyncMode(): void { - const path = Deno.makeTempFileSync(); - const pathInfo = Deno.statSync(path); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o600 & ~Deno.umask()); - } - } -); - -unitTest(function makeTempFileSyncPerm(): void { - // makeTempFileSync should require write permissions (for now). - let err; - try { - Deno.makeTempFileSync({ dir: "/baddir" }); - } catch (err_) { - err = err_; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest( - { perms: { write: true } }, - async function makeTempFileSuccess(): Promise { - const file1 = await Deno.makeTempFile({ prefix: "hello", suffix: "world" }); - const file2 = await Deno.makeTempFile({ prefix: "hello", suffix: "world" }); - // Check that both dirs are different. - assert(file1 !== file2); - for (const dir of [file1, file2]) { - // Check that the prefix and suffix are applied. - const lastPart = dir.replace(/^.*[\\\/]/, ""); - assert(lastPart.startsWith("hello")); - assert(lastPart.endsWith("world")); - } - // Check that the `dir` option works. - const dir = Deno.makeTempDirSync({ prefix: "tempdir" }); - const file3 = await Deno.makeTempFile({ dir }); - assert(file3.startsWith(dir)); - assert(/^[\\\/]/.test(file3.slice(dir.length))); - // Check that creating a temp file inside a nonexisting directory fails. - let err; - try { - await Deno.makeTempFile({ dir: "/baddir" }); - } catch (err_) { - err = err_; - } - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function makeTempFileMode(): Promise { - const path = await Deno.makeTempFile(); - const pathInfo = Deno.statSync(path); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o600 & ~Deno.umask()); - } - } -); diff --git a/cli/js/tests/metrics_test.ts b/cli/js/tests/metrics_test.ts deleted file mode 100644 index 9b7d83887..000000000 --- a/cli/js/tests/metrics_test.ts +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -unitTest(async function metrics(): Promise { - const m1 = Deno.metrics(); - assert(m1.opsDispatched > 0); - assert(m1.opsDispatchedSync > 0); - assert(m1.opsCompleted > 0); - assert(m1.opsCompletedSync > 0); - assert(m1.bytesSentControl > 0); - assert(m1.bytesSentData >= 0); - assert(m1.bytesReceived > 0); - - // Write to stdout to ensure a "data" message gets sent instead of just - // control messages. - const dataMsg = new Uint8Array([13, 13, 13]); // "\r\r\r", - await Deno.stdout.write(dataMsg); - - const m2 = Deno.metrics(); - assert(m2.opsDispatched > m1.opsDispatched); - assert(m2.opsDispatchedSync > m1.opsDispatchedSync); - assert(m2.opsDispatchedAsync > m1.opsDispatchedAsync); - assert(m2.opsCompleted > m1.opsCompleted); - assert(m2.opsCompletedSync > m1.opsCompletedSync); - assert(m2.opsCompletedAsync > m1.opsCompletedAsync); - assert(m2.bytesSentControl > m1.bytesSentControl); - assert(m2.bytesSentData >= m1.bytesSentData + dataMsg.byteLength); - assert(m2.bytesReceived > m1.bytesReceived); -}); - -unitTest( - { perms: { write: true } }, - function metricsUpdatedIfNoResponseSync(): void { - const filename = Deno.makeTempDirSync() + "/test.txt"; - - const data = new Uint8Array([41, 42, 43]); - Deno.writeFileSync(filename, data, { mode: 0o666 }); - - const metrics = Deno.metrics(); - assert(metrics.opsDispatched === metrics.opsCompleted); - assert(metrics.opsDispatchedSync === metrics.opsCompletedSync); - } -); - -unitTest( - { perms: { write: true } }, - async function metricsUpdatedIfNoResponseAsync(): Promise { - const filename = Deno.makeTempDirSync() + "/test.txt"; - - const data = new Uint8Array([41, 42, 43]); - await Deno.writeFile(filename, data, { mode: 0o666 }); - - const metrics = Deno.metrics(); - assert(metrics.opsDispatched === metrics.opsCompleted); - assert(metrics.opsDispatchedSync === metrics.opsCompletedSync); - assert(metrics.opsDispatchedAsync === metrics.opsCompletedAsync); - } -); diff --git a/cli/js/tests/mkdir_test.ts b/cli/js/tests/mkdir_test.ts deleted file mode 100644 index 68755ef4d..000000000 --- a/cli/js/tests/mkdir_test.ts +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals, assertThrows } from "./test_util.ts"; - -function assertDirectory(path: string, mode?: number): void { - const info = Deno.lstatSync(path); - assert(info.isDirectory); - if (Deno.build.os !== "windows" && mode !== undefined) { - assertEquals(info.mode! & 0o777, mode & ~Deno.umask()); - } -} - -unitTest( - { perms: { read: true, write: true } }, - function mkdirSyncSuccess(): void { - const path = Deno.makeTempDirSync() + "/dir"; - Deno.mkdirSync(path); - assertDirectory(path); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function mkdirSyncMode(): void { - const path = Deno.makeTempDirSync() + "/dir"; - Deno.mkdirSync(path, { mode: 0o737 }); - assertDirectory(path, 0o737); - } -); - -unitTest({ perms: { write: false } }, function mkdirSyncPerm(): void { - let err; - try { - Deno.mkdirSync("/baddir"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest( - { perms: { read: true, write: true } }, - async function mkdirSuccess(): Promise { - const path = Deno.makeTempDirSync() + "/dir"; - await Deno.mkdir(path); - assertDirectory(path); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function mkdirMode(): Promise { - const path = Deno.makeTempDirSync() + "/dir"; - await Deno.mkdir(path, { mode: 0o737 }); - assertDirectory(path, 0o737); - } -); - -unitTest({ perms: { write: true } }, function mkdirErrSyncIfExists(): void { - let err; - try { - Deno.mkdirSync("."); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.AlreadyExists); -}); - -unitTest({ perms: { write: true } }, async function mkdirErrIfExists(): Promise< - void -> { - let err; - try { - await Deno.mkdir("."); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.AlreadyExists); -}); - -unitTest( - { perms: { read: true, write: true } }, - function mkdirSyncRecursive(): void { - const path = Deno.makeTempDirSync() + "/nested/directory"; - Deno.mkdirSync(path, { recursive: true }); - assertDirectory(path); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function mkdirRecursive(): Promise { - const path = Deno.makeTempDirSync() + "/nested/directory"; - await Deno.mkdir(path, { recursive: true }); - assertDirectory(path); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function mkdirSyncRecursiveMode(): void { - const nested = Deno.makeTempDirSync() + "/nested"; - const path = nested + "/dir"; - Deno.mkdirSync(path, { mode: 0o737, recursive: true }); - assertDirectory(path, 0o737); - assertDirectory(nested, 0o737); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function mkdirRecursiveMode(): Promise { - const nested = Deno.makeTempDirSync() + "/nested"; - const path = nested + "/dir"; - await Deno.mkdir(path, { mode: 0o737, recursive: true }); - assertDirectory(path, 0o737); - assertDirectory(nested, 0o737); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function mkdirSyncRecursiveIfExists(): void { - const path = Deno.makeTempDirSync() + "/dir"; - Deno.mkdirSync(path, { mode: 0o737 }); - Deno.mkdirSync(path, { recursive: true }); - Deno.mkdirSync(path, { recursive: true, mode: 0o731 }); - assertDirectory(path, 0o737); - if (Deno.build.os !== "windows") { - const pathLink = path + "Link"; - Deno.symlinkSync(path, pathLink); - Deno.mkdirSync(pathLink, { recursive: true }); - Deno.mkdirSync(pathLink, { recursive: true, mode: 0o731 }); - assertDirectory(path, 0o737); - } - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function mkdirRecursiveIfExists(): Promise { - const path = Deno.makeTempDirSync() + "/dir"; - await Deno.mkdir(path, { mode: 0o737 }); - await Deno.mkdir(path, { recursive: true }); - await Deno.mkdir(path, { recursive: true, mode: 0o731 }); - assertDirectory(path, 0o737); - if (Deno.build.os !== "windows") { - const pathLink = path + "Link"; - Deno.symlinkSync(path, pathLink); - await Deno.mkdir(pathLink, { recursive: true }); - await Deno.mkdir(pathLink, { recursive: true, mode: 0o731 }); - assertDirectory(path, 0o737); - } - } -); - -unitTest( - { perms: { read: true, write: true } }, - function mkdirSyncErrors(): void { - const testDir = Deno.makeTempDirSync(); - const emptydir = testDir + "/empty"; - const fulldir = testDir + "/dir"; - const file = fulldir + "/file"; - Deno.mkdirSync(emptydir); - Deno.mkdirSync(fulldir); - Deno.createSync(file).close(); - - assertThrows((): void => { - Deno.mkdirSync(emptydir, { recursive: false }); - }, Deno.errors.AlreadyExists); - assertThrows((): void => { - Deno.mkdirSync(fulldir, { recursive: false }); - }, Deno.errors.AlreadyExists); - assertThrows((): void => { - Deno.mkdirSync(file, { recursive: false }); - }, Deno.errors.AlreadyExists); - assertThrows((): void => { - Deno.mkdirSync(file, { recursive: true }); - }, Deno.errors.AlreadyExists); - - if (Deno.build.os !== "windows") { - const fileLink = testDir + "/fileLink"; - const dirLink = testDir + "/dirLink"; - const danglingLink = testDir + "/danglingLink"; - Deno.symlinkSync(file, fileLink); - Deno.symlinkSync(emptydir, dirLink); - Deno.symlinkSync(testDir + "/nonexistent", danglingLink); - - assertThrows((): void => { - Deno.mkdirSync(dirLink, { recursive: false }); - }, Deno.errors.AlreadyExists); - assertThrows((): void => { - Deno.mkdirSync(fileLink, { recursive: false }); - }, Deno.errors.AlreadyExists); - assertThrows((): void => { - Deno.mkdirSync(fileLink, { recursive: true }); - }, Deno.errors.AlreadyExists); - assertThrows((): void => { - Deno.mkdirSync(danglingLink, { recursive: false }); - }, Deno.errors.AlreadyExists); - assertThrows((): void => { - Deno.mkdirSync(danglingLink, { recursive: true }); - }, Deno.errors.AlreadyExists); - } - } -); diff --git a/cli/js/tests/net_test.ts b/cli/js/tests/net_test.ts deleted file mode 100644 index 9e9a1e5e8..000000000 --- a/cli/js/tests/net_test.ts +++ /dev/null @@ -1,527 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - unitTest, - assert, - assertEquals, - createResolvable, -} from "./test_util.ts"; - -unitTest({ perms: { net: true } }, function netTcpListenClose(): void { - const listener = Deno.listen({ hostname: "127.0.0.1", port: 3500 }); - assert(listener.addr.transport === "tcp"); - assertEquals(listener.addr.hostname, "127.0.0.1"); - assertEquals(listener.addr.port, 3500); - listener.close(); -}); - -unitTest( - { - perms: { net: true }, - // TODO: - ignore: Deno.build.os === "windows", - }, - function netUdpListenClose(): void { - const socket = Deno.listenDatagram({ - hostname: "127.0.0.1", - port: 3500, - transport: "udp", - }); - assert(socket.addr.transport === "udp"); - assertEquals(socket.addr.hostname, "127.0.0.1"); - assertEquals(socket.addr.port, 3500); - socket.close(); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - function netUnixListenClose(): void { - const filePath = Deno.makeTempFileSync(); - const socket = Deno.listen({ - path: filePath, - transport: "unix", - }); - assert(socket.addr.transport === "unix"); - assertEquals(socket.addr.path, filePath); - socket.close(); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - function netUnixPacketListenClose(): void { - const filePath = Deno.makeTempFileSync(); - const socket = Deno.listenDatagram({ - path: filePath, - transport: "unixpacket", - }); - assert(socket.addr.transport === "unixpacket"); - assertEquals(socket.addr.path, filePath); - socket.close(); - } -); - -unitTest( - { - perms: { net: true }, - }, - async function netTcpCloseWhileAccept(): Promise { - const listener = Deno.listen({ port: 4501 }); - const p = listener.accept(); - listener.close(); - let err; - try { - await p; - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Error); - assertEquals(err.message, "Listener has been closed"); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - async function netUnixCloseWhileAccept(): Promise { - const filePath = await Deno.makeTempFile(); - const listener = Deno.listen({ - path: filePath, - transport: "unix", - }); - const p = listener.accept(); - listener.close(); - let err; - try { - await p; - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Error); - assertEquals(err.message, "Listener has been closed"); - } -); - -unitTest( - { perms: { net: true } }, - async function netTcpConcurrentAccept(): Promise { - const listener = Deno.listen({ port: 4502 }); - let acceptErrCount = 0; - const checkErr = (e: Error): void => { - if (e.message === "Listener has been closed") { - assertEquals(acceptErrCount, 1); - } else if (e.message === "Another accept task is ongoing") { - acceptErrCount++; - } else { - throw new Error("Unexpected error message"); - } - }; - const p = listener.accept().catch(checkErr); - const p1 = listener.accept().catch(checkErr); - await Promise.race([p, p1]); - listener.close(); - await Promise.all([p, p1]); - assertEquals(acceptErrCount, 1); - } -); - -// TODO(jsouto): Enable when tokio updates mio to v0.7! -unitTest( - { ignore: true, perms: { read: true, write: true } }, - async function netUnixConcurrentAccept(): Promise { - const filePath = await Deno.makeTempFile(); - const listener = Deno.listen({ transport: "unix", path: filePath }); - let acceptErrCount = 0; - const checkErr = (e: Error): void => { - if (e.message === "Listener has been closed") { - assertEquals(acceptErrCount, 1); - } else if (e.message === "Another accept task is ongoing") { - acceptErrCount++; - } else { - throw new Error("Unexpected error message"); - } - }; - const p = listener.accept().catch(checkErr); - const p1 = listener.accept().catch(checkErr); - await Promise.race([p, p1]); - listener.close(); - await [p, p1]; - assertEquals(acceptErrCount, 1); - } -); - -unitTest({ perms: { net: true } }, async function netTcpDialListen(): Promise< - void -> { - const listener = Deno.listen({ port: 3500 }); - listener.accept().then( - async (conn): Promise => { - assert(conn.remoteAddr != null); - assert(conn.localAddr.transport === "tcp"); - assertEquals(conn.localAddr.hostname, "127.0.0.1"); - assertEquals(conn.localAddr.port, 3500); - await conn.write(new Uint8Array([1, 2, 3])); - conn.close(); - } - ); - - const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 }); - assert(conn.remoteAddr.transport === "tcp"); - assertEquals(conn.remoteAddr.hostname, "127.0.0.1"); - assertEquals(conn.remoteAddr.port, 3500); - assert(conn.localAddr != null); - const buf = new Uint8Array(1024); - const readResult = await conn.read(buf); - assertEquals(3, readResult); - assertEquals(1, buf[0]); - assertEquals(2, buf[1]); - assertEquals(3, buf[2]); - assert(conn.rid > 0); - - assert(readResult !== null); - - const readResult2 = await conn.read(buf); - assertEquals(readResult2, null); - - listener.close(); - conn.close(); -}); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - async function netUnixDialListen(): Promise { - const filePath = await Deno.makeTempFile(); - const listener = Deno.listen({ path: filePath, transport: "unix" }); - listener.accept().then( - async (conn): Promise => { - assert(conn.remoteAddr != null); - assert(conn.localAddr.transport === "unix"); - assertEquals(conn.localAddr.path, filePath); - await conn.write(new Uint8Array([1, 2, 3])); - conn.close(); - } - ); - const conn = await Deno.connect({ path: filePath, transport: "unix" }); - assert(conn.remoteAddr.transport === "unix"); - assertEquals(conn.remoteAddr.path, filePath); - assert(conn.remoteAddr != null); - const buf = new Uint8Array(1024); - const readResult = await conn.read(buf); - assertEquals(3, readResult); - assertEquals(1, buf[0]); - assertEquals(2, buf[1]); - assertEquals(3, buf[2]); - assert(conn.rid > 0); - - assert(readResult !== null); - - const readResult2 = await conn.read(buf); - assertEquals(readResult2, null); - - listener.close(); - conn.close(); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { net: true } }, - async function netUdpSendReceive(): Promise { - const alice = Deno.listenDatagram({ port: 3500, transport: "udp" }); - assert(alice.addr.transport === "udp"); - assertEquals(alice.addr.port, 3500); - assertEquals(alice.addr.hostname, "127.0.0.1"); - - const bob = Deno.listenDatagram({ port: 4501, transport: "udp" }); - assert(bob.addr.transport === "udp"); - assertEquals(bob.addr.port, 4501); - assertEquals(bob.addr.hostname, "127.0.0.1"); - - const sent = new Uint8Array([1, 2, 3]); - await alice.send(sent, bob.addr); - - const [recvd, remote] = await bob.receive(); - assert(remote.transport === "udp"); - assertEquals(remote.port, 3500); - assertEquals(recvd.length, 3); - assertEquals(1, recvd[0]); - assertEquals(2, recvd[1]); - assertEquals(3, recvd[2]); - alice.close(); - bob.close(); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - async function netUnixPacketSendReceive(): Promise { - const filePath = await Deno.makeTempFile(); - const alice = Deno.listenDatagram({ - path: filePath, - transport: "unixpacket", - }); - assert(alice.addr.transport === "unixpacket"); - assertEquals(alice.addr.path, filePath); - - const bob = Deno.listenDatagram({ - path: filePath, - transport: "unixpacket", - }); - assert(bob.addr.transport === "unixpacket"); - assertEquals(bob.addr.path, filePath); - - const sent = new Uint8Array([1, 2, 3]); - await alice.send(sent, bob.addr); - - const [recvd, remote] = await bob.receive(); - assert(remote.transport === "unixpacket"); - assertEquals(remote.path, filePath); - assertEquals(recvd.length, 3); - assertEquals(1, recvd[0]); - assertEquals(2, recvd[1]); - assertEquals(3, recvd[2]); - alice.close(); - bob.close(); - } -); - -unitTest( - { perms: { net: true } }, - async function netTcpListenIteratorBreakClosesResource(): Promise { - const promise = createResolvable(); - - async function iterate(listener: Deno.Listener): Promise { - let i = 0; - - for await (const conn of listener) { - conn.close(); - i++; - - if (i > 1) { - break; - } - } - - promise.resolve(); - } - - const addr = { hostname: "127.0.0.1", port: 8888 }; - const listener = Deno.listen(addr); - iterate(listener); - - await new Promise((resolve: () => void, _) => { - setTimeout(resolve, 100); - }); - const conn1 = await Deno.connect(addr); - conn1.close(); - const conn2 = await Deno.connect(addr); - conn2.close(); - - await promise; - } -); - -unitTest( - { perms: { net: true } }, - async function netTcpListenCloseWhileIterating(): Promise { - const listener = Deno.listen({ port: 8000 }); - const nextWhileClosing = listener[Symbol.asyncIterator]().next(); - listener.close(); - assertEquals(await nextWhileClosing, { value: undefined, done: true }); - - const nextAfterClosing = listener[Symbol.asyncIterator]().next(); - assertEquals(await nextAfterClosing, { value: undefined, done: true }); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { net: true } }, - async function netUdpListenCloseWhileIterating(): Promise { - const socket = Deno.listenDatagram({ port: 8000, transport: "udp" }); - const nextWhileClosing = socket[Symbol.asyncIterator]().next(); - socket.close(); - assertEquals(await nextWhileClosing, { value: undefined, done: true }); - - const nextAfterClosing = socket[Symbol.asyncIterator]().next(); - assertEquals(await nextAfterClosing, { value: undefined, done: true }); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - async function netUnixListenCloseWhileIterating(): Promise { - const filePath = Deno.makeTempFileSync(); - const socket = Deno.listen({ path: filePath, transport: "unix" }); - const nextWhileClosing = socket[Symbol.asyncIterator]().next(); - socket.close(); - assertEquals(await nextWhileClosing, { value: undefined, done: true }); - - const nextAfterClosing = socket[Symbol.asyncIterator]().next(); - assertEquals(await nextAfterClosing, { value: undefined, done: true }); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - async function netUnixPacketListenCloseWhileIterating(): Promise { - const filePath = Deno.makeTempFileSync(); - const socket = Deno.listenDatagram({ - path: filePath, - transport: "unixpacket", - }); - const nextWhileClosing = socket[Symbol.asyncIterator]().next(); - socket.close(); - assertEquals(await nextWhileClosing, { value: undefined, done: true }); - - const nextAfterClosing = socket[Symbol.asyncIterator]().next(); - assertEquals(await nextAfterClosing, { value: undefined, done: true }); - } -); - -unitTest( - { - // FIXME(bartlomieju) - ignore: true, - perms: { net: true }, - }, - async function netListenAsyncIterator(): Promise { - const addr = { hostname: "127.0.0.1", port: 3500 }; - const listener = Deno.listen(addr); - const runAsyncIterator = async (): Promise => { - for await (const conn of listener) { - await conn.write(new Uint8Array([1, 2, 3])); - conn.close(); - } - }; - runAsyncIterator(); - const conn = await Deno.connect(addr); - const buf = new Uint8Array(1024); - const readResult = await conn.read(buf); - assertEquals(3, readResult); - assertEquals(1, buf[0]); - assertEquals(2, buf[1]); - assertEquals(3, buf[2]); - assert(conn.rid > 0); - - assert(readResult !== null); - - const readResult2 = await conn.read(buf); - assertEquals(readResult2, null); - - listener.close(); - conn.close(); - } -); - -unitTest( - { - // FIXME(bartlomieju) - ignore: true, - perms: { net: true }, - }, - async function netCloseWriteSuccess() { - const addr = { hostname: "127.0.0.1", port: 3500 }; - const listener = Deno.listen(addr); - const closeDeferred = createResolvable(); - listener.accept().then(async (conn) => { - await conn.write(new Uint8Array([1, 2, 3])); - await closeDeferred; - conn.close(); - }); - const conn = await Deno.connect(addr); - conn.closeWrite(); // closing write - const buf = new Uint8Array(1024); - // Check read not impacted - const readResult = await conn.read(buf); - assertEquals(3, readResult); - assertEquals(1, buf[0]); - assertEquals(2, buf[1]); - assertEquals(3, buf[2]); - // Check write should be closed - let err; - try { - await conn.write(new Uint8Array([1, 2, 3])); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Deno.errors.BrokenPipe); - closeDeferred.resolve(); - listener.close(); - conn.close(); - } -); - -unitTest( - { - // FIXME(bartlomieju) - ignore: true, - perms: { net: true }, - }, - async function netDoubleCloseWrite() { - const addr = { hostname: "127.0.0.1", port: 3500 }; - const listener = Deno.listen(addr); - const closeDeferred = createResolvable(); - listener.accept().then(async (conn) => { - await closeDeferred; - conn.close(); - }); - const conn = await Deno.connect(addr); - conn.closeWrite(); // closing write - let err; - try { - // Duplicated close should throw error - conn.closeWrite(); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof Deno.errors.NotConnected); - closeDeferred.resolve(); - listener.close(); - conn.close(); - } -); - -unitTest( - { - perms: { net: true }, - }, - async function netHangsOnClose() { - let acceptedConn: Deno.Conn; - const resolvable = createResolvable(); - - async function iteratorReq(listener: Deno.Listener): Promise { - const p = new Uint8Array(10); - const conn = await listener.accept(); - acceptedConn = conn; - - try { - while (true) { - const nread = await conn.read(p); - if (nread === null) { - break; - } - await conn.write(new Uint8Array([1, 2, 3])); - } - } catch (err) { - assert(!!err); - assert(err instanceof Deno.errors.BadResource); - } - - resolvable.resolve(); - } - - const addr = { hostname: "127.0.0.1", port: 3500 }; - const listener = Deno.listen(addr); - iteratorReq(listener); - const conn = await Deno.connect(addr); - await conn.write(new Uint8Array([1, 2, 3, 4])); - const buf = new Uint8Array(10); - await conn.read(buf); - conn!.close(); - acceptedConn!.close(); - listener.close(); - await resolvable; - } -); diff --git a/cli/js/tests/os_test.ts b/cli/js/tests/os_test.ts deleted file mode 100644 index e99002534..000000000 --- a/cli/js/tests/os_test.ts +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertNotEquals, - assertThrows, - unitTest, -} from "./test_util.ts"; - -unitTest({ perms: { env: true } }, function envSuccess(): void { - Deno.env.set("TEST_VAR", "A"); - const env = Deno.env.toObject(); - Deno.env.set("TEST_VAR", "B"); - assertEquals(env["TEST_VAR"], "A"); - assertNotEquals(Deno.env.get("TEST_VAR"), env["TEST_VAR"]); -}); - -unitTest({ perms: { env: true } }, function envNotFound(): void { - const r = Deno.env.get("env_var_does_not_exist!"); - assertEquals(r, undefined); -}); - -unitTest(function envPermissionDenied1(): void { - let err; - try { - Deno.env.toObject(); - } catch (e) { - err = e; - } - assertNotEquals(err, undefined); - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest(function envPermissionDenied2(): void { - let err; - try { - Deno.env.get("PATH"); - } catch (e) { - err = e; - } - assertNotEquals(err, undefined); - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -// This test verifies that on Windows, environment variables are -// case-insensitive. Case normalization needs be done using the collation -// that Windows uses, rather than naively using String.toLowerCase(). -unitTest( - { - ignore: Deno.build.os !== "windows", - perms: { read: true, env: true, run: true }, - }, - async function envCaseInsensitive() { - // Utility function that runs a Deno subprocess with the environment - // specified in `inputEnv`. The subprocess reads the environment variables - // which are in the keys of `expectedEnv` and writes them to stdout as JSON. - // It is then verified that these match with the values of `expectedEnv`. - const checkChildEnv = async ( - inputEnv: Record, - expectedEnv: Record - ): Promise => { - const src = ` - console.log( - ${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env.get(k)) - )`; - const proc = Deno.run({ - cmd: [Deno.execPath(), "eval", src], - env: inputEnv, - stdout: "piped", - }); - const status = await proc.status(); - assertEquals(status.success, true); - const expectedValues = Object.values(expectedEnv); - const actualValues = JSON.parse( - new TextDecoder().decode(await proc.output()) - ); - assertEquals(actualValues, expectedValues); - proc.close(); - }; - - assertEquals(Deno.env.get("path"), Deno.env.get("PATH")); - assertEquals(Deno.env.get("Path"), Deno.env.get("PATH")); - - // Check 'foo', 'Foo' and 'Foo' are case folded. - await checkChildEnv({ foo: "X" }, { foo: "X", Foo: "X", FOO: "X" }); - - // Check that 'µ' and 'Μ' are not case folded. - const lc1 = "µ"; - const uc1 = lc1.toUpperCase(); - assertNotEquals(lc1, uc1); - await checkChildEnv( - { [lc1]: "mu", [uc1]: "MU" }, - { [lc1]: "mu", [uc1]: "MU" } - ); - - // Check that 'dž' and 'DŽ' are folded, but 'Dž' is preserved. - const c2 = "Dž"; - const lc2 = c2.toLowerCase(); - const uc2 = c2.toUpperCase(); - assertNotEquals(c2, lc2); - assertNotEquals(c2, uc2); - await checkChildEnv( - { [c2]: "Dz", [lc2]: "dz" }, - { [c2]: "Dz", [lc2]: "dz", [uc2]: "dz" } - ); - await checkChildEnv( - { [c2]: "Dz", [uc2]: "DZ" }, - { [c2]: "Dz", [uc2]: "DZ", [lc2]: "DZ" } - ); - } -); - -unitTest(function osPid(): void { - assert(Deno.pid > 0); -}); - -unitTest({ perms: { env: true } }, function getDir(): void { - type supportOS = "darwin" | "windows" | "linux"; - - interface Runtime { - os: supportOS; - shouldHaveValue: boolean; - } - - interface Scenes { - kind: Deno.DirKind; - runtime: Runtime[]; - } - - const scenes: Scenes[] = [ - { - kind: "config", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: true }, - ], - }, - { - kind: "cache", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: true }, - ], - }, - { - kind: "executable", - runtime: [ - { os: "darwin", shouldHaveValue: false }, - { os: "windows", shouldHaveValue: false }, - { os: "linux", shouldHaveValue: true }, - ], - }, - { - kind: "data", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: true }, - ], - }, - { - kind: "data_local", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: true }, - ], - }, - { - kind: "audio", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: false }, - ], - }, - { - kind: "desktop", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: false }, - ], - }, - { - kind: "document", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: false }, - ], - }, - { - kind: "download", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: false }, - ], - }, - { - kind: "font", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: false }, - { os: "linux", shouldHaveValue: true }, - ], - }, - { - kind: "picture", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: false }, - ], - }, - { - kind: "public", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: false }, - ], - }, - { - kind: "template", - runtime: [ - { os: "darwin", shouldHaveValue: false }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: false }, - ], - }, - { - kind: "tmp", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: true }, - ], - }, - { - kind: "video", - runtime: [ - { os: "darwin", shouldHaveValue: true }, - { os: "windows", shouldHaveValue: true }, - { os: "linux", shouldHaveValue: false }, - ], - }, - ]; - - for (const s of scenes) { - for (const r of s.runtime) { - if (Deno.build.os !== r.os) continue; - if (r.shouldHaveValue) { - const d = Deno.dir(s.kind); - assert(d); - assert(d.length > 0); - } - } - } -}); - -unitTest(function getDirWithoutPermission(): void { - assertThrows( - () => Deno.dir("home"), - Deno.errors.PermissionDenied, - `run again with the --allow-env flag` - ); -}); - -unitTest({ perms: { read: true } }, function execPath(): void { - assertNotEquals(Deno.execPath(), ""); -}); - -unitTest({ perms: { read: false } }, function execPathPerm(): void { - let caughtError = false; - try { - Deno.execPath(); - } catch (err) { - caughtError = true; - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } - assert(caughtError); -}); - -unitTest({ perms: { env: true } }, function loadavgSuccess(): void { - const load = Deno.loadavg(); - assertEquals(load.length, 3); -}); - -unitTest({ perms: { env: false } }, function loadavgPerm(): void { - let caughtError = false; - try { - Deno.loadavg(); - } catch (err) { - caughtError = true; - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } - assert(caughtError); -}); - -unitTest({ perms: { env: true } }, function hostnameDir(): void { - assertNotEquals(Deno.hostname(), ""); -}); - -unitTest({ perms: { env: false } }, function hostnamePerm(): void { - let caughtError = false; - try { - Deno.hostname(); - } catch (err) { - caughtError = true; - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } - assert(caughtError); -}); - -unitTest({ perms: { env: true } }, function releaseDir(): void { - assertNotEquals(Deno.osRelease(), ""); -}); - -unitTest({ perms: { env: false } }, function releasePerm(): void { - let caughtError = false; - try { - Deno.osRelease(); - } catch (err) { - caughtError = true; - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } - assert(caughtError); -}); diff --git a/cli/js/tests/performance_test.ts b/cli/js/tests/performance_test.ts deleted file mode 100644 index 89b7cad8b..000000000 --- a/cli/js/tests/performance_test.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, createResolvable } from "./test_util.ts"; - -unitTest({ perms: { hrtime: false } }, async function performanceNow(): Promise< - void -> { - const resolvable = createResolvable(); - const start = performance.now(); - setTimeout((): void => { - const end = performance.now(); - assert(end - start >= 10); - resolvable.resolve(); - }, 10); - await resolvable; -}); diff --git a/cli/js/tests/permissions_test.ts b/cli/js/tests/permissions_test.ts deleted file mode 100644 index 7528768a1..000000000 --- a/cli/js/tests/permissions_test.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -unitTest(async function permissionInvalidName(): Promise { - let thrown = false; - try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await Deno.permissions.query({ name: "foo" as any }); - } catch (e) { - thrown = true; - assert(e instanceof Error); - } finally { - assert(thrown); - } -}); - -unitTest(async function permissionNetInvalidUrl(): Promise { - let thrown = false; - try { - await Deno.permissions.query({ name: "net", url: ":" }); - } catch (e) { - thrown = true; - assert(e instanceof URIError); - } finally { - assert(thrown); - } -}); diff --git a/cli/js/tests/process_test.ts b/cli/js/tests/process_test.ts deleted file mode 100644 index 1ea6f95b7..000000000 --- a/cli/js/tests/process_test.ts +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertStrContains, - unitTest, -} from "./test_util.ts"; -const { - kill, - run, - readFile, - open, - makeTempDir, - writeFile, - writeFileSync, -} = Deno; - -unitTest(function runPermissions(): void { - let caughtError = false; - try { - run({ cmd: ["python", "-c", "print('hello world')"] }); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { run: true } }, async function runSuccess(): Promise { - const p = run({ - cmd: ["python", "-c", "print('hello world')"], - stdout: "piped", - stderr: "null", - }); - const status = await p.status(); - assertEquals(status.success, true); - assertEquals(status.code, 0); - assertEquals(status.signal, undefined); - p.stdout!.close(); - p.close(); -}); - -unitTest( - { perms: { run: true } }, - async function runCommandFailedWithCode(): Promise { - const p = run({ - cmd: ["python", "-c", "import sys;sys.exit(41 + 1)"], - }); - const status = await p.status(); - assertEquals(status.success, false); - assertEquals(status.code, 42); - assertEquals(status.signal, undefined); - p.close(); - } -); - -unitTest( - { - // No signals on windows. - ignore: Deno.build.os === "windows", - perms: { run: true }, - }, - async function runCommandFailedWithSignal(): Promise { - const p = run({ - cmd: ["python", "-c", "import os;os.kill(os.getpid(), 9)"], - }); - const status = await p.status(); - assertEquals(status.success, false); - assertEquals(status.code, undefined); - assertEquals(status.signal, 9); - p.close(); - } -); - -unitTest({ perms: { run: true } }, function runNotFound(): void { - let error; - try { - run({ cmd: ["this file hopefully doesn't exist"] }); - } catch (e) { - error = e; - } - assert(error !== undefined); - assert(error instanceof Deno.errors.NotFound); -}); - -unitTest( - { perms: { write: true, run: true } }, - async function runWithCwdIsAsync(): Promise { - const enc = new TextEncoder(); - const cwd = await makeTempDir({ prefix: "deno_command_test" }); - - const exitCodeFile = "deno_was_here"; - const pyProgramFile = "poll_exit.py"; - const pyProgram = ` -from sys import exit -from time import sleep - -while True: - try: - with open("${exitCodeFile}", "r") as f: - line = f.readline() - code = int(line) - exit(code) - except IOError: - # Retry if we got here before deno wrote the file. - sleep(0.01) - pass -`; - - writeFileSync(`${cwd}/${pyProgramFile}.py`, enc.encode(pyProgram)); - const p = run({ - cwd, - cmd: ["python", `${pyProgramFile}.py`], - }); - - // Write the expected exit code *after* starting python. - // This is how we verify that `run()` is actually asynchronous. - const code = 84; - writeFileSync(`${cwd}/${exitCodeFile}`, enc.encode(`${code}`)); - - const status = await p.status(); - assertEquals(status.success, false); - assertEquals(status.code, code); - assertEquals(status.signal, undefined); - p.close(); - } -); - -unitTest({ perms: { run: true } }, async function runStdinPiped(): Promise< - void -> { - const p = run({ - cmd: ["python", "-c", "import sys; assert 'hello' == sys.stdin.read();"], - stdin: "piped", - }); - assert(p.stdin); - assert(!p.stdout); - assert(!p.stderr); - - const msg = new TextEncoder().encode("hello"); - const n = await p.stdin.write(msg); - assertEquals(n, msg.byteLength); - - p.stdin!.close(); - - const status = await p.status(); - assertEquals(status.success, true); - assertEquals(status.code, 0); - assertEquals(status.signal, undefined); - p.close(); -}); - -unitTest({ perms: { run: true } }, async function runStdoutPiped(): Promise< - void -> { - const p = run({ - cmd: ["python", "-c", "import sys; sys.stdout.write('hello')"], - stdout: "piped", - }); - assert(!p.stdin); - assert(!p.stderr); - - const data = new Uint8Array(10); - let r = await p.stdout!.read(data); - if (r === null) { - throw new Error("p.stdout.read(...) should not be null"); - } - assertEquals(r, 5); - const s = new TextDecoder().decode(data.subarray(0, r)); - assertEquals(s, "hello"); - r = await p.stdout!.read(data); - assertEquals(r, null); - p.stdout!.close(); - - const status = await p.status(); - assertEquals(status.success, true); - assertEquals(status.code, 0); - assertEquals(status.signal, undefined); - p.close(); -}); - -unitTest({ perms: { run: true } }, async function runStderrPiped(): Promise< - void -> { - const p = run({ - cmd: ["python", "-c", "import sys; sys.stderr.write('hello')"], - stderr: "piped", - }); - assert(!p.stdin); - assert(!p.stdout); - - const data = new Uint8Array(10); - let r = await p.stderr!.read(data); - if (r === null) { - throw new Error("p.stderr.read should not return null here"); - } - assertEquals(r, 5); - const s = new TextDecoder().decode(data.subarray(0, r)); - assertEquals(s, "hello"); - r = await p.stderr!.read(data); - assertEquals(r, null); - p.stderr!.close(); - - const status = await p.status(); - assertEquals(status.success, true); - assertEquals(status.code, 0); - assertEquals(status.signal, undefined); - p.close(); -}); - -unitTest({ perms: { run: true } }, async function runOutput(): Promise { - const p = run({ - cmd: ["python", "-c", "import sys; sys.stdout.write('hello')"], - stdout: "piped", - }); - const output = await p.output(); - const s = new TextDecoder().decode(output); - assertEquals(s, "hello"); - p.close(); -}); - -unitTest({ perms: { run: true } }, async function runStderrOutput(): Promise< - void -> { - const p = run({ - cmd: ["python", "-c", "import sys; sys.stderr.write('error')"], - stderr: "piped", - }); - const error = await p.stderrOutput(); - const s = new TextDecoder().decode(error); - assertEquals(s, "error"); - p.close(); -}); - -unitTest( - { perms: { run: true, write: true, read: true } }, - async function runRedirectStdoutStderr(): Promise { - const tempDir = await makeTempDir(); - const fileName = tempDir + "/redirected_stdio.txt"; - const file = await open(fileName, { - create: true, - write: true, - }); - - const p = run({ - cmd: [ - "python", - "-c", - "import sys; sys.stderr.write('error\\n'); sys.stdout.write('output\\n');", - ], - stdout: file.rid, - stderr: file.rid, - }); - - await p.status(); - p.close(); - file.close(); - - const fileContents = await readFile(fileName); - const decoder = new TextDecoder(); - const text = decoder.decode(fileContents); - - assertStrContains(text, "error"); - assertStrContains(text, "output"); - } -); - -unitTest( - { perms: { run: true, write: true, read: true } }, - async function runRedirectStdin(): Promise { - const tempDir = await makeTempDir(); - const fileName = tempDir + "/redirected_stdio.txt"; - const encoder = new TextEncoder(); - await writeFile(fileName, encoder.encode("hello")); - const file = await open(fileName); - - const p = run({ - cmd: ["python", "-c", "import sys; assert 'hello' == sys.stdin.read();"], - stdin: file.rid, - }); - - const status = await p.status(); - assertEquals(status.code, 0); - p.close(); - file.close(); - } -); - -unitTest({ perms: { run: true } }, async function runEnv(): Promise { - const p = run({ - cmd: [ - "python", - "-c", - "import os, sys; sys.stdout.write(os.environ.get('FOO', '') + os.environ.get('BAR', ''))", - ], - env: { - FOO: "0123", - BAR: "4567", - }, - stdout: "piped", - }); - const output = await p.output(); - const s = new TextDecoder().decode(output); - assertEquals(s, "01234567"); - p.close(); -}); - -unitTest({ perms: { run: true } }, async function runClose(): Promise { - const p = run({ - cmd: [ - "python", - "-c", - "from time import sleep; import sys; sleep(10000); sys.stderr.write('error')", - ], - stderr: "piped", - }); - assert(!p.stdin); - assert(!p.stdout); - - p.close(); - - const data = new Uint8Array(10); - const r = await p.stderr!.read(data); - assertEquals(r, null); - p.stderr!.close(); -}); - -unitTest(function signalNumbers(): void { - if (Deno.build.os === "darwin") { - assertEquals(Deno.Signal.SIGSTOP, 17); - } else if (Deno.build.os === "linux") { - assertEquals(Deno.Signal.SIGSTOP, 19); - } -}); - -unitTest(function killPermissions(): void { - let caughtError = false; - try { - // Unlike the other test cases, we don't have permission to spawn a - // subprocess we can safely kill. Instead we send SIGCONT to the current - // process - assuming that Deno does not have a special handler set for it - // and will just continue even if a signal is erroneously sent. - kill(Deno.pid, Deno.Signal.SIGCONT); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { run: true } }, async function killSuccess(): Promise { - const p = run({ - cmd: ["python", "-c", "from time import sleep; sleep(10000)"], - }); - - assertEquals(Deno.Signal.SIGINT, 2); - kill(p.pid, Deno.Signal.SIGINT); - const status = await p.status(); - - assertEquals(status.success, false); - // TODO(ry) On Linux, status.code is sometimes undefined and sometimes 1. - // The following assert is causing this test to be flaky. Investigate and - // re-enable when it can be made deterministic. - // assertEquals(status.code, 1); - // assertEquals(status.signal, Deno.Signal.SIGINT); - p.close(); -}); - -unitTest({ perms: { run: true } }, function killFailed(): void { - const p = run({ - cmd: ["python", "-c", "from time import sleep; sleep(10000)"], - }); - assert(!p.stdin); - assert(!p.stdout); - - let err; - try { - kill(p.pid, 12345); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof TypeError); - - p.close(); -}); diff --git a/cli/js/tests/read_dir_test.ts b/cli/js/tests/read_dir_test.ts deleted file mode 100644 index 79e2a1507..000000000 --- a/cli/js/tests/read_dir_test.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -function assertSameContent(files: Deno.DirEntry[]): void { - let counter = 0; - - for (const entry of files) { - if (entry.name === "subdir") { - assert(entry.isDirectory); - counter++; - } - } - - assertEquals(counter, 1); -} - -unitTest({ perms: { read: true } }, function readDirSyncSuccess(): void { - const files = [...Deno.readDirSync("cli/tests/")]; - assertSameContent(files); -}); - -unitTest({ perms: { read: false } }, function readDirSyncPerm(): void { - let caughtError = false; - try { - Deno.readDirSync("tests/"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, function readDirSyncNotDir(): void { - let caughtError = false; - let src; - - try { - src = Deno.readDirSync("cli/tests/fixture.json"); - } catch (err) { - caughtError = true; - assert(err instanceof Error); - } - assert(caughtError); - assertEquals(src, undefined); -}); - -unitTest({ perms: { read: true } }, function readDirSyncNotFound(): void { - let caughtError = false; - let src; - - try { - src = Deno.readDirSync("bad_dir_name"); - } catch (err) { - caughtError = true; - assert(err instanceof Deno.errors.NotFound); - } - assert(caughtError); - assertEquals(src, undefined); -}); - -unitTest({ perms: { read: true } }, async function readDirSuccess(): Promise< - void -> { - const files = []; - for await (const dirEntry of Deno.readDir("cli/tests/")) { - files.push(dirEntry); - } - assertSameContent(files); -}); - -unitTest({ perms: { read: false } }, async function readDirPerm(): Promise< - void -> { - let caughtError = false; - try { - await Deno.readDir("tests/")[Symbol.asyncIterator]().next(); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); diff --git a/cli/js/tests/read_file_test.ts b/cli/js/tests/read_file_test.ts deleted file mode 100644 index 0d3cdf422..000000000 --- a/cli/js/tests/read_file_test.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest({ perms: { read: true } }, function readFileSyncSuccess(): void { - const data = Deno.readFileSync("cli/tests/fixture.json"); - assert(data.byteLength > 0); - const decoder = new TextDecoder("utf-8"); - const json = decoder.decode(data); - const pkg = JSON.parse(json); - assertEquals(pkg.name, "deno"); -}); - -unitTest({ perms: { read: false } }, function readFileSyncPerm(): void { - let caughtError = false; - try { - Deno.readFileSync("cli/tests/fixture.json"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, function readFileSyncNotFound(): void { - let caughtError = false; - let data; - try { - data = Deno.readFileSync("bad_filename"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); - assert(data === undefined); -}); - -unitTest({ perms: { read: true } }, async function readFileSuccess(): Promise< - void -> { - const data = await Deno.readFile("cli/tests/fixture.json"); - assert(data.byteLength > 0); - const decoder = new TextDecoder("utf-8"); - const json = decoder.decode(data); - const pkg = JSON.parse(json); - assertEquals(pkg.name, "deno"); -}); - -unitTest({ perms: { read: false } }, async function readFilePerm(): Promise< - void -> { - let caughtError = false; - try { - await Deno.readFile("cli/tests/fixture.json"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, function readFileSyncLoop(): void { - for (let i = 0; i < 256; i++) { - Deno.readFileSync("cli/tests/fixture.json"); - } -}); diff --git a/cli/js/tests/read_link_test.ts b/cli/js/tests/read_link_test.ts deleted file mode 100644 index 6821d8dc8..000000000 --- a/cli/js/tests/read_link_test.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest( - { perms: { write: true, read: true } }, - function readLinkSyncSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const target = testDir + "/target"; - const symlink = testDir + "/symln"; - Deno.mkdirSync(target); - // TODO Add test for Windows once symlink is implemented for Windows. - // See https://github.com/denoland/deno/issues/815. - if (Deno.build.os !== "windows") { - Deno.symlinkSync(target, symlink); - const targetPath = Deno.readLinkSync(symlink); - assertEquals(targetPath, target); - } - } -); - -unitTest({ perms: { read: false } }, function readLinkSyncPerm(): void { - let caughtError = false; - try { - Deno.readLinkSync("/symlink"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, function readLinkSyncNotFound(): void { - let caughtError = false; - let data; - try { - data = Deno.readLinkSync("bad_filename"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); - assertEquals(data, undefined); -}); - -unitTest( - { perms: { write: true, read: true } }, - async function readLinkSuccess(): Promise { - const testDir = Deno.makeTempDirSync(); - const target = testDir + "/target"; - const symlink = testDir + "/symln"; - Deno.mkdirSync(target); - // TODO Add test for Windows once symlink is implemented for Windows. - // See https://github.com/denoland/deno/issues/815. - if (Deno.build.os !== "windows") { - Deno.symlinkSync(target, symlink); - const targetPath = await Deno.readLink(symlink); - assertEquals(targetPath, target); - } - } -); - -unitTest({ perms: { read: false } }, async function readLinkPerm(): Promise< - void -> { - let caughtError = false; - try { - await Deno.readLink("/symlink"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); diff --git a/cli/js/tests/read_text_file_test.ts b/cli/js/tests/read_text_file_test.ts deleted file mode 100644 index 3e7493e4a..000000000 --- a/cli/js/tests/read_text_file_test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest({ perms: { read: true } }, function readTextFileSyncSuccess(): void { - const data = Deno.readTextFileSync("cli/tests/fixture.json"); - assert(data.length > 0); - const pkg = JSON.parse(data); - assertEquals(pkg.name, "deno"); -}); - -unitTest({ perms: { read: false } }, function readTextFileSyncPerm(): void { - let caughtError = false; - try { - Deno.readTextFileSync("cli/tests/fixture.json"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, function readTextFileSyncNotFound(): void { - let caughtError = false; - let data; - try { - data = Deno.readTextFileSync("bad_filename"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); - assert(data === undefined); -}); - -unitTest( - { perms: { read: true } }, - async function readTextFileSuccess(): Promise { - const data = await Deno.readTextFile("cli/tests/fixture.json"); - assert(data.length > 0); - const pkg = JSON.parse(data); - assertEquals(pkg.name, "deno"); - } -); - -unitTest({ perms: { read: false } }, async function readTextFilePerm(): Promise< - void -> { - let caughtError = false; - try { - await Deno.readTextFile("cli/tests/fixture.json"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, function readTextFileSyncLoop(): void { - for (let i = 0; i < 256; i++) { - Deno.readTextFileSync("cli/tests/fixture.json"); - } -}); diff --git a/cli/js/tests/real_path_test.ts b/cli/js/tests/real_path_test.ts deleted file mode 100644 index c88955270..000000000 --- a/cli/js/tests/real_path_test.ts +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -unitTest({ perms: { read: true } }, function realPathSyncSuccess(): void { - const incompletePath = "cli/tests/fixture.json"; - const realPath = Deno.realPathSync(incompletePath); - if (Deno.build.os !== "windows") { - assert(realPath.startsWith("/")); - } else { - assert(/^[A-Z]/.test(realPath)); - } - assert(realPath.endsWith(incompletePath)); -}); - -unitTest( - { - ignore: Deno.build.os === "windows", - perms: { read: true, write: true }, - }, - function realPathSyncSymlink(): void { - const testDir = Deno.makeTempDirSync(); - const target = testDir + "/target"; - const symlink = testDir + "/symln"; - Deno.mkdirSync(target); - Deno.symlinkSync(target, symlink); - const targetPath = Deno.realPathSync(symlink); - assert(targetPath.startsWith("/")); - assert(targetPath.endsWith("/target")); - } -); - -unitTest({ perms: { read: false } }, function realPathSyncPerm(): void { - let caughtError = false; - try { - Deno.realPathSync("some_file"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, function realPathSyncNotFound(): void { - let caughtError = false; - try { - Deno.realPathSync("bad_filename"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, async function realPathSuccess(): Promise< - void -> { - const incompletePath = "cli/tests/fixture.json"; - const realPath = await Deno.realPath(incompletePath); - if (Deno.build.os !== "windows") { - assert(realPath.startsWith("/")); - } else { - assert(/^[A-Z]/.test(realPath)); - } - assert(realPath.endsWith(incompletePath)); -}); - -unitTest( - { - ignore: Deno.build.os === "windows", - perms: { read: true, write: true }, - }, - async function realPathSymlink(): Promise { - const testDir = Deno.makeTempDirSync(); - const target = testDir + "/target"; - const symlink = testDir + "/symln"; - Deno.mkdirSync(target); - Deno.symlinkSync(target, symlink); - const targetPath = await Deno.realPath(symlink); - assert(targetPath.startsWith("/")); - assert(targetPath.endsWith("/target")); - } -); - -unitTest({ perms: { read: false } }, async function realPathPerm(): Promise< - void -> { - let caughtError = false; - try { - await Deno.realPath("some_file"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, async function realPathNotFound(): Promise< - void -> { - let caughtError = false; - try { - await Deno.realPath("bad_filename"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); -}); diff --git a/cli/js/tests/remove_test.ts b/cli/js/tests/remove_test.ts deleted file mode 100644 index 35e5c821e..000000000 --- a/cli/js/tests/remove_test.ts +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -// SYNC - -unitTest( - { perms: { write: true, read: true } }, - function removeSyncDirSuccess(): void { - // REMOVE EMPTY DIRECTORY - const path = Deno.makeTempDirSync() + "/subdir"; - Deno.mkdirSync(path); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory); // check exist first - Deno.removeSync(path); // remove - // We then check again after remove - let err; - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - function removeSyncFileSuccess(): void { - // REMOVE FILE - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - const fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); // check exist first - Deno.removeSync(filename); // remove - // We then check again after remove - let err; - try { - Deno.statSync(filename); - } catch (e) { - err = e; - } - // File is gone - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - function removeSyncFail(): void { - // NON-EMPTY DIRECTORY - const path = Deno.makeTempDirSync() + "/dir/subdir"; - const subPath = path + "/subsubdir"; - Deno.mkdirSync(path, { recursive: true }); - Deno.mkdirSync(subPath); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory); // check exist first - const subPathInfo = Deno.statSync(subPath); - assert(subPathInfo.isDirectory); // check exist first - let err; - try { - // Should not be able to recursively remove - Deno.removeSync(path); - } catch (e) { - err = e; - } - // TODO(ry) Is Other really the error we should get here? What would Go do? - assert(err instanceof Error); - // NON-EXISTENT DIRECTORY/FILE - try { - // Non-existent - Deno.removeSync("/baddir"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - function removeSyncDanglingSymlinkSuccess(): void { - const danglingSymlinkPath = Deno.makeTempDirSync() + "/dangling_symlink"; - if (Deno.build.os === "windows") { - Deno.symlinkSync("unexistent_file", danglingSymlinkPath, { - type: "file", - }); - } else { - Deno.symlinkSync("unexistent_file", danglingSymlinkPath); - } - const pathInfo = Deno.lstatSync(danglingSymlinkPath); - assert(pathInfo.isSymlink); - Deno.removeSync(danglingSymlinkPath); - let err; - try { - Deno.lstatSync(danglingSymlinkPath); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - function removeSyncValidSymlinkSuccess(): void { - const encoder = new TextEncoder(); - const data = encoder.encode("Test"); - const tempDir = Deno.makeTempDirSync(); - const filePath = tempDir + "/test.txt"; - const validSymlinkPath = tempDir + "/valid_symlink"; - Deno.writeFileSync(filePath, data, { mode: 0o666 }); - if (Deno.build.os === "windows") { - Deno.symlinkSync(filePath, validSymlinkPath, { type: "file" }); - } else { - Deno.symlinkSync(filePath, validSymlinkPath); - } - const symlinkPathInfo = Deno.statSync(validSymlinkPath); - assert(symlinkPathInfo.isFile); - Deno.removeSync(validSymlinkPath); - let err; - try { - Deno.statSync(validSymlinkPath); - } catch (e) { - err = e; - } - Deno.removeSync(filePath); - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest({ perms: { write: false } }, function removeSyncPerm(): void { - let err; - try { - Deno.removeSync("/baddir"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest( - { perms: { write: true, read: true } }, - function removeAllSyncDirSuccess(): void { - // REMOVE EMPTY DIRECTORY - let path = Deno.makeTempDirSync() + "/dir/subdir"; - Deno.mkdirSync(path, { recursive: true }); - let pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory); // check exist first - Deno.removeSync(path, { recursive: true }); // remove - // We then check again after remove - let err; - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assert(err instanceof Deno.errors.NotFound); - - // REMOVE NON-EMPTY DIRECTORY - path = Deno.makeTempDirSync() + "/dir/subdir"; - const subPath = path + "/subsubdir"; - Deno.mkdirSync(path, { recursive: true }); - Deno.mkdirSync(subPath); - pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory); // check exist first - const subPathInfo = Deno.statSync(subPath); - assert(subPathInfo.isDirectory); // check exist first - Deno.removeSync(path, { recursive: true }); // remove - // We then check parent directory again after remove - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - function removeAllSyncFileSuccess(): void { - // REMOVE FILE - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - const fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); // check exist first - Deno.removeSync(filename, { recursive: true }); // remove - // We then check again after remove - let err; - try { - Deno.statSync(filename); - } catch (e) { - err = e; - } - // File is gone - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest({ perms: { write: true } }, function removeAllSyncFail(): void { - // NON-EXISTENT DIRECTORY/FILE - let err; - try { - // Non-existent - Deno.removeSync("/baddir", { recursive: true }); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); -}); - -unitTest({ perms: { write: false } }, function removeAllSyncPerm(): void { - let err; - try { - Deno.removeSync("/baddir", { recursive: true }); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -// ASYNC - -unitTest( - { perms: { write: true, read: true } }, - async function removeDirSuccess(): Promise { - // REMOVE EMPTY DIRECTORY - const path = Deno.makeTempDirSync() + "/dir/subdir"; - Deno.mkdirSync(path, { recursive: true }); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory); // check exist first - await Deno.remove(path); // remove - // We then check again after remove - let err; - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - async function removeFileSuccess(): Promise { - // REMOVE FILE - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - const fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); // check exist first - await Deno.remove(filename); // remove - // We then check again after remove - let err; - try { - Deno.statSync(filename); - } catch (e) { - err = e; - } - // File is gone - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - async function removeFail(): Promise { - // NON-EMPTY DIRECTORY - const path = Deno.makeTempDirSync() + "/dir/subdir"; - const subPath = path + "/subsubdir"; - Deno.mkdirSync(path, { recursive: true }); - Deno.mkdirSync(subPath); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory); // check exist first - const subPathInfo = Deno.statSync(subPath); - assert(subPathInfo.isDirectory); // check exist first - let err; - try { - // Should not be able to recursively remove - await Deno.remove(path); - } catch (e) { - err = e; - } - assert(err instanceof Error); - // NON-EXISTENT DIRECTORY/FILE - try { - // Non-existent - await Deno.remove("/baddir"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - async function removeDanglingSymlinkSuccess(): Promise { - const danglingSymlinkPath = Deno.makeTempDirSync() + "/dangling_symlink"; - if (Deno.build.os === "windows") { - Deno.symlinkSync("unexistent_file", danglingSymlinkPath, { - type: "file", - }); - } else { - Deno.symlinkSync("unexistent_file", danglingSymlinkPath); - } - const pathInfo = Deno.lstatSync(danglingSymlinkPath); - assert(pathInfo.isSymlink); - await Deno.remove(danglingSymlinkPath); - let err; - try { - Deno.lstatSync(danglingSymlinkPath); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - async function removeValidSymlinkSuccess(): Promise { - const encoder = new TextEncoder(); - const data = encoder.encode("Test"); - const tempDir = Deno.makeTempDirSync(); - const filePath = tempDir + "/test.txt"; - const validSymlinkPath = tempDir + "/valid_symlink"; - Deno.writeFileSync(filePath, data, { mode: 0o666 }); - if (Deno.build.os === "windows") { - Deno.symlinkSync(filePath, validSymlinkPath, { type: "file" }); - } else { - Deno.symlinkSync(filePath, validSymlinkPath); - } - const symlinkPathInfo = Deno.statSync(validSymlinkPath); - assert(symlinkPathInfo.isFile); - await Deno.remove(validSymlinkPath); - let err; - try { - Deno.statSync(validSymlinkPath); - } catch (e) { - err = e; - } - Deno.removeSync(filePath); - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest({ perms: { write: false } }, async function removePerm(): Promise< - void -> { - let err; - try { - await Deno.remove("/baddir"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest( - { perms: { write: true, read: true } }, - async function removeAllDirSuccess(): Promise { - // REMOVE EMPTY DIRECTORY - let path = Deno.makeTempDirSync() + "/dir/subdir"; - Deno.mkdirSync(path, { recursive: true }); - let pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory); // check exist first - await Deno.remove(path, { recursive: true }); // remove - // We then check again after remove - let err; - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assert(err instanceof Deno.errors.NotFound); - - // REMOVE NON-EMPTY DIRECTORY - path = Deno.makeTempDirSync() + "/dir/subdir"; - const subPath = path + "/subsubdir"; - Deno.mkdirSync(path, { recursive: true }); - Deno.mkdirSync(subPath); - pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory); // check exist first - const subPathInfo = Deno.statSync(subPath); - assert(subPathInfo.isDirectory); // check exist first - await Deno.remove(path, { recursive: true }); // remove - // We then check parent directory again after remove - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest( - { perms: { write: true, read: true } }, - async function removeAllFileSuccess(): Promise { - // REMOVE FILE - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - const fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); // check exist first - await Deno.remove(filename, { recursive: true }); // remove - // We then check again after remove - let err; - try { - Deno.statSync(filename); - } catch (e) { - err = e; - } - // File is gone - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest({ perms: { write: true } }, async function removeAllFail(): Promise< - void -> { - // NON-EXISTENT DIRECTORY/FILE - let err; - try { - // Non-existent - await Deno.remove("/baddir", { recursive: true }); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); -}); - -unitTest({ perms: { write: false } }, async function removeAllPerm(): Promise< - void -> { - let err; - try { - await Deno.remove("/baddir", { recursive: true }); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -if (Deno.build.os === "windows") { - unitTest( - { perms: { run: true, write: true, read: true } }, - async function removeFileSymlink(): Promise { - const symlink = Deno.run({ - cmd: ["cmd", "/c", "mklink", "file_link", "bar"], - stdout: "null", - }); - - assert(await symlink.status()); - symlink.close(); - await Deno.remove("file_link"); - let err; - try { - await Deno.lstat("file_link"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); - } - ); - - unitTest( - { perms: { run: true, write: true, read: true } }, - async function removeDirSymlink(): Promise { - const symlink = Deno.run({ - cmd: ["cmd", "/c", "mklink", "/d", "dir_link", "bar"], - stdout: "null", - }); - - assert(await symlink.status()); - symlink.close(); - - await Deno.remove("dir_link"); - let err; - try { - await Deno.lstat("dir_link"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); - } - ); -} diff --git a/cli/js/tests/rename_test.ts b/cli/js/tests/rename_test.ts deleted file mode 100644 index b4047f906..000000000 --- a/cli/js/tests/rename_test.ts +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals, assertThrows } from "./test_util.ts"; - -function assertMissing(path: string): void { - let caughtErr = false; - let info; - try { - info = Deno.lstatSync(path); - } catch (e) { - caughtErr = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtErr); - assertEquals(info, undefined); -} - -function assertFile(path: string): void { - const info = Deno.lstatSync(path); - assert(info.isFile); -} - -function assertDirectory(path: string, mode?: number): void { - const info = Deno.lstatSync(path); - assert(info.isDirectory); - if (Deno.build.os !== "windows" && mode !== undefined) { - assertEquals(info.mode! & 0o777, mode & ~Deno.umask()); - } -} - -unitTest( - { perms: { read: true, write: true } }, - function renameSyncSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const oldpath = testDir + "/oldpath"; - const newpath = testDir + "/newpath"; - Deno.mkdirSync(oldpath); - Deno.renameSync(oldpath, newpath); - assertDirectory(newpath); - assertMissing(oldpath); - } -); - -unitTest( - { perms: { read: false, write: true } }, - function renameSyncReadPerm(): void { - let err; - try { - const oldpath = "/oldbaddir"; - const newpath = "/newbaddir"; - Deno.renameSync(oldpath, newpath); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } -); - -unitTest( - { perms: { read: true, write: false } }, - function renameSyncWritePerm(): void { - let err; - try { - const oldpath = "/oldbaddir"; - const newpath = "/newbaddir"; - Deno.renameSync(oldpath, newpath); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function renameSuccess(): Promise { - const testDir = Deno.makeTempDirSync(); - const oldpath = testDir + "/oldpath"; - const newpath = testDir + "/newpath"; - Deno.mkdirSync(oldpath); - await Deno.rename(oldpath, newpath); - assertDirectory(newpath); - assertMissing(oldpath); - } -); - -function readFileString(filename: string): string { - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - return dec.decode(dataRead); -} - -function writeFileString(filename: string, s: string): void { - const enc = new TextEncoder(); - const data = enc.encode(s); - Deno.writeFileSync(filename, data, { mode: 0o666 }); -} - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - function renameSyncErrorsUnix(): void { - const testDir = Deno.makeTempDirSync(); - const oldfile = testDir + "/oldfile"; - const olddir = testDir + "/olddir"; - const emptydir = testDir + "/empty"; - const fulldir = testDir + "/dir"; - const file = fulldir + "/file"; - writeFileString(oldfile, "Hello"); - Deno.mkdirSync(olddir); - Deno.mkdirSync(emptydir); - Deno.mkdirSync(fulldir); - writeFileString(file, "world"); - - assertThrows( - (): void => { - Deno.renameSync(oldfile, emptydir); - }, - Error, - "Is a directory" - ); - assertThrows( - (): void => { - Deno.renameSync(olddir, fulldir); - }, - Error, - "Directory not empty" - ); - assertThrows( - (): void => { - Deno.renameSync(olddir, file); - }, - Error, - "Not a directory" - ); - - const fileLink = testDir + "/fileLink"; - const dirLink = testDir + "/dirLink"; - const danglingLink = testDir + "/danglingLink"; - Deno.symlinkSync(file, fileLink); - Deno.symlinkSync(emptydir, dirLink); - Deno.symlinkSync(testDir + "/nonexistent", danglingLink); - - assertThrows( - (): void => { - Deno.renameSync(olddir, fileLink); - }, - Error, - "Not a directory" - ); - assertThrows( - (): void => { - Deno.renameSync(olddir, dirLink); - }, - Error, - "Not a directory" - ); - assertThrows( - (): void => { - Deno.renameSync(olddir, danglingLink); - }, - Error, - "Not a directory" - ); - - // should succeed on Unix - Deno.renameSync(olddir, emptydir); - Deno.renameSync(oldfile, dirLink); - Deno.renameSync(dirLink, danglingLink); - assertFile(danglingLink); - assertEquals("Hello", readFileString(danglingLink)); - } -); - -unitTest( - { ignore: Deno.build.os !== "windows", perms: { read: true, write: true } }, - function renameSyncErrorsWin(): void { - const testDir = Deno.makeTempDirSync(); - const oldfile = testDir + "/oldfile"; - const olddir = testDir + "/olddir"; - const emptydir = testDir + "/empty"; - const fulldir = testDir + "/dir"; - const file = fulldir + "/file"; - writeFileString(oldfile, "Hello"); - Deno.mkdirSync(olddir); - Deno.mkdirSync(emptydir); - Deno.mkdirSync(fulldir); - writeFileString(file, "world"); - - assertThrows( - (): void => { - Deno.renameSync(oldfile, emptydir); - }, - Deno.errors.PermissionDenied, - "Access is denied" - ); - assertThrows( - (): void => { - Deno.renameSync(olddir, fulldir); - }, - Deno.errors.PermissionDenied, - "Access is denied" - ); - assertThrows( - (): void => { - Deno.renameSync(olddir, emptydir); - }, - Deno.errors.PermissionDenied, - "Access is denied" - ); - - // should succeed on Windows - Deno.renameSync(olddir, file); - assertDirectory(file); - } -); diff --git a/cli/js/tests/request_test.ts b/cli/js/tests/request_test.ts deleted file mode 100644 index 8a276c5e7..000000000 --- a/cli/js/tests/request_test.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest(function fromInit(): void { - const req = new Request("https://example.com", { - body: "ahoyhoy", - method: "POST", - headers: { - "test-header": "value", - }, - }); - - // @ts-ignore - assertEquals("ahoyhoy", req._bodySource); - assertEquals(req.url, "https://example.com"); - assertEquals(req.headers.get("test-header"), "value"); -}); - -unitTest(function fromRequest(): void { - const r = new Request("https://example.com"); - // @ts-ignore - r._bodySource = "ahoyhoy"; - r.headers.set("test-header", "value"); - - const req = new Request(r); - - // @ts-ignore - assertEquals(req._bodySource, r._bodySource); - assertEquals(req.url, r.url); - assertEquals(req.headers.get("test-header"), r.headers.get("test-header")); -}); - -unitTest(async function cloneRequestBodyStream(): Promise { - // hack to get a stream - const stream = new Request("", { body: "a test body" }).body; - const r1 = new Request("https://example.com", { - body: stream, - }); - - const r2 = r1.clone(); - - const b1 = await r1.text(); - const b2 = await r2.text(); - - assertEquals(b1, b2); - - // @ts-ignore - assert(r1._bodySource !== r2._bodySource); -}); diff --git a/cli/js/tests/resources_test.ts b/cli/js/tests/resources_test.ts deleted file mode 100644 index 385905a6e..000000000 --- a/cli/js/tests/resources_test.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals, assert } from "./test_util.ts"; - -unitTest(function resourcesCloseBadArgs(): void { - let err; - try { - Deno.close((null as unknown) as number); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.InvalidData); -}); - -unitTest(function resourcesStdio(): void { - const res = Deno.resources(); - - assertEquals(res[0], "stdin"); - assertEquals(res[1], "stdout"); - assertEquals(res[2], "stderr"); -}); - -unitTest({ perms: { net: true } }, async function resourcesNet(): Promise< - void -> { - const listener = Deno.listen({ port: 4501 }); - const dialerConn = await Deno.connect({ port: 4501 }); - const listenerConn = await listener.accept(); - - const res = Deno.resources(); - assertEquals( - Object.values(res).filter((r): boolean => r === "tcpListener").length, - 1 - ); - const tcpStreams = Object.values(res).filter( - (r): boolean => r === "tcpStream" - ); - assert(tcpStreams.length >= 2); - - listenerConn.close(); - dialerConn.close(); - listener.close(); -}); - -unitTest({ perms: { read: true } }, async function resourcesFile(): Promise< - void -> { - const resourcesBefore = Deno.resources(); - const f = await Deno.open("cli/tests/hello.txt"); - const resourcesAfter = Deno.resources(); - f.close(); - - // check that exactly one new resource (file) was added - assertEquals( - Object.keys(resourcesAfter).length, - Object.keys(resourcesBefore).length + 1 - ); - const newRid = +Object.keys(resourcesAfter).find((rid): boolean => { - return !resourcesBefore.hasOwnProperty(rid); - })!; - assertEquals(resourcesAfter[newRid], "fsFile"); -}); diff --git a/cli/js/tests/signal_test.ts b/cli/js/tests/signal_test.ts deleted file mode 100644 index 2f117f8d1..000000000 --- a/cli/js/tests/signal_test.ts +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - unitTest, - assert, - assertEquals, - assertThrows, - createResolvable, -} from "./test_util.ts"; - -function defer(n: number): Promise { - return new Promise((resolve: () => void, _) => { - setTimeout(resolve, n); - }); -} - -unitTest( - { ignore: Deno.build.os !== "windows" }, - function signalsNotImplemented(): void { - assertThrows( - () => { - Deno.signal(1); - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.alarm(); // for SIGALRM - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.child(); // for SIGCHLD - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.hungup(); // for SIGHUP - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.interrupt(); // for SIGINT - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.io(); // for SIGIO - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.pipe(); // for SIGPIPE - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.quit(); // for SIGQUIT - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.terminate(); // for SIGTERM - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.userDefined1(); // for SIGUSR1 - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.userDefined2(); // for SIGURS2 - }, - Error, - "not implemented" - ); - assertThrows( - () => { - Deno.signals.windowChange(); // for SIGWINCH - }, - Error, - "not implemented" - ); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { run: true, net: true } }, - async function signalStreamTest(): Promise { - const resolvable = createResolvable(); - // This prevents the program from exiting. - const t = setInterval(() => {}, 1000); - - let c = 0; - const sig = Deno.signal(Deno.Signal.SIGUSR1); - setTimeout(async () => { - await defer(20); - for (const _ of Array(3)) { - // Sends SIGUSR1 3 times. - Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); - await defer(20); - } - sig.dispose(); - resolvable.resolve(); - }); - - for await (const _ of sig) { - c += 1; - } - - assertEquals(c, 3); - - clearInterval(t); - await resolvable; - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { run: true } }, - async function signalPromiseTest(): Promise { - const resolvable = createResolvable(); - // This prevents the program from exiting. - const t = setInterval(() => {}, 1000); - - const sig = Deno.signal(Deno.Signal.SIGUSR1); - setTimeout(() => { - Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); - resolvable.resolve(); - }, 20); - await sig; - sig.dispose(); - - clearInterval(t); - await resolvable; - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { run: true } }, - function signalShorthandsTest(): void { - let s: Deno.SignalStream; - s = Deno.signals.alarm(); // for SIGALRM - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.child(); // for SIGCHLD - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.hungup(); // for SIGHUP - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.interrupt(); // for SIGINT - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.io(); // for SIGIO - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.pipe(); // for SIGPIPE - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.quit(); // for SIGQUIT - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.terminate(); // for SIGTERM - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.userDefined1(); // for SIGUSR1 - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.userDefined2(); // for SIGURS2 - assert(s instanceof Deno.SignalStream); - s.dispose(); - s = Deno.signals.windowChange(); // for SIGWINCH - assert(s instanceof Deno.SignalStream); - s.dispose(); - } -); diff --git a/cli/js/tests/stat_test.ts b/cli/js/tests/stat_test.ts deleted file mode 100644 index 7eaf73d58..000000000 --- a/cli/js/tests/stat_test.ts +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest( - { perms: { read: true, write: true } }, - function statSyncSuccess(): void { - const packageInfo = Deno.statSync("README.md"); - assert(packageInfo.isFile); - assert(!packageInfo.isSymlink); - - const modulesInfo = Deno.statSync("cli/tests/symlink_to_subdir"); - assert(modulesInfo.isDirectory); - assert(!modulesInfo.isSymlink); - - const testsInfo = Deno.statSync("cli/tests"); - assert(testsInfo.isDirectory); - assert(!testsInfo.isSymlink); - - const tempFile = Deno.makeTempFileSync(); - const tempInfo = Deno.statSync(tempFile); - const now = Date.now(); - assert(tempInfo.atime !== null && now - tempInfo.atime.valueOf() < 1000); - assert(tempInfo.mtime !== null && now - tempInfo.mtime.valueOf() < 1000); - assert( - tempInfo.birthtime === null || now - tempInfo.birthtime.valueOf() < 1000 - ); - } -); - -unitTest({ perms: { read: false } }, function statSyncPerm(): void { - let caughtError = false; - try { - Deno.statSync("README.md"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, function statSyncNotFound(): void { - let caughtError = false; - let badInfo; - - try { - badInfo = Deno.statSync("bad_file_name"); - } catch (err) { - caughtError = true; - assert(err instanceof Deno.errors.NotFound); - } - - assert(caughtError); - assertEquals(badInfo, undefined); -}); - -unitTest({ perms: { read: true } }, function lstatSyncSuccess(): void { - const packageInfo = Deno.lstatSync("README.md"); - assert(packageInfo.isFile); - assert(!packageInfo.isSymlink); - - const modulesInfo = Deno.lstatSync("cli/tests/symlink_to_subdir"); - assert(!modulesInfo.isDirectory); - assert(modulesInfo.isSymlink); - - const coreInfo = Deno.lstatSync("core"); - assert(coreInfo.isDirectory); - assert(!coreInfo.isSymlink); -}); - -unitTest({ perms: { read: false } }, function lstatSyncPerm(): void { - let caughtError = false; - try { - Deno.lstatSync("README.md"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, function lstatSyncNotFound(): void { - let caughtError = false; - let badInfo; - - try { - badInfo = Deno.lstatSync("bad_file_name"); - } catch (err) { - caughtError = true; - assert(err instanceof Deno.errors.NotFound); - } - - assert(caughtError); - assertEquals(badInfo, undefined); -}); - -unitTest( - { perms: { read: true, write: true } }, - async function statSuccess(): Promise { - const packageInfo = await Deno.stat("README.md"); - assert(packageInfo.isFile); - assert(!packageInfo.isSymlink); - - const modulesInfo = await Deno.stat("cli/tests/symlink_to_subdir"); - assert(modulesInfo.isDirectory); - assert(!modulesInfo.isSymlink); - - const testsInfo = await Deno.stat("cli/tests"); - assert(testsInfo.isDirectory); - assert(!testsInfo.isSymlink); - - const tempFile = await Deno.makeTempFile(); - const tempInfo = await Deno.stat(tempFile); - const now = Date.now(); - assert(tempInfo.atime !== null && now - tempInfo.atime.valueOf() < 1000); - assert(tempInfo.mtime !== null && now - tempInfo.mtime.valueOf() < 1000); - - assert( - tempInfo.birthtime === null || now - tempInfo.birthtime.valueOf() < 1000 - ); - } -); - -unitTest({ perms: { read: false } }, async function statPerm(): Promise { - let caughtError = false; - try { - await Deno.stat("README.md"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, async function statNotFound(): Promise< - void -> { - let caughtError = false; - let badInfo; - - try { - badInfo = await Deno.stat("bad_file_name"); - } catch (err) { - caughtError = true; - assert(err instanceof Deno.errors.NotFound); - } - - assert(caughtError); - assertEquals(badInfo, undefined); -}); - -unitTest({ perms: { read: true } }, async function lstatSuccess(): Promise< - void -> { - const packageInfo = await Deno.lstat("README.md"); - assert(packageInfo.isFile); - assert(!packageInfo.isSymlink); - - const modulesInfo = await Deno.lstat("cli/tests/symlink_to_subdir"); - assert(!modulesInfo.isDirectory); - assert(modulesInfo.isSymlink); - - const coreInfo = await Deno.lstat("core"); - assert(coreInfo.isDirectory); - assert(!coreInfo.isSymlink); -}); - -unitTest({ perms: { read: false } }, async function lstatPerm(): Promise { - let caughtError = false; - try { - await Deno.lstat("README.md"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest({ perms: { read: true } }, async function lstatNotFound(): Promise< - void -> { - let caughtError = false; - let badInfo; - - try { - badInfo = await Deno.lstat("bad_file_name"); - } catch (err) { - caughtError = true; - assert(err instanceof Deno.errors.NotFound); - } - - assert(caughtError); - assertEquals(badInfo, undefined); -}); - -unitTest( - { ignore: Deno.build.os !== "windows", perms: { read: true, write: true } }, - function statNoUnixFields(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - const s = Deno.statSync(filename); - assert(s.dev === null); - assert(s.ino === null); - assert(s.mode === null); - assert(s.nlink === null); - assert(s.uid === null); - assert(s.gid === null); - assert(s.rdev === null); - assert(s.blksize === null); - assert(s.blocks === null); - } -); - -unitTest( - { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, - function statUnixFields(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "/test.txt"; - const filename2 = tempDir + "/test2.txt"; - Deno.writeFileSync(filename, data, { mode: 0o666 }); - // Create a link - Deno.linkSync(filename, filename2); - const s = Deno.statSync(filename); - assert(s.dev !== null); - assert(s.ino !== null); - assertEquals(s.mode! & 0o666, 0o666); - assertEquals(s.nlink, 2); - assert(s.uid !== null); - assert(s.gid !== null); - assert(s.rdev !== null); - assert(s.blksize !== null); - assert(s.blocks !== null); - } -); diff --git a/cli/js/tests/streams_piping_test.ts b/cli/js/tests/streams_piping_test.ts deleted file mode 100644 index a947b3821..000000000 --- a/cli/js/tests/streams_piping_test.ts +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; -import { assertThrowsAsync } from "../../../std/testing/asserts.ts"; - -unitTest(function streamPipeLocks() { - const rs = new ReadableStream(); - const ws = new WritableStream(); - - assertEquals(rs.locked, false); - assertEquals(ws.locked, false); - - rs.pipeTo(ws); - - assert(rs.locked); - assert(ws.locked); -}); - -unitTest(async function streamPipeFinishUnlocks() { - const rs = new ReadableStream({ - start(controller: ReadableStreamDefaultController): void { - controller.close(); - }, - }); - const ws = new WritableStream(); - - await rs.pipeTo(ws); - assertEquals(rs.locked, false); - assertEquals(ws.locked, false); -}); - -unitTest(async function streamPipeReadableStreamLocked() { - const rs = new ReadableStream(); - const ws = new WritableStream(); - - rs.getReader(); - - await assertThrowsAsync(async () => { - await rs.pipeTo(ws); - }, TypeError); -}); - -unitTest(async function streamPipeReadableStreamLocked() { - const rs = new ReadableStream(); - const ws = new WritableStream(); - - ws.getWriter(); - - await assertThrowsAsync(async () => { - await rs.pipeTo(ws); - }, TypeError); -}); - -unitTest(async function streamPipeLotsOfChunks() { - const CHUNKS = 10; - - const rs = new ReadableStream({ - start(c: ReadableStreamDefaultController): void { - for (let i = 0; i < CHUNKS; ++i) { - c.enqueue(i); - } - c.close(); - }, - }); - - const written: Array = []; - const ws = new WritableStream( - { - write(chunk: number): void { - written.push(chunk); - }, - close(): void { - written.push("closed"); - }, - }, - new CountQueuingStrategy({ highWaterMark: CHUNKS }) - ); - - await rs.pipeTo(ws); - const targetValues = []; - for (let i = 0; i < CHUNKS; ++i) { - targetValues.push(i); - } - targetValues.push("closed"); - - assertEquals(written, targetValues, "the correct values must be written"); - - // Ensure both readable and writable are closed by the time the pipe finishes. - await Promise.all([rs.getReader().closed, ws.getWriter().closed]); -}); - -for (const preventAbort of [true, false]) { - unitTest(function undefinedRejectionFromPull() { - const rs = new ReadableStream({ - pull(): Promise { - return Promise.reject(undefined); - }, - }); - - return rs.pipeTo(new WritableStream(), { preventAbort }).then( - () => { - throw new Error("pipeTo promise should be rejected"); - }, - (value) => - assertEquals(value, undefined, "rejection value should be undefined") - ); - }); -} - -for (const preventCancel of [true, false]) { - unitTest(function undefinedRejectionWithPreventCancel() { - const rs = new ReadableStream({ - pull(controller: ReadableStreamDefaultController): void { - controller.enqueue(0); - }, - }); - - const ws = new WritableStream({ - write(): Promise { - return Promise.reject(undefined); - }, - }); - - return rs.pipeTo(ws, { preventCancel }).then( - () => { - throw new Error("pipeTo promise should be rejected"); - }, - (value) => - assertEquals(value, undefined, "rejection value should be undefined") - ); - }); -} diff --git a/cli/js/tests/streams_transform_test.ts b/cli/js/tests/streams_transform_test.ts deleted file mode 100644 index f3ec148ae..000000000 --- a/cli/js/tests/streams_transform_test.ts +++ /dev/null @@ -1,562 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - unitTest, - assert, - assertEquals, - assertNotEquals, - assertThrows, -} from "./test_util.ts"; - -function delay(seconds: number): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, seconds); - }); -} - -function readableStreamToArray( - readable: { getReader(): ReadableStreamDefaultReader }, - reader?: ReadableStreamDefaultReader -): Promise { - if (reader === undefined) { - reader = readable.getReader(); - } - - const chunks: R[] = []; - - return pump(); - - function pump(): Promise { - return reader!.read().then((result) => { - if (result.done) { - return chunks; - } - - chunks.push(result.value); - return pump(); - }); - } -} - -unitTest(function transformStreamConstructedWithTransformFunction() { - new TransformStream({ transform(): void {} }); -}); - -unitTest(function transformStreamConstructedNoTransform() { - new TransformStream(); - new TransformStream({}); -}); - -unitTest(function transformStreamIntstancesHaveProperProperties() { - const ts = new TransformStream({ transform(): void {} }); - const proto = Object.getPrototypeOf(ts); - - const writableStream = Object.getOwnPropertyDescriptor(proto, "writable"); - assert(writableStream !== undefined, "it has a writable property"); - assert(!writableStream.enumerable, "writable should be non-enumerable"); - assertEquals( - typeof writableStream.get, - "function", - "writable should have a getter" - ); - assertEquals( - writableStream.set, - undefined, - "writable should not have a setter" - ); - assert(writableStream.configurable, "writable should be configurable"); - assert( - ts.writable instanceof WritableStream, - "writable is an instance of WritableStream" - ); - assert( - WritableStream.prototype.getWriter.call(ts.writable), - "writable should pass WritableStream brand check" - ); - - const readableStream = Object.getOwnPropertyDescriptor(proto, "readable"); - assert(readableStream !== undefined, "it has a readable property"); - assert(!readableStream.enumerable, "readable should be non-enumerable"); - assertEquals( - typeof readableStream.get, - "function", - "readable should have a getter" - ); - assertEquals( - readableStream.set, - undefined, - "readable should not have a setter" - ); - assert(readableStream.configurable, "readable should be configurable"); - assert( - ts.readable instanceof ReadableStream, - "readable is an instance of ReadableStream" - ); - assertNotEquals( - ReadableStream.prototype.getReader.call(ts.readable), - undefined, - "readable should pass ReadableStream brand check" - ); -}); - -unitTest(function transformStreamWritableStartsAsWritable() { - const ts = new TransformStream({ transform(): void {} }); - - const writer = ts.writable.getWriter(); - assertEquals(writer.desiredSize, 1, "writer.desiredSize should be 1"); -}); - -unitTest(async function transformStreamReadableCanReadOutOfWritable() { - const ts = new TransformStream(); - - const writer = ts.writable.getWriter(); - writer.write("a"); - assertEquals( - writer.desiredSize, - 0, - "writer.desiredSize should be 0 after write()" - ); - - const result = await ts.readable.getReader().read(); - assertEquals( - result.value, - "a", - "result from reading the readable is the same as was written to writable" - ); - assert(!result.done, "stream should not be done"); - - await delay(0); - assert(writer.desiredSize === 1, "desiredSize should be 1 again"); -}); - -unitTest(async function transformStreamCanReadWhatIsWritten() { - let c: TransformStreamDefaultController; - const ts = new TransformStream({ - start(controller: TransformStreamDefaultController): void { - c = controller; - }, - transform(chunk: string): void { - c.enqueue(chunk.toUpperCase()); - }, - }); - - const writer = ts.writable.getWriter(); - writer.write("a"); - - const result = await ts.readable.getReader().read(); - assertEquals( - result.value, - "A", - "result from reading the readable is the transformation of what was written to writable" - ); - assert(!result.done, "stream should not be done"); -}); - -unitTest(async function transformStreamCanReadBothChunks() { - let c: TransformStreamDefaultController; - const ts = new TransformStream({ - start(controller: TransformStreamDefaultController): void { - c = controller; - }, - transform(chunk: string): void { - c.enqueue(chunk.toUpperCase()); - c.enqueue(chunk.toUpperCase()); - }, - }); - - const writer = ts.writable.getWriter(); - writer.write("a"); - - const reader = ts.readable.getReader(); - - const result1 = await reader.read(); - assertEquals( - result1.value, - "A", - "the first chunk read is the transformation of the single chunk written" - ); - assert(!result1.done, "stream should not be done"); - - const result2 = await reader.read(); - assertEquals( - result2.value, - "A", - "the second chunk read is also the transformation of the single chunk written" - ); - assert(!result2.done, "stream should not be done"); -}); - -unitTest(async function transformStreamCanReadWhatIsWritten() { - let c: TransformStreamDefaultController; - const ts = new TransformStream({ - start(controller: TransformStreamDefaultController): void { - c = controller; - }, - transform(chunk: string): Promise { - return delay(0).then(() => c.enqueue(chunk.toUpperCase())); - }, - }); - - const writer = ts.writable.getWriter(); - writer.write("a"); - - const result = await ts.readable.getReader().read(); - assertEquals( - result.value, - "A", - "result from reading the readable is the transformation of what was written to writable" - ); - assert(!result.done, "stream should not be done"); -}); - -unitTest(async function transformStreamAsyncReadMultipleChunks() { - let doSecondEnqueue: () => void; - let returnFromTransform: () => void; - const ts = new TransformStream({ - transform( - chunk: string, - controller: TransformStreamDefaultController - ): Promise { - delay(0).then(() => controller.enqueue(chunk.toUpperCase())); - doSecondEnqueue = (): void => controller.enqueue(chunk.toUpperCase()); - return new Promise((resolve) => { - returnFromTransform = resolve; - }); - }, - }); - - const reader = ts.readable.getReader(); - - const writer = ts.writable.getWriter(); - writer.write("a"); - - const result1 = await reader.read(); - assertEquals( - result1.value, - "A", - "the first chunk read is the transformation of the single chunk written" - ); - assert(!result1.done, "stream should not be done"); - doSecondEnqueue!(); - - const result2 = await reader.read(); - assertEquals( - result2.value, - "A", - "the second chunk read is also the transformation of the single chunk written" - ); - assert(!result2.done, "stream should not be done"); - returnFromTransform!(); -}); - -unitTest(function transformStreamClosingWriteClosesRead() { - const ts = new TransformStream({ transform(): void {} }); - - const writer = ts.writable.getWriter(); - writer.close(); - - return Promise.all([writer.closed, ts.readable.getReader().closed]).then( - undefined - ); -}); - -unitTest(async function transformStreamCloseWaitAwaitsTransforms() { - let transformResolve: () => void; - const transformPromise = new Promise((resolve) => { - transformResolve = resolve; - }); - const ts = new TransformStream( - { - transform(): Promise { - return transformPromise; - }, - }, - undefined, - { highWaterMark: 1 } - ); - - const writer = ts.writable.getWriter(); - writer.write("a"); - writer.close(); - - let rsClosed = false; - ts.readable.getReader().closed.then(() => { - rsClosed = true; - }); - - await delay(0); - assertEquals(rsClosed, false, "readable is not closed after a tick"); - transformResolve!(); - - await writer.closed; - // TODO: Is this expectation correct? - assertEquals(rsClosed, true, "readable is closed at that point"); -}); - -unitTest(async function transformStreamCloseWriteAfterSyncEnqueues() { - let c: TransformStreamDefaultController; - const ts = new TransformStream({ - start(controller: TransformStreamDefaultController): void { - c = controller; - }, - transform(): Promise { - c.enqueue("x"); - c.enqueue("y"); - return delay(0); - }, - }); - - const writer = ts.writable.getWriter(); - writer.write("a"); - writer.close(); - - const readableChunks = readableStreamToArray(ts.readable); - - await writer.closed; - const chunks = await readableChunks; - assertEquals( - chunks, - ["x", "y"], - "both enqueued chunks can be read from the readable" - ); -}); - -unitTest(async function transformStreamWritableCloseAsyncAfterAsyncEnqueues() { - let c: TransformStreamDefaultController; - const ts = new TransformStream({ - start(controller: TransformStreamDefaultController): void { - c = controller; - }, - transform(): Promise { - return delay(0) - .then(() => c.enqueue("x")) - .then(() => c.enqueue("y")) - .then(() => delay(0)); - }, - }); - - const writer = ts.writable.getWriter(); - writer.write("a"); - writer.close(); - - const readableChunks = readableStreamToArray(ts.readable); - - await writer.closed; - const chunks = await readableChunks; - assertEquals( - chunks, - ["x", "y"], - "both enqueued chunks can be read from the readable" - ); -}); - -unitTest(async function transformStreamTransformerMethodsCalledAsMethods() { - let c: TransformStreamDefaultController; - const transformer = { - suffix: "-suffix", - - start(controller: TransformStreamDefaultController): void { - c = controller; - c.enqueue("start" + this.suffix); - }, - - transform(chunk: string): void { - c.enqueue(chunk + this.suffix); - }, - - flush(): void { - c.enqueue("flushed" + this.suffix); - }, - }; - const ts = new TransformStream(transformer); - - const writer = ts.writable.getWriter(); - writer.write("a"); - writer.close(); - - const readableChunks = readableStreamToArray(ts.readable); - - await writer.closed; - const chunks = await readableChunks; - assertEquals( - chunks, - ["start-suffix", "a-suffix", "flushed-suffix"], - "all enqueued chunks have suffixes" - ); -}); - -unitTest(async function transformStreamMethodsShouldNotBeAppliedOrCalled() { - function functionWithOverloads(): void {} - functionWithOverloads.apply = (): void => { - throw new Error("apply() should not be called"); - }; - functionWithOverloads.call = (): void => { - throw new Error("call() should not be called"); - }; - const ts = new TransformStream({ - start: functionWithOverloads, - transform: functionWithOverloads, - flush: functionWithOverloads, - }); - const writer = ts.writable.getWriter(); - writer.write("a"); - writer.close(); - - await readableStreamToArray(ts.readable); -}); - -unitTest(async function transformStreamCallTransformSync() { - let transformCalled = false; - const ts = new TransformStream( - { - transform(): void { - transformCalled = true; - }, - }, - undefined, - { highWaterMark: Infinity } - ); - // transform() is only called synchronously when there is no backpressure and - // all microtasks have run. - await delay(0); - const writePromise = ts.writable.getWriter().write(undefined); - assert(transformCalled, "transform() should have been called"); - await writePromise; -}); - -unitTest(function transformStreamCloseWriteCloesesReadWithNoChunks() { - const ts = new TransformStream({}, undefined, { highWaterMark: 0 }); - - const writer = ts.writable.getWriter(); - writer.close(); - - return Promise.all([writer.closed, ts.readable.getReader().closed]).then( - undefined - ); -}); - -unitTest(function transformStreamEnqueueThrowsAfterTerminate() { - new TransformStream({ - start(controller: TransformStreamDefaultController): void { - controller.terminate(); - assertThrows(() => { - controller.enqueue(undefined); - }, TypeError); - }, - }); -}); - -unitTest(function transformStreamEnqueueThrowsAfterReadableCancel() { - let controller: TransformStreamDefaultController; - const ts = new TransformStream({ - start(c: TransformStreamDefaultController): void { - controller = c; - }, - }); - const cancelPromise = ts.readable.cancel(); - assertThrows( - () => controller.enqueue(undefined), - TypeError, - undefined, - "enqueue should throw" - ); - return cancelPromise; -}); - -unitTest(function transformStreamSecondTerminateNoOp() { - new TransformStream({ - start(controller: TransformStreamDefaultController): void { - controller.terminate(); - controller.terminate(); - }, - }); -}); - -unitTest(async function transformStreamTerminateAfterReadableCancelIsNoop() { - let controller: TransformStreamDefaultController; - const ts = new TransformStream({ - start(c: TransformStreamDefaultController): void { - controller = c; - }, - }); - const cancelReason = { name: "cancelReason" }; - const cancelPromise = ts.readable.cancel(cancelReason); - controller!.terminate(); - await cancelPromise; - try { - await ts.writable.getWriter().closed; - } catch (e) { - assert(e === cancelReason); - return; - } - throw new Error("closed should have rejected"); -}); - -unitTest(async function transformStreamStartCalledOnce() { - let calls = 0; - new TransformStream({ - start(): void { - ++calls; - }, - }); - await delay(0); - assertEquals(calls, 1, "start() should have been called exactly once"); -}); - -unitTest(function transformStreamReadableTypeThrows() { - assertThrows( - // eslint-disable-next-line - () => new TransformStream({ readableType: "bytes" as any }), - RangeError, - undefined, - "constructor should throw" - ); -}); - -unitTest(function transformStreamWirtableTypeThrows() { - assertThrows( - // eslint-disable-next-line - () => new TransformStream({ writableType: "bytes" as any }), - RangeError, - undefined, - "constructor should throw" - ); -}); - -unitTest(function transformStreamSubclassable() { - class Subclass extends TransformStream { - extraFunction(): boolean { - return true; - } - } - assert( - Object.getPrototypeOf(Subclass.prototype) === TransformStream.prototype, - "Subclass.prototype's prototype should be TransformStream.prototype" - ); - assert( - Object.getPrototypeOf(Subclass) === TransformStream, - "Subclass's prototype should be TransformStream" - ); - const sub = new Subclass(); - assert( - sub instanceof TransformStream, - "Subclass object should be an instance of TransformStream" - ); - assert( - sub instanceof Subclass, - "Subclass object should be an instance of Subclass" - ); - const readableGetter = Object.getOwnPropertyDescriptor( - TransformStream.prototype, - "readable" - )!.get; - assert( - readableGetter!.call(sub) === sub.readable, - "Subclass object should pass brand check" - ); - assert( - sub.extraFunction(), - "extraFunction() should be present on Subclass object" - ); -}); diff --git a/cli/js/tests/streams_writable_test.ts b/cli/js/tests/streams_writable_test.ts deleted file mode 100644 index 54c1624af..000000000 --- a/cli/js/tests/streams_writable_test.ts +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals, assertThrows } from "./test_util.ts"; - -unitTest(function writableStreamDesiredSizeOnReleasedWriter() { - const ws = new WritableStream(); - const writer = ws.getWriter(); - writer.releaseLock(); - assertThrows(() => { - writer.desiredSize; - }, TypeError); -}); - -unitTest(function writableStreamDesiredSizeInitialValue() { - const ws = new WritableStream(); - const writer = ws.getWriter(); - assertEquals(writer.desiredSize, 1); -}); - -unitTest(async function writableStreamDesiredSizeClosed() { - const ws = new WritableStream(); - const writer = ws.getWriter(); - await writer.close(); - assertEquals(writer.desiredSize, 0); -}); - -unitTest(function writableStreamStartThrowsDesiredSizeNull() { - const ws = new WritableStream({ - start(c): void { - c.error(); - }, - }); - - const writer = ws.getWriter(); - assertEquals(writer.desiredSize, null, "desiredSize should be null"); -}); - -unitTest(function getWriterOnClosingStream() { - const ws = new WritableStream({}); - - const writer = ws.getWriter(); - writer.close(); - writer.releaseLock(); - - ws.getWriter(); -}); - -unitTest(async function getWriterOnClosedStream() { - const ws = new WritableStream({}); - - const writer = ws.getWriter(); - await writer.close(); - writer.releaseLock(); - - ws.getWriter(); -}); - -unitTest(function getWriterOnAbortedStream() { - const ws = new WritableStream({}); - - const writer = ws.getWriter(); - writer.abort(); - writer.releaseLock(); - - ws.getWriter(); -}); - -unitTest(function getWriterOnErroredStream() { - const ws = new WritableStream({ - start(c): void { - c.error(); - }, - }); - - const writer = ws.getWriter(); - return writer.closed.then( - (v) => { - throw new Error(`writer.closed fulfilled unexpectedly with: ${v}`); - }, - () => { - writer.releaseLock(); - ws.getWriter(); - } - ); -}); - -unitTest(function closedAndReadyOnReleasedWriter() { - const ws = new WritableStream({}); - - const writer = ws.getWriter(); - writer.releaseLock(); - - return writer.closed.then( - (v) => { - throw new Error("writer.closed fulfilled unexpectedly with: " + v); - }, - (closedRejection) => { - assertEquals( - closedRejection.name, - "TypeError", - "closed promise should reject with a TypeError" - ); - return writer.ready.then( - (v) => { - throw new Error("writer.ready fulfilled unexpectedly with: " + v); - }, - (readyRejection) => - assertEquals( - readyRejection, - closedRejection, - "ready promise should reject with the same error" - ) - ); - } - ); -}); - -unitTest(function sinkMethodsCalledAsMethods() { - let thisObject: Sink | null = null; - // Calls to Sink methods after the first are implicitly ignored. Only the - // first value that is passed to the resolver is used. - class Sink { - start(): void { - assertEquals(this, thisObject, "start should be called as a method"); - } - - write(): void { - assertEquals(this, thisObject, "write should be called as a method"); - } - - close(): void { - assertEquals(this, thisObject, "close should be called as a method"); - } - - abort(): void { - assertEquals(this, thisObject, "abort should be called as a method"); - } - } - - const theSink = new Sink(); - thisObject = theSink; - const ws = new WritableStream(theSink); - - const writer = ws.getWriter(); - - writer.write("a"); - const closePromise = writer.close(); - - const ws2 = new WritableStream(theSink); - const writer2 = ws2.getWriter(); - const abortPromise = writer2.abort(); - - return Promise.all([closePromise, abortPromise]).then(undefined); -}); - -unitTest(function sizeShouldNotBeCalledAsMethod() { - const strategy = { - size(): number { - if (this !== undefined) { - throw new Error("size called as a method"); - } - return 1; - }, - }; - - const ws = new WritableStream({}, strategy); - const writer = ws.getWriter(); - return writer.write("a"); -}); - -unitTest(function redundantReleaseLockIsNoOp() { - const ws = new WritableStream(); - const writer1 = ws.getWriter(); - assertEquals( - undefined, - writer1.releaseLock(), - "releaseLock() should return undefined" - ); - const writer2 = ws.getWriter(); - assertEquals( - undefined, - writer1.releaseLock(), - "no-op releaseLock() should return undefined" - ); - // Calling releaseLock() on writer1 should not interfere with writer2. If it did, then the ready promise would be - // rejected. - return writer2.ready; -}); - -unitTest(function readyPromiseShouldFireBeforeReleaseLock() { - const events: string[] = []; - const ws = new WritableStream(); - const writer = ws.getWriter(); - return writer.ready.then(() => { - // Force the ready promise back to a pending state. - const writerPromise = writer.write("dummy"); - const readyPromise = writer.ready.catch(() => events.push("ready")); - const closedPromise = writer.closed.catch(() => events.push("closed")); - writer.releaseLock(); - return Promise.all([readyPromise, closedPromise]).then(() => { - assertEquals( - events, - ["ready", "closed"], - "ready promise should fire before closed promise" - ); - // Stop the writer promise hanging around after the test has finished. - return Promise.all([writerPromise, ws.abort()]).then(undefined); - }); - }); -}); - -unitTest(function subclassingWritableStream() { - class Subclass extends WritableStream { - extraFunction(): boolean { - return true; - } - } - assert( - Object.getPrototypeOf(Subclass.prototype) === WritableStream.prototype, - "Subclass.prototype's prototype should be WritableStream.prototype" - ); - assert( - Object.getPrototypeOf(Subclass) === WritableStream, - "Subclass's prototype should be WritableStream" - ); - const sub = new Subclass(); - assert( - sub instanceof WritableStream, - "Subclass object should be an instance of WritableStream" - ); - assert( - sub instanceof Subclass, - "Subclass object should be an instance of Subclass" - ); - const lockedGetter = Object.getOwnPropertyDescriptor( - WritableStream.prototype, - "locked" - )!.get!; - assert( - lockedGetter.call(sub) === sub.locked, - "Subclass object should pass brand check" - ); - assert( - sub.extraFunction(), - "extraFunction() should be present on Subclass object" - ); -}); - -unitTest(function lockedGetterShouldReturnTrue() { - const ws = new WritableStream(); - assert(!ws.locked, "stream should not be locked"); - ws.getWriter(); - assert(ws.locked, "stream should be locked"); -}); diff --git a/cli/js/tests/symlink_test.ts b/cli/js/tests/symlink_test.ts deleted file mode 100644 index 505a49bab..000000000 --- a/cli/js/tests/symlink_test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest( - { perms: { read: true, write: true } }, - function symlinkSyncSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const oldname = testDir + "/oldname"; - const newname = testDir + "/newname"; - Deno.mkdirSync(oldname); - // Just for now, until we implement symlink for Windows. - Deno.symlinkSync(oldname, newname); - const newNameInfoLStat = Deno.lstatSync(newname); - const newNameInfoStat = Deno.statSync(newname); - assert(newNameInfoLStat.isSymlink); - assert(newNameInfoStat.isDirectory); - } -); - -unitTest(function symlinkSyncPerm(): void { - let err; - try { - Deno.symlinkSync("oldbaddir", "newbaddir"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest( - { perms: { read: true, write: true } }, - async function symlinkSuccess(): Promise { - const testDir = Deno.makeTempDirSync(); - const oldname = testDir + "/oldname"; - const newname = testDir + "/newname"; - Deno.mkdirSync(oldname); - await Deno.symlink(oldname, newname); - const newNameInfoLStat = Deno.lstatSync(newname); - const newNameInfoStat = Deno.statSync(newname); - assert(newNameInfoLStat.isSymlink, "NOT SYMLINK"); - assert(newNameInfoStat.isDirectory, "NOT DIRECTORY"); - } -); diff --git a/cli/js/tests/test_util.ts b/cli/js/tests/test_util.ts deleted file mode 100644 index 24186db71..000000000 --- a/cli/js/tests/test_util.ts +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -import { assert, assertEquals } from "../../../std/testing/asserts.ts"; -export { - assert, - assertThrows, - assertEquals, - assertMatch, - assertNotEquals, - assertStrictEq, - assertStrContains, - unreachable, - fail, -} from "../../../std/testing/asserts.ts"; -export { readLines } from "../../../std/io/bufio.ts"; -export { parse as parseArgs } from "../../../std/flags/mod.ts"; - -export interface Permissions { - read: boolean; - write: boolean; - net: boolean; - env: boolean; - run: boolean; - plugin: boolean; - hrtime: boolean; -} - -export function fmtPerms(perms: Permissions): string { - const p = Object.keys(perms) - .filter((e): boolean => perms[e as keyof Permissions] === true) - .map((key) => `--allow-${key}`); - - if (p.length) { - return p.join(" "); - } - - return ""; -} - -const isGranted = async (name: Deno.PermissionName): Promise => - (await Deno.permissions.query({ name })).state === "granted"; - -export async function getProcessPermissions(): Promise { - return { - run: await isGranted("run"), - read: await isGranted("read"), - write: await isGranted("write"), - net: await isGranted("net"), - env: await isGranted("env"), - plugin: await isGranted("plugin"), - hrtime: await isGranted("hrtime"), - }; -} - -export function permissionsMatch( - processPerms: Permissions, - requiredPerms: Permissions -): boolean { - for (const permName in processPerms) { - if ( - processPerms[permName as keyof Permissions] !== - requiredPerms[permName as keyof Permissions] - ) { - return false; - } - } - - return true; -} - -export const permissionCombinations: Map = new Map(); - -function permToString(perms: Permissions): string { - const r = perms.read ? 1 : 0; - const w = perms.write ? 1 : 0; - const n = perms.net ? 1 : 0; - const e = perms.env ? 1 : 0; - const u = perms.run ? 1 : 0; - const p = perms.plugin ? 1 : 0; - const h = perms.hrtime ? 1 : 0; - return `permR${r}W${w}N${n}E${e}U${u}P${p}H${h}`; -} - -function registerPermCombination(perms: Permissions): void { - const key = permToString(perms); - if (!permissionCombinations.has(key)) { - permissionCombinations.set(key, perms); - } -} - -export async function registerUnitTests(): Promise { - const processPerms = await getProcessPermissions(); - - for (const unitTestDefinition of REGISTERED_UNIT_TESTS) { - if (!permissionsMatch(processPerms, unitTestDefinition.perms)) { - continue; - } - - Deno.test(unitTestDefinition); - } -} - -function normalizeTestPermissions(perms: UnitTestPermissions): Permissions { - return { - read: !!perms.read, - write: !!perms.write, - net: !!perms.net, - run: !!perms.run, - env: !!perms.env, - plugin: !!perms.plugin, - hrtime: !!perms.hrtime, - }; -} - -interface UnitTestPermissions { - read?: boolean; - write?: boolean; - net?: boolean; - env?: boolean; - run?: boolean; - plugin?: boolean; - hrtime?: boolean; -} - -interface UnitTestOptions { - ignore?: boolean; - perms?: UnitTestPermissions; -} - -interface UnitTestDefinition extends Deno.TestDefinition { - ignore: boolean; - perms: Permissions; -} - -type TestFunction = () => void | Promise; - -export const REGISTERED_UNIT_TESTS: UnitTestDefinition[] = []; - -export function unitTest(fn: TestFunction): void; -export function unitTest(options: UnitTestOptions, fn: TestFunction): void; -export function unitTest( - optionsOrFn: UnitTestOptions | TestFunction, - maybeFn?: TestFunction -): void { - assert(optionsOrFn, "At least one argument is required"); - - let options: UnitTestOptions; - let name: string; - let fn: TestFunction; - - if (typeof optionsOrFn === "function") { - options = {}; - fn = optionsOrFn; - name = fn.name; - assert(name, "Missing test function name"); - } else { - options = optionsOrFn; - assert(maybeFn, "Missing test function definition"); - assert( - typeof maybeFn === "function", - "Second argument should be test function definition" - ); - fn = maybeFn; - name = fn.name; - assert(name, "Missing test function name"); - } - - const normalizedPerms = normalizeTestPermissions(options.perms || {}); - registerPermCombination(normalizedPerms); - - const unitTestDefinition: UnitTestDefinition = { - name, - fn, - ignore: !!options.ignore, - perms: normalizedPerms, - }; - - REGISTERED_UNIT_TESTS.push(unitTestDefinition); -} - -export interface ResolvableMethods { - resolve: (value?: T | PromiseLike) => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reject: (reason?: any) => void; -} - -export type Resolvable = Promise & ResolvableMethods; - -export function createResolvable(): Resolvable { - let methods: ResolvableMethods; - const promise = new Promise((resolve, reject): void => { - methods = { resolve, reject }; - }); - // TypeScript doesn't know that the Promise callback occurs synchronously - // therefore use of not null assertion (`!`) - return Object.assign(promise, methods!) as Resolvable; -} - -const encoder = new TextEncoder(); - -// Replace functions with null, errors with their stack strings, and JSONify. -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function serializeTestMessage(message: any): string { - return JSON.stringify({ - start: message.start && { - ...message.start, - tests: message.start.tests.map((test: Deno.TestDefinition) => ({ - ...test, - fn: null, - })), - }, - testStart: message.testStart && { ...message.testStart, fn: null }, - testEnd: message.testEnd && { - ...message.testEnd, - error: String(message.testEnd.error?.stack), - }, - end: message.end && { - ...message.end, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - results: message.end.results.map((result: any) => ({ - ...result, - error: result.error?.stack, - })), - }, - }); -} - -export async function reportToConn( - conn: Deno.Conn, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - message: any -): Promise { - const line = serializeTestMessage(message); - const encodedMsg = encoder.encode(line + (message.end == null ? "\n" : "")); - await Deno.writeAll(conn, encodedMsg); - if (message.end != null) { - conn.closeWrite(); - } -} - -unitTest(function permissionsMatches(): void { - assert( - permissionsMatch( - { - read: true, - write: false, - net: false, - env: false, - run: false, - plugin: false, - hrtime: false, - }, - normalizeTestPermissions({ read: true }) - ) - ); - - assert( - permissionsMatch( - { - read: false, - write: false, - net: false, - env: false, - run: false, - plugin: false, - hrtime: false, - }, - normalizeTestPermissions({}) - ) - ); - - assertEquals( - permissionsMatch( - { - read: false, - write: true, - net: true, - env: true, - run: true, - plugin: true, - hrtime: true, - }, - normalizeTestPermissions({ read: true }) - ), - false - ); - - assertEquals( - permissionsMatch( - { - read: true, - write: false, - net: true, - env: false, - run: false, - plugin: false, - hrtime: false, - }, - normalizeTestPermissions({ read: true }) - ), - false - ); - - assert( - permissionsMatch( - { - read: true, - write: true, - net: true, - env: true, - run: true, - plugin: true, - hrtime: true, - }, - { - read: true, - write: true, - net: true, - env: true, - run: true, - plugin: true, - hrtime: true, - } - ) - ); -}); - -/* - * Ensure all unit test files (e.g. xxx_test.ts) are present as imports in - * cli/js/tests/unit_tests.ts as it is easy to miss this out - */ -unitTest( - { perms: { read: true } }, - function assertAllUnitTestFilesImported(): void { - const directoryTestFiles = [...Deno.readDirSync("./cli/js/tests/")] - .map((k) => k.name) - .filter( - (file) => - file!.endsWith(".ts") && - !file!.endsWith("unit_tests.ts") && - !file!.endsWith("test_util.ts") && - !file!.endsWith("unit_test_runner.ts") - ); - const unitTestsFile: Uint8Array = Deno.readFileSync( - "./cli/js/tests/unit_tests.ts" - ); - const importLines = new TextDecoder("utf-8") - .decode(unitTestsFile) - .split("\n") - .filter((line) => line.startsWith("import")); - const importedTestFiles = importLines.map( - (relativeFilePath) => relativeFilePath.match(/\/([^\/]+)";/)![1] - ); - - directoryTestFiles.forEach((dirFile) => { - if (!importedTestFiles.includes(dirFile!)) { - throw new Error( - "cil/js/tests/unit_tests.ts is missing import of test file: cli/js/" + - dirFile - ); - } - }); - } -); diff --git a/cli/js/tests/testing_test.ts b/cli/js/tests/testing_test.ts deleted file mode 100644 index 854e7161c..000000000 --- a/cli/js/tests/testing_test.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { assertThrows, unitTest } from "./test_util.ts"; - -unitTest(function testFnOverloading(): void { - // just verifying that you can use this test definition syntax - Deno.test("test fn overloading", (): void => {}); -}); - -unitTest(function nameOfTestCaseCantBeEmpty(): void { - assertThrows( - () => { - Deno.test("", () => {}); - }, - TypeError, - "The test name can't be empty" - ); - assertThrows( - () => { - Deno.test({ - name: "", - fn: () => {}, - }); - }, - TypeError, - "The test name can't be empty" - ); -}); diff --git a/cli/js/tests/text_encoding_test.ts b/cli/js/tests/text_encoding_test.ts deleted file mode 100644 index 87b601f36..000000000 --- a/cli/js/tests/text_encoding_test.ts +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest(function btoaSuccess(): void { - const text = "hello world"; - const encoded = btoa(text); - assertEquals(encoded, "aGVsbG8gd29ybGQ="); -}); - -unitTest(function atobSuccess(): void { - const encoded = "aGVsbG8gd29ybGQ="; - const decoded = atob(encoded); - assertEquals(decoded, "hello world"); -}); - -unitTest(function atobWithAsciiWhitespace(): void { - const encodedList = [ - " aGVsbG8gd29ybGQ=", - " aGVsbG8gd29ybGQ=", - "aGVsbG8gd29ybGQ= ", - "aGVsbG8gd29ybGQ=\n", - "aGVsbG\t8gd29ybGQ=", - `aGVsbG\t8g - d29ybGQ=`, - ]; - - for (const encoded of encodedList) { - const decoded = atob(encoded); - assertEquals(decoded, "hello world"); - } -}); - -unitTest(function atobThrows(): void { - let threw = false; - try { - atob("aGVsbG8gd29ybGQ=="); - } catch (e) { - threw = true; - } - assert(threw); -}); - -unitTest(function atobThrows2(): void { - let threw = false; - try { - atob("aGVsbG8gd29ybGQ==="); - } catch (e) { - threw = true; - } - assert(threw); -}); - -unitTest(function btoaFailed(): void { - const text = "你好"; - let err; - try { - btoa(text); - } catch (e) { - err = e; - } - assert(!!err); - assert(err instanceof TypeError); -}); - -unitTest(function textDecoder2(): void { - // prettier-ignore - const fixture = new Uint8Array([ - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd - ]); - const decoder = new TextDecoder(); - assertEquals(decoder.decode(fixture), "𝓽𝓮𝔁𝓽"); -}); - -unitTest(function textDecoderIgnoreBOM(): void { - // prettier-ignore - const fixture = new Uint8Array([ - 0xef, 0xbb, 0xbf, - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd - ]); - const decoder = new TextDecoder("utf-8", { ignoreBOM: true }); - assertEquals(decoder.decode(fixture), "𝓽𝓮𝔁𝓽"); -}); - -unitTest(function textDecoderNotBOM(): void { - // prettier-ignore - const fixture = new Uint8Array([ - 0xef, 0xbb, 0x89, - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd - ]); - const decoder = new TextDecoder("utf-8", { ignoreBOM: true }); - assertEquals(decoder.decode(fixture), "ﻉ𝓽𝓮𝔁𝓽"); -}); - -unitTest(function textDecoderASCII(): void { - const fixture = new Uint8Array([0x89, 0x95, 0x9f, 0xbf]); - const decoder = new TextDecoder("ascii"); - assertEquals(decoder.decode(fixture), "‰•Ÿ¿"); -}); - -unitTest(function textDecoderErrorEncoding(): void { - let didThrow = false; - try { - new TextDecoder("foo"); - } catch (e) { - didThrow = true; - assertEquals(e.message, "The encoding label provided ('foo') is invalid."); - } - assert(didThrow); -}); - -unitTest(function textEncoder(): void { - const fixture = "𝓽𝓮𝔁𝓽"; - const encoder = new TextEncoder(); - // prettier-ignore - assertEquals(Array.from(encoder.encode(fixture)), [ - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd - ]); -}); - -unitTest(function textEncodeInto(): void { - const fixture = "text"; - const encoder = new TextEncoder(); - const bytes = new Uint8Array(5); - const result = encoder.encodeInto(fixture, bytes); - assertEquals(result.read, 4); - assertEquals(result.written, 4); - // prettier-ignore - assertEquals(Array.from(bytes), [ - 0x74, 0x65, 0x78, 0x74, 0x00, - ]); -}); - -unitTest(function textEncodeInto2(): void { - const fixture = "𝓽𝓮𝔁𝓽"; - const encoder = new TextEncoder(); - const bytes = new Uint8Array(17); - const result = encoder.encodeInto(fixture, bytes); - assertEquals(result.read, 8); - assertEquals(result.written, 16); - // prettier-ignore - assertEquals(Array.from(bytes), [ - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd, 0x00, - ]); -}); - -unitTest(function textEncodeInto3(): void { - const fixture = "𝓽𝓮𝔁𝓽"; - const encoder = new TextEncoder(); - const bytes = new Uint8Array(5); - const result = encoder.encodeInto(fixture, bytes); - assertEquals(result.read, 2); - assertEquals(result.written, 4); - // prettier-ignore - assertEquals(Array.from(bytes), [ - 0xf0, 0x9d, 0x93, 0xbd, 0x00, - ]); -}); - -unitTest(function textDecoderSharedUint8Array(): void { - const ab = new SharedArrayBuffer(6); - const dataView = new DataView(ab); - const charCodeA = "A".charCodeAt(0); - for (let i = 0; i < ab.byteLength; i++) { - dataView.setUint8(i, charCodeA + i); - } - const ui8 = new Uint8Array(ab); - const decoder = new TextDecoder(); - const actual = decoder.decode(ui8); - assertEquals(actual, "ABCDEF"); -}); - -unitTest(function textDecoderSharedInt32Array(): void { - const ab = new SharedArrayBuffer(8); - const dataView = new DataView(ab); - const charCodeA = "A".charCodeAt(0); - for (let i = 0; i < ab.byteLength; i++) { - dataView.setUint8(i, charCodeA + i); - } - const i32 = new Int32Array(ab); - const decoder = new TextDecoder(); - const actual = decoder.decode(i32); - assertEquals(actual, "ABCDEFGH"); -}); - -unitTest(function toStringShouldBeWebCompatibility(): void { - const encoder = new TextEncoder(); - assertEquals(encoder.toString(), "[object TextEncoder]"); - - const decoder = new TextDecoder(); - assertEquals(decoder.toString(), "[object TextDecoder]"); -}); diff --git a/cli/js/tests/timers_test.ts b/cli/js/tests/timers_test.ts deleted file mode 100644 index 7adff0095..000000000 --- a/cli/js/tests/timers_test.ts +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - unitTest, - createResolvable, - assert, - assertEquals, - assertNotEquals, -} from "./test_util.ts"; - -function deferred(): { - promise: Promise<{}>; - resolve: (value?: {} | PromiseLike<{}>) => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reject: (reason?: any) => void; -} { - let resolve: (value?: {} | PromiseLike<{}>) => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let reject: ((reason?: any) => void) | undefined = undefined; - const promise = new Promise<{}>((res, rej): void => { - resolve = res; - reject = rej; - }); - return { - promise, - resolve: resolve!, - reject: reject!, - }; -} - -function waitForMs(ms: number): Promise { - return new Promise((resolve: () => void): number => setTimeout(resolve, ms)); -} - -unitTest(async function timeoutSuccess(): Promise { - const { promise, resolve } = deferred(); - let count = 0; - setTimeout((): void => { - count++; - resolve(); - }, 500); - await promise; - // count should increment - assertEquals(count, 1); -}); - -unitTest(async function timeoutArgs(): Promise { - const { promise, resolve } = deferred(); - const arg = 1; - setTimeout( - (a, b, c): void => { - assertEquals(a, arg); - assertEquals(b, arg.toString()); - assertEquals(c, [arg]); - resolve(); - }, - 10, - arg, - arg.toString(), - [arg] - ); - await promise; -}); - -unitTest(async function timeoutCancelSuccess(): Promise { - let count = 0; - const id = setTimeout((): void => { - count++; - }, 1); - // Cancelled, count should not increment - clearTimeout(id); - await waitForMs(600); - assertEquals(count, 0); -}); - -unitTest(async function timeoutCancelMultiple(): Promise { - function uncalled(): never { - throw new Error("This function should not be called."); - } - - // Set timers and cancel them in the same order. - const t1 = setTimeout(uncalled, 10); - const t2 = setTimeout(uncalled, 10); - const t3 = setTimeout(uncalled, 10); - clearTimeout(t1); - clearTimeout(t2); - clearTimeout(t3); - - // Set timers and cancel them in reverse order. - const t4 = setTimeout(uncalled, 20); - const t5 = setTimeout(uncalled, 20); - const t6 = setTimeout(uncalled, 20); - clearTimeout(t6); - clearTimeout(t5); - clearTimeout(t4); - - // Sleep until we're certain that the cancelled timers aren't gonna fire. - await waitForMs(50); -}); - -unitTest(async function timeoutCancelInvalidSilentFail(): Promise { - // Expect no panic - const { promise, resolve } = deferred(); - let count = 0; - const id = setTimeout((): void => { - count++; - // Should have no effect - clearTimeout(id); - resolve(); - }, 500); - await promise; - assertEquals(count, 1); - - // Should silently fail (no panic) - clearTimeout(2147483647); -}); - -unitTest(async function intervalSuccess(): Promise { - const { promise, resolve } = deferred(); - let count = 0; - const id = setInterval((): void => { - count++; - clearInterval(id); - resolve(); - }, 100); - await promise; - // Clear interval - clearInterval(id); - // count should increment twice - assertEquals(count, 1); - // Similar false async leaking alarm. - // Force next round of polling. - await waitForMs(0); -}); - -unitTest(async function intervalCancelSuccess(): Promise { - let count = 0; - const id = setInterval((): void => { - count++; - }, 1); - clearInterval(id); - await waitForMs(500); - assertEquals(count, 0); -}); - -unitTest(async function intervalOrdering(): Promise { - const timers: number[] = []; - let timeouts = 0; - function onTimeout(): void { - ++timeouts; - for (let i = 1; i < timers.length; i++) { - clearTimeout(timers[i]); - } - } - for (let i = 0; i < 10; i++) { - timers[i] = setTimeout(onTimeout, 1); - } - await waitForMs(500); - assertEquals(timeouts, 1); -}); - -unitTest(function intervalCancelInvalidSilentFail(): void { - // Should silently fail (no panic) - clearInterval(2147483647); -}); - -unitTest(async function fireCallbackImmediatelyWhenDelayOverMaxValue(): Promise< - void -> { - let count = 0; - setTimeout((): void => { - count++; - }, 2 ** 31); - await waitForMs(1); - assertEquals(count, 1); -}); - -unitTest(async function timeoutCallbackThis(): Promise { - const { promise, resolve } = deferred(); - const obj = { - foo(): void { - assertEquals(this, window); - resolve(); - }, - }; - setTimeout(obj.foo, 1); - await promise; -}); - -unitTest(async function timeoutBindThis(): Promise { - const thisCheckPassed = [null, undefined, window, globalThis]; - - const thisCheckFailed = [ - 0, - "", - true, - false, - {}, - [], - "foo", - (): void => {}, - Object.prototype, - ]; - - for (const thisArg of thisCheckPassed) { - const resolvable = createResolvable(); - let hasThrown = 0; - try { - setTimeout.call(thisArg, () => resolvable.resolve(), 1); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - await resolvable; - assertEquals(hasThrown, 1); - } - - for (const thisArg of thisCheckFailed) { - let hasThrown = 0; - try { - setTimeout.call(thisArg, () => {}, 1); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - } -}); - -unitTest(function clearTimeoutShouldConvertToNumber(): void { - let called = false; - const obj = { - valueOf(): number { - called = true; - return 1; - }, - }; - clearTimeout((obj as unknown) as number); - assert(called); -}); - -unitTest(function setTimeoutShouldThrowWithBigint(): void { - let hasThrown = 0; - try { - setTimeout((): void => {}, (1n as unknown) as number); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); -}); - -unitTest(function clearTimeoutShouldThrowWithBigint(): void { - let hasThrown = 0; - try { - clearTimeout((1n as unknown) as number); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); -}); - -unitTest(function testFunctionName(): void { - assertEquals(clearTimeout.name, "clearTimeout"); - assertEquals(clearInterval.name, "clearInterval"); -}); - -unitTest(function testFunctionParamsLength(): void { - assertEquals(setTimeout.length, 1); - assertEquals(setInterval.length, 1); - assertEquals(clearTimeout.length, 0); - assertEquals(clearInterval.length, 0); -}); - -unitTest(function clearTimeoutAndClearIntervalNotBeEquals(): void { - assertNotEquals(clearTimeout, clearInterval); -}); - -unitTest(async function timerMaxCpuBug(): Promise { - // There was a bug where clearing a timeout would cause Deno to use 100% CPU. - clearTimeout(setTimeout(() => {}, 1000)); - // We can check this by counting how many ops have triggered in the interim. - // Certainly less than 10 ops should have been dispatched in next 100 ms. - const { opsDispatched } = Deno.metrics(); - await waitForMs(100); - const opsDispatched_ = Deno.metrics().opsDispatched; - assert(opsDispatched_ - opsDispatched < 10); -}); - -unitTest(async function timerBasicMicrotaskOrdering(): Promise { - let s = ""; - let count = 0; - const { promise, resolve } = deferred(); - setTimeout(() => { - Promise.resolve().then(() => { - count++; - s += "de"; - if (count === 2) { - resolve(); - } - }); - }); - setTimeout(() => { - count++; - s += "no"; - if (count === 2) { - resolve(); - } - }); - await promise; - assertEquals(s, "deno"); -}); - -unitTest(async function timerNestedMicrotaskOrdering(): Promise { - let s = ""; - const { promise, resolve } = deferred(); - s += "0"; - setTimeout(() => { - s += "4"; - setTimeout(() => (s += "A")); - Promise.resolve() - .then(() => { - setTimeout(() => { - s += "B"; - resolve(); - }); - }) - .then(() => { - s += "5"; - }); - }); - setTimeout(() => (s += "6")); - Promise.resolve().then(() => (s += "2")); - Promise.resolve().then(() => - setTimeout(() => { - s += "7"; - Promise.resolve() - .then(() => (s += "8")) - .then(() => { - s += "9"; - }); - }) - ); - Promise.resolve().then(() => Promise.resolve().then(() => (s += "3"))); - s += "1"; - await promise; - assertEquals(s, "0123456789AB"); -}); - -unitTest(function testQueueMicrotask() { - assertEquals(typeof queueMicrotask, "function"); -}); diff --git a/cli/js/tests/tls_test.ts b/cli/js/tests/tls_test.ts deleted file mode 100644 index 3d071441d..000000000 --- a/cli/js/tests/tls_test.ts +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - createResolvable, - unitTest, -} from "./test_util.ts"; -import { BufWriter, BufReader } from "../../../std/io/bufio.ts"; -import { TextProtoReader } from "../../../std/textproto/mod.ts"; - -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); - -unitTest(async function connectTLSNoPerm(): Promise { - let err; - try { - await Deno.connectTls({ hostname: "github.com", port: 443 }); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest(async function connectTLSCertFileNoReadPerm(): Promise { - let err; - try { - await Deno.connectTls({ - hostname: "github.com", - port: 443, - certFile: "cli/tests/tls/RootCA.crt", - }); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest( - { perms: { read: true, net: true } }, - function listenTLSNonExistentCertKeyFiles(): void { - let err; - const options = { - hostname: "localhost", - port: 3500, - certFile: "cli/tests/tls/localhost.crt", - keyFile: "cli/tests/tls/localhost.key", - }; - - try { - Deno.listenTls({ - ...options, - certFile: "./non/existent/file", - }); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); - - try { - Deno.listenTls({ - ...options, - keyFile: "./non/existent/file", - }); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.NotFound); - } -); - -unitTest({ perms: { net: true } }, function listenTLSNoReadPerm(): void { - let err; - try { - Deno.listenTls({ - hostname: "localhost", - port: 3500, - certFile: "cli/tests/tls/localhost.crt", - keyFile: "cli/tests/tls/localhost.key", - }); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest( - { - perms: { read: true, write: true, net: true }, - }, - function listenTLSEmptyKeyFile(): void { - let err; - const options = { - hostname: "localhost", - port: 3500, - certFile: "cli/tests/tls/localhost.crt", - keyFile: "cli/tests/tls/localhost.key", - }; - - const testDir = Deno.makeTempDirSync(); - const keyFilename = testDir + "/key.pem"; - Deno.writeFileSync(keyFilename, new Uint8Array([]), { - mode: 0o666, - }); - - try { - Deno.listenTls({ - ...options, - keyFile: keyFilename, - }); - } catch (e) { - err = e; - } - assert(err instanceof Error); - } -); - -unitTest( - { perms: { read: true, write: true, net: true } }, - function listenTLSEmptyCertFile(): void { - let err; - const options = { - hostname: "localhost", - port: 3500, - certFile: "cli/tests/tls/localhost.crt", - keyFile: "cli/tests/tls/localhost.key", - }; - - const testDir = Deno.makeTempDirSync(); - const certFilename = testDir + "/cert.crt"; - Deno.writeFileSync(certFilename, new Uint8Array([]), { - mode: 0o666, - }); - - try { - Deno.listenTls({ - ...options, - certFile: certFilename, - }); - } catch (e) { - err = e; - } - assert(err instanceof Error); - } -); - -unitTest( - { perms: { read: true, net: true } }, - async function dialAndListenTLS(): Promise { - const resolvable = createResolvable(); - const hostname = "localhost"; - const port = 3500; - - const listener = Deno.listenTls({ - hostname, - port, - certFile: "cli/tests/tls/localhost.crt", - keyFile: "cli/tests/tls/localhost.key", - }); - - const response = encoder.encode( - "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n" - ); - - listener.accept().then( - async (conn): Promise => { - assert(conn.remoteAddr != null); - assert(conn.localAddr != null); - await conn.write(response); - // TODO(bartlomieju): this might be a bug - setTimeout(() => { - conn.close(); - resolvable.resolve(); - }, 0); - } - ); - - const conn = await Deno.connectTls({ - hostname, - port, - certFile: "cli/tests/tls/RootCA.pem", - }); - assert(conn.rid > 0); - const w = new BufWriter(conn); - const r = new BufReader(conn); - const body = `GET / HTTP/1.1\r\nHost: ${hostname}:${port}\r\n\r\n`; - const writeResult = await w.write(encoder.encode(body)); - assertEquals(body.length, writeResult); - await w.flush(); - const tpr = new TextProtoReader(r); - const statusLine = await tpr.readLine(); - assert(statusLine !== null, `line must be read: ${String(statusLine)}`); - const m = statusLine.match(/^(.+?) (.+?) (.+?)$/); - assert(m !== null, "must be matched"); - const [_, proto, status, ok] = m; - assertEquals(proto, "HTTP/1.1"); - assertEquals(status, "200"); - assertEquals(ok, "OK"); - const headers = await tpr.readMIMEHeader(); - assert(headers !== null); - const contentLength = parseInt(headers.get("content-length")!); - const bodyBuf = new Uint8Array(contentLength); - await r.readFull(bodyBuf); - assertEquals(decoder.decode(bodyBuf), "Hello World\n"); - conn.close(); - listener.close(); - await resolvable; - } -); - -unitTest( - { perms: { read: true, net: true } }, - async function startTls(): Promise { - const hostname = "smtp.gmail.com"; - const port = 587; - const encoder = new TextEncoder(); - - let conn = await Deno.connect({ - hostname, - port, - }); - - let writer = new BufWriter(conn); - let reader = new TextProtoReader(new BufReader(conn)); - - let line: string | null = (await reader.readLine()) as string; - assert(line.startsWith("220")); - - await writer.write(encoder.encode(`EHLO ${hostname}\r\n`)); - await writer.flush(); - - while ((line = (await reader.readLine()) as string)) { - assert(line.startsWith("250")); - if (line.startsWith("250 ")) break; - } - - await writer.write(encoder.encode("STARTTLS\r\n")); - await writer.flush(); - - line = await reader.readLine(); - - // Received the message that the server is ready to establish TLS - assertEquals(line, "220 2.0.0 Ready to start TLS"); - - conn = await Deno.startTls(conn, { hostname }); - writer = new BufWriter(conn); - reader = new TextProtoReader(new BufReader(conn)); - - // After that use TLS communication again - await writer.write(encoder.encode(`EHLO ${hostname}\r\n`)); - await writer.flush(); - - while ((line = (await reader.readLine()) as string)) { - assert(line.startsWith("250")); - if (line.startsWith("250 ")) break; - } - - conn.close(); - } -); diff --git a/cli/js/tests/truncate_test.ts b/cli/js/tests/truncate_test.ts deleted file mode 100644 index 3fd85c990..000000000 --- a/cli/js/tests/truncate_test.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals, assert } from "./test_util.ts"; - -function readDataSync(name: string): string { - const data = Deno.readFileSync(name); - const decoder = new TextDecoder("utf-8"); - const text = decoder.decode(data); - return text; -} - -async function readData(name: string): Promise { - const data = await Deno.readFile(name); - const decoder = new TextDecoder("utf-8"); - const text = decoder.decode(data); - return text; -} - -unitTest( - { perms: { read: true, write: true } }, - function truncateSyncSuccess(): void { - const enc = new TextEncoder(); - const d = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test_truncateSync.txt"; - Deno.writeFileSync(filename, d); - Deno.truncateSync(filename, 20); - let data = readDataSync(filename); - assertEquals(data.length, 20); - Deno.truncateSync(filename, 5); - data = readDataSync(filename); - assertEquals(data.length, 5); - Deno.truncateSync(filename, -5); - data = readDataSync(filename); - assertEquals(data.length, 0); - Deno.removeSync(filename); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function truncateSuccess(): Promise { - const enc = new TextEncoder(); - const d = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test_truncate.txt"; - await Deno.writeFile(filename, d); - await Deno.truncate(filename, 20); - let data = await readData(filename); - assertEquals(data.length, 20); - await Deno.truncate(filename, 5); - data = await readData(filename); - assertEquals(data.length, 5); - await Deno.truncate(filename, -5); - data = await readData(filename); - assertEquals(data.length, 0); - await Deno.remove(filename); - } -); - -unitTest({ perms: { write: false } }, function truncateSyncPerm(): void { - let err; - try { - Deno.mkdirSync("/test_truncateSyncPermission.txt"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -unitTest({ perms: { write: false } }, async function truncatePerm(): Promise< - void -> { - let err; - try { - await Deno.mkdir("/test_truncatePermission.txt"); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); diff --git a/cli/js/tests/tty_test.ts b/cli/js/tests/tty_test.ts deleted file mode 100644 index 116b0dfe9..000000000 --- a/cli/js/tests/tty_test.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -// Note tests for Deno.setRaw is in integration tests. - -unitTest({ perms: { read: true } }, function isatty(): void { - // CI not under TTY, so cannot test stdin/stdout/stderr. - const f = Deno.openSync("cli/tests/hello.txt"); - assert(!Deno.isatty(f.rid)); - f.close(); -}); - -unitTest(function isattyError(): void { - let caught = false; - try { - // Absurdly large rid. - Deno.isatty(0x7fffffff); - } catch (e) { - caught = true; - assert(e instanceof Deno.errors.BadResource); - } - assert(caught); -}); diff --git a/cli/js/tests/umask_test.ts b/cli/js/tests/umask_test.ts deleted file mode 100644 index bfac65d52..000000000 --- a/cli/js/tests/umask_test.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assertEquals } from "./test_util.ts"; - -unitTest( - { - ignore: Deno.build.os === "windows", - }, - function umaskSuccess(): void { - const prevMask = Deno.umask(0o020); - const newMask = Deno.umask(prevMask); - const finalMask = Deno.umask(); - assertEquals(newMask, 0o020); - assertEquals(finalMask, prevMask); - } -); diff --git a/cli/js/tests/unit_test_runner.ts b/cli/js/tests/unit_test_runner.ts deleted file mode 100755 index 12d9101b8..000000000 --- a/cli/js/tests/unit_test_runner.ts +++ /dev/null @@ -1,309 +0,0 @@ -#!/usr/bin/env -S deno run --reload --allow-run -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import "./unit_tests.ts"; -import { - readLines, - permissionCombinations, - Permissions, - registerUnitTests, - fmtPerms, - parseArgs, - reportToConn, -} from "./test_util.ts"; - -// @ts-ignore -const internalObj = Deno[Deno.internal]; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const reportToConsole = internalObj.reportToConsole as (message: any) => void; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const runTests = internalObj.runTests as (options: any) => Promise; - -interface PermissionSetTestResult { - perms: Permissions; - passed: boolean; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - endMessage: any; - permsStr: string; -} - -const PERMISSIONS: Deno.PermissionName[] = [ - "read", - "write", - "net", - "env", - "run", - "plugin", - "hrtime", -]; - -/** - * Take a list of permissions and revoke missing permissions. - */ -async function dropWorkerPermissions( - requiredPermissions: Deno.PermissionName[] -): Promise { - const permsToDrop = PERMISSIONS.filter((p): boolean => { - return !requiredPermissions.includes(p); - }); - - for (const perm of permsToDrop) { - await Deno.permissions.revoke({ name: perm }); - } -} - -async function workerRunnerMain( - addrStr: string, - permsStr: string, - filter?: string -): Promise { - const [hostname, port] = addrStr.split(":"); - const addr = { hostname, port: Number(port) }; - - let perms: Deno.PermissionName[] = []; - if (permsStr.length > 0) { - perms = permsStr.split(",") as Deno.PermissionName[]; - } - // Setup reporter - const conn = await Deno.connect(addr); - // Drop current process permissions to requested set - await dropWorkerPermissions(perms); - // Register unit tests that match process permissions - await registerUnitTests(); - // Execute tests - await runTests({ - exitOnFail: false, - filter, - reportToConsole: false, - onMessage: reportToConn.bind(null, conn), - }); -} - -function spawnWorkerRunner( - verbose: boolean, - addr: string, - perms: Permissions, - filter?: string -): Deno.Process { - // run subsequent tests using same deno executable - const permStr = Object.keys(perms) - .filter((permName): boolean => { - return perms[permName as Deno.PermissionName] === true; - }) - .join(","); - - const cmd = [ - Deno.execPath(), - "run", - "--unstable", // TODO(ry) be able to test stable vs unstable - "-A", - "cli/js/tests/unit_test_runner.ts", - "--worker", - `--addr=${addr}`, - `--perms=${permStr}`, - ]; - - if (filter) { - cmd.push("--"); - cmd.push(filter); - } - - const ioMode = verbose ? "inherit" : "null"; - - const p = Deno.run({ - cmd, - stdin: ioMode, - stdout: ioMode, - stderr: ioMode, - }); - - return p; -} - -async function runTestsForPermissionSet( - listener: Deno.Listener, - addrStr: string, - verbose: boolean, - perms: Permissions, - filter?: string -): Promise { - const permsFmt = fmtPerms(perms); - console.log(`Running tests for: ${permsFmt}`); - const workerProcess = spawnWorkerRunner(verbose, addrStr, perms, filter); - // Wait for worker subprocess to go online - const conn = await listener.accept(); - - let expectedPassedTests; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let endMessage: any; - - try { - for await (const line of readLines(conn)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const message = JSON.parse(line) as any; - reportToConsole(message); - if (message.start != null) { - expectedPassedTests = message.start.tests.length; - } else if (message.end != null) { - endMessage = message.end; - } - } - } finally { - // Close socket to worker. - conn.close(); - } - - if (expectedPassedTests == null) { - throw new Error("Worker runner didn't report start"); - } - - if (endMessage == null) { - throw new Error("Worker runner didn't report end"); - } - - const workerStatus = await workerProcess.status(); - if (!workerStatus.success) { - throw new Error( - `Worker runner exited with status code: ${workerStatus.code}` - ); - } - - workerProcess.close(); - - const passed = expectedPassedTests === endMessage.passed + endMessage.ignored; - - return { - perms, - passed, - permsStr: permsFmt, - endMessage, - }; -} - -async function masterRunnerMain( - verbose: boolean, - filter?: string -): Promise { - console.log( - "Discovered permission combinations for tests:", - permissionCombinations.size - ); - - for (const perms of permissionCombinations.values()) { - console.log("\t" + fmtPerms(perms)); - } - - const testResults = new Set(); - const addr = { hostname: "127.0.0.1", port: 4510 }; - const addrStr = `${addr.hostname}:${addr.port}`; - const listener = Deno.listen(addr); - - for (const perms of permissionCombinations.values()) { - const result = await runTestsForPermissionSet( - listener, - addrStr, - verbose, - perms, - filter - ); - testResults.add(result); - } - - // if any run tests returned non-zero status then whole test - // run should fail - let testsPassed = true; - - for (const testResult of testResults) { - const { permsStr, endMessage } = testResult; - console.log(`Summary for ${permsStr}`); - reportToConsole({ end: endMessage }); - testsPassed = testsPassed && testResult.passed; - } - - if (!testsPassed) { - console.error("Unit tests failed"); - Deno.exit(1); - } - - console.log("Unit tests passed"); -} - -const HELP = `Unit test runner - -Run tests matching current process permissions: - - deno --allow-write unit_test_runner.ts - - deno --allow-net --allow-hrtime unit_test_runner.ts - - deno --allow-write unit_test_runner.ts -- testWriteFile - -Run "master" process that creates "worker" processes -for each discovered permission combination: - - deno -A unit_test_runner.ts --master - -Run worker process for given permissions: - - deno -A unit_test_runner.ts --worker --perms=net,read,write --addr=127.0.0.1:4500 - - -OPTIONS: - --master - Run in master mode, spawning worker processes for - each discovered permission combination - - --worker - Run in worker mode, requires "perms" and "addr" flags, - should be run with "-A" flag; after setup worker will - drop permissions to required set specified in "perms" - - --perms=... - Set of permissions this process should run tests with, - - --addr= - Address of TCP socket for reporting - -ARGS: - -- ... - Run only tests with names matching filter, must - be used after "--" -`; - -function assertOrHelp(expr: unknown): asserts expr { - if (!expr) { - console.log(HELP); - Deno.exit(1); - } -} - -async function main(): Promise { - const args = parseArgs(Deno.args, { - boolean: ["master", "worker", "verbose"], - "--": true, - }); - - if (args.help) { - console.log(HELP); - return; - } - - const filter = args["--"][0]; - - // Master mode - if (args.master) { - return masterRunnerMain(args.verbose, filter); - } - - // Worker mode - if (args.worker) { - assertOrHelp(typeof args.addr === "string"); - assertOrHelp(typeof args.perms === "string"); - return workerRunnerMain(args.addr, args.perms, filter); - } - - // Running tests matching current process permissions - await registerUnitTests(); - await runTests({ filter }); -} - -main(); diff --git a/cli/js/tests/unit_tests.ts b/cli/js/tests/unit_tests.ts deleted file mode 100644 index 7327bcc05..000000000 --- a/cli/js/tests/unit_tests.ts +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -// This test is executed as part of unit test suite. -// -// Test runner automatically spawns subprocesses for each required permissions combination. - -import "./abort_controller_test.ts"; -import "./blob_test.ts"; -import "./body_test.ts"; -import "./buffer_test.ts"; -import "./build_test.ts"; -import "./chmod_test.ts"; -import "./chown_test.ts"; -import "./console_test.ts"; -import "./copy_file_test.ts"; -import "./custom_event_test.ts"; -import "./dir_test.ts"; -import "./dispatch_minimal_test.ts"; -import "./dispatch_json_test.ts"; -import "./dom_exception_test.ts"; -import "./error_stack_test.ts"; -import "./event_test.ts"; -import "./event_target_test.ts"; -import "./fetch_test.ts"; -import "./file_test.ts"; -import "./files_test.ts"; -import "./form_data_test.ts"; -import "./format_error_test.ts"; -import "./fs_events_test.ts"; -import "./get_random_values_test.ts"; -import "./globals_test.ts"; -import "./headers_test.ts"; -import "./internals_test.ts"; -import "./io_test.ts"; -import "./link_test.ts"; -import "./make_temp_test.ts"; -import "./metrics_test.ts"; -import "./dom_iterable_test.ts"; -import "./mkdir_test.ts"; -import "./net_test.ts"; -import "./os_test.ts"; -import "./permissions_test.ts"; -import "./process_test.ts"; -import "./real_path_test.ts"; -import "./read_dir_test.ts"; -import "./read_text_file_test.ts"; -import "./read_file_test.ts"; -import "./read_link_test.ts"; -import "./remove_test.ts"; -import "./rename_test.ts"; -import "./request_test.ts"; -import "./resources_test.ts"; -import "./signal_test.ts"; -import "./stat_test.ts"; -import "./streams_piping_test.ts"; -import "./streams_transform_test.ts"; -import "./streams_writable_test.ts"; -import "./symlink_test.ts"; -import "./text_encoding_test.ts"; -import "./testing_test.ts"; -import "./timers_test.ts"; -import "./tls_test.ts"; -import "./truncate_test.ts"; -import "./tty_test.ts"; -import "./umask_test.ts"; -import "./url_test.ts"; -import "./url_search_params_test.ts"; -import "./utime_test.ts"; -import "./write_file_test.ts"; -import "./write_text_file_test.ts"; -import "./performance_test.ts"; -import "./version_test.ts"; diff --git a/cli/js/tests/url_search_params_test.ts b/cli/js/tests/url_search_params_test.ts deleted file mode 100644 index 7b7dbab76..000000000 --- a/cli/js/tests/url_search_params_test.ts +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest(function urlSearchParamsInitString(): void { - const init = "c=4&a=2&b=3&%C3%A1=1"; - const searchParams = new URLSearchParams(init); - assert( - init === searchParams.toString(), - "The init query string does not match" - ); -}); - -unitTest(function urlSearchParamsInitIterable(): void { - const init = [ - ["a", "54"], - ["b", "true"], - ]; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.toString(), "a=54&b=true"); -}); - -unitTest(function urlSearchParamsInitRecord(): void { - const init = { a: "54", b: "true" }; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.toString(), "a=54&b=true"); -}); - -unitTest(function urlSearchParamsInit(): void { - const params1 = new URLSearchParams("a=b"); - assertEquals(params1.toString(), "a=b"); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const params2 = new URLSearchParams(params1 as any); - assertEquals(params2.toString(), "a=b"); -}); - -unitTest(function urlSearchParamsAppendSuccess(): void { - const searchParams = new URLSearchParams(); - searchParams.append("a", "true"); - assertEquals(searchParams.toString(), "a=true"); -}); - -unitTest(function urlSearchParamsDeleteSuccess(): void { - const init = "a=54&b=true"; - const searchParams = new URLSearchParams(init); - searchParams.delete("b"); - assertEquals(searchParams.toString(), "a=54"); -}); - -unitTest(function urlSearchParamsGetAllSuccess(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.getAll("a"), ["54", "true"]); - assertEquals(searchParams.getAll("b"), ["true"]); - assertEquals(searchParams.getAll("c"), []); -}); - -unitTest(function urlSearchParamsGetSuccess(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.get("a"), "54"); - assertEquals(searchParams.get("b"), "true"); - assertEquals(searchParams.get("c"), null); -}); - -unitTest(function urlSearchParamsHasSuccess(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - assert(searchParams.has("a")); - assert(searchParams.has("b")); - assert(!searchParams.has("c")); -}); - -unitTest(function urlSearchParamsSetReplaceFirstAndRemoveOthers(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - searchParams.set("a", "false"); - assertEquals(searchParams.toString(), "a=false&b=true"); -}); - -unitTest(function urlSearchParamsSetAppendNew(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - searchParams.set("c", "foo"); - assertEquals(searchParams.toString(), "a=54&b=true&a=true&c=foo"); -}); - -unitTest(function urlSearchParamsSortSuccess(): void { - const init = "c=4&a=2&b=3&a=1"; - const searchParams = new URLSearchParams(init); - searchParams.sort(); - assertEquals(searchParams.toString(), "a=2&a=1&b=3&c=4"); -}); - -unitTest(function urlSearchParamsForEachSuccess(): void { - const init = [ - ["a", "54"], - ["b", "true"], - ]; - const searchParams = new URLSearchParams(init); - let callNum = 0; - searchParams.forEach((value, key, parent): void => { - assertEquals(searchParams, parent); - assertEquals(value, init[callNum][1]); - assertEquals(key, init[callNum][0]); - callNum++; - }); - assertEquals(callNum, init.length); -}); - -unitTest(function urlSearchParamsMissingName(): void { - const init = "=4"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.get(""), "4"); - assertEquals(searchParams.toString(), "=4"); -}); - -unitTest(function urlSearchParamsMissingValue(): void { - const init = "4="; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.get("4"), ""); - assertEquals(searchParams.toString(), "4="); -}); - -unitTest(function urlSearchParamsMissingEqualSign(): void { - const init = "4"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.get("4"), ""); - assertEquals(searchParams.toString(), "4="); -}); - -unitTest(function urlSearchParamsMissingPair(): void { - const init = "c=4&&a=54&"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.toString(), "c=4&a=54"); -}); - -// If pair does not contain exactly two items, then throw a TypeError. -// ref https://url.spec.whatwg.org/#interface-urlsearchparams -unitTest(function urlSearchParamsShouldThrowTypeError(): void { - let hasThrown = 0; - - try { - new URLSearchParams([["1"]]); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - - assertEquals(hasThrown, 2); - - try { - new URLSearchParams([["1", "2", "3"]]); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - - assertEquals(hasThrown, 2); -}); - -unitTest(function urlSearchParamsAppendArgumentsCheck(): void { - const methodRequireOneParam = ["delete", "getAll", "get", "has", "forEach"]; - - const methodRequireTwoParams = ["append", "set"]; - - methodRequireOneParam - .concat(methodRequireTwoParams) - .forEach((method: string): void => { - const searchParams = new URLSearchParams(); - let hasThrown = 0; - try { - // @ts-ignore - searchParams[method](); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - }); - - methodRequireTwoParams.forEach((method: string): void => { - const searchParams = new URLSearchParams(); - let hasThrown = 0; - try { - // @ts-ignore - searchParams[method]("foo"); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - }); -}); - -// ref: https://github.com/web-platform-tests/wpt/blob/master/url/urlsearchparams-delete.any.js -unitTest(function urlSearchParamsDeletingAppendedMultiple(): void { - const params = new URLSearchParams(); - params.append("first", (1 as unknown) as string); - assert(params.has("first")); - assertEquals(params.get("first"), "1"); - params.delete("first"); - assertEquals(params.has("first"), false); - params.append("first", (1 as unknown) as string); - params.append("first", (10 as unknown) as string); - params.delete("first"); - assertEquals(params.has("first"), false); -}); - -// ref: https://github.com/web-platform-tests/wpt/blob/master/url/urlsearchparams-constructor.any.js#L176-L182 -unitTest(function urlSearchParamsCustomSymbolIterator(): void { - const params = new URLSearchParams(); - params[Symbol.iterator] = function* (): IterableIterator<[string, string]> { - yield ["a", "b"]; - }; - const params1 = new URLSearchParams((params as unknown) as string[][]); - assertEquals(params1.get("a"), "b"); -}); - -unitTest( - function urlSearchParamsCustomSymbolIteratorWithNonStringParams(): void { - const params = {}; - // @ts-ignore - params[Symbol.iterator] = function* (): IterableIterator<[number, number]> { - yield [1, 2]; - }; - const params1 = new URLSearchParams((params as unknown) as string[][]); - assertEquals(params1.get("1"), "2"); - } -); - -// If a class extends URLSearchParams, override one method should not change another's behavior. -unitTest( - function urlSearchParamsOverridingAppendNotChangeConstructorAndSet(): void { - let overridedAppendCalled = 0; - class CustomSearchParams extends URLSearchParams { - append(name: string, value: string): void { - ++overridedAppendCalled; - super.append(name, value); - } - } - new CustomSearchParams("foo=bar"); - new CustomSearchParams([["foo", "bar"]]); - new CustomSearchParams(new CustomSearchParams({ foo: "bar" })); - new CustomSearchParams().set("foo", "bar"); - assertEquals(overridedAppendCalled, 0); - } -); - -unitTest(function urlSearchParamsOverridingEntriesNotChangeForEach(): void { - class CustomSearchParams extends URLSearchParams { - *entries(): IterableIterator<[string, string]> { - yield* []; - } - } - let loopCount = 0; - const params = new CustomSearchParams({ foo: "bar" }); - params.forEach(() => void ++loopCount); - assertEquals(loopCount, 1); -}); diff --git a/cli/js/tests/url_test.ts b/cli/js/tests/url_test.ts deleted file mode 100644 index 68fcbd95e..000000000 --- a/cli/js/tests/url_test.ts +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals, assertThrows } from "./test_util.ts"; - -unitTest(function urlParsing(): void { - const url = new URL( - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - assertEquals(url.hash, "#qat"); - assertEquals(url.host, "baz.qat:8000"); - assertEquals(url.hostname, "baz.qat"); - assertEquals( - url.href, - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - assertEquals(url.origin, "https://baz.qat:8000"); - assertEquals(url.password, "bar"); - assertEquals(url.pathname, "/qux/quux"); - assertEquals(url.port, "8000"); - assertEquals(url.protocol, "https:"); - assertEquals(url.search, "?foo=bar&baz=12"); - assertEquals(url.searchParams.getAll("foo"), ["bar"]); - assertEquals(url.searchParams.getAll("baz"), ["12"]); - assertEquals(url.username, "foo"); - assertEquals( - String(url), - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - assertEquals( - JSON.stringify({ key: url }), - `{"key":"https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat"}` - ); -}); - -unitTest(function urlModifications(): void { - const url = new URL( - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - url.hash = ""; - assertEquals( - url.href, - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12" - ); - url.host = "qat.baz:8080"; - assertEquals( - url.href, - "https://foo:bar@qat.baz:8080/qux/quux?foo=bar&baz=12" - ); - url.hostname = "foo.bar"; - assertEquals( - url.href, - "https://foo:bar@foo.bar:8080/qux/quux?foo=bar&baz=12" - ); - url.password = "qux"; - assertEquals( - url.href, - "https://foo:qux@foo.bar:8080/qux/quux?foo=bar&baz=12" - ); - url.pathname = "/foo/bar%qat"; - assertEquals( - url.href, - "https://foo:qux@foo.bar:8080/foo/bar%qat?foo=bar&baz=12" - ); - url.port = ""; - assertEquals(url.href, "https://foo:qux@foo.bar/foo/bar%qat?foo=bar&baz=12"); - url.protocol = "http:"; - assertEquals(url.href, "http://foo:qux@foo.bar/foo/bar%qat?foo=bar&baz=12"); - url.search = "?foo=bar&foo=baz"; - assertEquals(url.href, "http://foo:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz"); - assertEquals(url.searchParams.getAll("foo"), ["bar", "baz"]); - url.username = "foo@bar"; - assertEquals( - url.href, - "http://foo%40bar:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz" - ); - url.searchParams.set("bar", "qat"); - assertEquals( - url.href, - "http://foo%40bar:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz&bar=qat" - ); - url.searchParams.delete("foo"); - assertEquals(url.href, "http://foo%40bar:qux@foo.bar/foo/bar%qat?bar=qat"); - url.searchParams.append("foo", "bar"); - assertEquals( - url.href, - "http://foo%40bar:qux@foo.bar/foo/bar%qat?bar=qat&foo=bar" - ); -}); - -unitTest(function urlModifyHref(): void { - const url = new URL("http://example.com/"); - url.href = "https://foo:bar@example.com:8080/baz/qat#qux"; - assertEquals(url.protocol, "https:"); - assertEquals(url.username, "foo"); - assertEquals(url.password, "bar"); - assertEquals(url.host, "example.com:8080"); - assertEquals(url.hostname, "example.com"); - assertEquals(url.pathname, "/baz/qat"); - assertEquals(url.hash, "#qux"); -}); - -unitTest(function urlNormalize(): void { - const url = new URL("http://example.com"); - assertEquals(url.pathname, "/"); - assertEquals(url.href, "http://example.com/"); -}); - -unitTest(function urlModifyPathname(): void { - const url = new URL("http://foo.bar/baz%qat/qux%quux"); - assertEquals(url.pathname, "/baz%qat/qux%quux"); - url.pathname = url.pathname; - assertEquals(url.pathname, "/baz%qat/qux%quux"); - url.pathname = "baz#qat qux"; - assertEquals(url.pathname, "/baz%23qat%20qux"); - url.pathname = url.pathname; - assertEquals(url.pathname, "/baz%23qat%20qux"); -}); - -unitTest(function urlModifyHash(): void { - const url = new URL("http://foo.bar"); - url.hash = "%foo bar/qat%qux#bar"; - assertEquals(url.hash, "#%foo%20bar/qat%qux#bar"); - url.hash = url.hash; - assertEquals(url.hash, "#%foo%20bar/qat%qux#bar"); -}); - -unitTest(function urlSearchParamsReuse(): void { - const url = new URL( - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - const sp = url.searchParams; - url.host = "baz.qat"; - assert(sp === url.searchParams, "Search params should be reused."); -}); - -unitTest(function urlBackSlashes(): void { - const url = new URL( - "https:\\\\foo:bar@baz.qat:8000\\qux\\quux?foo=bar&baz=12#qat" - ); - assertEquals( - url.href, - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); -}); - -unitTest(function urlRequireHost(): void { - assertEquals(new URL("file:///").href, "file:///"); - assertThrows(() => { - new URL("ftp:///"); - }); - assertThrows(() => { - new URL("http:///"); - }); - assertThrows(() => { - new URL("https:///"); - }); - assertThrows(() => { - new URL("ws:///"); - }); - assertThrows(() => { - new URL("wss:///"); - }); -}); - -unitTest(function urlDriveLetter() { - assertEquals( - new URL("file:///C:").href, - Deno.build.os == "windows" ? "file:///C:/" : "file:///C:" - ); - assertEquals(new URL("http://example.com/C:").href, "http://example.com/C:"); -}); - -unitTest(function urlHostnameUpperCase() { - assertEquals(new URL("https://EXAMPLE.COM").href, "https://example.com/"); -}); - -unitTest(function urlTrim() { - assertEquals(new URL(" https://example.com ").href, "https://example.com/"); -}); - -unitTest(function urlEncoding() { - assertEquals( - new URL("https://a !$&*()=,;+'\"@example.com").username, - "a%20!$&*()%3D,%3B+%27%22" - ); - assertEquals( - new URL("https://:a !$&*()=,;+'\"@example.com").password, - "a%20!$&*()%3D,%3B+%27%22" - ); - // FIXME: https://url.spec.whatwg.org/#idna - // assertEquals( - // new URL("https://a !$&*()=,+'\"").hostname, - // "a%20%21%24%26%2A%28%29%3D%2C+%27%22" - // ); - assertEquals( - new URL("https://example.com/a ~!@$&*()=:/,;+'\"\\").pathname, - "/a%20~!@$&*()=:/,;+'%22/" - ); - assertEquals( - new URL("https://example.com?a ~!@$&*()=:/,;?+'\"\\").search, - "?a%20~!@$&*()=:/,;?+%27%22\\" - ); - assertEquals( - new URL("https://example.com#a ~!@#$&*()=:/,;?+'\"\\").hash, - "#a%20~!@#$&*()=:/,;?+'%22\\" - ); -}); - -unitTest(function urlBaseURL(): void { - const base = new URL( - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - const url = new URL("/foo/bar?baz=foo#qux", base); - assertEquals(url.href, "https://foo:bar@baz.qat:8000/foo/bar?baz=foo#qux"); -}); - -unitTest(function urlBaseString(): void { - const url = new URL( - "/foo/bar?baz=foo#qux", - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - assertEquals(url.href, "https://foo:bar@baz.qat:8000/foo/bar?baz=foo#qux"); -}); - -unitTest(function urlRelativeWithBase(): void { - assertEquals(new URL("", "file:///a/a/a").href, "file:///a/a/a"); - assertEquals(new URL(".", "file:///a/a/a").href, "file:///a/a/"); - assertEquals(new URL("..", "file:///a/a/a").href, "file:///a/"); - assertEquals(new URL("b", "file:///a/a/a").href, "file:///a/a/b"); - assertEquals(new URL("b", "file:///a/a/a/").href, "file:///a/a/a/b"); - assertEquals(new URL("b/", "file:///a/a/a").href, "file:///a/a/b/"); - assertEquals(new URL("../b", "file:///a/a/a").href, "file:///a/b"); -}); - -unitTest(function urlDriveLetterBase() { - assertEquals( - new URL("/b", "file:///C:/a/b").href, - Deno.build.os == "windows" ? "file:///C:/b" : "file:///b" - ); - assertEquals( - new URL("D:", "file:///C:/a/b").href, - Deno.build.os == "windows" ? "file:///D:/" : "file:///C:/a/D:" - ); - assertEquals( - new URL("/D:", "file:///C:/a/b").href, - Deno.build.os == "windows" ? "file:///D:/" : "file:///D:" - ); - assertEquals( - new URL("D:/b", "file:///C:/a/b").href, - Deno.build.os == "windows" ? "file:///D:/b" : "file:///C:/a/D:/b" - ); -}); - -unitTest(function emptyBasePath(): void { - assertEquals(new URL("", "http://example.com").href, "http://example.com/"); -}); - -unitTest(function deletingAllParamsRemovesQuestionMarkFromURL(): void { - const url = new URL("http://example.com/?param1¶m2"); - url.searchParams.delete("param1"); - url.searchParams.delete("param2"); - assertEquals(url.href, "http://example.com/"); - assertEquals(url.search, ""); -}); - -unitTest(function removingNonExistentParamRemovesQuestionMarkFromURL(): void { - const url = new URL("http://example.com/?"); - assertEquals(url.href, "http://example.com/?"); - url.searchParams.delete("param1"); - assertEquals(url.href, "http://example.com/"); - assertEquals(url.search, ""); -}); - -unitTest(function sortingNonExistentParamRemovesQuestionMarkFromURL(): void { - const url = new URL("http://example.com/?"); - assertEquals(url.href, "http://example.com/?"); - url.searchParams.sort(); - assertEquals(url.href, "http://example.com/"); - assertEquals(url.search, ""); -}); - -unitTest( - { - // FIXME(bartlomieju) - ignore: true, - }, - function customInspectFunction(): void { - const url = new URL("http://example.com/?"); - assertEquals( - Deno.inspect(url), - 'URL { href: "http://example.com/?", origin: "http://example.com", protocol: "http:", username: "", password: "", host: "example.com", hostname: "example.com", port: "", pathname: "/", hash: "", search: "?" }' - ); - } -); - -unitTest(function protocolNotHttpOrFile() { - const url = new URL("about:blank"); - assertEquals(url.href, "about:blank"); - assertEquals(url.protocol, "about:"); - assertEquals(url.origin, "null"); -}); - -unitTest(function createBadUrl(): void { - assertThrows(() => { - new URL("0.0.0.0:8080"); - }); -}); - -unitTest(function throwForInvalidPortConstructor(): void { - const urls = [ - // If port is greater than 2^16 − 1, validation error, return failure. - `https://baz.qat:${2 ** 16}`, - "https://baz.qat:-32", - "https://baz.qat:deno", - "https://baz.qat:9land", - "https://baz.qat:10.5", - ]; - - for (const url of urls) { - assertThrows(() => new URL(url), TypeError, "Invalid URL."); - } - - // Do not throw for 0 & 65535 - new URL("https://baz.qat:65535"); - new URL("https://baz.qat:0"); -}); - -unitTest(function throwForInvalidSchemeConstructor(): void { - assertThrows( - () => new URL("invalid_scheme://baz.qat"), - TypeError, - "Invalid URL." - ); -}); - -unitTest(function doNotOverridePortIfInvalid(): void { - const initialPort = "3000"; - const ports = [ - // If port is greater than 2^16 − 1, validation error, return failure. - `${2 ** 16}`, - "-32", - "deno", - "9land", - "10.5", - ]; - - for (const port of ports) { - const url = new URL(`https://deno.land:${initialPort}`); - url.port = port; - assertEquals(url.port, initialPort); - } -}); - -unitTest(function emptyPortForSchemeDefaultPort(): void { - const nonDefaultPort = "3500"; - const urls = [ - { url: "ftp://baz.qat:21", port: "21", protocol: "ftp:" }, - { url: "https://baz.qat:443", port: "443", protocol: "https:" }, - { url: "wss://baz.qat:443", port: "443", protocol: "wss:" }, - { url: "http://baz.qat:80", port: "80", protocol: "http:" }, - { url: "ws://baz.qat:80", port: "80", protocol: "ws:" }, - { url: "file://home/index.html", port: "", protocol: "file:" }, - { url: "/foo", baseUrl: "ftp://baz.qat:21", port: "21", protocol: "ftp:" }, - { - url: "/foo", - baseUrl: "https://baz.qat:443", - port: "443", - protocol: "https:", - }, - { - url: "/foo", - baseUrl: "wss://baz.qat:443", - port: "443", - protocol: "wss:", - }, - { - url: "/foo", - baseUrl: "http://baz.qat:80", - port: "80", - protocol: "http:", - }, - { url: "/foo", baseUrl: "ws://baz.qat:80", port: "80", protocol: "ws:" }, - { - url: "/foo", - baseUrl: "file://home/index.html", - port: "", - protocol: "file:", - }, - ]; - - for (const { url: urlString, baseUrl, port, protocol } of urls) { - const url = new URL(urlString, baseUrl); - assertEquals(url.port, ""); - - url.port = nonDefaultPort; - assertEquals(url.port, nonDefaultPort); - - url.port = port; - assertEquals(url.port, ""); - - // change scheme - url.protocol = "sftp:"; - assertEquals(url.port, port); - - url.protocol = protocol; - assertEquals(url.port, ""); - } -}); diff --git a/cli/js/tests/utime_test.ts b/cli/js/tests/utime_test.ts deleted file mode 100644 index 63727ae62..000000000 --- a/cli/js/tests/utime_test.ts +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; - -// Allow 10 second difference. -// Note this might not be enough for FAT (but we are not testing on such fs). -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function assertFuzzyTimestampEquals(t1: Date | null, t2: Date): void { - assert(t1 instanceof Date); - assert(Math.abs(t1.valueOf() - t2.valueOf()) < 10_000); -} - -unitTest( - { perms: { read: true, write: true } }, - function utimeSyncFileSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const filename = testDir + "/file.txt"; - Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { - mode: 0o666, - }); - - const atime = 1000; - const mtime = 50000; - Deno.utimeSync(filename, atime, mtime); - - const fileInfo = Deno.statSync(filename); - assertFuzzyTimestampEquals(fileInfo.atime, new Date(atime * 1000)); - assertFuzzyTimestampEquals(fileInfo.mtime, new Date(mtime * 1000)); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function utimeSyncDirectorySuccess(): void { - const testDir = Deno.makeTempDirSync(); - - const atime = 1000; - const mtime = 50000; - Deno.utimeSync(testDir, atime, mtime); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.atime, new Date(atime * 1000)); - assertFuzzyTimestampEquals(dirInfo.mtime, new Date(mtime * 1000)); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function utimeSyncDateSuccess(): void { - const testDir = Deno.makeTempDirSync(); - - const atime = new Date(1000_000); - const mtime = new Date(50000_000); - Deno.utimeSync(testDir, atime, mtime); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.atime, atime); - assertFuzzyTimestampEquals(dirInfo.mtime, mtime); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function utimeSyncFileDateSuccess() { - const testDir = Deno.makeTempDirSync(); - const filename = testDir + "/file.txt"; - Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { - mode: 0o666, - }); - const atime = new Date(); - const mtime = new Date(); - Deno.utimeSync(filename, atime, mtime); - - const fileInfo = Deno.statSync(filename); - assertFuzzyTimestampEquals(fileInfo.atime, atime); - assertFuzzyTimestampEquals(fileInfo.mtime, mtime); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function utimeSyncLargeNumberSuccess(): void { - const testDir = Deno.makeTempDirSync(); - - // There are Rust side caps (might be fs relate), - // so JUST make them slightly larger than UINT32_MAX. - const atime = 0x100000001; - const mtime = 0x100000002; - Deno.utimeSync(testDir, atime, mtime); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.atime, new Date(atime * 1000)); - assertFuzzyTimestampEquals(dirInfo.mtime, new Date(mtime * 1000)); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function utimeSyncNotFound(): void { - const atime = 1000; - const mtime = 50000; - - let caughtError = false; - try { - Deno.utimeSync("/baddir", atime, mtime); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); - } -); - -unitTest( - { perms: { read: true, write: false } }, - function utimeSyncPerm(): void { - const atime = 1000; - const mtime = 50000; - - let caughtError = false; - try { - Deno.utimeSync("/some_dir", atime, mtime); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function utimeFileSuccess(): Promise { - const testDir = Deno.makeTempDirSync(); - const filename = testDir + "/file.txt"; - Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { - mode: 0o666, - }); - - const atime = 1000; - const mtime = 50000; - await Deno.utime(filename, atime, mtime); - - const fileInfo = Deno.statSync(filename); - assertFuzzyTimestampEquals(fileInfo.atime, new Date(atime * 1000)); - assertFuzzyTimestampEquals(fileInfo.mtime, new Date(mtime * 1000)); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function utimeDirectorySuccess(): Promise { - const testDir = Deno.makeTempDirSync(); - - const atime = 1000; - const mtime = 50000; - await Deno.utime(testDir, atime, mtime); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.atime, new Date(atime * 1000)); - assertFuzzyTimestampEquals(dirInfo.mtime, new Date(mtime * 1000)); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function utimeDateSuccess(): Promise { - const testDir = Deno.makeTempDirSync(); - - const atime = new Date(100_000); - const mtime = new Date(5000_000); - await Deno.utime(testDir, atime, mtime); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.atime, atime); - assertFuzzyTimestampEquals(dirInfo.mtime, mtime); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function utimeFileDateSuccess(): Promise { - const testDir = Deno.makeTempDirSync(); - const filename = testDir + "/file.txt"; - Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { - mode: 0o666, - }); - - const atime = new Date(); - const mtime = new Date(); - await Deno.utime(filename, atime, mtime); - - const fileInfo = Deno.statSync(filename); - assertFuzzyTimestampEquals(fileInfo.atime, atime); - assertFuzzyTimestampEquals(fileInfo.mtime, mtime); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function utimeNotFound(): Promise { - const atime = 1000; - const mtime = 50000; - - let caughtError = false; - try { - await Deno.utime("/baddir", atime, mtime); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); - } -); - -unitTest( - { perms: { read: true, write: false } }, - async function utimeSyncPerm(): Promise { - const atime = 1000; - const mtime = 50000; - - let caughtError = false; - try { - await Deno.utime("/some_dir", atime, mtime); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); - } -); diff --git a/cli/js/tests/version_test.ts b/cli/js/tests/version_test.ts deleted file mode 100644 index 0417b27de..000000000 --- a/cli/js/tests/version_test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { unitTest, assert } from "./test_util.ts"; - -unitTest(function version(): void { - const pattern = /^\d+\.\d+\.\d+/; - assert(pattern.test(Deno.version.deno)); - assert(pattern.test(Deno.version.v8)); - assert(pattern.test(Deno.version.typescript)); -}); diff --git a/cli/js/tests/write_file_test.ts b/cli/js/tests/write_file_test.ts deleted file mode 100644 index 80f711dbc..000000000 --- a/cli/js/tests/write_file_test.ts +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest( - { perms: { read: true, write: true } }, - function writeFileSyncSuccess(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data); - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - const actual = dec.decode(dataRead); - assertEquals("Hello", actual); - } -); - -unitTest({ perms: { write: true } }, function writeFileSyncFail(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = "/baddir/test.txt"; - // The following should fail because /baddir doesn't exist (hopefully). - let caughtError = false; - try { - Deno.writeFileSync(filename, data); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); -}); - -unitTest({ perms: { write: false } }, function writeFileSyncPerm(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = "/baddir/test.txt"; - // The following should fail due to no write permission - let caughtError = false; - try { - Deno.writeFileSync(filename, data); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest( - { perms: { read: true, write: true } }, - function writeFileSyncUpdateMode(): void { - if (Deno.build.os !== "windows") { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { mode: 0o755 }); - assertEquals(Deno.statSync(filename).mode! & 0o777, 0o755); - Deno.writeFileSync(filename, data, { mode: 0o666 }); - assertEquals(Deno.statSync(filename).mode! & 0o777, 0o666); - } - } -); - -unitTest( - { perms: { read: true, write: true } }, - function writeFileSyncCreate(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - let caughtError = false; - // if create turned off, the file won't be created - try { - Deno.writeFileSync(filename, data, { create: false }); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); - - // Turn on create, should have no error - Deno.writeFileSync(filename, data, { create: true }); - Deno.writeFileSync(filename, data, { create: false }); - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - const actual = dec.decode(dataRead); - assertEquals("Hello", actual); - } -); - -unitTest( - { perms: { read: true, write: true } }, - function writeFileSyncAppend(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data); - Deno.writeFileSync(filename, data, { append: true }); - let dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - let actual = dec.decode(dataRead); - assertEquals("HelloHello", actual); - // Now attempt overwrite - Deno.writeFileSync(filename, data, { append: false }); - dataRead = Deno.readFileSync(filename); - actual = dec.decode(dataRead); - assertEquals("Hello", actual); - // append not set should also overwrite - Deno.writeFileSync(filename, data); - dataRead = Deno.readFileSync(filename); - actual = dec.decode(dataRead); - assertEquals("Hello", actual); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function writeFileSuccess(): Promise { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - await Deno.writeFile(filename, data); - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - const actual = dec.decode(dataRead); - assertEquals("Hello", actual); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function writeFileNotFound(): Promise { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = "/baddir/test.txt"; - // The following should fail because /baddir doesn't exist (hopefully). - let caughtError = false; - try { - await Deno.writeFile(filename, data); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); - } -); - -unitTest( - { perms: { read: true, write: false } }, - async function writeFilePerm(): Promise { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = "/baddir/test.txt"; - // The following should fail due to no write permission - let caughtError = false; - try { - await Deno.writeFile(filename, data); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function writeFileUpdateMode(): Promise { - if (Deno.build.os !== "windows") { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - await Deno.writeFile(filename, data, { mode: 0o755 }); - assertEquals(Deno.statSync(filename).mode! & 0o777, 0o755); - await Deno.writeFile(filename, data, { mode: 0o666 }); - assertEquals(Deno.statSync(filename).mode! & 0o777, 0o666); - } - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function writeFileCreate(): Promise { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - let caughtError = false; - // if create turned off, the file won't be created - try { - await Deno.writeFile(filename, data, { create: false }); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); - - // Turn on create, should have no error - await Deno.writeFile(filename, data, { create: true }); - await Deno.writeFile(filename, data, { create: false }); - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - const actual = dec.decode(dataRead); - assertEquals("Hello", actual); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function writeFileAppend(): Promise { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - await Deno.writeFile(filename, data); - await Deno.writeFile(filename, data, { append: true }); - let dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - let actual = dec.decode(dataRead); - assertEquals("HelloHello", actual); - // Now attempt overwrite - await Deno.writeFile(filename, data, { append: false }); - dataRead = Deno.readFileSync(filename); - actual = dec.decode(dataRead); - assertEquals("Hello", actual); - // append not set should also overwrite - await Deno.writeFile(filename, data); - dataRead = Deno.readFileSync(filename); - actual = dec.decode(dataRead); - assertEquals("Hello", actual); - } -); diff --git a/cli/js/tests/write_text_file_test.ts b/cli/js/tests/write_text_file_test.ts deleted file mode 100644 index 321189b0e..000000000 --- a/cli/js/tests/write_text_file_test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { unitTest, assert, assertEquals } from "./test_util.ts"; - -unitTest( - { perms: { read: true, write: true } }, - function writeTextFileSyncSuccess(): void { - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeTextFileSync(filename, "Hello"); - const dataRead = Deno.readTextFileSync(filename); - assertEquals("Hello", dataRead); - } -); - -unitTest({ perms: { write: true } }, function writeTextFileSyncFail(): void { - const filename = "/baddir/test.txt"; - // The following should fail because /baddir doesn't exist (hopefully). - let caughtError = false; - try { - Deno.writeTextFileSync(filename, "hello"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); -}); - -unitTest({ perms: { write: false } }, function writeTextFileSyncPerm(): void { - const filename = "/baddir/test.txt"; - // The following should fail due to no write permission - let caughtError = false; - try { - Deno.writeTextFileSync(filename, "Hello"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); -}); - -unitTest( - { perms: { read: true, write: true } }, - async function writeTextFileSuccess(): Promise { - const filename = Deno.makeTempDirSync() + "/test.txt"; - await Deno.writeTextFile(filename, "Hello"); - const dataRead = Deno.readTextFileSync(filename); - assertEquals("Hello", dataRead); - } -); - -unitTest( - { perms: { read: true, write: true } }, - async function writeTextFileNotFound(): Promise { - const filename = "/baddir/test.txt"; - // The following should fail because /baddir doesn't exist (hopefully). - let caughtError = false; - try { - await Deno.writeTextFile(filename, "Hello"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.NotFound); - } - assert(caughtError); - } -); - -unitTest( - { perms: { write: false } }, - async function writeTextFilePerm(): Promise { - const filename = "/baddir/test.txt"; - // The following should fail due to no write permission - let caughtError = false; - try { - await Deno.writeTextFile(filename, "Hello"); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); - } -); diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 103e3142e..de894f064 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -400,7 +400,7 @@ fn js_unit_tests() { .arg("--unstable") .arg("--reload") .arg("-A") - .arg("cli/js/tests/unit_test_runner.ts") + .arg("cli/tests/unit/unit_test_runner.ts") .arg("--master") .arg("--verbose") .env("NO_COLOR", "1") diff --git a/cli/tests/unit/README.md b/cli/tests/unit/README.md new file mode 100644 index 000000000..c333b0046 --- /dev/null +++ b/cli/tests/unit/README.md @@ -0,0 +1,80 @@ +# Deno runtime tests + +Files in this directory are unit tests for Deno runtime. + +They are run under compiled Deno binary as opposed to files in `cli/js/` which +are bundled and snapshotted using `deno_typescript` crate. + +Testing Deno runtime code requires checking API under different runtime +permissions (ie. running with different `--allow-*` flags). To accomplish this +all tests exercised are created using `unitTest()` function. + +``` +import { unitTest } from "./test_util.ts"; + +unitTest(function simpleTestFn(): void { + // test code here +}); + +unitTest({ + ignore: Deno.build.os === "windows", + perms: { read: true, write: true }, + }, + function complexTestFn(): void { + // test code here + } +); +``` + +`unitTest` is is a wrapper function that enhances `Deno.test()` API in several +ways: + +- ability to conditionally skip tests using `UnitTestOptions.skip` +- ability to register required set of permissions for given test case using + `UnitTestOptions.perms` +- sanitization of resources - ensuring that tests close all opened resources + preventing interference between tests +- sanitization of async ops - ensuring that tests don't leak async ops by + ensuring that all started async ops are done before test finishes + +## Running tests + +`unit_test_runner.ts` is the main script used to run unit tests. + +Runner discovers required permissions combinations by loading +`cli/tests/unit/unit_tests.ts` and going through all registered instances of +`unitTest`. + +There are three ways to run `unit_test_runner.ts`: + +``` +# Run all tests. Spawns worker processes for each discovered permission +# combination: +target/debug/deno run -A cli/tests/unit/unit_test_runner.ts --master + +# By default all output of worker processes is discarded; for debug purposes +# the --verbose flag preserves output from the worker +target/debug/deno run -A cli/tests/unit/unit_test_runner.ts --master --verbose + +# Run subset of tests that don't require any permissions +target/debug/deno run --unstable cli/tests/unit/unit_test_runner.ts + +# Run subset tests that require "net" and "read" permissions +target/debug/deno run --unstable --allow-net --allow-read cli/tests/unit/unit_test_runner.ts + +# "worker" mode communicates with parent using TCP socket on provided address; +# after initial setup drops permissions to specified set. It shouldn't be used +# directly, only be "master" process. +target/debug/deno run -A cli/tests/unit/unit_test_runner.ts --worker --addr=127.0.0.1:4500 --perms=net,write,run + +# Run specific tests +target/debug/deno run --unstable --allow-net cli/tests/unit/unit_test_runner.ts -- netTcpListenClose + +RUST_BACKTRACE=1 cargo run -- run --unstable --allow-read --allow-write cli/tests/unit/unit_test_runner.ts -- netUnixDialListen +``` + +### Http server + +`tools/http_server.py` is required to run when one's running unit tests. During +CI it's spawned automatically, but if you want to run tests manually make sure +that server is spawned otherwise there'll be cascade of test failures. diff --git a/cli/tests/unit/abort_controller_test.ts b/cli/tests/unit/abort_controller_test.ts new file mode 100644 index 000000000..ecc1abb88 --- /dev/null +++ b/cli/tests/unit/abort_controller_test.ts @@ -0,0 +1,56 @@ +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest(function basicAbortController() { + const controller = new AbortController(); + assert(controller); + const { signal } = controller; + assert(signal); + assertEquals(signal.aborted, false); + controller.abort(); + assertEquals(signal.aborted, true); +}); + +unitTest(function signalCallsOnabort() { + const controller = new AbortController(); + const { signal } = controller; + let called = false; + signal.onabort = (evt): void => { + assert(evt); + assertEquals(evt.type, "abort"); + called = true; + }; + controller.abort(); + assert(called); +}); + +unitTest(function signalEventListener() { + const controller = new AbortController(); + const { signal } = controller; + let called = false; + signal.addEventListener("abort", function (ev) { + assert(this === signal); + assertEquals(ev.type, "abort"); + called = true; + }); + controller.abort(); + assert(called); +}); + +unitTest(function onlyAbortsOnce() { + const controller = new AbortController(); + const { signal } = controller; + let called = 0; + signal.addEventListener("abort", () => called++); + signal.onabort = (): void => { + called++; + }; + controller.abort(); + assertEquals(called, 2); + controller.abort(); + assertEquals(called, 2); +}); + +unitTest(function controllerHasProperToString() { + const actual = Object.prototype.toString.call(new AbortController()); + assertEquals(actual, "[object AbortController]"); +}); diff --git a/cli/tests/unit/blob_test.ts b/cli/tests/unit/blob_test.ts new file mode 100644 index 000000000..70f8fb3d5 --- /dev/null +++ b/cli/tests/unit/blob_test.ts @@ -0,0 +1,92 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; +import { concat } from "../../../std/bytes/mod.ts"; +import { decode } from "../../../std/encoding/utf8.ts"; + +unitTest(function blobString(): void { + const b1 = new Blob(["Hello World"]); + const str = "Test"; + const b2 = new Blob([b1, str]); + assertEquals(b2.size, b1.size + str.length); +}); + +unitTest(function blobBuffer(): void { + const buffer = new ArrayBuffer(12); + const u8 = new Uint8Array(buffer); + const f1 = new Float32Array(buffer); + const b1 = new Blob([buffer, u8]); + assertEquals(b1.size, 2 * u8.length); + const b2 = new Blob([b1, f1]); + assertEquals(b2.size, 3 * u8.length); +}); + +unitTest(function blobSlice(): void { + const blob = new Blob(["Deno", "Foo"]); + const b1 = blob.slice(0, 3, "Text/HTML"); + assert(b1 instanceof Blob); + assertEquals(b1.size, 3); + assertEquals(b1.type, "text/html"); + const b2 = blob.slice(-1, 3); + assertEquals(b2.size, 0); + const b3 = blob.slice(100, 3); + assertEquals(b3.size, 0); + const b4 = blob.slice(0, 10); + assertEquals(b4.size, blob.size); +}); + +unitTest(function blobInvalidType(): void { + const blob = new Blob(["foo"], { + type: "\u0521", + }); + + assertEquals(blob.type, ""); +}); + +unitTest(function blobShouldNotThrowError(): void { + let hasThrown = false; + + try { + const options1: object = { + ending: "utf8", + hasOwnProperty: "hasOwnProperty", + }; + const options2: object = Object.create(null); + new Blob(["Hello World"], options1); + new Blob(["Hello World"], options2); + } catch { + hasThrown = true; + } + + assertEquals(hasThrown, false); +}); + +unitTest(function nativeEndLine(): void { + const options: object = { + ending: "native", + }; + const blob = new Blob(["Hello\nWorld"], options); + + assertEquals(blob.size, Deno.build.os === "windows" ? 12 : 11); +}); + +unitTest(async function blobText(): Promise { + const blob = new Blob(["Hello World"]); + assertEquals(await blob.text(), "Hello World"); +}); + +unitTest(async function blobStream(): Promise { + const blob = new Blob(["Hello World"]); + const stream = blob.stream(); + assert(stream instanceof ReadableStream); + const reader = stream.getReader(); + let bytes = new Uint8Array(); + const read = async (): Promise => { + const { done, value } = await reader.read(); + if (!done && value) { + bytes = concat(bytes, value); + return read(); + } + }; + await read(); + assertEquals(decode(bytes), "Hello World"); +}); diff --git a/cli/tests/unit/body_test.ts b/cli/tests/unit/body_test.ts new file mode 100644 index 000000000..c8f783e04 --- /dev/null +++ b/cli/tests/unit/body_test.ts @@ -0,0 +1,74 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals, assert } from "./test_util.ts"; + +// just a hack to get a body object +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function buildBody(body: any): Body { + const stub = new Request("", { + body: body, + }); + return stub as Body; +} + +const intArrays = [ + Int8Array, + Int16Array, + Int32Array, + Uint8Array, + Uint16Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, +]; +unitTest(async function arrayBufferFromByteArrays(): Promise { + const buffer = new TextEncoder().encode("ahoyhoy8").buffer; + + for (const type of intArrays) { + const body = buildBody(new type(buffer)); + const text = new TextDecoder("utf-8").decode(await body.arrayBuffer()); + assertEquals(text, "ahoyhoy8"); + } +}); + +//FormData +unitTest( + { perms: { net: true } }, + async function bodyMultipartFormData(): Promise { + const response = await fetch( + "http://localhost:4545/cli/tests/subdir/multipart_form_data.txt" + ); + const text = await response.text(); + + const body = buildBody(text); + + // @ts-ignore + body.contentType = "multipart/form-data;boundary=boundary"; + + const formData = await body.formData(); + assert(formData.has("field_1")); + assertEquals(formData.get("field_1")!.toString(), "value_1 \r\n"); + assert(formData.has("field_2")); + } +); + +unitTest( + { perms: { net: true } }, + async function bodyURLEncodedFormData(): Promise { + const response = await fetch( + "http://localhost:4545/cli/tests/subdir/form_urlencoded.txt" + ); + const text = await response.text(); + + const body = buildBody(text); + + // @ts-ignore + body.contentType = "application/x-www-form-urlencoded"; + + const formData = await body.formData(); + assert(formData.has("field_1")); + assertEquals(formData.get("field_1")!.toString(), "Hi"); + assert(formData.has("field_2")); + assertEquals(formData.get("field_2")!.toString(), ""); + } +); diff --git a/cli/tests/unit/buffer_test.ts b/cli/tests/unit/buffer_test.ts new file mode 100644 index 000000000..ac2d7db7b --- /dev/null +++ b/cli/tests/unit/buffer_test.ts @@ -0,0 +1,294 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +// This code has been ported almost directly from Go's src/bytes/buffer_test.go +// Copyright 2009 The Go Authors. All rights reserved. BSD license. +// https://github.com/golang/go/blob/master/LICENSE +import { + assertEquals, + assert, + assertStrContains, + unitTest, +} from "./test_util.ts"; + +const { Buffer, readAll, readAllSync, writeAll, writeAllSync } = Deno; +type Buffer = Deno.Buffer; + +// N controls how many iterations of certain checks are performed. +const N = 100; +let testBytes: Uint8Array | null; +let testString: string | null; + +function init(): void { + if (testBytes == null) { + testBytes = new Uint8Array(N); + for (let i = 0; i < N; i++) { + testBytes[i] = "a".charCodeAt(0) + (i % 26); + } + const decoder = new TextDecoder(); + testString = decoder.decode(testBytes); + } +} + +function check(buf: Deno.Buffer, s: string): void { + const bytes = buf.bytes(); + assertEquals(buf.length, bytes.byteLength); + const decoder = new TextDecoder(); + const bytesStr = decoder.decode(bytes); + assertEquals(bytesStr, s); + assertEquals(buf.length, s.length); +} + +// Fill buf through n writes of byte slice fub. +// The initial contents of buf corresponds to the string s; +// the result is the final contents of buf returned as a string. +async function fillBytes( + buf: Buffer, + s: string, + n: number, + fub: Uint8Array +): Promise { + check(buf, s); + for (; n > 0; n--) { + const m = await buf.write(fub); + assertEquals(m, fub.byteLength); + const decoder = new TextDecoder(); + s += decoder.decode(fub); + check(buf, s); + } + return s; +} + +// Empty buf through repeated reads into fub. +// The initial contents of buf corresponds to the string s. +async function empty(buf: Buffer, s: string, fub: Uint8Array): Promise { + check(buf, s); + while (true) { + const r = await buf.read(fub); + if (r === null) { + break; + } + s = s.slice(r); + check(buf, s); + } + check(buf, ""); +} + +function repeat(c: string, bytes: number): Uint8Array { + assertEquals(c.length, 1); + const ui8 = new Uint8Array(bytes); + ui8.fill(c.charCodeAt(0)); + return ui8; +} + +unitTest(function bufferNewBuffer(): void { + init(); + assert(testBytes); + assert(testString); + const buf = new Buffer(testBytes.buffer as ArrayBuffer); + check(buf, testString); +}); + +unitTest(async function bufferBasicOperations(): Promise { + init(); + assert(testBytes); + assert(testString); + const buf = new Buffer(); + for (let i = 0; i < 5; i++) { + check(buf, ""); + + buf.reset(); + check(buf, ""); + + buf.truncate(0); + check(buf, ""); + + let n = await buf.write(testBytes.subarray(0, 1)); + assertEquals(n, 1); + check(buf, "a"); + + n = await buf.write(testBytes.subarray(1, 2)); + assertEquals(n, 1); + check(buf, "ab"); + + n = await buf.write(testBytes.subarray(2, 26)); + assertEquals(n, 24); + check(buf, testString.slice(0, 26)); + + buf.truncate(26); + check(buf, testString.slice(0, 26)); + + buf.truncate(20); + check(buf, testString.slice(0, 20)); + + await empty(buf, testString.slice(0, 20), new Uint8Array(5)); + await empty(buf, "", new Uint8Array(100)); + + // TODO buf.writeByte() + // TODO buf.readByte() + } +}); + +unitTest(async function bufferReadEmptyAtEOF(): Promise { + // check that EOF of 'buf' is not reached (even though it's empty) if + // results are written to buffer that has 0 length (ie. it can't store any data) + const buf = new Buffer(); + const zeroLengthTmp = new Uint8Array(0); + const result = await buf.read(zeroLengthTmp); + assertEquals(result, 0); +}); + +unitTest(async function bufferLargeByteWrites(): Promise { + init(); + const buf = new Buffer(); + const limit = 9; + for (let i = 3; i < limit; i += 3) { + const s = await fillBytes(buf, "", 5, testBytes!); + await empty(buf, s, new Uint8Array(Math.floor(testString!.length / i))); + } + check(buf, ""); +}); + +unitTest(async function bufferTooLargeByteWrites(): Promise { + init(); + const tmp = new Uint8Array(72); + const growLen = Number.MAX_VALUE; + const xBytes = repeat("x", 0); + const buf = new Buffer(xBytes.buffer as ArrayBuffer); + await buf.read(tmp); + + let err; + try { + buf.grow(growLen); + } catch (e) { + err = e; + } + + assert(err instanceof Error); + assertStrContains(err.message, "grown beyond the maximum size"); +}); + +unitTest(async function bufferLargeByteReads(): Promise { + init(); + assert(testBytes); + assert(testString); + const buf = new Buffer(); + for (let i = 3; i < 30; i += 3) { + const n = Math.floor(testBytes.byteLength / i); + const s = await fillBytes(buf, "", 5, testBytes.subarray(0, n)); + await empty(buf, s, new Uint8Array(testString.length)); + } + check(buf, ""); +}); + +unitTest(function bufferCapWithPreallocatedSlice(): void { + const buf = new Buffer(new ArrayBuffer(10)); + assertEquals(buf.capacity, 10); +}); + +unitTest(async function bufferReadFrom(): Promise { + init(); + assert(testBytes); + assert(testString); + const buf = new Buffer(); + for (let i = 3; i < 30; i += 3) { + const s = await fillBytes( + buf, + "", + 5, + testBytes.subarray(0, Math.floor(testBytes.byteLength / i)) + ); + const b = new Buffer(); + await b.readFrom(buf); + const fub = new Uint8Array(testString.length); + await empty(b, s, fub); + } +}); + +unitTest(async function bufferReadFromSync(): Promise { + init(); + assert(testBytes); + assert(testString); + const buf = new Buffer(); + for (let i = 3; i < 30; i += 3) { + const s = await fillBytes( + buf, + "", + 5, + testBytes.subarray(0, Math.floor(testBytes.byteLength / i)) + ); + const b = new Buffer(); + b.readFromSync(buf); + const fub = new Uint8Array(testString.length); + await empty(b, s, fub); + } +}); + +unitTest(async function bufferTestGrow(): Promise { + const tmp = new Uint8Array(72); + for (const startLen of [0, 100, 1000, 10000, 100000]) { + const xBytes = repeat("x", startLen); + for (const growLen of [0, 100, 1000, 10000, 100000]) { + const buf = new Buffer(xBytes.buffer as ArrayBuffer); + // If we read, this affects buf.off, which is good to test. + const nread = (await buf.read(tmp)) ?? 0; + buf.grow(growLen); + const yBytes = repeat("y", growLen); + await buf.write(yBytes); + // Check that buffer has correct data. + assertEquals( + buf.bytes().subarray(0, startLen - nread), + xBytes.subarray(nread) + ); + assertEquals( + buf.bytes().subarray(startLen - nread, startLen - nread + growLen), + yBytes + ); + } + } +}); + +unitTest(async function testReadAll(): Promise { + init(); + assert(testBytes); + const reader = new Buffer(testBytes.buffer as ArrayBuffer); + const actualBytes = await readAll(reader); + assertEquals(testBytes.byteLength, actualBytes.byteLength); + for (let i = 0; i < testBytes.length; ++i) { + assertEquals(testBytes[i], actualBytes[i]); + } +}); + +unitTest(function testReadAllSync(): void { + init(); + assert(testBytes); + const reader = new Buffer(testBytes.buffer as ArrayBuffer); + const actualBytes = readAllSync(reader); + assertEquals(testBytes.byteLength, actualBytes.byteLength); + for (let i = 0; i < testBytes.length; ++i) { + assertEquals(testBytes[i], actualBytes[i]); + } +}); + +unitTest(async function testWriteAll(): Promise { + init(); + assert(testBytes); + const writer = new Buffer(); + await writeAll(writer, testBytes); + const actualBytes = writer.bytes(); + assertEquals(testBytes.byteLength, actualBytes.byteLength); + for (let i = 0; i < testBytes.length; ++i) { + assertEquals(testBytes[i], actualBytes[i]); + } +}); + +unitTest(function testWriteAllSync(): void { + init(); + assert(testBytes); + const writer = new Buffer(); + writeAllSync(writer, testBytes); + const actualBytes = writer.bytes(); + assertEquals(testBytes.byteLength, actualBytes.byteLength); + for (let i = 0; i < testBytes.length; ++i) { + assertEquals(testBytes[i], actualBytes[i]); + } +}); diff --git a/cli/tests/unit/build_test.ts b/cli/tests/unit/build_test.ts new file mode 100644 index 000000000..987ed8d39 --- /dev/null +++ b/cli/tests/unit/build_test.ts @@ -0,0 +1,10 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +unitTest(function buildInfo(): void { + // Deno.build is injected by rollup at compile time. Here + // we check it has been properly transformed. + const { arch, os } = Deno.build; + assert(arch.length > 0); + assert(os === "darwin" || os === "windows" || os === "linux"); +}); diff --git a/cli/tests/unit/chmod_test.ts b/cli/tests/unit/chmod_test.ts new file mode 100644 index 000000000..d0d17629d --- /dev/null +++ b/cli/tests/unit/chmod_test.ts @@ -0,0 +1,149 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + function chmodSyncSuccess(): void { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const tempDir = Deno.makeTempDirSync(); + const filename = tempDir + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + + Deno.chmodSync(filename, 0o777); + + const fileInfo = Deno.statSync(filename); + assert(fileInfo.mode); + assertEquals(fileInfo.mode & 0o777, 0o777); + } +); + +// Check symlink when not on windows +unitTest( + { + ignore: Deno.build.os === "windows", + perms: { read: true, write: true }, + }, + function chmodSyncSymlinkSuccess(): void { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const tempDir = Deno.makeTempDirSync(); + + const filename = tempDir + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + const symlinkName = tempDir + "/test_symlink.txt"; + Deno.symlinkSync(filename, symlinkName); + + let symlinkInfo = Deno.lstatSync(symlinkName); + assert(symlinkInfo.mode); + const symlinkMode = symlinkInfo.mode & 0o777; // platform dependent + + Deno.chmodSync(symlinkName, 0o777); + + // Change actual file mode, not symlink + const fileInfo = Deno.statSync(filename); + assert(fileInfo.mode); + assertEquals(fileInfo.mode & 0o777, 0o777); + symlinkInfo = Deno.lstatSync(symlinkName); + assert(symlinkInfo.mode); + assertEquals(symlinkInfo.mode & 0o777, symlinkMode); + } +); + +unitTest({ perms: { write: true } }, function chmodSyncFailure(): void { + let err; + try { + const filename = "/badfile.txt"; + Deno.chmodSync(filename, 0o777); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); +}); + +unitTest({ perms: { write: false } }, function chmodSyncPerm(): void { + let err; + try { + Deno.chmodSync("/somefile.txt", 0o777); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + async function chmodSuccess(): Promise { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const tempDir = Deno.makeTempDirSync(); + const filename = tempDir + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + + await Deno.chmod(filename, 0o777); + + const fileInfo = Deno.statSync(filename); + assert(fileInfo.mode); + assertEquals(fileInfo.mode & 0o777, 0o777); + } +); + +// Check symlink when not on windows + +unitTest( + { + ignore: Deno.build.os === "windows", + perms: { read: true, write: true }, + }, + async function chmodSymlinkSuccess(): Promise { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const tempDir = Deno.makeTempDirSync(); + + const filename = tempDir + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + const symlinkName = tempDir + "/test_symlink.txt"; + Deno.symlinkSync(filename, symlinkName); + + let symlinkInfo = Deno.lstatSync(symlinkName); + assert(symlinkInfo.mode); + const symlinkMode = symlinkInfo.mode & 0o777; // platform dependent + + await Deno.chmod(symlinkName, 0o777); + + // Just change actual file mode, not symlink + const fileInfo = Deno.statSync(filename); + assert(fileInfo.mode); + assertEquals(fileInfo.mode & 0o777, 0o777); + symlinkInfo = Deno.lstatSync(symlinkName); + assert(symlinkInfo.mode); + assertEquals(symlinkInfo.mode & 0o777, symlinkMode); + } +); + +unitTest({ perms: { write: true } }, async function chmodFailure(): Promise< + void +> { + let err; + try { + const filename = "/badfile.txt"; + await Deno.chmod(filename, 0o777); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); +}); + +unitTest({ perms: { write: false } }, async function chmodPerm(): Promise< + void +> { + let err; + try { + await Deno.chmod("/somefile.txt", 0o777); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); diff --git a/cli/tests/unit/chown_test.ts b/cli/tests/unit/chown_test.ts new file mode 100644 index 000000000..724ea5a21 --- /dev/null +++ b/cli/tests/unit/chown_test.ts @@ -0,0 +1,147 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals, assert } from "./test_util.ts"; + +// chown on Windows is noop for now, so ignore its testing on Windows +if (Deno.build.os !== "windows") { + async function getUidAndGid(): Promise<{ uid: number; gid: number }> { + // get the user ID and group ID of the current process + const uidProc = Deno.run({ + stdout: "piped", + cmd: ["python", "-c", "import os; print(os.getuid())"], + }); + const gidProc = Deno.run({ + stdout: "piped", + cmd: ["python", "-c", "import os; print(os.getgid())"], + }); + + assertEquals((await uidProc.status()).code, 0); + assertEquals((await gidProc.status()).code, 0); + const uid = parseInt( + new TextDecoder("utf-8").decode(await uidProc.output()) + ); + uidProc.close(); + const gid = parseInt( + new TextDecoder("utf-8").decode(await gidProc.output()) + ); + gidProc.close(); + + return { uid, gid }; + } + + unitTest(async function chownNoWritePermission(): Promise { + const filePath = "chown_test_file.txt"; + try { + await Deno.chown(filePath, 1000, 1000); + } catch (e) { + assert(e instanceof Deno.errors.PermissionDenied); + } + }); + + unitTest( + { perms: { run: true, write: true } }, + async function chownSyncFileNotExist(): Promise { + const { uid, gid } = await getUidAndGid(); + const filePath = Deno.makeTempDirSync() + "/chown_test_file.txt"; + + try { + Deno.chownSync(filePath, uid, gid); + } catch (e) { + assert(e instanceof Deno.errors.NotFound); + } + } + ); + + unitTest( + { perms: { run: true, write: true } }, + async function chownFileNotExist(): Promise { + const { uid, gid } = await getUidAndGid(); + const filePath = (await Deno.makeTempDir()) + "/chown_test_file.txt"; + + try { + await Deno.chown(filePath, uid, gid); + } catch (e) { + assert(e instanceof Deno.errors.NotFound); + } + } + ); + + unitTest( + { perms: { write: true } }, + function chownSyncPermissionDenied(): void { + const enc = new TextEncoder(); + const dirPath = Deno.makeTempDirSync(); + const filePath = dirPath + "/chown_test_file.txt"; + const fileData = enc.encode("Hello"); + Deno.writeFileSync(filePath, fileData); + + try { + // try changing the file's owner to root + Deno.chownSync(filePath, 0, 0); + } catch (e) { + assert(e instanceof Deno.errors.PermissionDenied); + } + Deno.removeSync(dirPath, { recursive: true }); + } + ); + + unitTest( + { perms: { write: true } }, + async function chownPermissionDenied(): Promise { + const enc = new TextEncoder(); + const dirPath = await Deno.makeTempDir(); + const filePath = dirPath + "/chown_test_file.txt"; + const fileData = enc.encode("Hello"); + await Deno.writeFile(filePath, fileData); + + try { + // try changing the file's owner to root + await Deno.chown(filePath, 0, 0); + } catch (e) { + assert(e instanceof Deno.errors.PermissionDenied); + } + await Deno.remove(dirPath, { recursive: true }); + } + ); + + unitTest( + { perms: { run: true, write: true } }, + async function chownSyncSucceed(): Promise { + // TODO: when a file's owner is actually being changed, + // chown only succeeds if run under priviledged user (root) + // The test script has no such privilege, so need to find a better way to test this case + const { uid, gid } = await getUidAndGid(); + + const enc = new TextEncoder(); + const dirPath = Deno.makeTempDirSync(); + const filePath = dirPath + "/chown_test_file.txt"; + const fileData = enc.encode("Hello"); + Deno.writeFileSync(filePath, fileData); + + // the test script creates this file with the same uid and gid, + // here chown is a noop so it succeeds under non-priviledged user + Deno.chownSync(filePath, uid, gid); + + Deno.removeSync(dirPath, { recursive: true }); + } + ); + + unitTest( + { perms: { run: true, write: true } }, + async function chownSucceed(): Promise { + // TODO: same as chownSyncSucceed + const { uid, gid } = await getUidAndGid(); + + const enc = new TextEncoder(); + const dirPath = await Deno.makeTempDir(); + const filePath = dirPath + "/chown_test_file.txt"; + const fileData = enc.encode("Hello"); + await Deno.writeFile(filePath, fileData); + + // the test script creates this file with the same uid and gid, + // here chown is a noop so it succeeds under non-priviledged user + await Deno.chown(filePath, uid, gid); + + Deno.removeSync(dirPath, { recursive: true }); + } + ); +} diff --git a/cli/tests/unit/console_test.ts b/cli/tests/unit/console_test.ts new file mode 100644 index 000000000..02d71e4ca --- /dev/null +++ b/cli/tests/unit/console_test.ts @@ -0,0 +1,1139 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +// TODO(ry) The unit test functions in this module are too coarse. They should +// be broken up into smaller bits. + +// TODO(ry) These tests currentl strip all the ANSI colors out. We don't have a +// good way to control whether we produce color output or not since +// std/fmt/colors auto determines whether to put colors in or not. We need +// better infrastructure here so we can properly test the colors. + +import { assert, assertEquals, unitTest } from "./test_util.ts"; +import { stripColor } from "../../../std/fmt/colors.ts"; + +// Some of these APIs aren't exposed in the types and so we have to cast to any +// in order to "trick" TypeScript. +const { + inspect, + // eslint-disable-next-line @typescript-eslint/no-explicit-any +} = Deno as any; + +const customInspect = Deno.customInspect; +const { + Console, + stringifyArgs, + // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol +} = Deno[Deno.internal]; + +function stringify(...args: unknown[]): string { + return stripColor(stringifyArgs(args).replace(/\n$/, "")); +} + +// test cases from web-platform-tests +// via https://github.com/web-platform-tests/wpt/blob/master/console/console-is-a-namespace.any.js +unitTest(function consoleShouldBeANamespace(): void { + const prototype1 = Object.getPrototypeOf(console); + const prototype2 = Object.getPrototypeOf(prototype1); + + assertEquals(Object.getOwnPropertyNames(prototype1).length, 0); + assertEquals(prototype2, Object.prototype); +}); + +unitTest(function consoleHasRightInstance(): void { + assert(console instanceof Console); + assertEquals({} instanceof Console, false); +}); + +unitTest(function consoleTestAssertShouldNotThrowError(): void { + mockConsole((console) => { + console.assert(true); + let hasThrown = undefined; + try { + console.assert(false); + hasThrown = false; + } catch { + hasThrown = true; + } + assertEquals(hasThrown, false); + }); +}); + +unitTest(function consoleTestStringifyComplexObjects(): void { + assertEquals(stringify("foo"), "foo"); + assertEquals(stringify(["foo", "bar"]), `[ "foo", "bar" ]`); + assertEquals(stringify({ foo: "bar" }), `{ foo: "bar" }`); +}); + +unitTest(function consoleTestStringifyLongStrings(): void { + const veryLongString = "a".repeat(200); + // If we stringify an object containing the long string, it gets abbreviated. + let actual = stringify({ veryLongString }); + assert(actual.includes("...")); + assert(actual.length < 200); + // However if we stringify the string itself, we get it exactly. + actual = stringify(veryLongString); + assertEquals(actual, veryLongString); +}); + +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +unitTest(function consoleTestStringifyCircular(): void { + class Base { + a = 1; + m1() {} + } + + class Extended extends Base { + b = 2; + m2() {} + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const nestedObj: any = { + num: 1, + bool: true, + str: "a", + method() {}, + async asyncMethod() {}, + *generatorMethod() {}, + un: undefined, + nu: null, + arrowFunc: () => {}, + extendedClass: new Extended(), + nFunc: new Function(), + extendedCstr: Extended, + }; + + const circularObj = { + num: 2, + bool: false, + str: "b", + method() {}, + un: undefined, + nu: null, + nested: nestedObj, + emptyObj: {}, + arr: [1, "s", false, null, nestedObj], + baseClass: new Base(), + }; + + nestedObj.o = circularObj; + const nestedObjExpected = `{ + num: 1, + bool: true, + str: "a", + method: [Function: method], + asyncMethod: [AsyncFunction: asyncMethod], + generatorMethod: [GeneratorFunction: generatorMethod], + un: undefined, + nu: null, + arrowFunc: [Function: arrowFunc], + extendedClass: Extended { a: 1, b: 2 }, + nFunc: [Function], + extendedCstr: [Function: Extended], + o: { + num: 2, + bool: false, + str: "b", + method: [Function: method], + un: undefined, + nu: null, + nested: [Circular], + emptyObj: {}, + arr: [ 1, "s", false, null, [Circular] ], + baseClass: Base { a: 1 } + } +}`; + + assertEquals(stringify(1), "1"); + assertEquals(stringify(-0), "-0"); + assertEquals(stringify(1n), "1n"); + assertEquals(stringify("s"), "s"); + assertEquals(stringify(false), "false"); + assertEquals(stringify(new Number(1)), "[Number: 1]"); + assertEquals(stringify(new Boolean(true)), "[Boolean: true]"); + assertEquals(stringify(new String("deno")), `[String: "deno"]`); + assertEquals(stringify(/[0-9]*/), "/[0-9]*/"); + assertEquals( + stringify(new Date("2018-12-10T02:26:59.002Z")), + "2018-12-10T02:26:59.002Z" + ); + assertEquals(stringify(new Set([1, 2, 3])), "Set { 1, 2, 3 }"); + assertEquals( + stringify( + new Map([ + [1, "one"], + [2, "two"], + ]) + ), + `Map { 1 => "one", 2 => "two" }` + ); + assertEquals(stringify(new WeakSet()), "WeakSet { [items unknown] }"); + assertEquals(stringify(new WeakMap()), "WeakMap { [items unknown] }"); + assertEquals(stringify(Symbol(1)), "Symbol(1)"); + assertEquals(stringify(null), "null"); + assertEquals(stringify(undefined), "undefined"); + assertEquals(stringify(new Extended()), "Extended { a: 1, b: 2 }"); + assertEquals( + stringify(function f(): void {}), + "[Function: f]" + ); + assertEquals( + stringify(async function af(): Promise {}), + "[AsyncFunction: af]" + ); + assertEquals( + stringify(function* gf() {}), + "[GeneratorFunction: gf]" + ); + assertEquals( + stringify(async function* agf() {}), + "[AsyncGeneratorFunction: agf]" + ); + assertEquals( + stringify(new Uint8Array([1, 2, 3])), + "Uint8Array(3) [ 1, 2, 3 ]" + ); + assertEquals(stringify(Uint8Array.prototype), "TypedArray {}"); + assertEquals( + stringify({ a: { b: { c: { d: new Set([1]) } } } }), + "{ a: { b: { c: { d: [Set] } } } }" + ); + assertEquals(stringify(nestedObj), nestedObjExpected); + assertEquals(stringify(JSON), 'JSON { Symbol(Symbol.toStringTag): "JSON" }'); + assertEquals( + stringify(console), + `{ + log: [Function], + debug: [Function], + info: [Function], + dir: [Function], + dirxml: [Function], + warn: [Function], + error: [Function], + assert: [Function], + count: [Function], + countReset: [Function], + table: [Function], + time: [Function], + timeLog: [Function], + timeEnd: [Function], + group: [Function], + groupCollapsed: [Function], + groupEnd: [Function], + clear: [Function], + trace: [Function], + indentLevel: 0, + Symbol(isConsoleInstance): true +}` + ); + assertEquals( + stringify({ str: 1, [Symbol.for("sym")]: 2, [Symbol.toStringTag]: "TAG" }), + 'TAG { str: 1, Symbol(sym): 2, Symbol(Symbol.toStringTag): "TAG" }' + ); + // test inspect is working the same + assertEquals(stripColor(inspect(nestedObj)), nestedObjExpected); +}); +/* eslint-enable @typescript-eslint/explicit-function-return-type */ + +unitTest(function consoleTestStringifyWithDepth(): void { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const nestedObj: any = { a: { b: { c: { d: { e: { f: 42 } } } } } }; + assertEquals( + stripColor(stringifyArgs([nestedObj], { depth: 3 })), + "{ a: { b: { c: [Object] } } }" + ); + assertEquals( + stripColor(stringifyArgs([nestedObj], { depth: 4 })), + "{ a: { b: { c: { d: [Object] } } } }" + ); + assertEquals( + stripColor(stringifyArgs([nestedObj], { depth: 0 })), + "[Object]" + ); + assertEquals( + stripColor(stringifyArgs([nestedObj])), + "{ a: { b: { c: { d: [Object] } } } }" + ); + // test inspect is working the same way + assertEquals( + stripColor(inspect(nestedObj, { depth: 4 })), + "{ a: { b: { c: { d: [Object] } } } }" + ); +}); + +unitTest(function consoleTestStringifyLargeObject(): void { + const obj = { + a: 2, + o: { + a: "1", + b: "2", + c: "3", + d: "4", + e: "5", + f: "6", + g: 10, + asd: 2, + asda: 3, + x: { a: "asd", x: 3 }, + }, + }; + assertEquals( + stringify(obj), + `{ + a: 2, + o: { + a: "1", + b: "2", + c: "3", + d: "4", + e: "5", + f: "6", + g: 10, + asd: 2, + asda: 3, + x: { a: "asd", x: 3 } + } +}` + ); +}); + +unitTest(function consoleTestStringifyIterable() { + const shortArray = [1, 2, 3, 4, 5]; + assertEquals(stringify(shortArray), "[ 1, 2, 3, 4, 5 ]"); + + const longArray = new Array(200).fill(0); + assertEquals( + stringify(longArray), + `[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ... 100 more items +]` + ); + + const obj = { a: "a", longArray }; + assertEquals( + stringify(obj), + `{ + a: "a", + longArray: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ... 100 more items + ] +}` + ); + + const shortMap = new Map([ + ["a", 0], + ["b", 1], + ]); + assertEquals(stringify(shortMap), `Map { "a" => 0, "b" => 1 }`); + + const longMap = new Map(); + for (const key of Array(200).keys()) { + longMap.set(`${key}`, key); + } + assertEquals( + stringify(longMap), + `Map { + "0" => 0, + "1" => 1, + "2" => 2, + "3" => 3, + "4" => 4, + "5" => 5, + "6" => 6, + "7" => 7, + "8" => 8, + "9" => 9, + "10" => 10, + "11" => 11, + "12" => 12, + "13" => 13, + "14" => 14, + "15" => 15, + "16" => 16, + "17" => 17, + "18" => 18, + "19" => 19, + "20" => 20, + "21" => 21, + "22" => 22, + "23" => 23, + "24" => 24, + "25" => 25, + "26" => 26, + "27" => 27, + "28" => 28, + "29" => 29, + "30" => 30, + "31" => 31, + "32" => 32, + "33" => 33, + "34" => 34, + "35" => 35, + "36" => 36, + "37" => 37, + "38" => 38, + "39" => 39, + "40" => 40, + "41" => 41, + "42" => 42, + "43" => 43, + "44" => 44, + "45" => 45, + "46" => 46, + "47" => 47, + "48" => 48, + "49" => 49, + "50" => 50, + "51" => 51, + "52" => 52, + "53" => 53, + "54" => 54, + "55" => 55, + "56" => 56, + "57" => 57, + "58" => 58, + "59" => 59, + "60" => 60, + "61" => 61, + "62" => 62, + "63" => 63, + "64" => 64, + "65" => 65, + "66" => 66, + "67" => 67, + "68" => 68, + "69" => 69, + "70" => 70, + "71" => 71, + "72" => 72, + "73" => 73, + "74" => 74, + "75" => 75, + "76" => 76, + "77" => 77, + "78" => 78, + "79" => 79, + "80" => 80, + "81" => 81, + "82" => 82, + "83" => 83, + "84" => 84, + "85" => 85, + "86" => 86, + "87" => 87, + "88" => 88, + "89" => 89, + "90" => 90, + "91" => 91, + "92" => 92, + "93" => 93, + "94" => 94, + "95" => 95, + "96" => 96, + "97" => 97, + "98" => 98, + "99" => 99, + ... 100 more items +}` + ); + + const shortSet = new Set([1, 2, 3]); + assertEquals(stringify(shortSet), `Set { 1, 2, 3 }`); + const longSet = new Set(); + for (const key of Array(200).keys()) { + longSet.add(key); + } + assertEquals( + stringify(longSet), + `Set { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + ... 100 more items +}` + ); + + const withEmptyEl = Array(10); + withEmptyEl.fill(0, 4, 6); + assertEquals( + stringify(withEmptyEl), + `[ <4 empty items>, 0, 0, <4 empty items> ]` + ); + + /* TODO(ry) Fix this test + const lWithEmptyEl = Array(200); + lWithEmptyEl.fill(0, 50, 80); + assertEquals( + stringify(lWithEmptyEl), + `[ + <50 empty items>, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, <120 empty items> +]` + ); +*/ +}); + +unitTest(async function consoleTestStringifyPromises(): Promise { + const pendingPromise = new Promise((_res, _rej) => {}); + assertEquals(stringify(pendingPromise), "Promise { }"); + + const resolvedPromise = new Promise((res, _rej) => { + res("Resolved!"); + }); + assertEquals(stringify(resolvedPromise), `Promise { "Resolved!" }`); + + let rejectedPromise; + try { + rejectedPromise = new Promise((_, rej) => { + rej(Error("Whoops")); + }); + await rejectedPromise; + } catch (err) {} + const strLines = stringify(rejectedPromise).split("\n"); + assertEquals(strLines[0], "Promise {"); + assertEquals(strLines[1], " Error: Whoops"); +}); + +unitTest(function consoleTestWithCustomInspector(): void { + class A { + [customInspect](): string { + return "b"; + } + } + + assertEquals(stringify(new A()), "b"); +}); + +unitTest(function consoleTestWithCustomInspectorError(): void { + class A { + [customInspect](): string { + throw new Error("BOOM"); + return "b"; + } + } + + assertEquals(stringify(new A()), "A {}"); + + class B { + constructor(public field: { a: string }) {} + [customInspect](): string { + return this.field.a; + } + } + + assertEquals(stringify(new B({ a: "a" })), "a"); + assertEquals( + stringify(B.prototype), + "{ Symbol(Deno.symbols.customInspect): [Function: [Deno.symbols.customInspect]] }" + ); +}); + +unitTest(function consoleTestWithIntegerFormatSpecifier(): void { + assertEquals(stringify("%i"), "%i"); + assertEquals(stringify("%i", 42.0), "42"); + assertEquals(stringify("%i", 42), "42"); + assertEquals(stringify("%i", "42"), "42"); + assertEquals(stringify("%i", "42.0"), "42"); + assertEquals(stringify("%i", 1.5), "1"); + assertEquals(stringify("%i", -0.5), "0"); + assertEquals(stringify("%i", ""), "NaN"); + assertEquals(stringify("%i", Symbol()), "NaN"); + assertEquals(stringify("%i %d", 42, 43), "42 43"); + assertEquals(stringify("%d %i", 42), "42 %i"); + assertEquals(stringify("%d", 12345678901234567890123), "1"); + assertEquals( + stringify("%i", 12345678901234567890123n), + "12345678901234567890123n" + ); +}); + +unitTest(function consoleTestWithFloatFormatSpecifier(): void { + assertEquals(stringify("%f"), "%f"); + assertEquals(stringify("%f", 42.0), "42"); + assertEquals(stringify("%f", 42), "42"); + assertEquals(stringify("%f", "42"), "42"); + assertEquals(stringify("%f", "42.0"), "42"); + assertEquals(stringify("%f", 1.5), "1.5"); + assertEquals(stringify("%f", -0.5), "-0.5"); + assertEquals(stringify("%f", Math.PI), "3.141592653589793"); + assertEquals(stringify("%f", ""), "NaN"); + assertEquals(stringify("%f", Symbol("foo")), "NaN"); + assertEquals(stringify("%f", 5n), "5"); + assertEquals(stringify("%f %f", 42, 43), "42 43"); + assertEquals(stringify("%f %f", 42), "42 %f"); +}); + +unitTest(function consoleTestWithStringFormatSpecifier(): void { + assertEquals(stringify("%s"), "%s"); + assertEquals(stringify("%s", undefined), "undefined"); + assertEquals(stringify("%s", "foo"), "foo"); + assertEquals(stringify("%s", 42), "42"); + assertEquals(stringify("%s", "42"), "42"); + assertEquals(stringify("%s %s", 42, 43), "42 43"); + assertEquals(stringify("%s %s", 42), "42 %s"); + assertEquals(stringify("%s", Symbol("foo")), "Symbol(foo)"); +}); + +unitTest(function consoleTestWithObjectFormatSpecifier(): void { + assertEquals(stringify("%o"), "%o"); + assertEquals(stringify("%o", 42), "42"); + assertEquals(stringify("%o", "foo"), "foo"); + assertEquals(stringify("o: %o, a: %O", {}, []), "o: {}, a: []"); + assertEquals(stringify("%o", { a: 42 }), "{ a: 42 }"); + assertEquals( + stringify("%o", { a: { b: { c: { d: new Set([1]) } } } }), + "{ a: { b: { c: { d: [Set] } } } }" + ); +}); + +unitTest(function consoleTestWithVariousOrInvalidFormatSpecifier(): void { + assertEquals(stringify("%s:%s"), "%s:%s"); + assertEquals(stringify("%i:%i"), "%i:%i"); + assertEquals(stringify("%d:%d"), "%d:%d"); + assertEquals(stringify("%%s%s", "foo"), "%sfoo"); + assertEquals(stringify("%s:%s", undefined), "undefined:%s"); + assertEquals(stringify("%s:%s", "foo", "bar"), "foo:bar"); + assertEquals(stringify("%s:%s", "foo", "bar", "baz"), "foo:bar baz"); + assertEquals(stringify("%%%s%%", "hi"), "%hi%"); + assertEquals(stringify("%d:%d", 12), "12:%d"); + assertEquals(stringify("%i:%i", 12), "12:%i"); + assertEquals(stringify("%f:%f", 12), "12:%f"); + assertEquals(stringify("o: %o, a: %o", {}), "o: {}, a: %o"); + assertEquals(stringify("abc%", 1), "abc% 1"); +}); + +unitTest(function consoleTestCallToStringOnLabel(): void { + const methods = ["count", "countReset", "time", "timeLog", "timeEnd"]; + mockConsole((console) => { + for (const method of methods) { + let hasCalled = false; + // @ts-ignore + console[method]({ + toString(): void { + hasCalled = true; + }, + }); + assertEquals(hasCalled, true); + } + }); +}); + +unitTest(function consoleTestError(): void { + class MyError extends Error { + constructor(errStr: string) { + super(errStr); + this.name = "MyError"; + } + } + try { + throw new MyError("This is an error"); + } catch (e) { + assert( + stringify(e) + .split("\n")[0] // error has been caught + .includes("MyError: This is an error") + ); + } +}); + +unitTest(function consoleTestClear(): void { + mockConsole((console, out) => { + console.clear(); + assertEquals(out.toString(), "\x1b[1;1H" + "\x1b[0J"); + }); +}); + +// Test bound this issue +unitTest(function consoleDetachedLog(): void { + mockConsole((console) => { + const log = console.log; + const dir = console.dir; + const dirxml = console.dirxml; + const debug = console.debug; + const info = console.info; + const warn = console.warn; + const error = console.error; + const consoleAssert = console.assert; + const consoleCount = console.count; + const consoleCountReset = console.countReset; + const consoleTable = console.table; + const consoleTime = console.time; + const consoleTimeLog = console.timeLog; + const consoleTimeEnd = console.timeEnd; + const consoleGroup = console.group; + const consoleGroupEnd = console.groupEnd; + const consoleClear = console.clear; + log("Hello world"); + dir("Hello world"); + dirxml("Hello world"); + debug("Hello world"); + info("Hello world"); + warn("Hello world"); + error("Hello world"); + consoleAssert(true); + consoleCount("Hello world"); + consoleCountReset("Hello world"); + consoleTable({ test: "Hello world" }); + consoleTime("Hello world"); + consoleTimeLog("Hello world"); + consoleTimeEnd("Hello world"); + consoleGroup("Hello world"); + consoleGroupEnd(); + consoleClear(); + }); +}); + +class StringBuffer { + chunks: string[] = []; + add(x: string): void { + this.chunks.push(x); + } + toString(): string { + return this.chunks.join(""); + } +} + +type ConsoleExamineFunc = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + csl: any, + out: StringBuffer, + err?: StringBuffer, + both?: StringBuffer +) => void; + +function mockConsole(f: ConsoleExamineFunc): void { + const out = new StringBuffer(); + const err = new StringBuffer(); + const both = new StringBuffer(); + const csl = new Console( + (x: string, isErr: boolean, printsNewLine: boolean): void => { + const content = x + (printsNewLine ? "\n" : ""); + const buf = isErr ? err : out; + buf.add(content); + both.add(content); + } + ); + f(csl, out, err, both); +} + +// console.group test +unitTest(function consoleGroup(): void { + mockConsole((console, out): void => { + console.group("1"); + console.log("2"); + console.group("3"); + console.log("4"); + console.groupEnd(); + console.groupEnd(); + console.log("5"); + console.log("6"); + + assertEquals( + out.toString(), + `1 + 2 + 3 + 4 +5 +6 +` + ); + }); +}); + +// console.group with console.warn test +unitTest(function consoleGroupWarn(): void { + mockConsole((console, _out, _err, both): void => { + assert(both); + console.warn("1"); + console.group(); + console.warn("2"); + console.group(); + console.warn("3"); + console.groupEnd(); + console.warn("4"); + console.groupEnd(); + console.warn("5"); + + console.warn("6"); + console.warn("7"); + assertEquals( + both.toString(), + `1 + 2 + 3 + 4 +5 +6 +7 +` + ); + }); +}); + +// console.table test +unitTest(function consoleTable(): void { + mockConsole((console, out): void => { + console.table({ a: "test", b: 1 }); + assertEquals( + stripColor(out.toString()), + `┌───────┬────────┐ +│ (idx) │ Values │ +├───────┼────────┤ +│ a │ "test" │ +│ b │ 1 │ +└───────┴────────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table({ a: { b: 10 }, b: { b: 20, c: 30 } }, ["c"]); + assertEquals( + stripColor(out.toString()), + `┌───────┬────┐ +│ (idx) │ c │ +├───────┼────┤ +│ a │ │ +│ b │ 30 │ +└───────┴────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table([1, 2, [3, [4]], [5, 6], [[7], [8]]]); + assertEquals( + stripColor(out.toString()), + `┌───────┬───────┬───────┬────────┐ +│ (idx) │ 0 │ 1 │ Values │ +├───────┼───────┼───────┼────────┤ +│ 0 │ │ │ 1 │ +│ 1 │ │ │ 2 │ +│ 2 │ 3 │ [ 4 ] │ │ +│ 3 │ 5 │ 6 │ │ +│ 4 │ [ 7 ] │ [ 8 ] │ │ +└───────┴───────┴───────┴────────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table(new Set([1, 2, 3, "test"])); + assertEquals( + stripColor(out.toString()), + `┌────────────┬────────┐ +│ (iter idx) │ Values │ +├────────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +│ 3 │ "test" │ +└────────────┴────────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table( + new Map([ + [1, "one"], + [2, "two"], + ]) + ); + assertEquals( + stripColor(out.toString()), + `┌────────────┬─────┬────────┐ +│ (iter idx) │ Key │ Values │ +├────────────┼─────┼────────┤ +│ 0 │ 1 │ "one" │ +│ 1 │ 2 │ "two" │ +└────────────┴─────┴────────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table({ + a: true, + b: { c: { d: 10 }, e: [1, 2, [5, 6]] }, + f: "test", + g: new Set([1, 2, 3, "test"]), + h: new Map([[1, "one"]]), + }); + assertEquals( + stripColor(out.toString()), + `┌───────┬───────────┬───────────────────┬────────┐ +│ (idx) │ c │ e │ Values │ +├───────┼───────────┼───────────────────┼────────┤ +│ a │ │ │ true │ +│ b │ { d: 10 } │ [ 1, 2, [Array] ] │ │ +│ f │ │ │ "test" │ +│ g │ │ │ │ +│ h │ │ │ │ +└───────┴───────────┴───────────────────┴────────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table([ + 1, + "test", + false, + { a: 10 }, + ["test", { b: 20, c: "test" }], + ]); + assertEquals( + stripColor(out.toString()), + `┌───────┬────────┬──────────────────────┬────┬────────┐ +│ (idx) │ 0 │ 1 │ a │ Values │ +├───────┼────────┼──────────────────────┼────┼────────┤ +│ 0 │ │ │ │ 1 │ +│ 1 │ │ │ │ "test" │ +│ 2 │ │ │ │ false │ +│ 3 │ │ │ 10 │ │ +│ 4 │ "test" │ { b: 20, c: "test" } │ │ │ +└───────┴────────┴──────────────────────┴────┴────────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table([]); + assertEquals( + stripColor(out.toString()), + `┌───────┐ +│ (idx) │ +├───────┤ +└───────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table({}); + assertEquals( + stripColor(out.toString()), + `┌───────┐ +│ (idx) │ +├───────┤ +└───────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table(new Set()); + assertEquals( + stripColor(out.toString()), + `┌────────────┐ +│ (iter idx) │ +├────────────┤ +└────────────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table(new Map()); + assertEquals( + stripColor(out.toString()), + `┌────────────┐ +│ (iter idx) │ +├────────────┤ +└────────────┘ +` + ); + }); + mockConsole((console, out): void => { + console.table("test"); + assertEquals(out.toString(), "test\n"); + }); + mockConsole((console, out): void => { + console.table(["Hello", "你好", "Amapá"]); + assertEquals( + stripColor(out.toString()), + `┌───────┬─────────┐ +│ (idx) │ Values │ +├───────┼─────────┤ +│ 0 │ "Hello" │ +│ 1 │ "你好" │ +│ 2 │ "Amapá" │ +└───────┴─────────┘ +` + ); + }); +}); + +// console.log(Error) test +unitTest(function consoleLogShouldNotThrowError(): void { + mockConsole((console) => { + let result = 0; + try { + console.log(new Error("foo")); + result = 1; + } catch (e) { + result = 2; + } + assertEquals(result, 1); + }); + + // output errors to the console should not include "Uncaught" + mockConsole((console, out): void => { + console.log(new Error("foo")); + assertEquals(out.toString().includes("Uncaught"), false); + }); +}); + +// console.log(Invalid Date) test +unitTest(function consoleLogShoultNotThrowErrorWhenInvalidDateIsPassed(): void { + mockConsole((console, out) => { + const invalidDate = new Date("test"); + console.log(invalidDate); + assertEquals(stripColor(out.toString()), "Invalid Date\n"); + }); +}); + +// console.dir test +unitTest(function consoleDir(): void { + mockConsole((console, out): void => { + console.dir("DIR"); + assertEquals(out.toString(), "DIR\n"); + }); + mockConsole((console, out): void => { + console.dir("DIR", { indentLevel: 2 }); + assertEquals(out.toString(), " DIR\n"); + }); +}); + +// console.dir test +unitTest(function consoleDirXml(): void { + mockConsole((console, out): void => { + console.dirxml("DIRXML"); + assertEquals(out.toString(), "DIRXML\n"); + }); + mockConsole((console, out): void => { + console.dirxml("DIRXML", { indentLevel: 2 }); + assertEquals(out.toString(), " DIRXML\n"); + }); +}); + +// console.trace test +unitTest(function consoleTrace(): void { + mockConsole((console, _out, err): void => { + console.trace("%s", "custom message"); + assert(err); + assert(err.toString().includes("Trace: custom message")); + }); +}); diff --git a/cli/tests/unit/copy_file_test.ts b/cli/tests/unit/copy_file_test.ts new file mode 100644 index 000000000..986bab53b --- /dev/null +++ b/cli/tests/unit/copy_file_test.ts @@ -0,0 +1,176 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +function readFileString(filename: string): string { + const dataRead = Deno.readFileSync(filename); + const dec = new TextDecoder("utf-8"); + return dec.decode(dataRead); +} + +function writeFileString(filename: string, s: string): void { + const enc = new TextEncoder(); + const data = enc.encode(s); + Deno.writeFileSync(filename, data, { mode: 0o666 }); +} + +function assertSameContent(filename1: string, filename2: string): void { + const data1 = Deno.readFileSync(filename1); + const data2 = Deno.readFileSync(filename2); + assertEquals(data1, data2); +} + +unitTest( + { perms: { read: true, write: true } }, + function copyFileSyncSuccess(): void { + const tempDir = Deno.makeTempDirSync(); + const fromFilename = tempDir + "/from.txt"; + const toFilename = tempDir + "/to.txt"; + writeFileString(fromFilename, "Hello world!"); + Deno.copyFileSync(fromFilename, toFilename); + // No change to original file + assertEquals(readFileString(fromFilename), "Hello world!"); + // Original == Dest + assertSameContent(fromFilename, toFilename); + } +); + +unitTest( + { perms: { write: true, read: true } }, + function copyFileSyncFailure(): void { + const tempDir = Deno.makeTempDirSync(); + const fromFilename = tempDir + "/from.txt"; + const toFilename = tempDir + "/to.txt"; + // We skip initial writing here, from.txt does not exist + let err; + try { + Deno.copyFileSync(fromFilename, toFilename); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: false } }, + function copyFileSyncPerm1(): void { + let caughtError = false; + try { + Deno.copyFileSync("/from.txt", "/to.txt"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); + } +); + +unitTest( + { perms: { write: false, read: true } }, + function copyFileSyncPerm2(): void { + let caughtError = false; + try { + Deno.copyFileSync("/from.txt", "/to.txt"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function copyFileSyncOverwrite(): void { + const tempDir = Deno.makeTempDirSync(); + const fromFilename = tempDir + "/from.txt"; + const toFilename = tempDir + "/to.txt"; + writeFileString(fromFilename, "Hello world!"); + // Make Dest exist and have different content + writeFileString(toFilename, "Goodbye!"); + Deno.copyFileSync(fromFilename, toFilename); + // No change to original file + assertEquals(readFileString(fromFilename), "Hello world!"); + // Original == Dest + assertSameContent(fromFilename, toFilename); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function copyFileSuccess(): Promise { + const tempDir = Deno.makeTempDirSync(); + const fromFilename = tempDir + "/from.txt"; + const toFilename = tempDir + "/to.txt"; + writeFileString(fromFilename, "Hello world!"); + await Deno.copyFile(fromFilename, toFilename); + // No change to original file + assertEquals(readFileString(fromFilename), "Hello world!"); + // Original == Dest + assertSameContent(fromFilename, toFilename); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function copyFileFailure(): Promise { + const tempDir = Deno.makeTempDirSync(); + const fromFilename = tempDir + "/from.txt"; + const toFilename = tempDir + "/to.txt"; + // We skip initial writing here, from.txt does not exist + let err; + try { + await Deno.copyFile(fromFilename, toFilename); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function copyFileOverwrite(): Promise { + const tempDir = Deno.makeTempDirSync(); + const fromFilename = tempDir + "/from.txt"; + const toFilename = tempDir + "/to.txt"; + writeFileString(fromFilename, "Hello world!"); + // Make Dest exist and have different content + writeFileString(toFilename, "Goodbye!"); + await Deno.copyFile(fromFilename, toFilename); + // No change to original file + assertEquals(readFileString(fromFilename), "Hello world!"); + // Original == Dest + assertSameContent(fromFilename, toFilename); + } +); + +unitTest( + { perms: { read: false, write: true } }, + async function copyFilePerm1(): Promise { + let caughtError = false; + try { + await Deno.copyFile("/from.txt", "/to.txt"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); + } +); + +unitTest( + { perms: { read: true, write: false } }, + async function copyFilePerm2(): Promise { + let caughtError = false; + try { + await Deno.copyFile("/from.txt", "/to.txt"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); + } +); diff --git a/cli/tests/unit/custom_event_test.ts b/cli/tests/unit/custom_event_test.ts new file mode 100644 index 000000000..a8b2fcf88 --- /dev/null +++ b/cli/tests/unit/custom_event_test.ts @@ -0,0 +1,27 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals } from "./test_util.ts"; + +unitTest(function customEventInitializedWithDetail(): void { + const type = "touchstart"; + const detail = { message: "hello" }; + const customEventInit = { + bubbles: true, + cancelable: true, + detail, + } as CustomEventInit; + const event = new CustomEvent(type, customEventInit); + + assertEquals(event.bubbles, true); + assertEquals(event.cancelable, true); + assertEquals(event.currentTarget, null); + assertEquals(event.detail, detail); + assertEquals(event.isTrusted, false); + assertEquals(event.target, null); + assertEquals(event.type, type); +}); + +unitTest(function toStringShouldBeWebCompatibility(): void { + const type = "touchstart"; + const event = new CustomEvent(type, {}); + assertEquals(event.toString(), "[object CustomEvent]"); +}); diff --git a/cli/tests/unit/dir_test.ts b/cli/tests/unit/dir_test.ts new file mode 100644 index 000000000..dc05d9564 --- /dev/null +++ b/cli/tests/unit/dir_test.ts @@ -0,0 +1,60 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest({ perms: { read: true } }, function dirCwdNotNull(): void { + assert(Deno.cwd() != null); +}); + +unitTest( + { perms: { read: true, write: true } }, + function dirCwdChdirSuccess(): void { + const initialdir = Deno.cwd(); + const path = Deno.makeTempDirSync(); + Deno.chdir(path); + const current = Deno.cwd(); + if (Deno.build.os === "darwin") { + assertEquals(current, "/private" + path); + } else { + assertEquals(current, path); + } + Deno.chdir(initialdir); + } +); + +unitTest({ perms: { read: true, write: true } }, function dirCwdError(): void { + // excluding windows since it throws resource busy, while removeSync + if (["linux", "darwin"].includes(Deno.build.os)) { + const initialdir = Deno.cwd(); + const path = Deno.makeTempDirSync(); + Deno.chdir(path); + Deno.removeSync(path); + try { + Deno.cwd(); + throw Error("current directory removed, should throw error"); + } catch (err) { + if (err instanceof Deno.errors.NotFound) { + assert(err.name === "NotFound"); + } else { + throw Error("raised different exception"); + } + } + Deno.chdir(initialdir); + } +}); + +unitTest( + { perms: { read: true, write: true } }, + function dirChdirError(): void { + const path = Deno.makeTempDirSync() + "test"; + try { + Deno.chdir(path); + throw Error("directory not available, should throw error"); + } catch (err) { + if (err instanceof Deno.errors.NotFound) { + assert(err.name === "NotFound"); + } else { + throw Error("raised different exception"); + } + } + } +); diff --git a/cli/tests/unit/dispatch_json_test.ts b/cli/tests/unit/dispatch_json_test.ts new file mode 100644 index 000000000..4e95b86a2 --- /dev/null +++ b/cli/tests/unit/dispatch_json_test.ts @@ -0,0 +1,32 @@ +import { assert, unitTest, assertMatch, unreachable } from "./test_util.ts"; + +const openErrorStackPattern = new RegExp( + `^.* + at unwrapResponse \\(.*dispatch_json\\.ts:.*\\) + at Object.sendAsync \\(.*dispatch_json\\.ts:.*\\) + at async Object\\.open \\(.*files\\.ts:.*\\).*$`, + "ms" +); + +unitTest( + { perms: { read: true } }, + async function sendAsyncStackTrace(): Promise { + await Deno.open("nonexistent.txt") + .then(unreachable) + .catch((error): void => { + assertMatch(error.stack, openErrorStackPattern); + }); + } +); + +unitTest(function malformedJsonControlBuffer(): void { + // @ts-ignore + const opId = Deno.core.ops()["op_open"]; + // @ts-ignore + const res = Deno.core.send(opId, new Uint8Array([1, 2, 3, 4, 5])); + const resText = new TextDecoder().decode(res); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const resJson = JSON.parse(resText) as any; + assert(!resJson.ok); + assert(resJson.err); +}); diff --git a/cli/tests/unit/dispatch_minimal_test.ts b/cli/tests/unit/dispatch_minimal_test.ts new file mode 100644 index 000000000..afc17f4fb --- /dev/null +++ b/cli/tests/unit/dispatch_minimal_test.ts @@ -0,0 +1,43 @@ +import { + assert, + assertEquals, + assertMatch, + unitTest, + unreachable, +} from "./test_util.ts"; + +const readErrorStackPattern = new RegExp( + `^.* + at unwrapResponse \\(.*dispatch_minimal\\.ts:.*\\) + at Object.sendAsyncMinimal \\(.*dispatch_minimal\\.ts:.*\\) + at async Object\\.read \\(.*io\\.ts:.*\\).*$`, + "ms" +); + +unitTest(async function sendAsyncStackTrace(): Promise { + const buf = new Uint8Array(10); + const rid = 10; + try { + await Deno.read(rid, buf); + unreachable(); + } catch (error) { + assertMatch(error.stack, readErrorStackPattern); + } +}); + +unitTest(function malformedMinimalControlBuffer(): void { + // @ts-ignore + const readOpId = Deno.core.ops()["op_read"]; + // @ts-ignore + const res = Deno.core.send(readOpId, new Uint8Array([1, 2, 3, 4, 5])); + const header = res.slice(0, 12); + const buf32 = new Int32Array( + header.buffer, + header.byteOffset, + header.byteLength / 4 + ); + const arg = buf32[1]; + const message = new TextDecoder().decode(res.slice(12)).trim(); + assert(arg < 0); + assertEquals(message, "Unparsable control buffer"); +}); diff --git a/cli/tests/unit/dom_exception_test.ts b/cli/tests/unit/dom_exception_test.ts new file mode 100644 index 000000000..2eb7633e1 --- /dev/null +++ b/cli/tests/unit/dom_exception_test.ts @@ -0,0 +1,9 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals, assert } from "./test_util.ts"; + +unitTest(function testDomError() { + const de = new DOMException("foo", "bar"); + assert(de); + assertEquals(de.message, "foo"); + assertEquals(de.name, "bar"); +}); diff --git a/cli/tests/unit/dom_iterable_test.ts b/cli/tests/unit/dom_iterable_test.ts new file mode 100644 index 000000000..b9435b3bc --- /dev/null +++ b/cli/tests/unit/dom_iterable_test.ts @@ -0,0 +1,87 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +function setup() { + const dataSymbol = Symbol("data symbol"); + class Base { + [dataSymbol] = new Map(); + + constructor( + data: Array<[string, number]> | IterableIterator<[string, number]> + ) { + for (const [key, value] of data) { + this[dataSymbol].set(key, value); + } + } + } + + return { + Base, + // This is using an internal API we don't want published as types, so having + // to cast to any to "trick" TypeScript + // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol + DomIterable: Deno[Deno.internal].DomIterableMixin(Base, dataSymbol), + }; +} + +unitTest(function testDomIterable(): void { + const { DomIterable, Base } = setup(); + + const fixture: Array<[string, number]> = [ + ["foo", 1], + ["bar", 2], + ]; + + const domIterable = new DomIterable(fixture); + + assertEquals(Array.from(domIterable.entries()), fixture); + assertEquals(Array.from(domIterable.values()), [1, 2]); + assertEquals(Array.from(domIterable.keys()), ["foo", "bar"]); + + let result: Array<[string, number]> = []; + for (const [key, value] of domIterable) { + assert(key != null); + assert(value != null); + result.push([key, value]); + } + assertEquals(fixture, result); + + result = []; + const scope = {}; + function callback( + this: typeof scope, + value: number, + key: string, + parent: typeof domIterable + ): void { + assertEquals(parent, domIterable); + assert(key != null); + assert(value != null); + assert(this === scope); + result.push([key, value]); + } + domIterable.forEach(callback, scope); + assertEquals(fixture, result); + + assertEquals(DomIterable.name, Base.name); +}); + +unitTest(function testDomIterableScope(): void { + const { DomIterable } = setup(); + + const domIterable = new DomIterable([["foo", 1]]); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function checkScope(thisArg: any, expected: any): void { + function callback(this: typeof thisArg): void { + assertEquals(this, expected); + } + domIterable.forEach(callback, thisArg); + } + + checkScope(0, Object(0)); + checkScope("", Object("")); + checkScope(null, window); + checkScope(undefined, window); +}); diff --git a/cli/tests/unit/error_stack_test.ts b/cli/tests/unit/error_stack_test.ts new file mode 100644 index 000000000..e5cedfcf5 --- /dev/null +++ b/cli/tests/unit/error_stack_test.ts @@ -0,0 +1,108 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +// @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol +const { setPrepareStackTrace } = Deno[Deno.internal]; + +interface CallSite { + getThis(): unknown; + getTypeName(): string | null; + getFunction(): Function | null; + getFunctionName(): string | null; + getMethodName(): string | null; + getFileName(): string | null; + getLineNumber(): number | null; + getColumnNumber(): number | null; + getEvalOrigin(): string | null; + isToplevel(): boolean | null; + isEval(): boolean; + isNative(): boolean; + isConstructor(): boolean; + isAsync(): boolean; + isPromiseAll(): boolean; + getPromiseIndex(): number | null; +} + +function getMockCallSite( + fileName: string, + lineNumber: number | null, + columnNumber: number | null +): CallSite { + return { + getThis(): unknown { + return undefined; + }, + getTypeName(): string { + return ""; + }, + getFunction(): Function { + return (): void => {}; + }, + getFunctionName(): string { + return ""; + }, + getMethodName(): string { + return ""; + }, + getFileName(): string { + return fileName; + }, + getLineNumber(): number | null { + return lineNumber; + }, + getColumnNumber(): number | null { + return columnNumber; + }, + getEvalOrigin(): null { + return null; + }, + isToplevel(): false { + return false; + }, + isEval(): false { + return false; + }, + isNative(): false { + return false; + }, + isConstructor(): false { + return false; + }, + isAsync(): false { + return false; + }, + isPromiseAll(): false { + return false; + }, + getPromiseIndex(): null { + return null; + }, + }; +} + +unitTest(function prepareStackTrace(): void { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const MockError = {} as any; + setPrepareStackTrace(MockError); + assert(typeof MockError.prepareStackTrace === "function"); + const prepareStackTrace: ( + error: Error, + structuredStackTrace: CallSite[] + ) => string = MockError.prepareStackTrace; + const result = prepareStackTrace(new Error("foo"), [ + getMockCallSite("CLI_SNAPSHOT.js", 23, 0), + ]); + assert(result.startsWith("Error: foo\n")); + assert(result.includes(".ts:"), "should remap to something in 'js/'"); +}); + +unitTest(function applySourceMap(): void { + const result = Deno.applySourceMap({ + fileName: "CLI_SNAPSHOT.js", + lineNumber: 23, + columnNumber: 0, + }); + assert(result.fileName.endsWith(".ts")); + assert(result.lineNumber != null); + assert(result.columnNumber != null); +}); diff --git a/cli/tests/unit/event_target_test.ts b/cli/tests/unit/event_target_test.ts new file mode 100644 index 000000000..0c4eb4d0d --- /dev/null +++ b/cli/tests/unit/event_target_test.ts @@ -0,0 +1,231 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals } from "./test_util.ts"; + +unitTest(function addEventListenerTest(): void { + const document = new EventTarget(); + + // @ts-ignore tests ignoring the type system for resilience + assertEquals(document.addEventListener("x", null, false), undefined); + // @ts-ignore + assertEquals(document.addEventListener("x", null, true), undefined); + // @ts-ignore + assertEquals(document.addEventListener("x", null), undefined); +}); + +unitTest(function constructedEventTargetCanBeUsedAsExpected(): void { + const target = new EventTarget(); + const event = new Event("foo", { bubbles: true, cancelable: false }); + let callCount = 0; + + const listener = (e: Event): void => { + assertEquals(e, event); + ++callCount; + }; + + target.addEventListener("foo", listener); + + target.dispatchEvent(event); + assertEquals(callCount, 1); + + target.dispatchEvent(event); + assertEquals(callCount, 2); + + target.removeEventListener("foo", listener); + target.dispatchEvent(event); + assertEquals(callCount, 2); +}); + +unitTest(function anEventTargetCanBeSubclassed(): void { + class NicerEventTarget extends EventTarget { + on( + type: string, + callback: ((e: Event) => void) | null, + options?: AddEventListenerOptions + ): void { + this.addEventListener(type, callback, options); + } + + off( + type: string, + callback: ((e: Event) => void) | null, + options?: EventListenerOptions + ): void { + this.removeEventListener(type, callback, options); + } + } + + const target = new NicerEventTarget(); + new Event("foo", { bubbles: true, cancelable: false }); + let callCount = 0; + + const listener = (): void => { + ++callCount; + }; + + target.on("foo", listener); + assertEquals(callCount, 0); + + target.off("foo", listener); + assertEquals(callCount, 0); +}); + +unitTest(function removingNullEventListenerShouldSucceed(): void { + const document = new EventTarget(); + // @ts-ignore + assertEquals(document.removeEventListener("x", null, false), undefined); + // @ts-ignore + assertEquals(document.removeEventListener("x", null, true), undefined); + // @ts-ignore + assertEquals(document.removeEventListener("x", null), undefined); +}); + +unitTest(function constructedEventTargetUseObjectPrototype(): void { + const target = new EventTarget(); + const event = new Event("toString", { bubbles: true, cancelable: false }); + let callCount = 0; + + const listener = (e: Event): void => { + assertEquals(e, event); + ++callCount; + }; + + target.addEventListener("toString", listener); + + target.dispatchEvent(event); + assertEquals(callCount, 1); + + target.dispatchEvent(event); + assertEquals(callCount, 2); + + target.removeEventListener("toString", listener); + target.dispatchEvent(event); + assertEquals(callCount, 2); +}); + +unitTest(function toStringShouldBeWebCompatible(): void { + const target = new EventTarget(); + assertEquals(target.toString(), "[object EventTarget]"); +}); + +unitTest(function dispatchEventShouldNotThrowError(): void { + let hasThrown = false; + + try { + const target = new EventTarget(); + const event = new Event("hasOwnProperty", { + bubbles: true, + cancelable: false, + }); + const listener = (): void => {}; + target.addEventListener("hasOwnProperty", listener); + target.dispatchEvent(event); + } catch { + hasThrown = true; + } + + assertEquals(hasThrown, false); +}); + +unitTest(function eventTargetThisShouldDefaultToWindow(): void { + const { + addEventListener, + dispatchEvent, + removeEventListener, + } = EventTarget.prototype; + let n = 1; + const event = new Event("hello"); + const listener = (): void => { + n = 2; + }; + + addEventListener("hello", listener); + window.dispatchEvent(event); + assertEquals(n, 2); + n = 1; + removeEventListener("hello", listener); + window.dispatchEvent(event); + assertEquals(n, 1); + + window.addEventListener("hello", listener); + dispatchEvent(event); + assertEquals(n, 2); + n = 1; + window.removeEventListener("hello", listener); + dispatchEvent(event); + assertEquals(n, 1); +}); + +unitTest(function eventTargetShouldAcceptEventListenerObject(): void { + const target = new EventTarget(); + const event = new Event("foo", { bubbles: true, cancelable: false }); + let callCount = 0; + + const listener = { + handleEvent(e: Event): void { + assertEquals(e, event); + ++callCount; + }, + }; + + target.addEventListener("foo", listener); + + target.dispatchEvent(event); + assertEquals(callCount, 1); + + target.dispatchEvent(event); + assertEquals(callCount, 2); + + target.removeEventListener("foo", listener); + target.dispatchEvent(event); + assertEquals(callCount, 2); +}); + +unitTest(function eventTargetShouldAcceptAsyncFunction(): void { + const target = new EventTarget(); + const event = new Event("foo", { bubbles: true, cancelable: false }); + let callCount = 0; + + const listener = (e: Event): void => { + assertEquals(e, event); + ++callCount; + }; + + target.addEventListener("foo", listener); + + target.dispatchEvent(event); + assertEquals(callCount, 1); + + target.dispatchEvent(event); + assertEquals(callCount, 2); + + target.removeEventListener("foo", listener); + target.dispatchEvent(event); + assertEquals(callCount, 2); +}); + +unitTest( + function eventTargetShouldAcceptAsyncFunctionForEventListenerObject(): void { + const target = new EventTarget(); + const event = new Event("foo", { bubbles: true, cancelable: false }); + let callCount = 0; + + const listener = { + handleEvent(e: Event): void { + assertEquals(e, event); + ++callCount; + }, + }; + + target.addEventListener("foo", listener); + + target.dispatchEvent(event); + assertEquals(callCount, 1); + + target.dispatchEvent(event); + assertEquals(callCount, 2); + + target.removeEventListener("foo", listener); + target.dispatchEvent(event); + assertEquals(callCount, 2); + } +); diff --git a/cli/tests/unit/event_test.ts b/cli/tests/unit/event_test.ts new file mode 100644 index 000000000..ce3076e58 --- /dev/null +++ b/cli/tests/unit/event_test.ts @@ -0,0 +1,94 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals, assert } from "./test_util.ts"; + +unitTest(function eventInitializedWithType(): void { + const type = "click"; + const event = new Event(type); + + assertEquals(event.isTrusted, false); + assertEquals(event.target, null); + assertEquals(event.currentTarget, null); + assertEquals(event.type, "click"); + assertEquals(event.bubbles, false); + assertEquals(event.cancelable, false); +}); + +unitTest(function eventInitializedWithTypeAndDict(): void { + const init = "submit"; + const eventInit = { bubbles: true, cancelable: true } as EventInit; + const event = new Event(init, eventInit); + + assertEquals(event.isTrusted, false); + assertEquals(event.target, null); + assertEquals(event.currentTarget, null); + assertEquals(event.type, "submit"); + assertEquals(event.bubbles, true); + assertEquals(event.cancelable, true); +}); + +unitTest(function eventComposedPathSuccess(): void { + const type = "click"; + const event = new Event(type); + const composedPath = event.composedPath(); + + assertEquals(composedPath, []); +}); + +unitTest(function eventStopPropagationSuccess(): void { + const type = "click"; + const event = new Event(type); + + assertEquals(event.cancelBubble, false); + event.stopPropagation(); + assertEquals(event.cancelBubble, true); +}); + +unitTest(function eventStopImmediatePropagationSuccess(): void { + const type = "click"; + const event = new Event(type); + + assertEquals(event.cancelBubble, false); + event.stopImmediatePropagation(); + assertEquals(event.cancelBubble, true); +}); + +unitTest(function eventPreventDefaultSuccess(): void { + const type = "click"; + const event = new Event(type); + + assertEquals(event.defaultPrevented, false); + event.preventDefault(); + assertEquals(event.defaultPrevented, false); + + const eventInit = { bubbles: true, cancelable: true } as EventInit; + const cancelableEvent = new Event(type, eventInit); + assertEquals(cancelableEvent.defaultPrevented, false); + cancelableEvent.preventDefault(); + assertEquals(cancelableEvent.defaultPrevented, true); +}); + +unitTest(function eventInitializedWithNonStringType(): void { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const type: any = undefined; + const event = new Event(type); + + assertEquals(event.isTrusted, false); + assertEquals(event.target, null); + assertEquals(event.currentTarget, null); + assertEquals(event.type, "undefined"); + assertEquals(event.bubbles, false); + assertEquals(event.cancelable, false); +}); + +// ref https://github.com/web-platform-tests/wpt/blob/master/dom/events/Event-isTrusted.any.js +unitTest(function eventIsTrusted(): void { + const desc1 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted"); + assert(desc1); + assertEquals(typeof desc1.get, "function"); + + const desc2 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted"); + assert(desc2); + assertEquals(typeof desc2!.get, "function"); + + assertEquals(desc1!.get, desc2!.get); +}); diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts new file mode 100644 index 000000000..37fca2112 --- /dev/null +++ b/cli/tests/unit/fetch_test.ts @@ -0,0 +1,534 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + unitTest, + assert, + assertEquals, + assertStrContains, + assertThrows, + fail, +} from "./test_util.ts"; + +unitTest({ perms: { net: true } }, async function fetchProtocolError(): Promise< + void +> { + let err; + try { + await fetch("file:///"); + } catch (err_) { + err = err_; + } + assert(err instanceof TypeError); + assertStrContains(err.message, "not supported"); +}); + +unitTest( + { perms: { net: true } }, + async function fetchConnectionError(): Promise { + let err; + try { + await fetch("http://localhost:4000"); + } catch (err_) { + err = err_; + } + assert(err instanceof Deno.errors.Http); + assertStrContains(err.message, "error trying to connect"); + } +); + +unitTest({ perms: { net: true } }, async function fetchJsonSuccess(): Promise< + void +> { + const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); + const json = await response.json(); + assertEquals(json.name, "deno"); +}); + +unitTest(async function fetchPerm(): Promise { + let err; + try { + await fetch("http://localhost:4545/cli/tests/fixture.json"); + } catch (err_) { + err = err_; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest({ perms: { net: true } }, async function fetchUrl(): Promise { + const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); + assertEquals(response.url, "http://localhost:4545/cli/tests/fixture.json"); + const _json = await response.json(); +}); + +unitTest({ perms: { net: true } }, async function fetchURL(): Promise { + const response = await fetch( + new URL("http://localhost:4545/cli/tests/fixture.json") + ); + assertEquals(response.url, "http://localhost:4545/cli/tests/fixture.json"); + const _json = await response.json(); +}); + +unitTest({ perms: { net: true } }, async function fetchHeaders(): Promise< + void +> { + const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); + const headers = response.headers; + assertEquals(headers.get("Content-Type"), "application/json"); + assert(headers.get("Server")!.startsWith("SimpleHTTP")); + const _json = await response.json(); +}); + +unitTest({ perms: { net: true } }, async function fetchBlob(): Promise { + const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); + const headers = response.headers; + const blob = await response.blob(); + assertEquals(blob.type, headers.get("Content-Type")); + assertEquals(blob.size, Number(headers.get("Content-Length"))); +}); + +unitTest({ perms: { net: true } }, async function fetchBodyUsed(): Promise< + void +> { + const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); + assertEquals(response.bodyUsed, false); + assertThrows((): void => { + // Assigning to read-only property throws in the strict mode. + // @ts-ignore + response.bodyUsed = true; + }); + await response.blob(); + assertEquals(response.bodyUsed, true); +}); + +// TODO(ry) response.body shouldn't be iterable. Instead we should use +// response.body.getReader(). +/* +unitTest({ perms: { net: true } }, async function fetchAsyncIterator(): Promise< + void +> { + const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); + const headers = response.headers; + let total = 0; + for await (const chunk of response.body) { + total += chunk.length; + } + + assertEquals(total, Number(headers.get("Content-Length"))); + const _json = await response.json(); +}); +*/ + +unitTest({ perms: { net: true } }, async function responseClone(): Promise< + void +> { + const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); + const response1 = response.clone(); + assert(response !== response1); + assertEquals(response.status, response1.status); + assertEquals(response.statusText, response1.statusText); + const u8a = new Uint8Array(await response.arrayBuffer()); + const u8a1 = new Uint8Array(await response1.arrayBuffer()); + for (let i = 0; i < u8a.byteLength; i++) { + assertEquals(u8a[i], u8a1[i]); + } +}); + +unitTest({ perms: { net: true } }, async function fetchEmptyInvalid(): Promise< + void +> { + let err; + try { + await fetch(""); + } catch (err_) { + err = err_; + } + assert(err instanceof URIError); +}); + +unitTest( + { perms: { net: true } }, + async function fetchMultipartFormDataSuccess(): Promise { + const response = await fetch( + "http://localhost:4545/cli/tests/subdir/multipart_form_data.txt" + ); + const formData = await response.formData(); + assert(formData.has("field_1")); + assertEquals(formData.get("field_1")!.toString(), "value_1 \r\n"); + assert(formData.has("field_2")); + /* TODO(ry) Re-enable this test once we bring back the global File type. + const file = formData.get("field_2") as File; + assertEquals(file.name, "file.js"); + */ + // Currently we cannot read from file... + } +); + +unitTest( + { perms: { net: true } }, + async function fetchURLEncodedFormDataSuccess(): Promise { + const response = await fetch( + "http://localhost:4545/cli/tests/subdir/form_urlencoded.txt" + ); + const formData = await response.formData(); + assert(formData.has("field_1")); + assertEquals(formData.get("field_1")!.toString(), "Hi"); + assert(formData.has("field_2")); + assertEquals(formData.get("field_2")!.toString(), ""); + } +); + +unitTest( + { + perms: { net: true }, + }, + async function fetchWithRedirection(): Promise { + const response = await fetch("http://localhost:4546/"); // will redirect to http://localhost:4545/ + assertEquals(response.status, 200); + assertEquals(response.statusText, "OK"); + assertEquals(response.url, "http://localhost:4545/"); + const body = await response.text(); + assert(body.includes("Directory listing for /")); + } +); + +unitTest( + { + perms: { net: true }, + }, + async function fetchWithRelativeRedirection(): Promise { + const response = await fetch("http://localhost:4545/cli/tests"); // will redirect to /cli/tests/ + assertEquals(response.status, 200); + assertEquals(response.statusText, "OK"); + const body = await response.text(); + assert(body.includes("Directory listing for /cli/tests/")); + } +); + +unitTest( + { + // FIXME(bartlomieju): + // The feature below is not implemented, but the test should work after implementation + ignore: true, + perms: { net: true }, + }, + async function fetchWithInfRedirection(): Promise { + const response = await fetch("http://localhost:4549/cli/tests"); // will redirect to the same place + assertEquals(response.status, 0); // network error + } +); + +unitTest( + { perms: { net: true } }, + async function fetchInitStringBody(): Promise { + const data = "Hello World"; + const response = await fetch("http://localhost:4545/echo_server", { + method: "POST", + body: data, + }); + const text = await response.text(); + assertEquals(text, data); + assert(response.headers.get("content-type")!.startsWith("text/plain")); + } +); + +unitTest( + { perms: { net: true } }, + async function fetchRequestInitStringBody(): Promise { + const data = "Hello World"; + const req = new Request("http://localhost:4545/echo_server", { + method: "POST", + body: data, + }); + const response = await fetch(req); + const text = await response.text(); + assertEquals(text, data); + } +); + +unitTest( + { perms: { net: true } }, + async function fetchInitTypedArrayBody(): Promise { + const data = "Hello World"; + const response = await fetch("http://localhost:4545/echo_server", { + method: "POST", + body: new TextEncoder().encode(data), + }); + const text = await response.text(); + assertEquals(text, data); + } +); + +unitTest( + { perms: { net: true } }, + async function fetchInitURLSearchParamsBody(): Promise { + const data = "param1=value1¶m2=value2"; + const params = new URLSearchParams(data); + const response = await fetch("http://localhost:4545/echo_server", { + method: "POST", + body: params, + }); + const text = await response.text(); + assertEquals(text, data); + assert( + response.headers + .get("content-type")! + .startsWith("application/x-www-form-urlencoded") + ); + } +); + +unitTest({ perms: { net: true } }, async function fetchInitBlobBody(): Promise< + void +> { + const data = "const a = 1"; + const blob = new Blob([data], { + type: "text/javascript", + }); + const response = await fetch("http://localhost:4545/echo_server", { + method: "POST", + body: blob, + }); + const text = await response.text(); + assertEquals(text, data); + assert(response.headers.get("content-type")!.startsWith("text/javascript")); +}); + +unitTest( + { perms: { net: true } }, + async function fetchInitFormDataBody(): Promise { + const form = new FormData(); + form.append("field", "value"); + const response = await fetch("http://localhost:4545/echo_server", { + method: "POST", + body: form, + }); + const resultForm = await response.formData(); + assertEquals(form.get("field"), resultForm.get("field")); + } +); + +unitTest({ perms: { net: true } }, async function fetchUserAgent(): Promise< + void +> { + const data = "Hello World"; + const response = await fetch("http://localhost:4545/echo_server", { + method: "POST", + body: new TextEncoder().encode(data), + }); + assertEquals(response.headers.get("user-agent"), `Deno/${Deno.version.deno}`); + await response.text(); +}); + +// TODO(ry) The following tests work but are flaky. There's a race condition +// somewhere. Here is what one of these flaky failures looks like: +// +// unitTest fetchPostBodyString_permW0N1E0R0 +// assertEquals failed. actual = expected = POST /blah HTTP/1.1 +// hello: World +// foo: Bar +// host: 127.0.0.1:4502 +// content-length: 11 +// hello world +// Error: actual: expected: POST /blah HTTP/1.1 +// hello: World +// foo: Bar +// host: 127.0.0.1:4502 +// content-length: 11 +// hello world +// at Object.assertEquals (file:///C:/deno/js/testing/util.ts:29:11) +// at fetchPostBodyString (file + +function bufferServer(addr: string): Deno.Buffer { + const [hostname, port] = addr.split(":"); + const listener = Deno.listen({ + hostname, + port: Number(port), + }) as Deno.Listener; + const buf = new Deno.Buffer(); + listener.accept().then(async (conn: Deno.Conn) => { + const p1 = buf.readFrom(conn); + const p2 = conn.write( + new TextEncoder().encode( + "HTTP/1.0 404 Not Found\r\nContent-Length: 2\r\n\r\nNF" + ) + ); + // Wait for both an EOF on the read side of the socket and for the write to + // complete before closing it. Due to keep-alive, the EOF won't be sent + // until the Connection close (HTTP/1.0) response, so readFrom() can't + // proceed write. Conversely, if readFrom() is async, waiting for the + // write() to complete is not a guarantee that we've read the incoming + // request. + await Promise.all([p1, p2]); + conn.close(); + listener.close(); + }); + return buf; +} + +unitTest( + { + // FIXME(bartlomieju) + ignore: true, + perms: { net: true }, + }, + async function fetchRequest(): Promise { + const addr = "127.0.0.1:4501"; + const buf = bufferServer(addr); + const response = await fetch(`http://${addr}/blah`, { + method: "POST", + headers: [ + ["Hello", "World"], + ["Foo", "Bar"], + ], + }); + assertEquals(response.status, 404); + assertEquals(response.headers.get("Content-Length"), "2"); + + const actual = new TextDecoder().decode(buf.bytes()); + const expected = [ + "POST /blah HTTP/1.1\r\n", + "hello: World\r\n", + "foo: Bar\r\n", + `host: ${addr}\r\n\r\n`, + ].join(""); + assertEquals(actual, expected); + } +); + +unitTest( + { + // FIXME(bartlomieju) + ignore: true, + perms: { net: true }, + }, + async function fetchPostBodyString(): Promise { + const addr = "127.0.0.1:4502"; + const buf = bufferServer(addr); + const body = "hello world"; + const response = await fetch(`http://${addr}/blah`, { + method: "POST", + headers: [ + ["Hello", "World"], + ["Foo", "Bar"], + ], + body, + }); + assertEquals(response.status, 404); + assertEquals(response.headers.get("Content-Length"), "2"); + + const actual = new TextDecoder().decode(buf.bytes()); + const expected = [ + "POST /blah HTTP/1.1\r\n", + "hello: World\r\n", + "foo: Bar\r\n", + `host: ${addr}\r\n`, + `content-length: ${body.length}\r\n\r\n`, + body, + ].join(""); + assertEquals(actual, expected); + } +); + +unitTest( + { + // FIXME(bartlomieju) + ignore: true, + perms: { net: true }, + }, + async function fetchPostBodyTypedArray(): Promise { + const addr = "127.0.0.1:4503"; + const buf = bufferServer(addr); + const bodyStr = "hello world"; + const body = new TextEncoder().encode(bodyStr); + const response = await fetch(`http://${addr}/blah`, { + method: "POST", + headers: [ + ["Hello", "World"], + ["Foo", "Bar"], + ], + body, + }); + assertEquals(response.status, 404); + assertEquals(response.headers.get("Content-Length"), "2"); + + const actual = new TextDecoder().decode(buf.bytes()); + const expected = [ + "POST /blah HTTP/1.1\r\n", + "hello: World\r\n", + "foo: Bar\r\n", + `host: ${addr}\r\n`, + `content-length: ${body.byteLength}\r\n\r\n`, + bodyStr, + ].join(""); + assertEquals(actual, expected); + } +); + +unitTest( + { + perms: { net: true }, + }, + async function fetchWithManualRedirection(): Promise { + const response = await fetch("http://localhost:4546/", { + redirect: "manual", + }); // will redirect to http://localhost:4545/ + assertEquals(response.status, 0); + assertEquals(response.statusText, ""); + assertEquals(response.url, ""); + assertEquals(response.type, "opaqueredirect"); + try { + await response.text(); + fail( + "Reponse.text() didn't throw on a filtered response without a body (type opaqueredirect)" + ); + } catch (e) { + return; + } + } +); + +unitTest( + { + perms: { net: true }, + }, + async function fetchWithErrorRedirection(): Promise { + const response = await fetch("http://localhost:4546/", { + redirect: "error", + }); // will redirect to http://localhost:4545/ + assertEquals(response.status, 0); + assertEquals(response.statusText, ""); + assertEquals(response.url, ""); + assertEquals(response.type, "error"); + try { + await response.text(); + fail( + "Reponse.text() didn't throw on a filtered response without a body (type error)" + ); + } catch (e) { + return; + } + } +); + +unitTest(function responseRedirect(): void { + const redir = Response.redirect("example.com/newLocation", 301); + assertEquals(redir.status, 301); + assertEquals(redir.statusText, ""); + assertEquals(redir.url, ""); + assertEquals(redir.headers.get("Location"), "example.com/newLocation"); + assertEquals(redir.type, "default"); +}); + +unitTest(function responseConstructionHeaderRemoval(): void { + const res = new Response( + "example.com", + 200, + "OK", + [["Set-Cookie", "mysessionid"]], + -1, + false, + "basic", + null + ); + assert(res.headers.get("Set-Cookie") != "mysessionid"); +}); diff --git a/cli/tests/unit/file_test.ts b/cli/tests/unit/file_test.ts new file mode 100644 index 000000000..4941554ad --- /dev/null +++ b/cli/tests/unit/file_test.ts @@ -0,0 +1,105 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function testFirstArgument(arg1: any[], expectedSize: number): void { + const file = new File(arg1, "name"); + assert(file instanceof File); + assertEquals(file.name, "name"); + assertEquals(file.size, expectedSize); + assertEquals(file.type, ""); +} + +unitTest(function fileEmptyFileBits(): void { + testFirstArgument([], 0); +}); + +unitTest(function fileStringFileBits(): void { + testFirstArgument(["bits"], 4); +}); + +unitTest(function fileUnicodeStringFileBits(): void { + testFirstArgument(["𝓽𝓮𝔁𝓽"], 16); +}); + +unitTest(function fileStringObjectFileBits(): void { + testFirstArgument([new String("string object")], 13); +}); + +unitTest(function fileEmptyBlobFileBits(): void { + testFirstArgument([new Blob()], 0); +}); + +unitTest(function fileBlobFileBits(): void { + testFirstArgument([new Blob(["bits"])], 4); +}); + +unitTest(function fileEmptyFileFileBits(): void { + testFirstArgument([new File([], "world.txt")], 0); +}); + +unitTest(function fileFileFileBits(): void { + testFirstArgument([new File(["bits"], "world.txt")], 4); +}); + +unitTest(function fileArrayBufferFileBits(): void { + testFirstArgument([new ArrayBuffer(8)], 8); +}); + +unitTest(function fileTypedArrayFileBits(): void { + testFirstArgument([new Uint8Array([0x50, 0x41, 0x53, 0x53])], 4); +}); + +unitTest(function fileVariousFileBits(): void { + testFirstArgument( + [ + "bits", + new Blob(["bits"]), + new Blob(), + new Uint8Array([0x50, 0x41]), + new Uint16Array([0x5353]), + new Uint32Array([0x53534150]), + ], + 16 + ); +}); + +unitTest(function fileNumberInFileBits(): void { + testFirstArgument([12], 2); +}); + +unitTest(function fileArrayInFileBits(): void { + testFirstArgument([[1, 2, 3]], 5); +}); + +unitTest(function fileObjectInFileBits(): void { + // "[object Object]" + testFirstArgument([{}], 15); +}); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function testSecondArgument(arg2: any, expectedFileName: string): void { + const file = new File(["bits"], arg2); + assert(file instanceof File); + assertEquals(file.name, expectedFileName); +} + +unitTest(function fileUsingFileName(): void { + testSecondArgument("dummy", "dummy"); +}); + +unitTest(function fileUsingSpecialCharacterInFileName(): void { + testSecondArgument("dummy/foo", "dummy:foo"); +}); + +unitTest(function fileUsingNullFileName(): void { + testSecondArgument(null, "null"); +}); + +unitTest(function fileUsingNumberFileName(): void { + testSecondArgument(1, "1"); +}); + +unitTest(function fileUsingEmptyStringFileName(): void { + testSecondArgument("", ""); +}); diff --git a/cli/tests/unit/files_test.ts b/cli/tests/unit/files_test.ts new file mode 100644 index 000000000..a035c7074 --- /dev/null +++ b/cli/tests/unit/files_test.ts @@ -0,0 +1,571 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + unitTest, + assert, + assertEquals, + assertStrContains, +} from "./test_util.ts"; + +unitTest(function filesStdioFileDescriptors(): void { + assertEquals(Deno.stdin.rid, 0); + assertEquals(Deno.stdout.rid, 1); + assertEquals(Deno.stderr.rid, 2); +}); + +unitTest({ perms: { read: true } }, async function filesCopyToStdout(): Promise< + void +> { + const filename = "cli/tests/fixture.json"; + const file = await Deno.open(filename); + assert(file.rid > 2); + const bytesWritten = await Deno.copy(file, Deno.stdout); + const fileSize = Deno.statSync(filename).size; + assertEquals(bytesWritten, fileSize); + console.log("bytes written", bytesWritten); + file.close(); +}); + +unitTest({ perms: { read: true } }, async function filesIter(): Promise { + const filename = "cli/tests/hello.txt"; + const file = await Deno.open(filename); + + let totalSize = 0; + for await (const buf of Deno.iter(file)) { + totalSize += buf.byteLength; + } + + assertEquals(totalSize, 12); + file.close(); +}); + +unitTest( + { perms: { read: true } }, + async function filesIterCustomBufSize(): Promise { + const filename = "cli/tests/hello.txt"; + const file = await Deno.open(filename); + + let totalSize = 0; + let iterations = 0; + for await (const buf of Deno.iter(file, { bufSize: 6 })) { + totalSize += buf.byteLength; + iterations += 1; + } + + assertEquals(totalSize, 12); + assertEquals(iterations, 2); + file.close(); + } +); + +unitTest({ perms: { read: true } }, function filesIterSync(): void { + const filename = "cli/tests/hello.txt"; + const file = Deno.openSync(filename); + + let totalSize = 0; + for (const buf of Deno.iterSync(file)) { + totalSize += buf.byteLength; + } + + assertEquals(totalSize, 12); + file.close(); +}); + +unitTest( + { perms: { read: true } }, + function filesIterSyncCustomBufSize(): void { + const filename = "cli/tests/hello.txt"; + const file = Deno.openSync(filename); + + let totalSize = 0; + let iterations = 0; + for (const buf of Deno.iterSync(file, { bufSize: 6 })) { + totalSize += buf.byteLength; + iterations += 1; + } + + assertEquals(totalSize, 12); + assertEquals(iterations, 2); + file.close(); + } +); + +unitTest(async function readerIter(): Promise { + // ref: https://github.com/denoland/deno/issues/2330 + const encoder = new TextEncoder(); + + class TestReader implements Deno.Reader { + #offset = 0; + #buf: Uint8Array; + + constructor(s: string) { + this.#buf = new Uint8Array(encoder.encode(s)); + } + + read(p: Uint8Array): Promise { + const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset); + p.set(this.#buf.slice(this.#offset, this.#offset + n)); + this.#offset += n; + + if (n === 0) { + return Promise.resolve(null); + } + + return Promise.resolve(n); + } + } + + const reader = new TestReader("hello world!"); + + let totalSize = 0; + for await (const buf of Deno.iter(reader)) { + totalSize += buf.byteLength; + } + + assertEquals(totalSize, 12); +}); + +unitTest(async function readerIterSync(): Promise { + // ref: https://github.com/denoland/deno/issues/2330 + const encoder = new TextEncoder(); + + class TestReader implements Deno.ReaderSync { + #offset = 0; + #buf: Uint8Array; + + constructor(s: string) { + this.#buf = new Uint8Array(encoder.encode(s)); + } + + readSync(p: Uint8Array): number | null { + const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset); + p.set(this.#buf.slice(this.#offset, this.#offset + n)); + this.#offset += n; + + if (n === 0) { + return null; + } + + return n; + } + } + + const reader = new TestReader("hello world!"); + + let totalSize = 0; + for await (const buf of Deno.iterSync(reader)) { + totalSize += buf.byteLength; + } + + assertEquals(totalSize, 12); +}); + +unitTest( + { + perms: { read: true, write: true }, + }, + function openSyncMode(): void { + const path = Deno.makeTempDirSync() + "/test_openSync.txt"; + const file = Deno.openSync(path, { + write: true, + createNew: true, + mode: 0o626, + }); + file.close(); + const pathInfo = Deno.statSync(path); + if (Deno.build.os !== "windows") { + assertEquals(pathInfo.mode! & 0o777, 0o626 & ~Deno.umask()); + } + } +); + +unitTest( + { + perms: { read: true, write: true }, + }, + async function openMode(): Promise { + const path = (await Deno.makeTempDir()) + "/test_open.txt"; + const file = await Deno.open(path, { + write: true, + createNew: true, + mode: 0o626, + }); + file.close(); + const pathInfo = Deno.statSync(path); + if (Deno.build.os !== "windows") { + assertEquals(pathInfo.mode! & 0o777, 0o626 & ~Deno.umask()); + } + } +); + +unitTest( + { perms: { write: false } }, + async function writePermFailure(): Promise { + const filename = "tests/hello.txt"; + const openOptions: Deno.OpenOptions[] = [{ write: true }, { append: true }]; + for (const options of openOptions) { + let err; + try { + await Deno.open(filename, options); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } + } +); + +unitTest(async function openOptions(): Promise { + const filename = "cli/tests/fixture.json"; + let err; + try { + await Deno.open(filename, { write: false }); + } catch (e) { + err = e; + } + assert(!!err); + assertStrContains( + err.message, + "OpenOptions requires at least one option to be true" + ); + + try { + await Deno.open(filename, { truncate: true, write: false }); + } catch (e) { + err = e; + } + assert(!!err); + assertStrContains(err.message, "'truncate' option requires 'write' option"); + + try { + await Deno.open(filename, { create: true, write: false }); + } catch (e) { + err = e; + } + assert(!!err); + assertStrContains( + err.message, + "'create' or 'createNew' options require 'write' or 'append' option" + ); + + try { + await Deno.open(filename, { createNew: true, append: false }); + } catch (e) { + err = e; + } + assert(!!err); + assertStrContains( + err.message, + "'create' or 'createNew' options require 'write' or 'append' option" + ); +}); + +unitTest({ perms: { read: false } }, async function readPermFailure(): Promise< + void +> { + let caughtError = false; + try { + await Deno.open("package.json", { read: true }); + await Deno.open("cli/tests/fixture.json", { read: true }); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest( + { perms: { write: true } }, + async function writeNullBufferFailure(): Promise { + const tempDir = Deno.makeTempDirSync(); + const filename = tempDir + "hello.txt"; + const w = { + write: true, + truncate: true, + create: true, + }; + const file = await Deno.open(filename, w); + + // writing null should throw an error + let err; + try { + // @ts-ignore + await file.write(null); + } catch (e) { + err = e; + } + // TODO: Check error kind when dispatch_minimal pipes errors properly + assert(!!err); + + file.close(); + await Deno.remove(tempDir, { recursive: true }); + } +); + +unitTest( + { perms: { write: true, read: true } }, + async function readNullBufferFailure(): Promise { + const tempDir = Deno.makeTempDirSync(); + const filename = tempDir + "hello.txt"; + const file = await Deno.open(filename, { + read: true, + write: true, + truncate: true, + create: true, + }); + + // reading into an empty buffer should return 0 immediately + const bytesRead = await file.read(new Uint8Array(0)); + assert(bytesRead === 0); + + // reading file into null buffer should throw an error + let err; + try { + // @ts-ignore + await file.read(null); + } catch (e) { + err = e; + } + // TODO: Check error kind when dispatch_minimal pipes errors properly + assert(!!err); + + file.close(); + await Deno.remove(tempDir, { recursive: true }); + } +); + +unitTest( + { perms: { write: false, read: false } }, + async function readWritePermFailure(): Promise { + const filename = "tests/hello.txt"; + let err; + try { + await Deno.open(filename, { read: true }); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function createFile(): Promise { + const tempDir = await Deno.makeTempDir(); + const filename = tempDir + "/test.txt"; + const f = await Deno.create(filename); + let fileInfo = Deno.statSync(filename); + assert(fileInfo.isFile); + assert(fileInfo.size === 0); + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + await f.write(data); + fileInfo = Deno.statSync(filename); + assert(fileInfo.size === 5); + f.close(); + + // TODO: test different modes + await Deno.remove(tempDir, { recursive: true }); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function openModeWrite(): Promise { + const tempDir = Deno.makeTempDirSync(); + const encoder = new TextEncoder(); + const filename = tempDir + "hello.txt"; + const data = encoder.encode("Hello world!\n"); + let file = await Deno.open(filename, { + create: true, + write: true, + truncate: true, + }); + // assert file was created + let fileInfo = Deno.statSync(filename); + assert(fileInfo.isFile); + assertEquals(fileInfo.size, 0); + // write some data + await file.write(data); + fileInfo = Deno.statSync(filename); + assertEquals(fileInfo.size, 13); + // assert we can't read from file + let thrown = false; + try { + const buf = new Uint8Array(20); + await file.read(buf); + } catch (e) { + thrown = true; + } finally { + assert(thrown, "'w' mode shouldn't allow to read file"); + } + file.close(); + // assert that existing file is truncated on open + file = await Deno.open(filename, { + write: true, + truncate: true, + }); + file.close(); + const fileSize = Deno.statSync(filename).size; + assertEquals(fileSize, 0); + await Deno.remove(tempDir, { recursive: true }); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function openModeWriteRead(): Promise { + const tempDir = Deno.makeTempDirSync(); + const encoder = new TextEncoder(); + const filename = tempDir + "hello.txt"; + const data = encoder.encode("Hello world!\n"); + + const file = await Deno.open(filename, { + write: true, + truncate: true, + create: true, + read: true, + }); + const seekPosition = 0; + // assert file was created + let fileInfo = Deno.statSync(filename); + assert(fileInfo.isFile); + assertEquals(fileInfo.size, 0); + // write some data + await file.write(data); + fileInfo = Deno.statSync(filename); + assertEquals(fileInfo.size, 13); + + const buf = new Uint8Array(20); + // seeking from beginning of a file + const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Start); + assertEquals(seekPosition, cursorPosition); + const result = await file.read(buf); + assertEquals(result, 13); + file.close(); + + await Deno.remove(tempDir, { recursive: true }); + } +); + +unitTest({ perms: { read: true } }, async function seekStart(): Promise { + const filename = "cli/tests/hello.txt"; + const file = await Deno.open(filename); + const seekPosition = 6; + // Deliberately move 1 step forward + await file.read(new Uint8Array(1)); // "H" + // Skipping "Hello " + // seeking from beginning of a file plus seekPosition + const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Start); + assertEquals(seekPosition, cursorPosition); + const buf = new Uint8Array(6); + await file.read(buf); + const decoded = new TextDecoder().decode(buf); + assertEquals(decoded, "world!"); + file.close(); +}); + +unitTest({ perms: { read: true } }, function seekSyncStart(): void { + const filename = "cli/tests/hello.txt"; + const file = Deno.openSync(filename); + const seekPosition = 6; + // Deliberately move 1 step forward + file.readSync(new Uint8Array(1)); // "H" + // Skipping "Hello " + // seeking from beginning of a file plus seekPosition + const cursorPosition = file.seekSync(seekPosition, Deno.SeekMode.Start); + assertEquals(seekPosition, cursorPosition); + const buf = new Uint8Array(6); + file.readSync(buf); + const decoded = new TextDecoder().decode(buf); + assertEquals(decoded, "world!"); + file.close(); +}); + +unitTest({ perms: { read: true } }, async function seekCurrent(): Promise< + void +> { + const filename = "cli/tests/hello.txt"; + const file = await Deno.open(filename); + // Deliberately move 1 step forward + await file.read(new Uint8Array(1)); // "H" + // Skipping "ello " + const seekPosition = 5; + // seekPosition is relative to current cursor position after read + const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Current); + assertEquals(seekPosition + 1, cursorPosition); + const buf = new Uint8Array(6); + await file.read(buf); + const decoded = new TextDecoder().decode(buf); + assertEquals(decoded, "world!"); + file.close(); +}); + +unitTest({ perms: { read: true } }, function seekSyncCurrent(): void { + const filename = "cli/tests/hello.txt"; + const file = Deno.openSync(filename); + // Deliberately move 1 step forward + file.readSync(new Uint8Array(1)); // "H" + // Skipping "ello " + const seekPosition = 5; + // seekPosition is relative to current cursor position after read + const cursorPosition = file.seekSync(seekPosition, Deno.SeekMode.Current); + assertEquals(seekPosition + 1, cursorPosition); + const buf = new Uint8Array(6); + file.readSync(buf); + const decoded = new TextDecoder().decode(buf); + assertEquals(decoded, "world!"); + file.close(); +}); + +unitTest({ perms: { read: true } }, async function seekEnd(): Promise { + const filename = "cli/tests/hello.txt"; + const file = await Deno.open(filename); + const seekPosition = -6; + // seek from end of file that has 12 chars, 12 - 6 = 6 + const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.End); + assertEquals(6, cursorPosition); + const buf = new Uint8Array(6); + await file.read(buf); + const decoded = new TextDecoder().decode(buf); + assertEquals(decoded, "world!"); + file.close(); +}); + +unitTest({ perms: { read: true } }, function seekSyncEnd(): void { + const filename = "cli/tests/hello.txt"; + const file = Deno.openSync(filename); + const seekPosition = -6; + // seek from end of file that has 12 chars, 12 - 6 = 6 + const cursorPosition = file.seekSync(seekPosition, Deno.SeekMode.End); + assertEquals(6, cursorPosition); + const buf = new Uint8Array(6); + file.readSync(buf); + const decoded = new TextDecoder().decode(buf); + assertEquals(decoded, "world!"); + file.close(); +}); + +unitTest({ perms: { read: true } }, async function seekMode(): Promise { + const filename = "cli/tests/hello.txt"; + const file = await Deno.open(filename); + let err; + try { + await file.seek(1, -1); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof TypeError); + assertStrContains(err.message, "Invalid seek mode"); + + // We should still be able to read the file + // since it is still open. + const buf = new Uint8Array(1); + await file.read(buf); // "H" + assertEquals(new TextDecoder().decode(buf), "H"); + file.close(); +}); diff --git a/cli/tests/unit/form_data_test.ts b/cli/tests/unit/form_data_test.ts new file mode 100644 index 000000000..10cbd30a7 --- /dev/null +++ b/cli/tests/unit/form_data_test.ts @@ -0,0 +1,203 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + unitTest, + assert, + assertEquals, + assertStrContains, +} from "./test_util.ts"; + +unitTest({ ignore: true }, function formDataHasCorrectNameProp(): void { + assertEquals(FormData.name, "FormData"); +}); + +unitTest(function formDataParamsAppendSuccess(): void { + const formData = new FormData(); + formData.append("a", "true"); + assertEquals(formData.get("a"), "true"); +}); + +unitTest(function formDataParamsDeleteSuccess(): void { + const formData = new FormData(); + formData.append("a", "true"); + formData.append("b", "false"); + assertEquals(formData.get("b"), "false"); + formData.delete("b"); + assertEquals(formData.get("a"), "true"); + assertEquals(formData.get("b"), null); +}); + +unitTest(function formDataParamsGetAllSuccess(): void { + const formData = new FormData(); + formData.append("a", "true"); + formData.append("b", "false"); + formData.append("a", "null"); + assertEquals(formData.getAll("a"), ["true", "null"]); + assertEquals(formData.getAll("b"), ["false"]); + assertEquals(formData.getAll("c"), []); +}); + +unitTest(function formDataParamsGetSuccess(): void { + const formData = new FormData(); + formData.append("a", "true"); + formData.append("b", "false"); + formData.append("a", "null"); + // @ts-ignore + formData.append("d", undefined); + // @ts-ignore + formData.append("e", null); + assertEquals(formData.get("a"), "true"); + assertEquals(formData.get("b"), "false"); + assertEquals(formData.get("c"), null); + assertEquals(formData.get("d"), "undefined"); + assertEquals(formData.get("e"), "null"); +}); + +unitTest(function formDataParamsHasSuccess(): void { + const formData = new FormData(); + formData.append("a", "true"); + formData.append("b", "false"); + assert(formData.has("a")); + assert(formData.has("b")); + assert(!formData.has("c")); +}); + +unitTest(function formDataParamsSetSuccess(): void { + const formData = new FormData(); + formData.append("a", "true"); + formData.append("b", "false"); + formData.append("a", "null"); + assertEquals(formData.getAll("a"), ["true", "null"]); + assertEquals(formData.getAll("b"), ["false"]); + formData.set("a", "false"); + assertEquals(formData.getAll("a"), ["false"]); + // @ts-ignore + formData.set("d", undefined); + assertEquals(formData.get("d"), "undefined"); + // @ts-ignore + formData.set("e", null); + assertEquals(formData.get("e"), "null"); +}); + +unitTest(function fromDataUseDomFile(): void { + const formData = new FormData(); + const file = new File(["foo"], "bar", { + type: "text/plain", + }); + formData.append("file", file); + assertEquals(formData.get("file"), file); +}); + +unitTest(function formDataSetEmptyBlobSuccess(): void { + const formData = new FormData(); + formData.set("a", new Blob([]), "blank.txt"); + formData.get("a"); + /* TODO Fix this test. + assert(file instanceof File); + if (typeof file !== "string") { + assertEquals(file.name, "blank.txt"); + } + */ +}); + +unitTest(function formDataParamsForEachSuccess(): void { + const init = [ + ["a", "54"], + ["b", "true"], + ]; + const formData = new FormData(); + for (const [name, value] of init) { + formData.append(name, value); + } + let callNum = 0; + formData.forEach((value, key, parent): void => { + assertEquals(formData, parent); + assertEquals(value, init[callNum][1]); + assertEquals(key, init[callNum][0]); + callNum++; + }); + assertEquals(callNum, init.length); +}); + +unitTest(function formDataParamsArgumentsCheck(): void { + const methodRequireOneParam = [ + "delete", + "getAll", + "get", + "has", + "forEach", + ] as const; + + const methodRequireTwoParams = ["append", "set"] as const; + + methodRequireOneParam.forEach((method): void => { + const formData = new FormData(); + let hasThrown = 0; + let errMsg = ""; + try { + // @ts-ignore + formData[method](); + hasThrown = 1; + } catch (err) { + errMsg = err.message; + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); + assertStrContains( + errMsg, + `${method} requires at least 1 argument, but only 0 present` + ); + }); + + methodRequireTwoParams.forEach((method: string): void => { + const formData = new FormData(); + let hasThrown = 0; + let errMsg = ""; + + try { + // @ts-ignore + formData[method](); + hasThrown = 1; + } catch (err) { + errMsg = err.message; + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); + assertStrContains( + errMsg, + `${method} requires at least 2 arguments, but only 0 present` + ); + + hasThrown = 0; + errMsg = ""; + try { + // @ts-ignore + formData[method]("foo"); + hasThrown = 1; + } catch (err) { + errMsg = err.message; + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); + assertStrContains( + errMsg, + `${method} requires at least 2 arguments, but only 1 present` + ); + }); +}); + +unitTest(function toStringShouldBeWebCompatibility(): void { + const formData = new FormData(); + assertEquals(formData.toString(), "[object FormData]"); +}); diff --git a/cli/tests/unit/format_error_test.ts b/cli/tests/unit/format_error_test.ts new file mode 100644 index 000000000..ae7559c82 --- /dev/null +++ b/cli/tests/unit/format_error_test.ts @@ -0,0 +1,33 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { assert, unitTest } from "./test_util.ts"; + +unitTest(function formatDiagnosticBasic() { + const fixture: Deno.DiagnosticItem[] = [ + { + message: "Example error", + category: Deno.DiagnosticCategory.Error, + sourceLine: "abcdefghijklmnopqrstuv", + lineNumber: 1000, + scriptResourceName: "foo.ts", + startColumn: 1, + endColumn: 2, + code: 4000, + }, + ]; + const out = Deno.formatDiagnostics(fixture); + assert(out.includes("Example error")); + assert(out.includes("foo.ts")); +}); + +unitTest(function formatDiagnosticError() { + let thrown = false; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const bad = ([{ hello: 123 }] as any) as Deno.DiagnosticItem[]; + try { + Deno.formatDiagnostics(bad); + } catch (e) { + assert(e instanceof Deno.errors.InvalidData); + thrown = true; + } + assert(thrown); +}); diff --git a/cli/tests/unit/fs_events_test.ts b/cli/tests/unit/fs_events_test.ts new file mode 100644 index 000000000..ad8ba8502 --- /dev/null +++ b/cli/tests/unit/fs_events_test.ts @@ -0,0 +1,71 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +// TODO(ry) Add more tests to specify format. + +unitTest({ perms: { read: false } }, function watchFsPermissions() { + let thrown = false; + try { + Deno.watchFs("."); + } catch (err) { + assert(err instanceof Deno.errors.PermissionDenied); + thrown = true; + } + assert(thrown); +}); + +unitTest({ perms: { read: true } }, function watchFsInvalidPath() { + let thrown = false; + try { + Deno.watchFs("non-existant.file"); + } catch (err) { + console.error(err); + if (Deno.build.os === "windows") { + assert( + err.message.includes( + "Input watch path is neither a file nor a directory" + ) + ); + } else { + assert(err instanceof Deno.errors.NotFound); + } + thrown = true; + } + assert(thrown); +}); + +async function getTwoEvents( + iter: AsyncIterableIterator +): Promise { + const events = []; + for await (const event of iter) { + events.push(event); + if (events.length > 2) break; + } + return events; +} + +unitTest( + { perms: { read: true, write: true } }, + async function watchFsBasic(): Promise { + const testDir = await Deno.makeTempDir(); + const iter = Deno.watchFs(testDir); + + // Asynchornously capture two fs events. + const eventsPromise = getTwoEvents(iter); + + // Make some random file system activity. + const file1 = testDir + "/file1.txt"; + const file2 = testDir + "/file2.txt"; + Deno.writeFileSync(file1, new Uint8Array([0, 1, 2])); + Deno.writeFileSync(file2, new Uint8Array([0, 1, 2])); + + // We should have gotten two fs events. + const events = await eventsPromise; + assert(events.length >= 2); + assert(events[0].kind == "create"); + assert(events[0].paths[0].includes(testDir)); + assert(events[1].kind == "create" || events[1].kind == "modify"); + assert(events[1].paths[0].includes(testDir)); + } +); diff --git a/cli/tests/unit/get_random_values_test.ts b/cli/tests/unit/get_random_values_test.ts new file mode 100644 index 000000000..76fa732ea --- /dev/null +++ b/cli/tests/unit/get_random_values_test.ts @@ -0,0 +1,51 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertNotEquals, assertStrictEq } from "./test_util.ts"; + +unitTest(function getRandomValuesInt8Array(): void { + const arr = new Int8Array(32); + crypto.getRandomValues(arr); + assertNotEquals(arr, new Int8Array(32)); +}); + +unitTest(function getRandomValuesUint8Array(): void { + const arr = new Uint8Array(32); + crypto.getRandomValues(arr); + assertNotEquals(arr, new Uint8Array(32)); +}); + +unitTest(function getRandomValuesUint8ClampedArray(): void { + const arr = new Uint8ClampedArray(32); + crypto.getRandomValues(arr); + assertNotEquals(arr, new Uint8ClampedArray(32)); +}); + +unitTest(function getRandomValuesInt16Array(): void { + const arr = new Int16Array(4); + crypto.getRandomValues(arr); + assertNotEquals(arr, new Int16Array(4)); +}); + +unitTest(function getRandomValuesUint16Array(): void { + const arr = new Uint16Array(4); + crypto.getRandomValues(arr); + assertNotEquals(arr, new Uint16Array(4)); +}); + +unitTest(function getRandomValuesInt32Array(): void { + const arr = new Int32Array(8); + crypto.getRandomValues(arr); + assertNotEquals(arr, new Int32Array(8)); +}); + +unitTest(function getRandomValuesUint32Array(): void { + const arr = new Uint32Array(8); + crypto.getRandomValues(arr); + assertNotEquals(arr, new Uint32Array(8)); +}); + +unitTest(function getRandomValuesReturnValue(): void { + const arr = new Uint32Array(8); + const rtn = crypto.getRandomValues(arr); + assertNotEquals(arr, new Uint32Array(8)); + assertStrictEq(rtn, arr); +}); diff --git a/cli/tests/unit/globals_test.ts b/cli/tests/unit/globals_test.ts new file mode 100644 index 000000000..aa8b4f46e --- /dev/null +++ b/cli/tests/unit/globals_test.ts @@ -0,0 +1,112 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +unitTest(function globalThisExists(): void { + assert(globalThis != null); +}); + +unitTest(function windowExists(): void { + assert(window != null); +}); + +unitTest(function selfExists(): void { + assert(self != null); +}); + +unitTest(function windowWindowExists(): void { + assert(window.window === window); +}); + +unitTest(function windowSelfExists(): void { + assert(window.self === window); +}); + +unitTest(function globalThisEqualsWindow(): void { + assert(globalThis === window); +}); + +unitTest(function globalThisEqualsSelf(): void { + assert(globalThis === self); +}); + +unitTest(function DenoNamespaceExists(): void { + assert(Deno != null); +}); + +unitTest(function DenoNamespaceEqualsWindowDeno(): void { + assert(Deno === window.Deno); +}); + +unitTest(function DenoNamespaceIsFrozen(): void { + assert(Object.isFrozen(Deno)); +}); + +unitTest(function webAssemblyExists(): void { + assert(typeof WebAssembly.compile === "function"); +}); + +unitTest(function DenoNamespaceImmutable(): void { + const denoCopy = window.Deno; + try { + // @ts-ignore + Deno = 1; + } catch {} + assert(denoCopy === Deno); + try { + // @ts-ignore + window.Deno = 1; + } catch {} + assert(denoCopy === Deno); + try { + delete window.Deno; + } catch {} + assert(denoCopy === Deno); + + const { readFile } = Deno; + try { + // @ts-ignore + Deno.readFile = 1; + } catch {} + assert(readFile === Deno.readFile); + try { + delete window.Deno.readFile; + } catch {} + assert(readFile === Deno.readFile); + + // @ts-ignore + const { print } = Deno.core; + try { + // @ts-ignore + Deno.core.print = 1; + } catch {} + // @ts-ignore + assert(print === Deno.core.print); + try { + // @ts-ignore + delete Deno.core.print; + } catch {} + // @ts-ignore + assert(print === Deno.core.print); +}); + +unitTest(async function windowQueueMicrotask(): Promise { + let resolve1: () => void | undefined; + let resolve2: () => void | undefined; + let microtaskDone = false; + const p1 = new Promise((res): void => { + resolve1 = (): void => { + microtaskDone = true; + res(); + }; + }); + const p2 = new Promise((res): void => { + resolve2 = (): void => { + assert(microtaskDone); + res(); + }; + }); + window.queueMicrotask(resolve1!); + setTimeout(resolve2!, 0); + await p1; + await p2; +}); diff --git a/cli/tests/unit/headers_test.ts b/cli/tests/unit/headers_test.ts new file mode 100644 index 000000000..aaa829837 --- /dev/null +++ b/cli/tests/unit/headers_test.ts @@ -0,0 +1,420 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + unitTest, + assert, + assertEquals, + assertStrContains, +} from "./test_util.ts"; +const { + stringifyArgs, + // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol +} = Deno[Deno.internal]; + +// Logic heavily copied from web-platform-tests, make +// sure pass mostly header basic test +// ref: https://github.com/web-platform-tests/wpt/blob/7c50c216081d6ea3c9afe553ee7b64534020a1b2/fetch/api/headers/headers-basic.html +unitTest(function newHeaderTest(): void { + new Headers(); + new Headers(undefined); + new Headers({}); + try { + // @ts-ignore + new Headers(null); + } catch (e) { + assertEquals( + e.message, + "Failed to construct 'Headers'; The provided value was not valid" + ); + } +}); + +const headerDict: Record = { + name1: "value1", + name2: "value2", + name3: "value3", + // @ts-ignore + name4: undefined, + "Content-Type": "value4", +}; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const headerSeq: any[] = []; +for (const name in headerDict) { + headerSeq.push([name, headerDict[name]]); +} + +unitTest(function newHeaderWithSequence(): void { + const headers = new Headers(headerSeq); + for (const name in headerDict) { + assertEquals(headers.get(name), String(headerDict[name])); + } + assertEquals(headers.get("length"), null); +}); + +unitTest(function newHeaderWithRecord(): void { + const headers = new Headers(headerDict); + for (const name in headerDict) { + assertEquals(headers.get(name), String(headerDict[name])); + } +}); + +unitTest(function newHeaderWithHeadersInstance(): void { + const headers = new Headers(headerDict); + const headers2 = new Headers(headers); + for (const name in headerDict) { + assertEquals(headers2.get(name), String(headerDict[name])); + } +}); + +unitTest(function headerAppendSuccess(): void { + const headers = new Headers(); + for (const name in headerDict) { + headers.append(name, headerDict[name]); + assertEquals(headers.get(name), String(headerDict[name])); + } +}); + +unitTest(function headerSetSuccess(): void { + const headers = new Headers(); + for (const name in headerDict) { + headers.set(name, headerDict[name]); + assertEquals(headers.get(name), String(headerDict[name])); + } +}); + +unitTest(function headerHasSuccess(): void { + const headers = new Headers(headerDict); + for (const name in headerDict) { + assert(headers.has(name), "headers has name " + name); + assert( + !headers.has("nameNotInHeaders"), + "headers do not have header: nameNotInHeaders" + ); + } +}); + +unitTest(function headerDeleteSuccess(): void { + const headers = new Headers(headerDict); + for (const name in headerDict) { + assert(headers.has(name), "headers have a header: " + name); + headers.delete(name); + assert(!headers.has(name), "headers do not have anymore a header: " + name); + } +}); + +unitTest(function headerGetSuccess(): void { + const headers = new Headers(headerDict); + for (const name in headerDict) { + assertEquals(headers.get(name), String(headerDict[name])); + assertEquals(headers.get("nameNotInHeaders"), null); + } +}); + +unitTest(function headerEntriesSuccess(): void { + const headers = new Headers(headerDict); + const iterators = headers.entries(); + for (const it of iterators) { + const key = it[0]; + const value = it[1]; + assert(headers.has(key)); + assertEquals(value, headers.get(key)); + } +}); + +unitTest(function headerKeysSuccess(): void { + const headers = new Headers(headerDict); + const iterators = headers.keys(); + for (const it of iterators) { + assert(headers.has(it)); + } +}); + +unitTest(function headerValuesSuccess(): void { + const headers = new Headers(headerDict); + const iterators = headers.values(); + const entries = headers.entries(); + const values = []; + for (const pair of entries) { + values.push(pair[1]); + } + for (const it of iterators) { + assert(values.includes(it)); + } +}); + +const headerEntriesDict: Record = { + name1: "value1", + Name2: "value2", + name: "value3", + "content-Type": "value4", + "Content-Typ": "value5", + "Content-Types": "value6", +}; + +unitTest(function headerForEachSuccess(): void { + const headers = new Headers(headerEntriesDict); + const keys = Object.keys(headerEntriesDict); + keys.forEach((key): void => { + const value = headerEntriesDict[key]; + const newkey = key.toLowerCase(); + headerEntriesDict[newkey] = value; + }); + let callNum = 0; + headers.forEach((value, key, container): void => { + assertEquals(headers, container); + assertEquals(value, headerEntriesDict[key]); + callNum++; + }); + assertEquals(callNum, keys.length); +}); + +unitTest(function headerSymbolIteratorSuccess(): void { + assert(Symbol.iterator in Headers.prototype); + const headers = new Headers(headerEntriesDict); + for (const header of headers) { + const key = header[0]; + const value = header[1]; + assert(headers.has(key)); + assertEquals(value, headers.get(key)); + } +}); + +unitTest(function headerTypesAvailable(): void { + function newHeaders(): Headers { + return new Headers(); + } + const headers = newHeaders(); + assert(headers instanceof Headers); +}); + +// Modified from https://github.com/bitinn/node-fetch/blob/7d3293200a91ad52b5ca7962f9d6fd1c04983edb/test/test.js#L2001-L2014 +// Copyright (c) 2016 David Frank. MIT License. +unitTest(function headerIllegalReject(): void { + let errorCount = 0; + try { + new Headers({ "He y": "ok" }); + } catch (e) { + errorCount++; + } + try { + new Headers({ "Hé-y": "ok" }); + } catch (e) { + errorCount++; + } + try { + new Headers({ "He-y": "ăk" }); + } catch (e) { + errorCount++; + } + const headers = new Headers(); + try { + headers.append("Hé-y", "ok"); + } catch (e) { + errorCount++; + } + try { + headers.delete("Hé-y"); + } catch (e) { + errorCount++; + } + try { + headers.get("Hé-y"); + } catch (e) { + errorCount++; + } + try { + headers.has("Hé-y"); + } catch (e) { + errorCount++; + } + try { + headers.set("Hé-y", "ok"); + } catch (e) { + errorCount++; + } + try { + headers.set("", "ok"); + } catch (e) { + errorCount++; + } + assertEquals(errorCount, 9); + // 'o k' is valid value but invalid name + new Headers({ "He-y": "o k" }); +}); + +// If pair does not contain exactly two items,then throw a TypeError. +unitTest(function headerParamsShouldThrowTypeError(): void { + let hasThrown = 0; + + try { + new Headers(([["1"]] as unknown) as Array<[string, string]>); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + + assertEquals(hasThrown, 2); +}); + +unitTest(function headerParamsArgumentsCheck(): void { + const methodRequireOneParam = ["delete", "get", "has", "forEach"]; + + const methodRequireTwoParams = ["append", "set"]; + + methodRequireOneParam.forEach((method): void => { + const headers = new Headers(); + let hasThrown = 0; + let errMsg = ""; + try { + // @ts-ignore + headers[method](); + hasThrown = 1; + } catch (err) { + errMsg = err.message; + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); + assertStrContains( + errMsg, + `${method} requires at least 1 argument, but only 0 present` + ); + }); + + methodRequireTwoParams.forEach((method): void => { + const headers = new Headers(); + let hasThrown = 0; + let errMsg = ""; + + try { + // @ts-ignore + headers[method](); + hasThrown = 1; + } catch (err) { + errMsg = err.message; + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); + assertStrContains( + errMsg, + `${method} requires at least 2 arguments, but only 0 present` + ); + + hasThrown = 0; + errMsg = ""; + try { + // @ts-ignore + headers[method]("foo"); + hasThrown = 1; + } catch (err) { + errMsg = err.message; + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); + assertStrContains( + errMsg, + `${method} requires at least 2 arguments, but only 1 present` + ); + }); +}); + +unitTest(function headersInitMultiple(): void { + const headers = new Headers([ + ["Set-Cookie", "foo=bar"], + ["Set-Cookie", "bar=baz"], + ["X-Deno", "foo"], + ["X-Deno", "bar"], + ]); + const actual = [...headers]; + assertEquals(actual, [ + ["set-cookie", "foo=bar"], + ["set-cookie", "bar=baz"], + ["x-deno", "foo, bar"], + ]); +}); + +unitTest(function headersAppendMultiple(): void { + const headers = new Headers([ + ["Set-Cookie", "foo=bar"], + ["X-Deno", "foo"], + ]); + headers.append("set-Cookie", "bar=baz"); + headers.append("x-Deno", "bar"); + const actual = [...headers]; + assertEquals(actual, [ + ["set-cookie", "foo=bar"], + ["x-deno", "foo, bar"], + ["set-cookie", "bar=baz"], + ]); +}); + +unitTest(function headersAppendDuplicateSetCookieKey(): void { + const headers = new Headers([["Set-Cookie", "foo=bar"]]); + headers.append("set-Cookie", "foo=baz"); + headers.append("Set-cookie", "baz=bar"); + const actual = [...headers]; + assertEquals(actual, [ + ["set-cookie", "foo=baz"], + ["set-cookie", "baz=bar"], + ]); +}); + +unitTest(function headersSetDuplicateCookieKey(): void { + const headers = new Headers([["Set-Cookie", "foo=bar"]]); + headers.set("set-Cookie", "foo=baz"); + headers.set("set-cookie", "bar=qat"); + const actual = [...headers]; + assertEquals(actual, [ + ["set-cookie", "foo=baz"], + ["set-cookie", "bar=qat"], + ]); +}); + +unitTest(function headersGetSetCookie(): void { + const headers = new Headers([ + ["Set-Cookie", "foo=bar"], + ["set-Cookie", "bar=qat"], + ]); + assertEquals(headers.get("SET-COOKIE"), "foo=bar, bar=qat"); +}); + +unitTest(function toStringShouldBeWebCompatibility(): void { + const headers = new Headers(); + assertEquals(headers.toString(), "[object Headers]"); +}); + +function stringify(...args: unknown[]): string { + return stringifyArgs(args).replace(/\n$/, ""); +} + +unitTest(function customInspectReturnsCorrectHeadersFormat(): void { + const blankHeaders = new Headers(); + assertEquals(stringify(blankHeaders), "Headers {}"); + const singleHeader = new Headers([["Content-Type", "application/json"]]); + assertEquals( + stringify(singleHeader), + "Headers { content-type: application/json }" + ); + const multiParamHeader = new Headers([ + ["Content-Type", "application/json"], + ["Content-Length", "1337"], + ]); + assertEquals( + stringify(multiParamHeader), + "Headers { content-type: application/json, content-length: 1337 }" + ); +}); diff --git a/cli/tests/unit/internals_test.ts b/cli/tests/unit/internals_test.ts new file mode 100644 index 000000000..abd4c94c3 --- /dev/null +++ b/cli/tests/unit/internals_test.ts @@ -0,0 +1,10 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +unitTest(function internalsExists(): void { + const { + stringifyArgs, + // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol + } = Deno[Deno.internal]; + assert(!!stringifyArgs); +}); diff --git a/cli/tests/unit/io_test.ts b/cli/tests/unit/io_test.ts new file mode 100644 index 000000000..0ccd83ea2 --- /dev/null +++ b/cli/tests/unit/io_test.ts @@ -0,0 +1,73 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals } from "./test_util.ts"; + +const DEFAULT_BUF_SIZE = 32 * 1024; + +type Spy = { calls: number }; + +function repeat(c: string, bytes: number): Uint8Array { + assertEquals(c.length, 1); + const ui8 = new Uint8Array(bytes); + ui8.fill(c.charCodeAt(0)); + return ui8; +} + +function spyRead(obj: Deno.Buffer): Spy { + const spy: Spy = { + calls: 0, + }; + + const orig = obj.read.bind(obj); + + obj.read = (p: Uint8Array): Promise => { + spy.calls++; + return orig(p); + }; + + return spy; +} + +unitTest(async function copyWithDefaultBufferSize() { + const xBytes = repeat("b", DEFAULT_BUF_SIZE); + const reader = new Deno.Buffer(xBytes.buffer as ArrayBuffer); + const write = new Deno.Buffer(); + + const readSpy = spyRead(reader); + + const n = await Deno.copy(reader, write); + + assertEquals(n, xBytes.length); + assertEquals(write.length, xBytes.length); + assertEquals(readSpy.calls, 2); // read with DEFAULT_BUF_SIZE bytes + read with 0 bytes +}); + +unitTest(async function copyWithCustomBufferSize() { + const bufSize = 1024; + const xBytes = repeat("b", DEFAULT_BUF_SIZE); + const reader = new Deno.Buffer(xBytes.buffer as ArrayBuffer); + const write = new Deno.Buffer(); + + const readSpy = spyRead(reader); + + const n = await Deno.copy(reader, write, { bufSize }); + + assertEquals(n, xBytes.length); + assertEquals(write.length, xBytes.length); + assertEquals(readSpy.calls, DEFAULT_BUF_SIZE / bufSize + 1); +}); + +unitTest({ perms: { write: true } }, async function copyBufferToFile() { + const filePath = "test-file.txt"; + // bigger than max File possible buffer 16kb + const bufSize = 32 * 1024; + const xBytes = repeat("b", bufSize); + const reader = new Deno.Buffer(xBytes.buffer as ArrayBuffer); + const write = await Deno.open(filePath, { write: true, create: true }); + + const n = await Deno.copy(reader, write, { bufSize }); + + assertEquals(n, xBytes.length); + + write.close(); + await Deno.remove(filePath); +}); diff --git a/cli/tests/unit/link_test.ts b/cli/tests/unit/link_test.ts new file mode 100644 index 000000000..c6ea4901e --- /dev/null +++ b/cli/tests/unit/link_test.ts @@ -0,0 +1,147 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest( + { perms: { read: true, write: true } }, + function linkSyncSuccess(): void { + const testDir = Deno.makeTempDirSync(); + const oldData = "Hardlink"; + const oldName = testDir + "/oldname"; + const newName = testDir + "/newname"; + Deno.writeFileSync(oldName, new TextEncoder().encode(oldData)); + // Create the hard link. + Deno.linkSync(oldName, newName); + // We should expect reading the same content. + const newData = new TextDecoder().decode(Deno.readFileSync(newName)); + assertEquals(oldData, newData); + // Writing to newname also affects oldname. + const newData2 = "Modified"; + Deno.writeFileSync(newName, new TextEncoder().encode(newData2)); + assertEquals( + newData2, + new TextDecoder().decode(Deno.readFileSync(oldName)) + ); + // Writing to oldname also affects newname. + const newData3 = "ModifiedAgain"; + Deno.writeFileSync(oldName, new TextEncoder().encode(newData3)); + assertEquals( + newData3, + new TextDecoder().decode(Deno.readFileSync(newName)) + ); + // Remove oldname. File still accessible through newname. + Deno.removeSync(oldName); + const newNameStat = Deno.statSync(newName); + assert(newNameStat.isFile); + assert(!newNameStat.isSymlink); // Not a symlink. + assertEquals( + newData3, + new TextDecoder().decode(Deno.readFileSync(newName)) + ); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function linkSyncExists(): void { + const testDir = Deno.makeTempDirSync(); + const oldName = testDir + "/oldname"; + const newName = testDir + "/newname"; + Deno.writeFileSync(oldName, new TextEncoder().encode("oldName")); + // newname is already created. + Deno.writeFileSync(newName, new TextEncoder().encode("newName")); + + let err; + try { + Deno.linkSync(oldName, newName); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Deno.errors.AlreadyExists); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function linkSyncNotFound(): void { + const testDir = Deno.makeTempDirSync(); + const oldName = testDir + "/oldname"; + const newName = testDir + "/newname"; + + let err; + try { + Deno.linkSync(oldName, newName); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { read: false, write: true } }, + function linkSyncReadPerm(): void { + let err; + try { + Deno.linkSync("oldbaddir", "newbaddir"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } +); + +unitTest( + { perms: { read: true, write: false } }, + function linkSyncWritePerm(): void { + let err; + try { + Deno.linkSync("oldbaddir", "newbaddir"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function linkSuccess(): Promise { + const testDir = Deno.makeTempDirSync(); + const oldData = "Hardlink"; + const oldName = testDir + "/oldname"; + const newName = testDir + "/newname"; + Deno.writeFileSync(oldName, new TextEncoder().encode(oldData)); + // Create the hard link. + await Deno.link(oldName, newName); + // We should expect reading the same content. + const newData = new TextDecoder().decode(Deno.readFileSync(newName)); + assertEquals(oldData, newData); + // Writing to newname also affects oldname. + const newData2 = "Modified"; + Deno.writeFileSync(newName, new TextEncoder().encode(newData2)); + assertEquals( + newData2, + new TextDecoder().decode(Deno.readFileSync(oldName)) + ); + // Writing to oldname also affects newname. + const newData3 = "ModifiedAgain"; + Deno.writeFileSync(oldName, new TextEncoder().encode(newData3)); + assertEquals( + newData3, + new TextDecoder().decode(Deno.readFileSync(newName)) + ); + // Remove oldname. File still accessible through newname. + Deno.removeSync(oldName); + const newNameStat = Deno.statSync(newName); + assert(newNameStat.isFile); + assert(!newNameStat.isSymlink); // Not a symlink. + assertEquals( + newData3, + new TextDecoder().decode(Deno.readFileSync(newName)) + ); + } +); diff --git a/cli/tests/unit/make_temp_test.ts b/cli/tests/unit/make_temp_test.ts new file mode 100644 index 000000000..59fe8c5f5 --- /dev/null +++ b/cli/tests/unit/make_temp_test.ts @@ -0,0 +1,178 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest({ perms: { write: true } }, function makeTempDirSyncSuccess(): void { + const dir1 = Deno.makeTempDirSync({ prefix: "hello", suffix: "world" }); + const dir2 = Deno.makeTempDirSync({ prefix: "hello", suffix: "world" }); + // Check that both dirs are different. + assert(dir1 !== dir2); + for (const dir of [dir1, dir2]) { + // Check that the prefix and suffix are applied. + const lastPart = dir.replace(/^.*[\\\/]/, ""); + assert(lastPart.startsWith("hello")); + assert(lastPart.endsWith("world")); + } + // Check that the `dir` option works. + const dir3 = Deno.makeTempDirSync({ dir: dir1 }); + assert(dir3.startsWith(dir1)); + assert(/^[\\\/]/.test(dir3.slice(dir1.length))); + // Check that creating a temp dir inside a nonexisting directory fails. + let err; + try { + Deno.makeTempDirSync({ dir: "/baddir" }); + } catch (err_) { + err = err_; + } + assert(err instanceof Deno.errors.NotFound); +}); + +unitTest( + { perms: { read: true, write: true } }, + function makeTempDirSyncMode(): void { + const path = Deno.makeTempDirSync(); + const pathInfo = Deno.statSync(path); + if (Deno.build.os !== "windows") { + assertEquals(pathInfo.mode! & 0o777, 0o700 & ~Deno.umask()); + } + } +); + +unitTest(function makeTempDirSyncPerm(): void { + // makeTempDirSync should require write permissions (for now). + let err; + try { + Deno.makeTempDirSync({ dir: "/baddir" }); + } catch (err_) { + err = err_; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest( + { perms: { write: true } }, + async function makeTempDirSuccess(): Promise { + const dir1 = await Deno.makeTempDir({ prefix: "hello", suffix: "world" }); + const dir2 = await Deno.makeTempDir({ prefix: "hello", suffix: "world" }); + // Check that both dirs are different. + assert(dir1 !== dir2); + for (const dir of [dir1, dir2]) { + // Check that the prefix and suffix are applied. + const lastPart = dir.replace(/^.*[\\\/]/, ""); + assert(lastPart.startsWith("hello")); + assert(lastPart.endsWith("world")); + } + // Check that the `dir` option works. + const dir3 = await Deno.makeTempDir({ dir: dir1 }); + assert(dir3.startsWith(dir1)); + assert(/^[\\\/]/.test(dir3.slice(dir1.length))); + // Check that creating a temp dir inside a nonexisting directory fails. + let err; + try { + await Deno.makeTempDir({ dir: "/baddir" }); + } catch (err_) { + err = err_; + } + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function makeTempDirMode(): Promise { + const path = await Deno.makeTempDir(); + const pathInfo = Deno.statSync(path); + if (Deno.build.os !== "windows") { + assertEquals(pathInfo.mode! & 0o777, 0o700 & ~Deno.umask()); + } + } +); + +unitTest({ perms: { write: true } }, function makeTempFileSyncSuccess(): void { + const file1 = Deno.makeTempFileSync({ prefix: "hello", suffix: "world" }); + const file2 = Deno.makeTempFileSync({ prefix: "hello", suffix: "world" }); + // Check that both dirs are different. + assert(file1 !== file2); + for (const dir of [file1, file2]) { + // Check that the prefix and suffix are applied. + const lastPart = dir.replace(/^.*[\\\/]/, ""); + assert(lastPart.startsWith("hello")); + assert(lastPart.endsWith("world")); + } + // Check that the `dir` option works. + const dir = Deno.makeTempDirSync({ prefix: "tempdir" }); + const file3 = Deno.makeTempFileSync({ dir }); + assert(file3.startsWith(dir)); + assert(/^[\\\/]/.test(file3.slice(dir.length))); + // Check that creating a temp file inside a nonexisting directory fails. + let err; + try { + Deno.makeTempFileSync({ dir: "/baddir" }); + } catch (err_) { + err = err_; + } + assert(err instanceof Deno.errors.NotFound); +}); + +unitTest( + { perms: { read: true, write: true } }, + function makeTempFileSyncMode(): void { + const path = Deno.makeTempFileSync(); + const pathInfo = Deno.statSync(path); + if (Deno.build.os !== "windows") { + assertEquals(pathInfo.mode! & 0o777, 0o600 & ~Deno.umask()); + } + } +); + +unitTest(function makeTempFileSyncPerm(): void { + // makeTempFileSync should require write permissions (for now). + let err; + try { + Deno.makeTempFileSync({ dir: "/baddir" }); + } catch (err_) { + err = err_; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest( + { perms: { write: true } }, + async function makeTempFileSuccess(): Promise { + const file1 = await Deno.makeTempFile({ prefix: "hello", suffix: "world" }); + const file2 = await Deno.makeTempFile({ prefix: "hello", suffix: "world" }); + // Check that both dirs are different. + assert(file1 !== file2); + for (const dir of [file1, file2]) { + // Check that the prefix and suffix are applied. + const lastPart = dir.replace(/^.*[\\\/]/, ""); + assert(lastPart.startsWith("hello")); + assert(lastPart.endsWith("world")); + } + // Check that the `dir` option works. + const dir = Deno.makeTempDirSync({ prefix: "tempdir" }); + const file3 = await Deno.makeTempFile({ dir }); + assert(file3.startsWith(dir)); + assert(/^[\\\/]/.test(file3.slice(dir.length))); + // Check that creating a temp file inside a nonexisting directory fails. + let err; + try { + await Deno.makeTempFile({ dir: "/baddir" }); + } catch (err_) { + err = err_; + } + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function makeTempFileMode(): Promise { + const path = await Deno.makeTempFile(); + const pathInfo = Deno.statSync(path); + if (Deno.build.os !== "windows") { + assertEquals(pathInfo.mode! & 0o777, 0o600 & ~Deno.umask()); + } + } +); diff --git a/cli/tests/unit/metrics_test.ts b/cli/tests/unit/metrics_test.ts new file mode 100644 index 000000000..9b7d83887 --- /dev/null +++ b/cli/tests/unit/metrics_test.ts @@ -0,0 +1,58 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +unitTest(async function metrics(): Promise { + const m1 = Deno.metrics(); + assert(m1.opsDispatched > 0); + assert(m1.opsDispatchedSync > 0); + assert(m1.opsCompleted > 0); + assert(m1.opsCompletedSync > 0); + assert(m1.bytesSentControl > 0); + assert(m1.bytesSentData >= 0); + assert(m1.bytesReceived > 0); + + // Write to stdout to ensure a "data" message gets sent instead of just + // control messages. + const dataMsg = new Uint8Array([13, 13, 13]); // "\r\r\r", + await Deno.stdout.write(dataMsg); + + const m2 = Deno.metrics(); + assert(m2.opsDispatched > m1.opsDispatched); + assert(m2.opsDispatchedSync > m1.opsDispatchedSync); + assert(m2.opsDispatchedAsync > m1.opsDispatchedAsync); + assert(m2.opsCompleted > m1.opsCompleted); + assert(m2.opsCompletedSync > m1.opsCompletedSync); + assert(m2.opsCompletedAsync > m1.opsCompletedAsync); + assert(m2.bytesSentControl > m1.bytesSentControl); + assert(m2.bytesSentData >= m1.bytesSentData + dataMsg.byteLength); + assert(m2.bytesReceived > m1.bytesReceived); +}); + +unitTest( + { perms: { write: true } }, + function metricsUpdatedIfNoResponseSync(): void { + const filename = Deno.makeTempDirSync() + "/test.txt"; + + const data = new Uint8Array([41, 42, 43]); + Deno.writeFileSync(filename, data, { mode: 0o666 }); + + const metrics = Deno.metrics(); + assert(metrics.opsDispatched === metrics.opsCompleted); + assert(metrics.opsDispatchedSync === metrics.opsCompletedSync); + } +); + +unitTest( + { perms: { write: true } }, + async function metricsUpdatedIfNoResponseAsync(): Promise { + const filename = Deno.makeTempDirSync() + "/test.txt"; + + const data = new Uint8Array([41, 42, 43]); + await Deno.writeFile(filename, data, { mode: 0o666 }); + + const metrics = Deno.metrics(); + assert(metrics.opsDispatched === metrics.opsCompleted); + assert(metrics.opsDispatchedSync === metrics.opsCompletedSync); + assert(metrics.opsDispatchedAsync === metrics.opsCompletedAsync); + } +); diff --git a/cli/tests/unit/mkdir_test.ts b/cli/tests/unit/mkdir_test.ts new file mode 100644 index 000000000..68755ef4d --- /dev/null +++ b/cli/tests/unit/mkdir_test.ts @@ -0,0 +1,206 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals, assertThrows } from "./test_util.ts"; + +function assertDirectory(path: string, mode?: number): void { + const info = Deno.lstatSync(path); + assert(info.isDirectory); + if (Deno.build.os !== "windows" && mode !== undefined) { + assertEquals(info.mode! & 0o777, mode & ~Deno.umask()); + } +} + +unitTest( + { perms: { read: true, write: true } }, + function mkdirSyncSuccess(): void { + const path = Deno.makeTempDirSync() + "/dir"; + Deno.mkdirSync(path); + assertDirectory(path); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function mkdirSyncMode(): void { + const path = Deno.makeTempDirSync() + "/dir"; + Deno.mkdirSync(path, { mode: 0o737 }); + assertDirectory(path, 0o737); + } +); + +unitTest({ perms: { write: false } }, function mkdirSyncPerm(): void { + let err; + try { + Deno.mkdirSync("/baddir"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest( + { perms: { read: true, write: true } }, + async function mkdirSuccess(): Promise { + const path = Deno.makeTempDirSync() + "/dir"; + await Deno.mkdir(path); + assertDirectory(path); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function mkdirMode(): Promise { + const path = Deno.makeTempDirSync() + "/dir"; + await Deno.mkdir(path, { mode: 0o737 }); + assertDirectory(path, 0o737); + } +); + +unitTest({ perms: { write: true } }, function mkdirErrSyncIfExists(): void { + let err; + try { + Deno.mkdirSync("."); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.AlreadyExists); +}); + +unitTest({ perms: { write: true } }, async function mkdirErrIfExists(): Promise< + void +> { + let err; + try { + await Deno.mkdir("."); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.AlreadyExists); +}); + +unitTest( + { perms: { read: true, write: true } }, + function mkdirSyncRecursive(): void { + const path = Deno.makeTempDirSync() + "/nested/directory"; + Deno.mkdirSync(path, { recursive: true }); + assertDirectory(path); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function mkdirRecursive(): Promise { + const path = Deno.makeTempDirSync() + "/nested/directory"; + await Deno.mkdir(path, { recursive: true }); + assertDirectory(path); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function mkdirSyncRecursiveMode(): void { + const nested = Deno.makeTempDirSync() + "/nested"; + const path = nested + "/dir"; + Deno.mkdirSync(path, { mode: 0o737, recursive: true }); + assertDirectory(path, 0o737); + assertDirectory(nested, 0o737); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function mkdirRecursiveMode(): Promise { + const nested = Deno.makeTempDirSync() + "/nested"; + const path = nested + "/dir"; + await Deno.mkdir(path, { mode: 0o737, recursive: true }); + assertDirectory(path, 0o737); + assertDirectory(nested, 0o737); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function mkdirSyncRecursiveIfExists(): void { + const path = Deno.makeTempDirSync() + "/dir"; + Deno.mkdirSync(path, { mode: 0o737 }); + Deno.mkdirSync(path, { recursive: true }); + Deno.mkdirSync(path, { recursive: true, mode: 0o731 }); + assertDirectory(path, 0o737); + if (Deno.build.os !== "windows") { + const pathLink = path + "Link"; + Deno.symlinkSync(path, pathLink); + Deno.mkdirSync(pathLink, { recursive: true }); + Deno.mkdirSync(pathLink, { recursive: true, mode: 0o731 }); + assertDirectory(path, 0o737); + } + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function mkdirRecursiveIfExists(): Promise { + const path = Deno.makeTempDirSync() + "/dir"; + await Deno.mkdir(path, { mode: 0o737 }); + await Deno.mkdir(path, { recursive: true }); + await Deno.mkdir(path, { recursive: true, mode: 0o731 }); + assertDirectory(path, 0o737); + if (Deno.build.os !== "windows") { + const pathLink = path + "Link"; + Deno.symlinkSync(path, pathLink); + await Deno.mkdir(pathLink, { recursive: true }); + await Deno.mkdir(pathLink, { recursive: true, mode: 0o731 }); + assertDirectory(path, 0o737); + } + } +); + +unitTest( + { perms: { read: true, write: true } }, + function mkdirSyncErrors(): void { + const testDir = Deno.makeTempDirSync(); + const emptydir = testDir + "/empty"; + const fulldir = testDir + "/dir"; + const file = fulldir + "/file"; + Deno.mkdirSync(emptydir); + Deno.mkdirSync(fulldir); + Deno.createSync(file).close(); + + assertThrows((): void => { + Deno.mkdirSync(emptydir, { recursive: false }); + }, Deno.errors.AlreadyExists); + assertThrows((): void => { + Deno.mkdirSync(fulldir, { recursive: false }); + }, Deno.errors.AlreadyExists); + assertThrows((): void => { + Deno.mkdirSync(file, { recursive: false }); + }, Deno.errors.AlreadyExists); + assertThrows((): void => { + Deno.mkdirSync(file, { recursive: true }); + }, Deno.errors.AlreadyExists); + + if (Deno.build.os !== "windows") { + const fileLink = testDir + "/fileLink"; + const dirLink = testDir + "/dirLink"; + const danglingLink = testDir + "/danglingLink"; + Deno.symlinkSync(file, fileLink); + Deno.symlinkSync(emptydir, dirLink); + Deno.symlinkSync(testDir + "/nonexistent", danglingLink); + + assertThrows((): void => { + Deno.mkdirSync(dirLink, { recursive: false }); + }, Deno.errors.AlreadyExists); + assertThrows((): void => { + Deno.mkdirSync(fileLink, { recursive: false }); + }, Deno.errors.AlreadyExists); + assertThrows((): void => { + Deno.mkdirSync(fileLink, { recursive: true }); + }, Deno.errors.AlreadyExists); + assertThrows((): void => { + Deno.mkdirSync(danglingLink, { recursive: false }); + }, Deno.errors.AlreadyExists); + assertThrows((): void => { + Deno.mkdirSync(danglingLink, { recursive: true }); + }, Deno.errors.AlreadyExists); + } + } +); diff --git a/cli/tests/unit/net_test.ts b/cli/tests/unit/net_test.ts new file mode 100644 index 000000000..9e9a1e5e8 --- /dev/null +++ b/cli/tests/unit/net_test.ts @@ -0,0 +1,527 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + unitTest, + assert, + assertEquals, + createResolvable, +} from "./test_util.ts"; + +unitTest({ perms: { net: true } }, function netTcpListenClose(): void { + const listener = Deno.listen({ hostname: "127.0.0.1", port: 3500 }); + assert(listener.addr.transport === "tcp"); + assertEquals(listener.addr.hostname, "127.0.0.1"); + assertEquals(listener.addr.port, 3500); + listener.close(); +}); + +unitTest( + { + perms: { net: true }, + // TODO: + ignore: Deno.build.os === "windows", + }, + function netUdpListenClose(): void { + const socket = Deno.listenDatagram({ + hostname: "127.0.0.1", + port: 3500, + transport: "udp", + }); + assert(socket.addr.transport === "udp"); + assertEquals(socket.addr.hostname, "127.0.0.1"); + assertEquals(socket.addr.port, 3500); + socket.close(); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + function netUnixListenClose(): void { + const filePath = Deno.makeTempFileSync(); + const socket = Deno.listen({ + path: filePath, + transport: "unix", + }); + assert(socket.addr.transport === "unix"); + assertEquals(socket.addr.path, filePath); + socket.close(); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + function netUnixPacketListenClose(): void { + const filePath = Deno.makeTempFileSync(); + const socket = Deno.listenDatagram({ + path: filePath, + transport: "unixpacket", + }); + assert(socket.addr.transport === "unixpacket"); + assertEquals(socket.addr.path, filePath); + socket.close(); + } +); + +unitTest( + { + perms: { net: true }, + }, + async function netTcpCloseWhileAccept(): Promise { + const listener = Deno.listen({ port: 4501 }); + const p = listener.accept(); + listener.close(); + let err; + try { + await p; + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Error); + assertEquals(err.message, "Listener has been closed"); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + async function netUnixCloseWhileAccept(): Promise { + const filePath = await Deno.makeTempFile(); + const listener = Deno.listen({ + path: filePath, + transport: "unix", + }); + const p = listener.accept(); + listener.close(); + let err; + try { + await p; + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Error); + assertEquals(err.message, "Listener has been closed"); + } +); + +unitTest( + { perms: { net: true } }, + async function netTcpConcurrentAccept(): Promise { + const listener = Deno.listen({ port: 4502 }); + let acceptErrCount = 0; + const checkErr = (e: Error): void => { + if (e.message === "Listener has been closed") { + assertEquals(acceptErrCount, 1); + } else if (e.message === "Another accept task is ongoing") { + acceptErrCount++; + } else { + throw new Error("Unexpected error message"); + } + }; + const p = listener.accept().catch(checkErr); + const p1 = listener.accept().catch(checkErr); + await Promise.race([p, p1]); + listener.close(); + await Promise.all([p, p1]); + assertEquals(acceptErrCount, 1); + } +); + +// TODO(jsouto): Enable when tokio updates mio to v0.7! +unitTest( + { ignore: true, perms: { read: true, write: true } }, + async function netUnixConcurrentAccept(): Promise { + const filePath = await Deno.makeTempFile(); + const listener = Deno.listen({ transport: "unix", path: filePath }); + let acceptErrCount = 0; + const checkErr = (e: Error): void => { + if (e.message === "Listener has been closed") { + assertEquals(acceptErrCount, 1); + } else if (e.message === "Another accept task is ongoing") { + acceptErrCount++; + } else { + throw new Error("Unexpected error message"); + } + }; + const p = listener.accept().catch(checkErr); + const p1 = listener.accept().catch(checkErr); + await Promise.race([p, p1]); + listener.close(); + await [p, p1]; + assertEquals(acceptErrCount, 1); + } +); + +unitTest({ perms: { net: true } }, async function netTcpDialListen(): Promise< + void +> { + const listener = Deno.listen({ port: 3500 }); + listener.accept().then( + async (conn): Promise => { + assert(conn.remoteAddr != null); + assert(conn.localAddr.transport === "tcp"); + assertEquals(conn.localAddr.hostname, "127.0.0.1"); + assertEquals(conn.localAddr.port, 3500); + await conn.write(new Uint8Array([1, 2, 3])); + conn.close(); + } + ); + + const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 }); + assert(conn.remoteAddr.transport === "tcp"); + assertEquals(conn.remoteAddr.hostname, "127.0.0.1"); + assertEquals(conn.remoteAddr.port, 3500); + assert(conn.localAddr != null); + const buf = new Uint8Array(1024); + const readResult = await conn.read(buf); + assertEquals(3, readResult); + assertEquals(1, buf[0]); + assertEquals(2, buf[1]); + assertEquals(3, buf[2]); + assert(conn.rid > 0); + + assert(readResult !== null); + + const readResult2 = await conn.read(buf); + assertEquals(readResult2, null); + + listener.close(); + conn.close(); +}); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + async function netUnixDialListen(): Promise { + const filePath = await Deno.makeTempFile(); + const listener = Deno.listen({ path: filePath, transport: "unix" }); + listener.accept().then( + async (conn): Promise => { + assert(conn.remoteAddr != null); + assert(conn.localAddr.transport === "unix"); + assertEquals(conn.localAddr.path, filePath); + await conn.write(new Uint8Array([1, 2, 3])); + conn.close(); + } + ); + const conn = await Deno.connect({ path: filePath, transport: "unix" }); + assert(conn.remoteAddr.transport === "unix"); + assertEquals(conn.remoteAddr.path, filePath); + assert(conn.remoteAddr != null); + const buf = new Uint8Array(1024); + const readResult = await conn.read(buf); + assertEquals(3, readResult); + assertEquals(1, buf[0]); + assertEquals(2, buf[1]); + assertEquals(3, buf[2]); + assert(conn.rid > 0); + + assert(readResult !== null); + + const readResult2 = await conn.read(buf); + assertEquals(readResult2, null); + + listener.close(); + conn.close(); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { net: true } }, + async function netUdpSendReceive(): Promise { + const alice = Deno.listenDatagram({ port: 3500, transport: "udp" }); + assert(alice.addr.transport === "udp"); + assertEquals(alice.addr.port, 3500); + assertEquals(alice.addr.hostname, "127.0.0.1"); + + const bob = Deno.listenDatagram({ port: 4501, transport: "udp" }); + assert(bob.addr.transport === "udp"); + assertEquals(bob.addr.port, 4501); + assertEquals(bob.addr.hostname, "127.0.0.1"); + + const sent = new Uint8Array([1, 2, 3]); + await alice.send(sent, bob.addr); + + const [recvd, remote] = await bob.receive(); + assert(remote.transport === "udp"); + assertEquals(remote.port, 3500); + assertEquals(recvd.length, 3); + assertEquals(1, recvd[0]); + assertEquals(2, recvd[1]); + assertEquals(3, recvd[2]); + alice.close(); + bob.close(); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + async function netUnixPacketSendReceive(): Promise { + const filePath = await Deno.makeTempFile(); + const alice = Deno.listenDatagram({ + path: filePath, + transport: "unixpacket", + }); + assert(alice.addr.transport === "unixpacket"); + assertEquals(alice.addr.path, filePath); + + const bob = Deno.listenDatagram({ + path: filePath, + transport: "unixpacket", + }); + assert(bob.addr.transport === "unixpacket"); + assertEquals(bob.addr.path, filePath); + + const sent = new Uint8Array([1, 2, 3]); + await alice.send(sent, bob.addr); + + const [recvd, remote] = await bob.receive(); + assert(remote.transport === "unixpacket"); + assertEquals(remote.path, filePath); + assertEquals(recvd.length, 3); + assertEquals(1, recvd[0]); + assertEquals(2, recvd[1]); + assertEquals(3, recvd[2]); + alice.close(); + bob.close(); + } +); + +unitTest( + { perms: { net: true } }, + async function netTcpListenIteratorBreakClosesResource(): Promise { + const promise = createResolvable(); + + async function iterate(listener: Deno.Listener): Promise { + let i = 0; + + for await (const conn of listener) { + conn.close(); + i++; + + if (i > 1) { + break; + } + } + + promise.resolve(); + } + + const addr = { hostname: "127.0.0.1", port: 8888 }; + const listener = Deno.listen(addr); + iterate(listener); + + await new Promise((resolve: () => void, _) => { + setTimeout(resolve, 100); + }); + const conn1 = await Deno.connect(addr); + conn1.close(); + const conn2 = await Deno.connect(addr); + conn2.close(); + + await promise; + } +); + +unitTest( + { perms: { net: true } }, + async function netTcpListenCloseWhileIterating(): Promise { + const listener = Deno.listen({ port: 8000 }); + const nextWhileClosing = listener[Symbol.asyncIterator]().next(); + listener.close(); + assertEquals(await nextWhileClosing, { value: undefined, done: true }); + + const nextAfterClosing = listener[Symbol.asyncIterator]().next(); + assertEquals(await nextAfterClosing, { value: undefined, done: true }); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { net: true } }, + async function netUdpListenCloseWhileIterating(): Promise { + const socket = Deno.listenDatagram({ port: 8000, transport: "udp" }); + const nextWhileClosing = socket[Symbol.asyncIterator]().next(); + socket.close(); + assertEquals(await nextWhileClosing, { value: undefined, done: true }); + + const nextAfterClosing = socket[Symbol.asyncIterator]().next(); + assertEquals(await nextAfterClosing, { value: undefined, done: true }); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + async function netUnixListenCloseWhileIterating(): Promise { + const filePath = Deno.makeTempFileSync(); + const socket = Deno.listen({ path: filePath, transport: "unix" }); + const nextWhileClosing = socket[Symbol.asyncIterator]().next(); + socket.close(); + assertEquals(await nextWhileClosing, { value: undefined, done: true }); + + const nextAfterClosing = socket[Symbol.asyncIterator]().next(); + assertEquals(await nextAfterClosing, { value: undefined, done: true }); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + async function netUnixPacketListenCloseWhileIterating(): Promise { + const filePath = Deno.makeTempFileSync(); + const socket = Deno.listenDatagram({ + path: filePath, + transport: "unixpacket", + }); + const nextWhileClosing = socket[Symbol.asyncIterator]().next(); + socket.close(); + assertEquals(await nextWhileClosing, { value: undefined, done: true }); + + const nextAfterClosing = socket[Symbol.asyncIterator]().next(); + assertEquals(await nextAfterClosing, { value: undefined, done: true }); + } +); + +unitTest( + { + // FIXME(bartlomieju) + ignore: true, + perms: { net: true }, + }, + async function netListenAsyncIterator(): Promise { + const addr = { hostname: "127.0.0.1", port: 3500 }; + const listener = Deno.listen(addr); + const runAsyncIterator = async (): Promise => { + for await (const conn of listener) { + await conn.write(new Uint8Array([1, 2, 3])); + conn.close(); + } + }; + runAsyncIterator(); + const conn = await Deno.connect(addr); + const buf = new Uint8Array(1024); + const readResult = await conn.read(buf); + assertEquals(3, readResult); + assertEquals(1, buf[0]); + assertEquals(2, buf[1]); + assertEquals(3, buf[2]); + assert(conn.rid > 0); + + assert(readResult !== null); + + const readResult2 = await conn.read(buf); + assertEquals(readResult2, null); + + listener.close(); + conn.close(); + } +); + +unitTest( + { + // FIXME(bartlomieju) + ignore: true, + perms: { net: true }, + }, + async function netCloseWriteSuccess() { + const addr = { hostname: "127.0.0.1", port: 3500 }; + const listener = Deno.listen(addr); + const closeDeferred = createResolvable(); + listener.accept().then(async (conn) => { + await conn.write(new Uint8Array([1, 2, 3])); + await closeDeferred; + conn.close(); + }); + const conn = await Deno.connect(addr); + conn.closeWrite(); // closing write + const buf = new Uint8Array(1024); + // Check read not impacted + const readResult = await conn.read(buf); + assertEquals(3, readResult); + assertEquals(1, buf[0]); + assertEquals(2, buf[1]); + assertEquals(3, buf[2]); + // Check write should be closed + let err; + try { + await conn.write(new Uint8Array([1, 2, 3])); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Deno.errors.BrokenPipe); + closeDeferred.resolve(); + listener.close(); + conn.close(); + } +); + +unitTest( + { + // FIXME(bartlomieju) + ignore: true, + perms: { net: true }, + }, + async function netDoubleCloseWrite() { + const addr = { hostname: "127.0.0.1", port: 3500 }; + const listener = Deno.listen(addr); + const closeDeferred = createResolvable(); + listener.accept().then(async (conn) => { + await closeDeferred; + conn.close(); + }); + const conn = await Deno.connect(addr); + conn.closeWrite(); // closing write + let err; + try { + // Duplicated close should throw error + conn.closeWrite(); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof Deno.errors.NotConnected); + closeDeferred.resolve(); + listener.close(); + conn.close(); + } +); + +unitTest( + { + perms: { net: true }, + }, + async function netHangsOnClose() { + let acceptedConn: Deno.Conn; + const resolvable = createResolvable(); + + async function iteratorReq(listener: Deno.Listener): Promise { + const p = new Uint8Array(10); + const conn = await listener.accept(); + acceptedConn = conn; + + try { + while (true) { + const nread = await conn.read(p); + if (nread === null) { + break; + } + await conn.write(new Uint8Array([1, 2, 3])); + } + } catch (err) { + assert(!!err); + assert(err instanceof Deno.errors.BadResource); + } + + resolvable.resolve(); + } + + const addr = { hostname: "127.0.0.1", port: 3500 }; + const listener = Deno.listen(addr); + iteratorReq(listener); + const conn = await Deno.connect(addr); + await conn.write(new Uint8Array([1, 2, 3, 4])); + const buf = new Uint8Array(10); + await conn.read(buf); + conn!.close(); + acceptedConn!.close(); + listener.close(); + await resolvable; + } +); diff --git a/cli/tests/unit/os_test.ts b/cli/tests/unit/os_test.ts new file mode 100644 index 000000000..e99002534 --- /dev/null +++ b/cli/tests/unit/os_test.ts @@ -0,0 +1,338 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + assert, + assertEquals, + assertNotEquals, + assertThrows, + unitTest, +} from "./test_util.ts"; + +unitTest({ perms: { env: true } }, function envSuccess(): void { + Deno.env.set("TEST_VAR", "A"); + const env = Deno.env.toObject(); + Deno.env.set("TEST_VAR", "B"); + assertEquals(env["TEST_VAR"], "A"); + assertNotEquals(Deno.env.get("TEST_VAR"), env["TEST_VAR"]); +}); + +unitTest({ perms: { env: true } }, function envNotFound(): void { + const r = Deno.env.get("env_var_does_not_exist!"); + assertEquals(r, undefined); +}); + +unitTest(function envPermissionDenied1(): void { + let err; + try { + Deno.env.toObject(); + } catch (e) { + err = e; + } + assertNotEquals(err, undefined); + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest(function envPermissionDenied2(): void { + let err; + try { + Deno.env.get("PATH"); + } catch (e) { + err = e; + } + assertNotEquals(err, undefined); + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +// This test verifies that on Windows, environment variables are +// case-insensitive. Case normalization needs be done using the collation +// that Windows uses, rather than naively using String.toLowerCase(). +unitTest( + { + ignore: Deno.build.os !== "windows", + perms: { read: true, env: true, run: true }, + }, + async function envCaseInsensitive() { + // Utility function that runs a Deno subprocess with the environment + // specified in `inputEnv`. The subprocess reads the environment variables + // which are in the keys of `expectedEnv` and writes them to stdout as JSON. + // It is then verified that these match with the values of `expectedEnv`. + const checkChildEnv = async ( + inputEnv: Record, + expectedEnv: Record + ): Promise => { + const src = ` + console.log( + ${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env.get(k)) + )`; + const proc = Deno.run({ + cmd: [Deno.execPath(), "eval", src], + env: inputEnv, + stdout: "piped", + }); + const status = await proc.status(); + assertEquals(status.success, true); + const expectedValues = Object.values(expectedEnv); + const actualValues = JSON.parse( + new TextDecoder().decode(await proc.output()) + ); + assertEquals(actualValues, expectedValues); + proc.close(); + }; + + assertEquals(Deno.env.get("path"), Deno.env.get("PATH")); + assertEquals(Deno.env.get("Path"), Deno.env.get("PATH")); + + // Check 'foo', 'Foo' and 'Foo' are case folded. + await checkChildEnv({ foo: "X" }, { foo: "X", Foo: "X", FOO: "X" }); + + // Check that 'µ' and 'Μ' are not case folded. + const lc1 = "µ"; + const uc1 = lc1.toUpperCase(); + assertNotEquals(lc1, uc1); + await checkChildEnv( + { [lc1]: "mu", [uc1]: "MU" }, + { [lc1]: "mu", [uc1]: "MU" } + ); + + // Check that 'dž' and 'DŽ' are folded, but 'Dž' is preserved. + const c2 = "Dž"; + const lc2 = c2.toLowerCase(); + const uc2 = c2.toUpperCase(); + assertNotEquals(c2, lc2); + assertNotEquals(c2, uc2); + await checkChildEnv( + { [c2]: "Dz", [lc2]: "dz" }, + { [c2]: "Dz", [lc2]: "dz", [uc2]: "dz" } + ); + await checkChildEnv( + { [c2]: "Dz", [uc2]: "DZ" }, + { [c2]: "Dz", [uc2]: "DZ", [lc2]: "DZ" } + ); + } +); + +unitTest(function osPid(): void { + assert(Deno.pid > 0); +}); + +unitTest({ perms: { env: true } }, function getDir(): void { + type supportOS = "darwin" | "windows" | "linux"; + + interface Runtime { + os: supportOS; + shouldHaveValue: boolean; + } + + interface Scenes { + kind: Deno.DirKind; + runtime: Runtime[]; + } + + const scenes: Scenes[] = [ + { + kind: "config", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: true }, + ], + }, + { + kind: "cache", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: true }, + ], + }, + { + kind: "executable", + runtime: [ + { os: "darwin", shouldHaveValue: false }, + { os: "windows", shouldHaveValue: false }, + { os: "linux", shouldHaveValue: true }, + ], + }, + { + kind: "data", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: true }, + ], + }, + { + kind: "data_local", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: true }, + ], + }, + { + kind: "audio", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: false }, + ], + }, + { + kind: "desktop", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: false }, + ], + }, + { + kind: "document", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: false }, + ], + }, + { + kind: "download", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: false }, + ], + }, + { + kind: "font", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: false }, + { os: "linux", shouldHaveValue: true }, + ], + }, + { + kind: "picture", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: false }, + ], + }, + { + kind: "public", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: false }, + ], + }, + { + kind: "template", + runtime: [ + { os: "darwin", shouldHaveValue: false }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: false }, + ], + }, + { + kind: "tmp", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: true }, + ], + }, + { + kind: "video", + runtime: [ + { os: "darwin", shouldHaveValue: true }, + { os: "windows", shouldHaveValue: true }, + { os: "linux", shouldHaveValue: false }, + ], + }, + ]; + + for (const s of scenes) { + for (const r of s.runtime) { + if (Deno.build.os !== r.os) continue; + if (r.shouldHaveValue) { + const d = Deno.dir(s.kind); + assert(d); + assert(d.length > 0); + } + } + } +}); + +unitTest(function getDirWithoutPermission(): void { + assertThrows( + () => Deno.dir("home"), + Deno.errors.PermissionDenied, + `run again with the --allow-env flag` + ); +}); + +unitTest({ perms: { read: true } }, function execPath(): void { + assertNotEquals(Deno.execPath(), ""); +}); + +unitTest({ perms: { read: false } }, function execPathPerm(): void { + let caughtError = false; + try { + Deno.execPath(); + } catch (err) { + caughtError = true; + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } + assert(caughtError); +}); + +unitTest({ perms: { env: true } }, function loadavgSuccess(): void { + const load = Deno.loadavg(); + assertEquals(load.length, 3); +}); + +unitTest({ perms: { env: false } }, function loadavgPerm(): void { + let caughtError = false; + try { + Deno.loadavg(); + } catch (err) { + caughtError = true; + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } + assert(caughtError); +}); + +unitTest({ perms: { env: true } }, function hostnameDir(): void { + assertNotEquals(Deno.hostname(), ""); +}); + +unitTest({ perms: { env: false } }, function hostnamePerm(): void { + let caughtError = false; + try { + Deno.hostname(); + } catch (err) { + caughtError = true; + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } + assert(caughtError); +}); + +unitTest({ perms: { env: true } }, function releaseDir(): void { + assertNotEquals(Deno.osRelease(), ""); +}); + +unitTest({ perms: { env: false } }, function releasePerm(): void { + let caughtError = false; + try { + Deno.osRelease(); + } catch (err) { + caughtError = true; + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } + assert(caughtError); +}); diff --git a/cli/tests/unit/performance_test.ts b/cli/tests/unit/performance_test.ts new file mode 100644 index 000000000..89b7cad8b --- /dev/null +++ b/cli/tests/unit/performance_test.ts @@ -0,0 +1,15 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, createResolvable } from "./test_util.ts"; + +unitTest({ perms: { hrtime: false } }, async function performanceNow(): Promise< + void +> { + const resolvable = createResolvable(); + const start = performance.now(); + setTimeout((): void => { + const end = performance.now(); + assert(end - start >= 10); + resolvable.resolve(); + }, 10); + await resolvable; +}); diff --git a/cli/tests/unit/permissions_test.ts b/cli/tests/unit/permissions_test.ts new file mode 100644 index 000000000..7528768a1 --- /dev/null +++ b/cli/tests/unit/permissions_test.ts @@ -0,0 +1,27 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +unitTest(async function permissionInvalidName(): Promise { + let thrown = false; + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await Deno.permissions.query({ name: "foo" as any }); + } catch (e) { + thrown = true; + assert(e instanceof Error); + } finally { + assert(thrown); + } +}); + +unitTest(async function permissionNetInvalidUrl(): Promise { + let thrown = false; + try { + await Deno.permissions.query({ name: "net", url: ":" }); + } catch (e) { + thrown = true; + assert(e instanceof URIError); + } finally { + assert(thrown); + } +}); diff --git a/cli/tests/unit/process_test.ts b/cli/tests/unit/process_test.ts new file mode 100644 index 000000000..1ea6f95b7 --- /dev/null +++ b/cli/tests/unit/process_test.ts @@ -0,0 +1,386 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + assert, + assertEquals, + assertStrContains, + unitTest, +} from "./test_util.ts"; +const { + kill, + run, + readFile, + open, + makeTempDir, + writeFile, + writeFileSync, +} = Deno; + +unitTest(function runPermissions(): void { + let caughtError = false; + try { + run({ cmd: ["python", "-c", "print('hello world')"] }); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { run: true } }, async function runSuccess(): Promise { + const p = run({ + cmd: ["python", "-c", "print('hello world')"], + stdout: "piped", + stderr: "null", + }); + const status = await p.status(); + assertEquals(status.success, true); + assertEquals(status.code, 0); + assertEquals(status.signal, undefined); + p.stdout!.close(); + p.close(); +}); + +unitTest( + { perms: { run: true } }, + async function runCommandFailedWithCode(): Promise { + const p = run({ + cmd: ["python", "-c", "import sys;sys.exit(41 + 1)"], + }); + const status = await p.status(); + assertEquals(status.success, false); + assertEquals(status.code, 42); + assertEquals(status.signal, undefined); + p.close(); + } +); + +unitTest( + { + // No signals on windows. + ignore: Deno.build.os === "windows", + perms: { run: true }, + }, + async function runCommandFailedWithSignal(): Promise { + const p = run({ + cmd: ["python", "-c", "import os;os.kill(os.getpid(), 9)"], + }); + const status = await p.status(); + assertEquals(status.success, false); + assertEquals(status.code, undefined); + assertEquals(status.signal, 9); + p.close(); + } +); + +unitTest({ perms: { run: true } }, function runNotFound(): void { + let error; + try { + run({ cmd: ["this file hopefully doesn't exist"] }); + } catch (e) { + error = e; + } + assert(error !== undefined); + assert(error instanceof Deno.errors.NotFound); +}); + +unitTest( + { perms: { write: true, run: true } }, + async function runWithCwdIsAsync(): Promise { + const enc = new TextEncoder(); + const cwd = await makeTempDir({ prefix: "deno_command_test" }); + + const exitCodeFile = "deno_was_here"; + const pyProgramFile = "poll_exit.py"; + const pyProgram = ` +from sys import exit +from time import sleep + +while True: + try: + with open("${exitCodeFile}", "r") as f: + line = f.readline() + code = int(line) + exit(code) + except IOError: + # Retry if we got here before deno wrote the file. + sleep(0.01) + pass +`; + + writeFileSync(`${cwd}/${pyProgramFile}.py`, enc.encode(pyProgram)); + const p = run({ + cwd, + cmd: ["python", `${pyProgramFile}.py`], + }); + + // Write the expected exit code *after* starting python. + // This is how we verify that `run()` is actually asynchronous. + const code = 84; + writeFileSync(`${cwd}/${exitCodeFile}`, enc.encode(`${code}`)); + + const status = await p.status(); + assertEquals(status.success, false); + assertEquals(status.code, code); + assertEquals(status.signal, undefined); + p.close(); + } +); + +unitTest({ perms: { run: true } }, async function runStdinPiped(): Promise< + void +> { + const p = run({ + cmd: ["python", "-c", "import sys; assert 'hello' == sys.stdin.read();"], + stdin: "piped", + }); + assert(p.stdin); + assert(!p.stdout); + assert(!p.stderr); + + const msg = new TextEncoder().encode("hello"); + const n = await p.stdin.write(msg); + assertEquals(n, msg.byteLength); + + p.stdin!.close(); + + const status = await p.status(); + assertEquals(status.success, true); + assertEquals(status.code, 0); + assertEquals(status.signal, undefined); + p.close(); +}); + +unitTest({ perms: { run: true } }, async function runStdoutPiped(): Promise< + void +> { + const p = run({ + cmd: ["python", "-c", "import sys; sys.stdout.write('hello')"], + stdout: "piped", + }); + assert(!p.stdin); + assert(!p.stderr); + + const data = new Uint8Array(10); + let r = await p.stdout!.read(data); + if (r === null) { + throw new Error("p.stdout.read(...) should not be null"); + } + assertEquals(r, 5); + const s = new TextDecoder().decode(data.subarray(0, r)); + assertEquals(s, "hello"); + r = await p.stdout!.read(data); + assertEquals(r, null); + p.stdout!.close(); + + const status = await p.status(); + assertEquals(status.success, true); + assertEquals(status.code, 0); + assertEquals(status.signal, undefined); + p.close(); +}); + +unitTest({ perms: { run: true } }, async function runStderrPiped(): Promise< + void +> { + const p = run({ + cmd: ["python", "-c", "import sys; sys.stderr.write('hello')"], + stderr: "piped", + }); + assert(!p.stdin); + assert(!p.stdout); + + const data = new Uint8Array(10); + let r = await p.stderr!.read(data); + if (r === null) { + throw new Error("p.stderr.read should not return null here"); + } + assertEquals(r, 5); + const s = new TextDecoder().decode(data.subarray(0, r)); + assertEquals(s, "hello"); + r = await p.stderr!.read(data); + assertEquals(r, null); + p.stderr!.close(); + + const status = await p.status(); + assertEquals(status.success, true); + assertEquals(status.code, 0); + assertEquals(status.signal, undefined); + p.close(); +}); + +unitTest({ perms: { run: true } }, async function runOutput(): Promise { + const p = run({ + cmd: ["python", "-c", "import sys; sys.stdout.write('hello')"], + stdout: "piped", + }); + const output = await p.output(); + const s = new TextDecoder().decode(output); + assertEquals(s, "hello"); + p.close(); +}); + +unitTest({ perms: { run: true } }, async function runStderrOutput(): Promise< + void +> { + const p = run({ + cmd: ["python", "-c", "import sys; sys.stderr.write('error')"], + stderr: "piped", + }); + const error = await p.stderrOutput(); + const s = new TextDecoder().decode(error); + assertEquals(s, "error"); + p.close(); +}); + +unitTest( + { perms: { run: true, write: true, read: true } }, + async function runRedirectStdoutStderr(): Promise { + const tempDir = await makeTempDir(); + const fileName = tempDir + "/redirected_stdio.txt"; + const file = await open(fileName, { + create: true, + write: true, + }); + + const p = run({ + cmd: [ + "python", + "-c", + "import sys; sys.stderr.write('error\\n'); sys.stdout.write('output\\n');", + ], + stdout: file.rid, + stderr: file.rid, + }); + + await p.status(); + p.close(); + file.close(); + + const fileContents = await readFile(fileName); + const decoder = new TextDecoder(); + const text = decoder.decode(fileContents); + + assertStrContains(text, "error"); + assertStrContains(text, "output"); + } +); + +unitTest( + { perms: { run: true, write: true, read: true } }, + async function runRedirectStdin(): Promise { + const tempDir = await makeTempDir(); + const fileName = tempDir + "/redirected_stdio.txt"; + const encoder = new TextEncoder(); + await writeFile(fileName, encoder.encode("hello")); + const file = await open(fileName); + + const p = run({ + cmd: ["python", "-c", "import sys; assert 'hello' == sys.stdin.read();"], + stdin: file.rid, + }); + + const status = await p.status(); + assertEquals(status.code, 0); + p.close(); + file.close(); + } +); + +unitTest({ perms: { run: true } }, async function runEnv(): Promise { + const p = run({ + cmd: [ + "python", + "-c", + "import os, sys; sys.stdout.write(os.environ.get('FOO', '') + os.environ.get('BAR', ''))", + ], + env: { + FOO: "0123", + BAR: "4567", + }, + stdout: "piped", + }); + const output = await p.output(); + const s = new TextDecoder().decode(output); + assertEquals(s, "01234567"); + p.close(); +}); + +unitTest({ perms: { run: true } }, async function runClose(): Promise { + const p = run({ + cmd: [ + "python", + "-c", + "from time import sleep; import sys; sleep(10000); sys.stderr.write('error')", + ], + stderr: "piped", + }); + assert(!p.stdin); + assert(!p.stdout); + + p.close(); + + const data = new Uint8Array(10); + const r = await p.stderr!.read(data); + assertEquals(r, null); + p.stderr!.close(); +}); + +unitTest(function signalNumbers(): void { + if (Deno.build.os === "darwin") { + assertEquals(Deno.Signal.SIGSTOP, 17); + } else if (Deno.build.os === "linux") { + assertEquals(Deno.Signal.SIGSTOP, 19); + } +}); + +unitTest(function killPermissions(): void { + let caughtError = false; + try { + // Unlike the other test cases, we don't have permission to spawn a + // subprocess we can safely kill. Instead we send SIGCONT to the current + // process - assuming that Deno does not have a special handler set for it + // and will just continue even if a signal is erroneously sent. + kill(Deno.pid, Deno.Signal.SIGCONT); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { run: true } }, async function killSuccess(): Promise { + const p = run({ + cmd: ["python", "-c", "from time import sleep; sleep(10000)"], + }); + + assertEquals(Deno.Signal.SIGINT, 2); + kill(p.pid, Deno.Signal.SIGINT); + const status = await p.status(); + + assertEquals(status.success, false); + // TODO(ry) On Linux, status.code is sometimes undefined and sometimes 1. + // The following assert is causing this test to be flaky. Investigate and + // re-enable when it can be made deterministic. + // assertEquals(status.code, 1); + // assertEquals(status.signal, Deno.Signal.SIGINT); + p.close(); +}); + +unitTest({ perms: { run: true } }, function killFailed(): void { + const p = run({ + cmd: ["python", "-c", "from time import sleep; sleep(10000)"], + }); + assert(!p.stdin); + assert(!p.stdout); + + let err; + try { + kill(p.pid, 12345); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof TypeError); + + p.close(); +}); diff --git a/cli/tests/unit/read_dir_test.ts b/cli/tests/unit/read_dir_test.ts new file mode 100644 index 000000000..79e2a1507 --- /dev/null +++ b/cli/tests/unit/read_dir_test.ts @@ -0,0 +1,82 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +function assertSameContent(files: Deno.DirEntry[]): void { + let counter = 0; + + for (const entry of files) { + if (entry.name === "subdir") { + assert(entry.isDirectory); + counter++; + } + } + + assertEquals(counter, 1); +} + +unitTest({ perms: { read: true } }, function readDirSyncSuccess(): void { + const files = [...Deno.readDirSync("cli/tests/")]; + assertSameContent(files); +}); + +unitTest({ perms: { read: false } }, function readDirSyncPerm(): void { + let caughtError = false; + try { + Deno.readDirSync("tests/"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, function readDirSyncNotDir(): void { + let caughtError = false; + let src; + + try { + src = Deno.readDirSync("cli/tests/fixture.json"); + } catch (err) { + caughtError = true; + assert(err instanceof Error); + } + assert(caughtError); + assertEquals(src, undefined); +}); + +unitTest({ perms: { read: true } }, function readDirSyncNotFound(): void { + let caughtError = false; + let src; + + try { + src = Deno.readDirSync("bad_dir_name"); + } catch (err) { + caughtError = true; + assert(err instanceof Deno.errors.NotFound); + } + assert(caughtError); + assertEquals(src, undefined); +}); + +unitTest({ perms: { read: true } }, async function readDirSuccess(): Promise< + void +> { + const files = []; + for await (const dirEntry of Deno.readDir("cli/tests/")) { + files.push(dirEntry); + } + assertSameContent(files); +}); + +unitTest({ perms: { read: false } }, async function readDirPerm(): Promise< + void +> { + let caughtError = false; + try { + await Deno.readDir("tests/")[Symbol.asyncIterator]().next(); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); diff --git a/cli/tests/unit/read_file_test.ts b/cli/tests/unit/read_file_test.ts new file mode 100644 index 000000000..0d3cdf422 --- /dev/null +++ b/cli/tests/unit/read_file_test.ts @@ -0,0 +1,65 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest({ perms: { read: true } }, function readFileSyncSuccess(): void { + const data = Deno.readFileSync("cli/tests/fixture.json"); + assert(data.byteLength > 0); + const decoder = new TextDecoder("utf-8"); + const json = decoder.decode(data); + const pkg = JSON.parse(json); + assertEquals(pkg.name, "deno"); +}); + +unitTest({ perms: { read: false } }, function readFileSyncPerm(): void { + let caughtError = false; + try { + Deno.readFileSync("cli/tests/fixture.json"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, function readFileSyncNotFound(): void { + let caughtError = false; + let data; + try { + data = Deno.readFileSync("bad_filename"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); + assert(data === undefined); +}); + +unitTest({ perms: { read: true } }, async function readFileSuccess(): Promise< + void +> { + const data = await Deno.readFile("cli/tests/fixture.json"); + assert(data.byteLength > 0); + const decoder = new TextDecoder("utf-8"); + const json = decoder.decode(data); + const pkg = JSON.parse(json); + assertEquals(pkg.name, "deno"); +}); + +unitTest({ perms: { read: false } }, async function readFilePerm(): Promise< + void +> { + let caughtError = false; + try { + await Deno.readFile("cli/tests/fixture.json"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, function readFileSyncLoop(): void { + for (let i = 0; i < 256; i++) { + Deno.readFileSync("cli/tests/fixture.json"); + } +}); diff --git a/cli/tests/unit/read_link_test.ts b/cli/tests/unit/read_link_test.ts new file mode 100644 index 000000000..6821d8dc8 --- /dev/null +++ b/cli/tests/unit/read_link_test.ts @@ -0,0 +1,73 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest( + { perms: { write: true, read: true } }, + function readLinkSyncSuccess(): void { + const testDir = Deno.makeTempDirSync(); + const target = testDir + "/target"; + const symlink = testDir + "/symln"; + Deno.mkdirSync(target); + // TODO Add test for Windows once symlink is implemented for Windows. + // See https://github.com/denoland/deno/issues/815. + if (Deno.build.os !== "windows") { + Deno.symlinkSync(target, symlink); + const targetPath = Deno.readLinkSync(symlink); + assertEquals(targetPath, target); + } + } +); + +unitTest({ perms: { read: false } }, function readLinkSyncPerm(): void { + let caughtError = false; + try { + Deno.readLinkSync("/symlink"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, function readLinkSyncNotFound(): void { + let caughtError = false; + let data; + try { + data = Deno.readLinkSync("bad_filename"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); + assertEquals(data, undefined); +}); + +unitTest( + { perms: { write: true, read: true } }, + async function readLinkSuccess(): Promise { + const testDir = Deno.makeTempDirSync(); + const target = testDir + "/target"; + const symlink = testDir + "/symln"; + Deno.mkdirSync(target); + // TODO Add test for Windows once symlink is implemented for Windows. + // See https://github.com/denoland/deno/issues/815. + if (Deno.build.os !== "windows") { + Deno.symlinkSync(target, symlink); + const targetPath = await Deno.readLink(symlink); + assertEquals(targetPath, target); + } + } +); + +unitTest({ perms: { read: false } }, async function readLinkPerm(): Promise< + void +> { + let caughtError = false; + try { + await Deno.readLink("/symlink"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); diff --git a/cli/tests/unit/read_text_file_test.ts b/cli/tests/unit/read_text_file_test.ts new file mode 100644 index 000000000..3e7493e4a --- /dev/null +++ b/cli/tests/unit/read_text_file_test.ts @@ -0,0 +1,61 @@ +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest({ perms: { read: true } }, function readTextFileSyncSuccess(): void { + const data = Deno.readTextFileSync("cli/tests/fixture.json"); + assert(data.length > 0); + const pkg = JSON.parse(data); + assertEquals(pkg.name, "deno"); +}); + +unitTest({ perms: { read: false } }, function readTextFileSyncPerm(): void { + let caughtError = false; + try { + Deno.readTextFileSync("cli/tests/fixture.json"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, function readTextFileSyncNotFound(): void { + let caughtError = false; + let data; + try { + data = Deno.readTextFileSync("bad_filename"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); + assert(data === undefined); +}); + +unitTest( + { perms: { read: true } }, + async function readTextFileSuccess(): Promise { + const data = await Deno.readTextFile("cli/tests/fixture.json"); + assert(data.length > 0); + const pkg = JSON.parse(data); + assertEquals(pkg.name, "deno"); + } +); + +unitTest({ perms: { read: false } }, async function readTextFilePerm(): Promise< + void +> { + let caughtError = false; + try { + await Deno.readTextFile("cli/tests/fixture.json"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, function readTextFileSyncLoop(): void { + for (let i = 0; i < 256; i++) { + Deno.readTextFileSync("cli/tests/fixture.json"); + } +}); diff --git a/cli/tests/unit/real_path_test.ts b/cli/tests/unit/real_path_test.ts new file mode 100644 index 000000000..c88955270 --- /dev/null +++ b/cli/tests/unit/real_path_test.ts @@ -0,0 +1,108 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +unitTest({ perms: { read: true } }, function realPathSyncSuccess(): void { + const incompletePath = "cli/tests/fixture.json"; + const realPath = Deno.realPathSync(incompletePath); + if (Deno.build.os !== "windows") { + assert(realPath.startsWith("/")); + } else { + assert(/^[A-Z]/.test(realPath)); + } + assert(realPath.endsWith(incompletePath)); +}); + +unitTest( + { + ignore: Deno.build.os === "windows", + perms: { read: true, write: true }, + }, + function realPathSyncSymlink(): void { + const testDir = Deno.makeTempDirSync(); + const target = testDir + "/target"; + const symlink = testDir + "/symln"; + Deno.mkdirSync(target); + Deno.symlinkSync(target, symlink); + const targetPath = Deno.realPathSync(symlink); + assert(targetPath.startsWith("/")); + assert(targetPath.endsWith("/target")); + } +); + +unitTest({ perms: { read: false } }, function realPathSyncPerm(): void { + let caughtError = false; + try { + Deno.realPathSync("some_file"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, function realPathSyncNotFound(): void { + let caughtError = false; + try { + Deno.realPathSync("bad_filename"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, async function realPathSuccess(): Promise< + void +> { + const incompletePath = "cli/tests/fixture.json"; + const realPath = await Deno.realPath(incompletePath); + if (Deno.build.os !== "windows") { + assert(realPath.startsWith("/")); + } else { + assert(/^[A-Z]/.test(realPath)); + } + assert(realPath.endsWith(incompletePath)); +}); + +unitTest( + { + ignore: Deno.build.os === "windows", + perms: { read: true, write: true }, + }, + async function realPathSymlink(): Promise { + const testDir = Deno.makeTempDirSync(); + const target = testDir + "/target"; + const symlink = testDir + "/symln"; + Deno.mkdirSync(target); + Deno.symlinkSync(target, symlink); + const targetPath = await Deno.realPath(symlink); + assert(targetPath.startsWith("/")); + assert(targetPath.endsWith("/target")); + } +); + +unitTest({ perms: { read: false } }, async function realPathPerm(): Promise< + void +> { + let caughtError = false; + try { + await Deno.realPath("some_file"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, async function realPathNotFound(): Promise< + void +> { + let caughtError = false; + try { + await Deno.realPath("bad_filename"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); +}); diff --git a/cli/tests/unit/remove_test.ts b/cli/tests/unit/remove_test.ts new file mode 100644 index 000000000..35e5c821e --- /dev/null +++ b/cli/tests/unit/remove_test.ts @@ -0,0 +1,506 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +// SYNC + +unitTest( + { perms: { write: true, read: true } }, + function removeSyncDirSuccess(): void { + // REMOVE EMPTY DIRECTORY + const path = Deno.makeTempDirSync() + "/subdir"; + Deno.mkdirSync(path); + const pathInfo = Deno.statSync(path); + assert(pathInfo.isDirectory); // check exist first + Deno.removeSync(path); // remove + // We then check again after remove + let err; + try { + Deno.statSync(path); + } catch (e) { + err = e; + } + // Directory is gone + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + function removeSyncFileSuccess(): void { + // REMOVE FILE + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + const fileInfo = Deno.statSync(filename); + assert(fileInfo.isFile); // check exist first + Deno.removeSync(filename); // remove + // We then check again after remove + let err; + try { + Deno.statSync(filename); + } catch (e) { + err = e; + } + // File is gone + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + function removeSyncFail(): void { + // NON-EMPTY DIRECTORY + const path = Deno.makeTempDirSync() + "/dir/subdir"; + const subPath = path + "/subsubdir"; + Deno.mkdirSync(path, { recursive: true }); + Deno.mkdirSync(subPath); + const pathInfo = Deno.statSync(path); + assert(pathInfo.isDirectory); // check exist first + const subPathInfo = Deno.statSync(subPath); + assert(subPathInfo.isDirectory); // check exist first + let err; + try { + // Should not be able to recursively remove + Deno.removeSync(path); + } catch (e) { + err = e; + } + // TODO(ry) Is Other really the error we should get here? What would Go do? + assert(err instanceof Error); + // NON-EXISTENT DIRECTORY/FILE + try { + // Non-existent + Deno.removeSync("/baddir"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + function removeSyncDanglingSymlinkSuccess(): void { + const danglingSymlinkPath = Deno.makeTempDirSync() + "/dangling_symlink"; + if (Deno.build.os === "windows") { + Deno.symlinkSync("unexistent_file", danglingSymlinkPath, { + type: "file", + }); + } else { + Deno.symlinkSync("unexistent_file", danglingSymlinkPath); + } + const pathInfo = Deno.lstatSync(danglingSymlinkPath); + assert(pathInfo.isSymlink); + Deno.removeSync(danglingSymlinkPath); + let err; + try { + Deno.lstatSync(danglingSymlinkPath); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + function removeSyncValidSymlinkSuccess(): void { + const encoder = new TextEncoder(); + const data = encoder.encode("Test"); + const tempDir = Deno.makeTempDirSync(); + const filePath = tempDir + "/test.txt"; + const validSymlinkPath = tempDir + "/valid_symlink"; + Deno.writeFileSync(filePath, data, { mode: 0o666 }); + if (Deno.build.os === "windows") { + Deno.symlinkSync(filePath, validSymlinkPath, { type: "file" }); + } else { + Deno.symlinkSync(filePath, validSymlinkPath); + } + const symlinkPathInfo = Deno.statSync(validSymlinkPath); + assert(symlinkPathInfo.isFile); + Deno.removeSync(validSymlinkPath); + let err; + try { + Deno.statSync(validSymlinkPath); + } catch (e) { + err = e; + } + Deno.removeSync(filePath); + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest({ perms: { write: false } }, function removeSyncPerm(): void { + let err; + try { + Deno.removeSync("/baddir"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest( + { perms: { write: true, read: true } }, + function removeAllSyncDirSuccess(): void { + // REMOVE EMPTY DIRECTORY + let path = Deno.makeTempDirSync() + "/dir/subdir"; + Deno.mkdirSync(path, { recursive: true }); + let pathInfo = Deno.statSync(path); + assert(pathInfo.isDirectory); // check exist first + Deno.removeSync(path, { recursive: true }); // remove + // We then check again after remove + let err; + try { + Deno.statSync(path); + } catch (e) { + err = e; + } + // Directory is gone + assert(err instanceof Deno.errors.NotFound); + + // REMOVE NON-EMPTY DIRECTORY + path = Deno.makeTempDirSync() + "/dir/subdir"; + const subPath = path + "/subsubdir"; + Deno.mkdirSync(path, { recursive: true }); + Deno.mkdirSync(subPath); + pathInfo = Deno.statSync(path); + assert(pathInfo.isDirectory); // check exist first + const subPathInfo = Deno.statSync(subPath); + assert(subPathInfo.isDirectory); // check exist first + Deno.removeSync(path, { recursive: true }); // remove + // We then check parent directory again after remove + try { + Deno.statSync(path); + } catch (e) { + err = e; + } + // Directory is gone + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + function removeAllSyncFileSuccess(): void { + // REMOVE FILE + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + const fileInfo = Deno.statSync(filename); + assert(fileInfo.isFile); // check exist first + Deno.removeSync(filename, { recursive: true }); // remove + // We then check again after remove + let err; + try { + Deno.statSync(filename); + } catch (e) { + err = e; + } + // File is gone + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest({ perms: { write: true } }, function removeAllSyncFail(): void { + // NON-EXISTENT DIRECTORY/FILE + let err; + try { + // Non-existent + Deno.removeSync("/baddir", { recursive: true }); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); +}); + +unitTest({ perms: { write: false } }, function removeAllSyncPerm(): void { + let err; + try { + Deno.removeSync("/baddir", { recursive: true }); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +// ASYNC + +unitTest( + { perms: { write: true, read: true } }, + async function removeDirSuccess(): Promise { + // REMOVE EMPTY DIRECTORY + const path = Deno.makeTempDirSync() + "/dir/subdir"; + Deno.mkdirSync(path, { recursive: true }); + const pathInfo = Deno.statSync(path); + assert(pathInfo.isDirectory); // check exist first + await Deno.remove(path); // remove + // We then check again after remove + let err; + try { + Deno.statSync(path); + } catch (e) { + err = e; + } + // Directory is gone + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + async function removeFileSuccess(): Promise { + // REMOVE FILE + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + const fileInfo = Deno.statSync(filename); + assert(fileInfo.isFile); // check exist first + await Deno.remove(filename); // remove + // We then check again after remove + let err; + try { + Deno.statSync(filename); + } catch (e) { + err = e; + } + // File is gone + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + async function removeFail(): Promise { + // NON-EMPTY DIRECTORY + const path = Deno.makeTempDirSync() + "/dir/subdir"; + const subPath = path + "/subsubdir"; + Deno.mkdirSync(path, { recursive: true }); + Deno.mkdirSync(subPath); + const pathInfo = Deno.statSync(path); + assert(pathInfo.isDirectory); // check exist first + const subPathInfo = Deno.statSync(subPath); + assert(subPathInfo.isDirectory); // check exist first + let err; + try { + // Should not be able to recursively remove + await Deno.remove(path); + } catch (e) { + err = e; + } + assert(err instanceof Error); + // NON-EXISTENT DIRECTORY/FILE + try { + // Non-existent + await Deno.remove("/baddir"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + async function removeDanglingSymlinkSuccess(): Promise { + const danglingSymlinkPath = Deno.makeTempDirSync() + "/dangling_symlink"; + if (Deno.build.os === "windows") { + Deno.symlinkSync("unexistent_file", danglingSymlinkPath, { + type: "file", + }); + } else { + Deno.symlinkSync("unexistent_file", danglingSymlinkPath); + } + const pathInfo = Deno.lstatSync(danglingSymlinkPath); + assert(pathInfo.isSymlink); + await Deno.remove(danglingSymlinkPath); + let err; + try { + Deno.lstatSync(danglingSymlinkPath); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + async function removeValidSymlinkSuccess(): Promise { + const encoder = new TextEncoder(); + const data = encoder.encode("Test"); + const tempDir = Deno.makeTempDirSync(); + const filePath = tempDir + "/test.txt"; + const validSymlinkPath = tempDir + "/valid_symlink"; + Deno.writeFileSync(filePath, data, { mode: 0o666 }); + if (Deno.build.os === "windows") { + Deno.symlinkSync(filePath, validSymlinkPath, { type: "file" }); + } else { + Deno.symlinkSync(filePath, validSymlinkPath); + } + const symlinkPathInfo = Deno.statSync(validSymlinkPath); + assert(symlinkPathInfo.isFile); + await Deno.remove(validSymlinkPath); + let err; + try { + Deno.statSync(validSymlinkPath); + } catch (e) { + err = e; + } + Deno.removeSync(filePath); + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest({ perms: { write: false } }, async function removePerm(): Promise< + void +> { + let err; + try { + await Deno.remove("/baddir"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest( + { perms: { write: true, read: true } }, + async function removeAllDirSuccess(): Promise { + // REMOVE EMPTY DIRECTORY + let path = Deno.makeTempDirSync() + "/dir/subdir"; + Deno.mkdirSync(path, { recursive: true }); + let pathInfo = Deno.statSync(path); + assert(pathInfo.isDirectory); // check exist first + await Deno.remove(path, { recursive: true }); // remove + // We then check again after remove + let err; + try { + Deno.statSync(path); + } catch (e) { + err = e; + } + // Directory is gone + assert(err instanceof Deno.errors.NotFound); + + // REMOVE NON-EMPTY DIRECTORY + path = Deno.makeTempDirSync() + "/dir/subdir"; + const subPath = path + "/subsubdir"; + Deno.mkdirSync(path, { recursive: true }); + Deno.mkdirSync(subPath); + pathInfo = Deno.statSync(path); + assert(pathInfo.isDirectory); // check exist first + const subPathInfo = Deno.statSync(subPath); + assert(subPathInfo.isDirectory); // check exist first + await Deno.remove(path, { recursive: true }); // remove + // We then check parent directory again after remove + try { + Deno.statSync(path); + } catch (e) { + err = e; + } + // Directory is gone + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest( + { perms: { write: true, read: true } }, + async function removeAllFileSuccess(): Promise { + // REMOVE FILE + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + const fileInfo = Deno.statSync(filename); + assert(fileInfo.isFile); // check exist first + await Deno.remove(filename, { recursive: true }); // remove + // We then check again after remove + let err; + try { + Deno.statSync(filename); + } catch (e) { + err = e; + } + // File is gone + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest({ perms: { write: true } }, async function removeAllFail(): Promise< + void +> { + // NON-EXISTENT DIRECTORY/FILE + let err; + try { + // Non-existent + await Deno.remove("/baddir", { recursive: true }); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); +}); + +unitTest({ perms: { write: false } }, async function removeAllPerm(): Promise< + void +> { + let err; + try { + await Deno.remove("/baddir", { recursive: true }); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +if (Deno.build.os === "windows") { + unitTest( + { perms: { run: true, write: true, read: true } }, + async function removeFileSymlink(): Promise { + const symlink = Deno.run({ + cmd: ["cmd", "/c", "mklink", "file_link", "bar"], + stdout: "null", + }); + + assert(await symlink.status()); + symlink.close(); + await Deno.remove("file_link"); + let err; + try { + await Deno.lstat("file_link"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); + } + ); + + unitTest( + { perms: { run: true, write: true, read: true } }, + async function removeDirSymlink(): Promise { + const symlink = Deno.run({ + cmd: ["cmd", "/c", "mklink", "/d", "dir_link", "bar"], + stdout: "null", + }); + + assert(await symlink.status()); + symlink.close(); + + await Deno.remove("dir_link"); + let err; + try { + await Deno.lstat("dir_link"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); + } + ); +} diff --git a/cli/tests/unit/rename_test.ts b/cli/tests/unit/rename_test.ts new file mode 100644 index 000000000..b4047f906 --- /dev/null +++ b/cli/tests/unit/rename_test.ts @@ -0,0 +1,216 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals, assertThrows } from "./test_util.ts"; + +function assertMissing(path: string): void { + let caughtErr = false; + let info; + try { + info = Deno.lstatSync(path); + } catch (e) { + caughtErr = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtErr); + assertEquals(info, undefined); +} + +function assertFile(path: string): void { + const info = Deno.lstatSync(path); + assert(info.isFile); +} + +function assertDirectory(path: string, mode?: number): void { + const info = Deno.lstatSync(path); + assert(info.isDirectory); + if (Deno.build.os !== "windows" && mode !== undefined) { + assertEquals(info.mode! & 0o777, mode & ~Deno.umask()); + } +} + +unitTest( + { perms: { read: true, write: true } }, + function renameSyncSuccess(): void { + const testDir = Deno.makeTempDirSync(); + const oldpath = testDir + "/oldpath"; + const newpath = testDir + "/newpath"; + Deno.mkdirSync(oldpath); + Deno.renameSync(oldpath, newpath); + assertDirectory(newpath); + assertMissing(oldpath); + } +); + +unitTest( + { perms: { read: false, write: true } }, + function renameSyncReadPerm(): void { + let err; + try { + const oldpath = "/oldbaddir"; + const newpath = "/newbaddir"; + Deno.renameSync(oldpath, newpath); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } +); + +unitTest( + { perms: { read: true, write: false } }, + function renameSyncWritePerm(): void { + let err; + try { + const oldpath = "/oldbaddir"; + const newpath = "/newbaddir"; + Deno.renameSync(oldpath, newpath); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function renameSuccess(): Promise { + const testDir = Deno.makeTempDirSync(); + const oldpath = testDir + "/oldpath"; + const newpath = testDir + "/newpath"; + Deno.mkdirSync(oldpath); + await Deno.rename(oldpath, newpath); + assertDirectory(newpath); + assertMissing(oldpath); + } +); + +function readFileString(filename: string): string { + const dataRead = Deno.readFileSync(filename); + const dec = new TextDecoder("utf-8"); + return dec.decode(dataRead); +} + +function writeFileString(filename: string, s: string): void { + const enc = new TextEncoder(); + const data = enc.encode(s); + Deno.writeFileSync(filename, data, { mode: 0o666 }); +} + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + function renameSyncErrorsUnix(): void { + const testDir = Deno.makeTempDirSync(); + const oldfile = testDir + "/oldfile"; + const olddir = testDir + "/olddir"; + const emptydir = testDir + "/empty"; + const fulldir = testDir + "/dir"; + const file = fulldir + "/file"; + writeFileString(oldfile, "Hello"); + Deno.mkdirSync(olddir); + Deno.mkdirSync(emptydir); + Deno.mkdirSync(fulldir); + writeFileString(file, "world"); + + assertThrows( + (): void => { + Deno.renameSync(oldfile, emptydir); + }, + Error, + "Is a directory" + ); + assertThrows( + (): void => { + Deno.renameSync(olddir, fulldir); + }, + Error, + "Directory not empty" + ); + assertThrows( + (): void => { + Deno.renameSync(olddir, file); + }, + Error, + "Not a directory" + ); + + const fileLink = testDir + "/fileLink"; + const dirLink = testDir + "/dirLink"; + const danglingLink = testDir + "/danglingLink"; + Deno.symlinkSync(file, fileLink); + Deno.symlinkSync(emptydir, dirLink); + Deno.symlinkSync(testDir + "/nonexistent", danglingLink); + + assertThrows( + (): void => { + Deno.renameSync(olddir, fileLink); + }, + Error, + "Not a directory" + ); + assertThrows( + (): void => { + Deno.renameSync(olddir, dirLink); + }, + Error, + "Not a directory" + ); + assertThrows( + (): void => { + Deno.renameSync(olddir, danglingLink); + }, + Error, + "Not a directory" + ); + + // should succeed on Unix + Deno.renameSync(olddir, emptydir); + Deno.renameSync(oldfile, dirLink); + Deno.renameSync(dirLink, danglingLink); + assertFile(danglingLink); + assertEquals("Hello", readFileString(danglingLink)); + } +); + +unitTest( + { ignore: Deno.build.os !== "windows", perms: { read: true, write: true } }, + function renameSyncErrorsWin(): void { + const testDir = Deno.makeTempDirSync(); + const oldfile = testDir + "/oldfile"; + const olddir = testDir + "/olddir"; + const emptydir = testDir + "/empty"; + const fulldir = testDir + "/dir"; + const file = fulldir + "/file"; + writeFileString(oldfile, "Hello"); + Deno.mkdirSync(olddir); + Deno.mkdirSync(emptydir); + Deno.mkdirSync(fulldir); + writeFileString(file, "world"); + + assertThrows( + (): void => { + Deno.renameSync(oldfile, emptydir); + }, + Deno.errors.PermissionDenied, + "Access is denied" + ); + assertThrows( + (): void => { + Deno.renameSync(olddir, fulldir); + }, + Deno.errors.PermissionDenied, + "Access is denied" + ); + assertThrows( + (): void => { + Deno.renameSync(olddir, emptydir); + }, + Deno.errors.PermissionDenied, + "Access is denied" + ); + + // should succeed on Windows + Deno.renameSync(olddir, file); + assertDirectory(file); + } +); diff --git a/cli/tests/unit/request_test.ts b/cli/tests/unit/request_test.ts new file mode 100644 index 000000000..8a276c5e7 --- /dev/null +++ b/cli/tests/unit/request_test.ts @@ -0,0 +1,49 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest(function fromInit(): void { + const req = new Request("https://example.com", { + body: "ahoyhoy", + method: "POST", + headers: { + "test-header": "value", + }, + }); + + // @ts-ignore + assertEquals("ahoyhoy", req._bodySource); + assertEquals(req.url, "https://example.com"); + assertEquals(req.headers.get("test-header"), "value"); +}); + +unitTest(function fromRequest(): void { + const r = new Request("https://example.com"); + // @ts-ignore + r._bodySource = "ahoyhoy"; + r.headers.set("test-header", "value"); + + const req = new Request(r); + + // @ts-ignore + assertEquals(req._bodySource, r._bodySource); + assertEquals(req.url, r.url); + assertEquals(req.headers.get("test-header"), r.headers.get("test-header")); +}); + +unitTest(async function cloneRequestBodyStream(): Promise { + // hack to get a stream + const stream = new Request("", { body: "a test body" }).body; + const r1 = new Request("https://example.com", { + body: stream, + }); + + const r2 = r1.clone(); + + const b1 = await r1.text(); + const b2 = await r2.text(); + + assertEquals(b1, b2); + + // @ts-ignore + assert(r1._bodySource !== r2._bodySource); +}); diff --git a/cli/tests/unit/resources_test.ts b/cli/tests/unit/resources_test.ts new file mode 100644 index 000000000..385905a6e --- /dev/null +++ b/cli/tests/unit/resources_test.ts @@ -0,0 +1,61 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals, assert } from "./test_util.ts"; + +unitTest(function resourcesCloseBadArgs(): void { + let err; + try { + Deno.close((null as unknown) as number); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.InvalidData); +}); + +unitTest(function resourcesStdio(): void { + const res = Deno.resources(); + + assertEquals(res[0], "stdin"); + assertEquals(res[1], "stdout"); + assertEquals(res[2], "stderr"); +}); + +unitTest({ perms: { net: true } }, async function resourcesNet(): Promise< + void +> { + const listener = Deno.listen({ port: 4501 }); + const dialerConn = await Deno.connect({ port: 4501 }); + const listenerConn = await listener.accept(); + + const res = Deno.resources(); + assertEquals( + Object.values(res).filter((r): boolean => r === "tcpListener").length, + 1 + ); + const tcpStreams = Object.values(res).filter( + (r): boolean => r === "tcpStream" + ); + assert(tcpStreams.length >= 2); + + listenerConn.close(); + dialerConn.close(); + listener.close(); +}); + +unitTest({ perms: { read: true } }, async function resourcesFile(): Promise< + void +> { + const resourcesBefore = Deno.resources(); + const f = await Deno.open("cli/tests/hello.txt"); + const resourcesAfter = Deno.resources(); + f.close(); + + // check that exactly one new resource (file) was added + assertEquals( + Object.keys(resourcesAfter).length, + Object.keys(resourcesBefore).length + 1 + ); + const newRid = +Object.keys(resourcesAfter).find((rid): boolean => { + return !resourcesBefore.hasOwnProperty(rid); + })!; + assertEquals(resourcesAfter[newRid], "fsFile"); +}); diff --git a/cli/tests/unit/signal_test.ts b/cli/tests/unit/signal_test.ts new file mode 100644 index 000000000..2f117f8d1 --- /dev/null +++ b/cli/tests/unit/signal_test.ts @@ -0,0 +1,195 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + unitTest, + assert, + assertEquals, + assertThrows, + createResolvable, +} from "./test_util.ts"; + +function defer(n: number): Promise { + return new Promise((resolve: () => void, _) => { + setTimeout(resolve, n); + }); +} + +unitTest( + { ignore: Deno.build.os !== "windows" }, + function signalsNotImplemented(): void { + assertThrows( + () => { + Deno.signal(1); + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.alarm(); // for SIGALRM + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.child(); // for SIGCHLD + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.hungup(); // for SIGHUP + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.interrupt(); // for SIGINT + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.io(); // for SIGIO + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.pipe(); // for SIGPIPE + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.quit(); // for SIGQUIT + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.terminate(); // for SIGTERM + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.userDefined1(); // for SIGUSR1 + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.userDefined2(); // for SIGURS2 + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.windowChange(); // for SIGWINCH + }, + Error, + "not implemented" + ); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { run: true, net: true } }, + async function signalStreamTest(): Promise { + const resolvable = createResolvable(); + // This prevents the program from exiting. + const t = setInterval(() => {}, 1000); + + let c = 0; + const sig = Deno.signal(Deno.Signal.SIGUSR1); + setTimeout(async () => { + await defer(20); + for (const _ of Array(3)) { + // Sends SIGUSR1 3 times. + Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); + await defer(20); + } + sig.dispose(); + resolvable.resolve(); + }); + + for await (const _ of sig) { + c += 1; + } + + assertEquals(c, 3); + + clearInterval(t); + await resolvable; + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { run: true } }, + async function signalPromiseTest(): Promise { + const resolvable = createResolvable(); + // This prevents the program from exiting. + const t = setInterval(() => {}, 1000); + + const sig = Deno.signal(Deno.Signal.SIGUSR1); + setTimeout(() => { + Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); + resolvable.resolve(); + }, 20); + await sig; + sig.dispose(); + + clearInterval(t); + await resolvable; + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { run: true } }, + function signalShorthandsTest(): void { + let s: Deno.SignalStream; + s = Deno.signals.alarm(); // for SIGALRM + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.child(); // for SIGCHLD + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.hungup(); // for SIGHUP + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.interrupt(); // for SIGINT + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.io(); // for SIGIO + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.pipe(); // for SIGPIPE + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.quit(); // for SIGQUIT + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.terminate(); // for SIGTERM + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.userDefined1(); // for SIGUSR1 + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.userDefined2(); // for SIGURS2 + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.windowChange(); // for SIGWINCH + assert(s instanceof Deno.SignalStream); + s.dispose(); + } +); diff --git a/cli/tests/unit/stat_test.ts b/cli/tests/unit/stat_test.ts new file mode 100644 index 000000000..7eaf73d58 --- /dev/null +++ b/cli/tests/unit/stat_test.ts @@ -0,0 +1,238 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest( + { perms: { read: true, write: true } }, + function statSyncSuccess(): void { + const packageInfo = Deno.statSync("README.md"); + assert(packageInfo.isFile); + assert(!packageInfo.isSymlink); + + const modulesInfo = Deno.statSync("cli/tests/symlink_to_subdir"); + assert(modulesInfo.isDirectory); + assert(!modulesInfo.isSymlink); + + const testsInfo = Deno.statSync("cli/tests"); + assert(testsInfo.isDirectory); + assert(!testsInfo.isSymlink); + + const tempFile = Deno.makeTempFileSync(); + const tempInfo = Deno.statSync(tempFile); + const now = Date.now(); + assert(tempInfo.atime !== null && now - tempInfo.atime.valueOf() < 1000); + assert(tempInfo.mtime !== null && now - tempInfo.mtime.valueOf() < 1000); + assert( + tempInfo.birthtime === null || now - tempInfo.birthtime.valueOf() < 1000 + ); + } +); + +unitTest({ perms: { read: false } }, function statSyncPerm(): void { + let caughtError = false; + try { + Deno.statSync("README.md"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, function statSyncNotFound(): void { + let caughtError = false; + let badInfo; + + try { + badInfo = Deno.statSync("bad_file_name"); + } catch (err) { + caughtError = true; + assert(err instanceof Deno.errors.NotFound); + } + + assert(caughtError); + assertEquals(badInfo, undefined); +}); + +unitTest({ perms: { read: true } }, function lstatSyncSuccess(): void { + const packageInfo = Deno.lstatSync("README.md"); + assert(packageInfo.isFile); + assert(!packageInfo.isSymlink); + + const modulesInfo = Deno.lstatSync("cli/tests/symlink_to_subdir"); + assert(!modulesInfo.isDirectory); + assert(modulesInfo.isSymlink); + + const coreInfo = Deno.lstatSync("core"); + assert(coreInfo.isDirectory); + assert(!coreInfo.isSymlink); +}); + +unitTest({ perms: { read: false } }, function lstatSyncPerm(): void { + let caughtError = false; + try { + Deno.lstatSync("README.md"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, function lstatSyncNotFound(): void { + let caughtError = false; + let badInfo; + + try { + badInfo = Deno.lstatSync("bad_file_name"); + } catch (err) { + caughtError = true; + assert(err instanceof Deno.errors.NotFound); + } + + assert(caughtError); + assertEquals(badInfo, undefined); +}); + +unitTest( + { perms: { read: true, write: true } }, + async function statSuccess(): Promise { + const packageInfo = await Deno.stat("README.md"); + assert(packageInfo.isFile); + assert(!packageInfo.isSymlink); + + const modulesInfo = await Deno.stat("cli/tests/symlink_to_subdir"); + assert(modulesInfo.isDirectory); + assert(!modulesInfo.isSymlink); + + const testsInfo = await Deno.stat("cli/tests"); + assert(testsInfo.isDirectory); + assert(!testsInfo.isSymlink); + + const tempFile = await Deno.makeTempFile(); + const tempInfo = await Deno.stat(tempFile); + const now = Date.now(); + assert(tempInfo.atime !== null && now - tempInfo.atime.valueOf() < 1000); + assert(tempInfo.mtime !== null && now - tempInfo.mtime.valueOf() < 1000); + + assert( + tempInfo.birthtime === null || now - tempInfo.birthtime.valueOf() < 1000 + ); + } +); + +unitTest({ perms: { read: false } }, async function statPerm(): Promise { + let caughtError = false; + try { + await Deno.stat("README.md"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, async function statNotFound(): Promise< + void +> { + let caughtError = false; + let badInfo; + + try { + badInfo = await Deno.stat("bad_file_name"); + } catch (err) { + caughtError = true; + assert(err instanceof Deno.errors.NotFound); + } + + assert(caughtError); + assertEquals(badInfo, undefined); +}); + +unitTest({ perms: { read: true } }, async function lstatSuccess(): Promise< + void +> { + const packageInfo = await Deno.lstat("README.md"); + assert(packageInfo.isFile); + assert(!packageInfo.isSymlink); + + const modulesInfo = await Deno.lstat("cli/tests/symlink_to_subdir"); + assert(!modulesInfo.isDirectory); + assert(modulesInfo.isSymlink); + + const coreInfo = await Deno.lstat("core"); + assert(coreInfo.isDirectory); + assert(!coreInfo.isSymlink); +}); + +unitTest({ perms: { read: false } }, async function lstatPerm(): Promise { + let caughtError = false; + try { + await Deno.lstat("README.md"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { read: true } }, async function lstatNotFound(): Promise< + void +> { + let caughtError = false; + let badInfo; + + try { + badInfo = await Deno.lstat("bad_file_name"); + } catch (err) { + caughtError = true; + assert(err instanceof Deno.errors.NotFound); + } + + assert(caughtError); + assertEquals(badInfo, undefined); +}); + +unitTest( + { ignore: Deno.build.os !== "windows", perms: { read: true, write: true } }, + function statNoUnixFields(): void { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const tempDir = Deno.makeTempDirSync(); + const filename = tempDir + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + const s = Deno.statSync(filename); + assert(s.dev === null); + assert(s.ino === null); + assert(s.mode === null); + assert(s.nlink === null); + assert(s.uid === null); + assert(s.gid === null); + assert(s.rdev === null); + assert(s.blksize === null); + assert(s.blocks === null); + } +); + +unitTest( + { ignore: Deno.build.os === "windows", perms: { read: true, write: true } }, + function statUnixFields(): void { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const tempDir = Deno.makeTempDirSync(); + const filename = tempDir + "/test.txt"; + const filename2 = tempDir + "/test2.txt"; + Deno.writeFileSync(filename, data, { mode: 0o666 }); + // Create a link + Deno.linkSync(filename, filename2); + const s = Deno.statSync(filename); + assert(s.dev !== null); + assert(s.ino !== null); + assertEquals(s.mode! & 0o666, 0o666); + assertEquals(s.nlink, 2); + assert(s.uid !== null); + assert(s.gid !== null); + assert(s.rdev !== null); + assert(s.blksize !== null); + assert(s.blocks !== null); + } +); diff --git a/cli/tests/unit/streams_piping_test.ts b/cli/tests/unit/streams_piping_test.ts new file mode 100644 index 000000000..a947b3821 --- /dev/null +++ b/cli/tests/unit/streams_piping_test.ts @@ -0,0 +1,131 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; +import { assertThrowsAsync } from "../../../std/testing/asserts.ts"; + +unitTest(function streamPipeLocks() { + const rs = new ReadableStream(); + const ws = new WritableStream(); + + assertEquals(rs.locked, false); + assertEquals(ws.locked, false); + + rs.pipeTo(ws); + + assert(rs.locked); + assert(ws.locked); +}); + +unitTest(async function streamPipeFinishUnlocks() { + const rs = new ReadableStream({ + start(controller: ReadableStreamDefaultController): void { + controller.close(); + }, + }); + const ws = new WritableStream(); + + await rs.pipeTo(ws); + assertEquals(rs.locked, false); + assertEquals(ws.locked, false); +}); + +unitTest(async function streamPipeReadableStreamLocked() { + const rs = new ReadableStream(); + const ws = new WritableStream(); + + rs.getReader(); + + await assertThrowsAsync(async () => { + await rs.pipeTo(ws); + }, TypeError); +}); + +unitTest(async function streamPipeReadableStreamLocked() { + const rs = new ReadableStream(); + const ws = new WritableStream(); + + ws.getWriter(); + + await assertThrowsAsync(async () => { + await rs.pipeTo(ws); + }, TypeError); +}); + +unitTest(async function streamPipeLotsOfChunks() { + const CHUNKS = 10; + + const rs = new ReadableStream({ + start(c: ReadableStreamDefaultController): void { + for (let i = 0; i < CHUNKS; ++i) { + c.enqueue(i); + } + c.close(); + }, + }); + + const written: Array = []; + const ws = new WritableStream( + { + write(chunk: number): void { + written.push(chunk); + }, + close(): void { + written.push("closed"); + }, + }, + new CountQueuingStrategy({ highWaterMark: CHUNKS }) + ); + + await rs.pipeTo(ws); + const targetValues = []; + for (let i = 0; i < CHUNKS; ++i) { + targetValues.push(i); + } + targetValues.push("closed"); + + assertEquals(written, targetValues, "the correct values must be written"); + + // Ensure both readable and writable are closed by the time the pipe finishes. + await Promise.all([rs.getReader().closed, ws.getWriter().closed]); +}); + +for (const preventAbort of [true, false]) { + unitTest(function undefinedRejectionFromPull() { + const rs = new ReadableStream({ + pull(): Promise { + return Promise.reject(undefined); + }, + }); + + return rs.pipeTo(new WritableStream(), { preventAbort }).then( + () => { + throw new Error("pipeTo promise should be rejected"); + }, + (value) => + assertEquals(value, undefined, "rejection value should be undefined") + ); + }); +} + +for (const preventCancel of [true, false]) { + unitTest(function undefinedRejectionWithPreventCancel() { + const rs = new ReadableStream({ + pull(controller: ReadableStreamDefaultController): void { + controller.enqueue(0); + }, + }); + + const ws = new WritableStream({ + write(): Promise { + return Promise.reject(undefined); + }, + }); + + return rs.pipeTo(ws, { preventCancel }).then( + () => { + throw new Error("pipeTo promise should be rejected"); + }, + (value) => + assertEquals(value, undefined, "rejection value should be undefined") + ); + }); +} diff --git a/cli/tests/unit/streams_transform_test.ts b/cli/tests/unit/streams_transform_test.ts new file mode 100644 index 000000000..f3ec148ae --- /dev/null +++ b/cli/tests/unit/streams_transform_test.ts @@ -0,0 +1,562 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + unitTest, + assert, + assertEquals, + assertNotEquals, + assertThrows, +} from "./test_util.ts"; + +function delay(seconds: number): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, seconds); + }); +} + +function readableStreamToArray( + readable: { getReader(): ReadableStreamDefaultReader }, + reader?: ReadableStreamDefaultReader +): Promise { + if (reader === undefined) { + reader = readable.getReader(); + } + + const chunks: R[] = []; + + return pump(); + + function pump(): Promise { + return reader!.read().then((result) => { + if (result.done) { + return chunks; + } + + chunks.push(result.value); + return pump(); + }); + } +} + +unitTest(function transformStreamConstructedWithTransformFunction() { + new TransformStream({ transform(): void {} }); +}); + +unitTest(function transformStreamConstructedNoTransform() { + new TransformStream(); + new TransformStream({}); +}); + +unitTest(function transformStreamIntstancesHaveProperProperties() { + const ts = new TransformStream({ transform(): void {} }); + const proto = Object.getPrototypeOf(ts); + + const writableStream = Object.getOwnPropertyDescriptor(proto, "writable"); + assert(writableStream !== undefined, "it has a writable property"); + assert(!writableStream.enumerable, "writable should be non-enumerable"); + assertEquals( + typeof writableStream.get, + "function", + "writable should have a getter" + ); + assertEquals( + writableStream.set, + undefined, + "writable should not have a setter" + ); + assert(writableStream.configurable, "writable should be configurable"); + assert( + ts.writable instanceof WritableStream, + "writable is an instance of WritableStream" + ); + assert( + WritableStream.prototype.getWriter.call(ts.writable), + "writable should pass WritableStream brand check" + ); + + const readableStream = Object.getOwnPropertyDescriptor(proto, "readable"); + assert(readableStream !== undefined, "it has a readable property"); + assert(!readableStream.enumerable, "readable should be non-enumerable"); + assertEquals( + typeof readableStream.get, + "function", + "readable should have a getter" + ); + assertEquals( + readableStream.set, + undefined, + "readable should not have a setter" + ); + assert(readableStream.configurable, "readable should be configurable"); + assert( + ts.readable instanceof ReadableStream, + "readable is an instance of ReadableStream" + ); + assertNotEquals( + ReadableStream.prototype.getReader.call(ts.readable), + undefined, + "readable should pass ReadableStream brand check" + ); +}); + +unitTest(function transformStreamWritableStartsAsWritable() { + const ts = new TransformStream({ transform(): void {} }); + + const writer = ts.writable.getWriter(); + assertEquals(writer.desiredSize, 1, "writer.desiredSize should be 1"); +}); + +unitTest(async function transformStreamReadableCanReadOutOfWritable() { + const ts = new TransformStream(); + + const writer = ts.writable.getWriter(); + writer.write("a"); + assertEquals( + writer.desiredSize, + 0, + "writer.desiredSize should be 0 after write()" + ); + + const result = await ts.readable.getReader().read(); + assertEquals( + result.value, + "a", + "result from reading the readable is the same as was written to writable" + ); + assert(!result.done, "stream should not be done"); + + await delay(0); + assert(writer.desiredSize === 1, "desiredSize should be 1 again"); +}); + +unitTest(async function transformStreamCanReadWhatIsWritten() { + let c: TransformStreamDefaultController; + const ts = new TransformStream({ + start(controller: TransformStreamDefaultController): void { + c = controller; + }, + transform(chunk: string): void { + c.enqueue(chunk.toUpperCase()); + }, + }); + + const writer = ts.writable.getWriter(); + writer.write("a"); + + const result = await ts.readable.getReader().read(); + assertEquals( + result.value, + "A", + "result from reading the readable is the transformation of what was written to writable" + ); + assert(!result.done, "stream should not be done"); +}); + +unitTest(async function transformStreamCanReadBothChunks() { + let c: TransformStreamDefaultController; + const ts = new TransformStream({ + start(controller: TransformStreamDefaultController): void { + c = controller; + }, + transform(chunk: string): void { + c.enqueue(chunk.toUpperCase()); + c.enqueue(chunk.toUpperCase()); + }, + }); + + const writer = ts.writable.getWriter(); + writer.write("a"); + + const reader = ts.readable.getReader(); + + const result1 = await reader.read(); + assertEquals( + result1.value, + "A", + "the first chunk read is the transformation of the single chunk written" + ); + assert(!result1.done, "stream should not be done"); + + const result2 = await reader.read(); + assertEquals( + result2.value, + "A", + "the second chunk read is also the transformation of the single chunk written" + ); + assert(!result2.done, "stream should not be done"); +}); + +unitTest(async function transformStreamCanReadWhatIsWritten() { + let c: TransformStreamDefaultController; + const ts = new TransformStream({ + start(controller: TransformStreamDefaultController): void { + c = controller; + }, + transform(chunk: string): Promise { + return delay(0).then(() => c.enqueue(chunk.toUpperCase())); + }, + }); + + const writer = ts.writable.getWriter(); + writer.write("a"); + + const result = await ts.readable.getReader().read(); + assertEquals( + result.value, + "A", + "result from reading the readable is the transformation of what was written to writable" + ); + assert(!result.done, "stream should not be done"); +}); + +unitTest(async function transformStreamAsyncReadMultipleChunks() { + let doSecondEnqueue: () => void; + let returnFromTransform: () => void; + const ts = new TransformStream({ + transform( + chunk: string, + controller: TransformStreamDefaultController + ): Promise { + delay(0).then(() => controller.enqueue(chunk.toUpperCase())); + doSecondEnqueue = (): void => controller.enqueue(chunk.toUpperCase()); + return new Promise((resolve) => { + returnFromTransform = resolve; + }); + }, + }); + + const reader = ts.readable.getReader(); + + const writer = ts.writable.getWriter(); + writer.write("a"); + + const result1 = await reader.read(); + assertEquals( + result1.value, + "A", + "the first chunk read is the transformation of the single chunk written" + ); + assert(!result1.done, "stream should not be done"); + doSecondEnqueue!(); + + const result2 = await reader.read(); + assertEquals( + result2.value, + "A", + "the second chunk read is also the transformation of the single chunk written" + ); + assert(!result2.done, "stream should not be done"); + returnFromTransform!(); +}); + +unitTest(function transformStreamClosingWriteClosesRead() { + const ts = new TransformStream({ transform(): void {} }); + + const writer = ts.writable.getWriter(); + writer.close(); + + return Promise.all([writer.closed, ts.readable.getReader().closed]).then( + undefined + ); +}); + +unitTest(async function transformStreamCloseWaitAwaitsTransforms() { + let transformResolve: () => void; + const transformPromise = new Promise((resolve) => { + transformResolve = resolve; + }); + const ts = new TransformStream( + { + transform(): Promise { + return transformPromise; + }, + }, + undefined, + { highWaterMark: 1 } + ); + + const writer = ts.writable.getWriter(); + writer.write("a"); + writer.close(); + + let rsClosed = false; + ts.readable.getReader().closed.then(() => { + rsClosed = true; + }); + + await delay(0); + assertEquals(rsClosed, false, "readable is not closed after a tick"); + transformResolve!(); + + await writer.closed; + // TODO: Is this expectation correct? + assertEquals(rsClosed, true, "readable is closed at that point"); +}); + +unitTest(async function transformStreamCloseWriteAfterSyncEnqueues() { + let c: TransformStreamDefaultController; + const ts = new TransformStream({ + start(controller: TransformStreamDefaultController): void { + c = controller; + }, + transform(): Promise { + c.enqueue("x"); + c.enqueue("y"); + return delay(0); + }, + }); + + const writer = ts.writable.getWriter(); + writer.write("a"); + writer.close(); + + const readableChunks = readableStreamToArray(ts.readable); + + await writer.closed; + const chunks = await readableChunks; + assertEquals( + chunks, + ["x", "y"], + "both enqueued chunks can be read from the readable" + ); +}); + +unitTest(async function transformStreamWritableCloseAsyncAfterAsyncEnqueues() { + let c: TransformStreamDefaultController; + const ts = new TransformStream({ + start(controller: TransformStreamDefaultController): void { + c = controller; + }, + transform(): Promise { + return delay(0) + .then(() => c.enqueue("x")) + .then(() => c.enqueue("y")) + .then(() => delay(0)); + }, + }); + + const writer = ts.writable.getWriter(); + writer.write("a"); + writer.close(); + + const readableChunks = readableStreamToArray(ts.readable); + + await writer.closed; + const chunks = await readableChunks; + assertEquals( + chunks, + ["x", "y"], + "both enqueued chunks can be read from the readable" + ); +}); + +unitTest(async function transformStreamTransformerMethodsCalledAsMethods() { + let c: TransformStreamDefaultController; + const transformer = { + suffix: "-suffix", + + start(controller: TransformStreamDefaultController): void { + c = controller; + c.enqueue("start" + this.suffix); + }, + + transform(chunk: string): void { + c.enqueue(chunk + this.suffix); + }, + + flush(): void { + c.enqueue("flushed" + this.suffix); + }, + }; + const ts = new TransformStream(transformer); + + const writer = ts.writable.getWriter(); + writer.write("a"); + writer.close(); + + const readableChunks = readableStreamToArray(ts.readable); + + await writer.closed; + const chunks = await readableChunks; + assertEquals( + chunks, + ["start-suffix", "a-suffix", "flushed-suffix"], + "all enqueued chunks have suffixes" + ); +}); + +unitTest(async function transformStreamMethodsShouldNotBeAppliedOrCalled() { + function functionWithOverloads(): void {} + functionWithOverloads.apply = (): void => { + throw new Error("apply() should not be called"); + }; + functionWithOverloads.call = (): void => { + throw new Error("call() should not be called"); + }; + const ts = new TransformStream({ + start: functionWithOverloads, + transform: functionWithOverloads, + flush: functionWithOverloads, + }); + const writer = ts.writable.getWriter(); + writer.write("a"); + writer.close(); + + await readableStreamToArray(ts.readable); +}); + +unitTest(async function transformStreamCallTransformSync() { + let transformCalled = false; + const ts = new TransformStream( + { + transform(): void { + transformCalled = true; + }, + }, + undefined, + { highWaterMark: Infinity } + ); + // transform() is only called synchronously when there is no backpressure and + // all microtasks have run. + await delay(0); + const writePromise = ts.writable.getWriter().write(undefined); + assert(transformCalled, "transform() should have been called"); + await writePromise; +}); + +unitTest(function transformStreamCloseWriteCloesesReadWithNoChunks() { + const ts = new TransformStream({}, undefined, { highWaterMark: 0 }); + + const writer = ts.writable.getWriter(); + writer.close(); + + return Promise.all([writer.closed, ts.readable.getReader().closed]).then( + undefined + ); +}); + +unitTest(function transformStreamEnqueueThrowsAfterTerminate() { + new TransformStream({ + start(controller: TransformStreamDefaultController): void { + controller.terminate(); + assertThrows(() => { + controller.enqueue(undefined); + }, TypeError); + }, + }); +}); + +unitTest(function transformStreamEnqueueThrowsAfterReadableCancel() { + let controller: TransformStreamDefaultController; + const ts = new TransformStream({ + start(c: TransformStreamDefaultController): void { + controller = c; + }, + }); + const cancelPromise = ts.readable.cancel(); + assertThrows( + () => controller.enqueue(undefined), + TypeError, + undefined, + "enqueue should throw" + ); + return cancelPromise; +}); + +unitTest(function transformStreamSecondTerminateNoOp() { + new TransformStream({ + start(controller: TransformStreamDefaultController): void { + controller.terminate(); + controller.terminate(); + }, + }); +}); + +unitTest(async function transformStreamTerminateAfterReadableCancelIsNoop() { + let controller: TransformStreamDefaultController; + const ts = new TransformStream({ + start(c: TransformStreamDefaultController): void { + controller = c; + }, + }); + const cancelReason = { name: "cancelReason" }; + const cancelPromise = ts.readable.cancel(cancelReason); + controller!.terminate(); + await cancelPromise; + try { + await ts.writable.getWriter().closed; + } catch (e) { + assert(e === cancelReason); + return; + } + throw new Error("closed should have rejected"); +}); + +unitTest(async function transformStreamStartCalledOnce() { + let calls = 0; + new TransformStream({ + start(): void { + ++calls; + }, + }); + await delay(0); + assertEquals(calls, 1, "start() should have been called exactly once"); +}); + +unitTest(function transformStreamReadableTypeThrows() { + assertThrows( + // eslint-disable-next-line + () => new TransformStream({ readableType: "bytes" as any }), + RangeError, + undefined, + "constructor should throw" + ); +}); + +unitTest(function transformStreamWirtableTypeThrows() { + assertThrows( + // eslint-disable-next-line + () => new TransformStream({ writableType: "bytes" as any }), + RangeError, + undefined, + "constructor should throw" + ); +}); + +unitTest(function transformStreamSubclassable() { + class Subclass extends TransformStream { + extraFunction(): boolean { + return true; + } + } + assert( + Object.getPrototypeOf(Subclass.prototype) === TransformStream.prototype, + "Subclass.prototype's prototype should be TransformStream.prototype" + ); + assert( + Object.getPrototypeOf(Subclass) === TransformStream, + "Subclass's prototype should be TransformStream" + ); + const sub = new Subclass(); + assert( + sub instanceof TransformStream, + "Subclass object should be an instance of TransformStream" + ); + assert( + sub instanceof Subclass, + "Subclass object should be an instance of Subclass" + ); + const readableGetter = Object.getOwnPropertyDescriptor( + TransformStream.prototype, + "readable" + )!.get; + assert( + readableGetter!.call(sub) === sub.readable, + "Subclass object should pass brand check" + ); + assert( + sub.extraFunction(), + "extraFunction() should be present on Subclass object" + ); +}); diff --git a/cli/tests/unit/streams_writable_test.ts b/cli/tests/unit/streams_writable_test.ts new file mode 100644 index 000000000..54c1624af --- /dev/null +++ b/cli/tests/unit/streams_writable_test.ts @@ -0,0 +1,253 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals, assertThrows } from "./test_util.ts"; + +unitTest(function writableStreamDesiredSizeOnReleasedWriter() { + const ws = new WritableStream(); + const writer = ws.getWriter(); + writer.releaseLock(); + assertThrows(() => { + writer.desiredSize; + }, TypeError); +}); + +unitTest(function writableStreamDesiredSizeInitialValue() { + const ws = new WritableStream(); + const writer = ws.getWriter(); + assertEquals(writer.desiredSize, 1); +}); + +unitTest(async function writableStreamDesiredSizeClosed() { + const ws = new WritableStream(); + const writer = ws.getWriter(); + await writer.close(); + assertEquals(writer.desiredSize, 0); +}); + +unitTest(function writableStreamStartThrowsDesiredSizeNull() { + const ws = new WritableStream({ + start(c): void { + c.error(); + }, + }); + + const writer = ws.getWriter(); + assertEquals(writer.desiredSize, null, "desiredSize should be null"); +}); + +unitTest(function getWriterOnClosingStream() { + const ws = new WritableStream({}); + + const writer = ws.getWriter(); + writer.close(); + writer.releaseLock(); + + ws.getWriter(); +}); + +unitTest(async function getWriterOnClosedStream() { + const ws = new WritableStream({}); + + const writer = ws.getWriter(); + await writer.close(); + writer.releaseLock(); + + ws.getWriter(); +}); + +unitTest(function getWriterOnAbortedStream() { + const ws = new WritableStream({}); + + const writer = ws.getWriter(); + writer.abort(); + writer.releaseLock(); + + ws.getWriter(); +}); + +unitTest(function getWriterOnErroredStream() { + const ws = new WritableStream({ + start(c): void { + c.error(); + }, + }); + + const writer = ws.getWriter(); + return writer.closed.then( + (v) => { + throw new Error(`writer.closed fulfilled unexpectedly with: ${v}`); + }, + () => { + writer.releaseLock(); + ws.getWriter(); + } + ); +}); + +unitTest(function closedAndReadyOnReleasedWriter() { + const ws = new WritableStream({}); + + const writer = ws.getWriter(); + writer.releaseLock(); + + return writer.closed.then( + (v) => { + throw new Error("writer.closed fulfilled unexpectedly with: " + v); + }, + (closedRejection) => { + assertEquals( + closedRejection.name, + "TypeError", + "closed promise should reject with a TypeError" + ); + return writer.ready.then( + (v) => { + throw new Error("writer.ready fulfilled unexpectedly with: " + v); + }, + (readyRejection) => + assertEquals( + readyRejection, + closedRejection, + "ready promise should reject with the same error" + ) + ); + } + ); +}); + +unitTest(function sinkMethodsCalledAsMethods() { + let thisObject: Sink | null = null; + // Calls to Sink methods after the first are implicitly ignored. Only the + // first value that is passed to the resolver is used. + class Sink { + start(): void { + assertEquals(this, thisObject, "start should be called as a method"); + } + + write(): void { + assertEquals(this, thisObject, "write should be called as a method"); + } + + close(): void { + assertEquals(this, thisObject, "close should be called as a method"); + } + + abort(): void { + assertEquals(this, thisObject, "abort should be called as a method"); + } + } + + const theSink = new Sink(); + thisObject = theSink; + const ws = new WritableStream(theSink); + + const writer = ws.getWriter(); + + writer.write("a"); + const closePromise = writer.close(); + + const ws2 = new WritableStream(theSink); + const writer2 = ws2.getWriter(); + const abortPromise = writer2.abort(); + + return Promise.all([closePromise, abortPromise]).then(undefined); +}); + +unitTest(function sizeShouldNotBeCalledAsMethod() { + const strategy = { + size(): number { + if (this !== undefined) { + throw new Error("size called as a method"); + } + return 1; + }, + }; + + const ws = new WritableStream({}, strategy); + const writer = ws.getWriter(); + return writer.write("a"); +}); + +unitTest(function redundantReleaseLockIsNoOp() { + const ws = new WritableStream(); + const writer1 = ws.getWriter(); + assertEquals( + undefined, + writer1.releaseLock(), + "releaseLock() should return undefined" + ); + const writer2 = ws.getWriter(); + assertEquals( + undefined, + writer1.releaseLock(), + "no-op releaseLock() should return undefined" + ); + // Calling releaseLock() on writer1 should not interfere with writer2. If it did, then the ready promise would be + // rejected. + return writer2.ready; +}); + +unitTest(function readyPromiseShouldFireBeforeReleaseLock() { + const events: string[] = []; + const ws = new WritableStream(); + const writer = ws.getWriter(); + return writer.ready.then(() => { + // Force the ready promise back to a pending state. + const writerPromise = writer.write("dummy"); + const readyPromise = writer.ready.catch(() => events.push("ready")); + const closedPromise = writer.closed.catch(() => events.push("closed")); + writer.releaseLock(); + return Promise.all([readyPromise, closedPromise]).then(() => { + assertEquals( + events, + ["ready", "closed"], + "ready promise should fire before closed promise" + ); + // Stop the writer promise hanging around after the test has finished. + return Promise.all([writerPromise, ws.abort()]).then(undefined); + }); + }); +}); + +unitTest(function subclassingWritableStream() { + class Subclass extends WritableStream { + extraFunction(): boolean { + return true; + } + } + assert( + Object.getPrototypeOf(Subclass.prototype) === WritableStream.prototype, + "Subclass.prototype's prototype should be WritableStream.prototype" + ); + assert( + Object.getPrototypeOf(Subclass) === WritableStream, + "Subclass's prototype should be WritableStream" + ); + const sub = new Subclass(); + assert( + sub instanceof WritableStream, + "Subclass object should be an instance of WritableStream" + ); + assert( + sub instanceof Subclass, + "Subclass object should be an instance of Subclass" + ); + const lockedGetter = Object.getOwnPropertyDescriptor( + WritableStream.prototype, + "locked" + )!.get!; + assert( + lockedGetter.call(sub) === sub.locked, + "Subclass object should pass brand check" + ); + assert( + sub.extraFunction(), + "extraFunction() should be present on Subclass object" + ); +}); + +unitTest(function lockedGetterShouldReturnTrue() { + const ws = new WritableStream(); + assert(!ws.locked, "stream should not be locked"); + ws.getWriter(); + assert(ws.locked, "stream should be locked"); +}); diff --git a/cli/tests/unit/symlink_test.ts b/cli/tests/unit/symlink_test.ts new file mode 100644 index 000000000..505a49bab --- /dev/null +++ b/cli/tests/unit/symlink_test.ts @@ -0,0 +1,44 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest( + { perms: { read: true, write: true } }, + function symlinkSyncSuccess(): void { + const testDir = Deno.makeTempDirSync(); + const oldname = testDir + "/oldname"; + const newname = testDir + "/newname"; + Deno.mkdirSync(oldname); + // Just for now, until we implement symlink for Windows. + Deno.symlinkSync(oldname, newname); + const newNameInfoLStat = Deno.lstatSync(newname); + const newNameInfoStat = Deno.statSync(newname); + assert(newNameInfoLStat.isSymlink); + assert(newNameInfoStat.isDirectory); + } +); + +unitTest(function symlinkSyncPerm(): void { + let err; + try { + Deno.symlinkSync("oldbaddir", "newbaddir"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest( + { perms: { read: true, write: true } }, + async function symlinkSuccess(): Promise { + const testDir = Deno.makeTempDirSync(); + const oldname = testDir + "/oldname"; + const newname = testDir + "/newname"; + Deno.mkdirSync(oldname); + await Deno.symlink(oldname, newname); + const newNameInfoLStat = Deno.lstatSync(newname); + const newNameInfoStat = Deno.statSync(newname); + assert(newNameInfoLStat.isSymlink, "NOT SYMLINK"); + assert(newNameInfoStat.isDirectory, "NOT DIRECTORY"); + } +); diff --git a/cli/tests/unit/test_util.ts b/cli/tests/unit/test_util.ts new file mode 100644 index 000000000..1c5b6ff21 --- /dev/null +++ b/cli/tests/unit/test_util.ts @@ -0,0 +1,364 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +import { assert, assertEquals } from "../../../std/testing/asserts.ts"; +export { + assert, + assertThrows, + assertEquals, + assertMatch, + assertNotEquals, + assertStrictEq, + assertStrContains, + unreachable, + fail, +} from "../../../std/testing/asserts.ts"; +export { readLines } from "../../../std/io/bufio.ts"; +export { parse as parseArgs } from "../../../std/flags/mod.ts"; + +export interface Permissions { + read: boolean; + write: boolean; + net: boolean; + env: boolean; + run: boolean; + plugin: boolean; + hrtime: boolean; +} + +export function fmtPerms(perms: Permissions): string { + const p = Object.keys(perms) + .filter((e): boolean => perms[e as keyof Permissions] === true) + .map((key) => `--allow-${key}`); + + if (p.length) { + return p.join(" "); + } + + return ""; +} + +const isGranted = async (name: Deno.PermissionName): Promise => + (await Deno.permissions.query({ name })).state === "granted"; + +export async function getProcessPermissions(): Promise { + return { + run: await isGranted("run"), + read: await isGranted("read"), + write: await isGranted("write"), + net: await isGranted("net"), + env: await isGranted("env"), + plugin: await isGranted("plugin"), + hrtime: await isGranted("hrtime"), + }; +} + +export function permissionsMatch( + processPerms: Permissions, + requiredPerms: Permissions +): boolean { + for (const permName in processPerms) { + if ( + processPerms[permName as keyof Permissions] !== + requiredPerms[permName as keyof Permissions] + ) { + return false; + } + } + + return true; +} + +export const permissionCombinations: Map = new Map(); + +function permToString(perms: Permissions): string { + const r = perms.read ? 1 : 0; + const w = perms.write ? 1 : 0; + const n = perms.net ? 1 : 0; + const e = perms.env ? 1 : 0; + const u = perms.run ? 1 : 0; + const p = perms.plugin ? 1 : 0; + const h = perms.hrtime ? 1 : 0; + return `permR${r}W${w}N${n}E${e}U${u}P${p}H${h}`; +} + +function registerPermCombination(perms: Permissions): void { + const key = permToString(perms); + if (!permissionCombinations.has(key)) { + permissionCombinations.set(key, perms); + } +} + +export async function registerUnitTests(): Promise { + const processPerms = await getProcessPermissions(); + + for (const unitTestDefinition of REGISTERED_UNIT_TESTS) { + if (!permissionsMatch(processPerms, unitTestDefinition.perms)) { + continue; + } + + Deno.test(unitTestDefinition); + } +} + +function normalizeTestPermissions(perms: UnitTestPermissions): Permissions { + return { + read: !!perms.read, + write: !!perms.write, + net: !!perms.net, + run: !!perms.run, + env: !!perms.env, + plugin: !!perms.plugin, + hrtime: !!perms.hrtime, + }; +} + +interface UnitTestPermissions { + read?: boolean; + write?: boolean; + net?: boolean; + env?: boolean; + run?: boolean; + plugin?: boolean; + hrtime?: boolean; +} + +interface UnitTestOptions { + ignore?: boolean; + perms?: UnitTestPermissions; +} + +interface UnitTestDefinition extends Deno.TestDefinition { + ignore: boolean; + perms: Permissions; +} + +type TestFunction = () => void | Promise; + +export const REGISTERED_UNIT_TESTS: UnitTestDefinition[] = []; + +export function unitTest(fn: TestFunction): void; +export function unitTest(options: UnitTestOptions, fn: TestFunction): void; +export function unitTest( + optionsOrFn: UnitTestOptions | TestFunction, + maybeFn?: TestFunction +): void { + assert(optionsOrFn, "At least one argument is required"); + + let options: UnitTestOptions; + let name: string; + let fn: TestFunction; + + if (typeof optionsOrFn === "function") { + options = {}; + fn = optionsOrFn; + name = fn.name; + assert(name, "Missing test function name"); + } else { + options = optionsOrFn; + assert(maybeFn, "Missing test function definition"); + assert( + typeof maybeFn === "function", + "Second argument should be test function definition" + ); + fn = maybeFn; + name = fn.name; + assert(name, "Missing test function name"); + } + + const normalizedPerms = normalizeTestPermissions(options.perms || {}); + registerPermCombination(normalizedPerms); + + const unitTestDefinition: UnitTestDefinition = { + name, + fn, + ignore: !!options.ignore, + perms: normalizedPerms, + }; + + REGISTERED_UNIT_TESTS.push(unitTestDefinition); +} + +export interface ResolvableMethods { + resolve: (value?: T | PromiseLike) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reject: (reason?: any) => void; +} + +export type Resolvable = Promise & ResolvableMethods; + +export function createResolvable(): Resolvable { + let methods: ResolvableMethods; + const promise = new Promise((resolve, reject): void => { + methods = { resolve, reject }; + }); + // TypeScript doesn't know that the Promise callback occurs synchronously + // therefore use of not null assertion (`!`) + return Object.assign(promise, methods!) as Resolvable; +} + +const encoder = new TextEncoder(); + +// Replace functions with null, errors with their stack strings, and JSONify. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function serializeTestMessage(message: any): string { + return JSON.stringify({ + start: message.start && { + ...message.start, + tests: message.start.tests.map((test: Deno.TestDefinition) => ({ + ...test, + fn: null, + })), + }, + testStart: message.testStart && { ...message.testStart, fn: null }, + testEnd: message.testEnd && { + ...message.testEnd, + error: String(message.testEnd.error?.stack), + }, + end: message.end && { + ...message.end, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + results: message.end.results.map((result: any) => ({ + ...result, + error: result.error?.stack, + })), + }, + }); +} + +export async function reportToConn( + conn: Deno.Conn, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + message: any +): Promise { + const line = serializeTestMessage(message); + const encodedMsg = encoder.encode(line + (message.end == null ? "\n" : "")); + await Deno.writeAll(conn, encodedMsg); + if (message.end != null) { + conn.closeWrite(); + } +} + +unitTest(function permissionsMatches(): void { + assert( + permissionsMatch( + { + read: true, + write: false, + net: false, + env: false, + run: false, + plugin: false, + hrtime: false, + }, + normalizeTestPermissions({ read: true }) + ) + ); + + assert( + permissionsMatch( + { + read: false, + write: false, + net: false, + env: false, + run: false, + plugin: false, + hrtime: false, + }, + normalizeTestPermissions({}) + ) + ); + + assertEquals( + permissionsMatch( + { + read: false, + write: true, + net: true, + env: true, + run: true, + plugin: true, + hrtime: true, + }, + normalizeTestPermissions({ read: true }) + ), + false + ); + + assertEquals( + permissionsMatch( + { + read: true, + write: false, + net: true, + env: false, + run: false, + plugin: false, + hrtime: false, + }, + normalizeTestPermissions({ read: true }) + ), + false + ); + + assert( + permissionsMatch( + { + read: true, + write: true, + net: true, + env: true, + run: true, + plugin: true, + hrtime: true, + }, + { + read: true, + write: true, + net: true, + env: true, + run: true, + plugin: true, + hrtime: true, + } + ) + ); +}); + +/* + * Ensure all unit test files (e.g. xxx_test.ts) are present as imports in + * cli/tests/unit/unit_tests.ts as it is easy to miss this out + */ +unitTest( + { perms: { read: true } }, + function assertAllUnitTestFilesImported(): void { + const directoryTestFiles = [...Deno.readDirSync("./cli/tests/unit/")] + .map((k) => k.name) + .filter( + (file) => + file!.endsWith(".ts") && + !file!.endsWith("unit_tests.ts") && + !file!.endsWith("test_util.ts") && + !file!.endsWith("unit_test_runner.ts") + ); + const unitTestsFile: Uint8Array = Deno.readFileSync( + "./cli/tests/unit/unit_tests.ts" + ); + const importLines = new TextDecoder("utf-8") + .decode(unitTestsFile) + .split("\n") + .filter((line) => line.startsWith("import")); + const importedTestFiles = importLines.map( + (relativeFilePath) => relativeFilePath.match(/\/([^\/]+)";/)![1] + ); + + directoryTestFiles.forEach((dirFile) => { + if (!importedTestFiles.includes(dirFile!)) { + throw new Error( + "cil/tests/unit/unit_tests.ts is missing import of test file: cli/js/" + + dirFile + ); + } + }); + } +); diff --git a/cli/tests/unit/testing_test.ts b/cli/tests/unit/testing_test.ts new file mode 100644 index 000000000..854e7161c --- /dev/null +++ b/cli/tests/unit/testing_test.ts @@ -0,0 +1,27 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { assertThrows, unitTest } from "./test_util.ts"; + +unitTest(function testFnOverloading(): void { + // just verifying that you can use this test definition syntax + Deno.test("test fn overloading", (): void => {}); +}); + +unitTest(function nameOfTestCaseCantBeEmpty(): void { + assertThrows( + () => { + Deno.test("", () => {}); + }, + TypeError, + "The test name can't be empty" + ); + assertThrows( + () => { + Deno.test({ + name: "", + fn: () => {}, + }); + }, + TypeError, + "The test name can't be empty" + ); +}); diff --git a/cli/tests/unit/text_encoding_test.ts b/cli/tests/unit/text_encoding_test.ts new file mode 100644 index 000000000..87b601f36 --- /dev/null +++ b/cli/tests/unit/text_encoding_test.ts @@ -0,0 +1,206 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest(function btoaSuccess(): void { + const text = "hello world"; + const encoded = btoa(text); + assertEquals(encoded, "aGVsbG8gd29ybGQ="); +}); + +unitTest(function atobSuccess(): void { + const encoded = "aGVsbG8gd29ybGQ="; + const decoded = atob(encoded); + assertEquals(decoded, "hello world"); +}); + +unitTest(function atobWithAsciiWhitespace(): void { + const encodedList = [ + " aGVsbG8gd29ybGQ=", + " aGVsbG8gd29ybGQ=", + "aGVsbG8gd29ybGQ= ", + "aGVsbG8gd29ybGQ=\n", + "aGVsbG\t8gd29ybGQ=", + `aGVsbG\t8g + d29ybGQ=`, + ]; + + for (const encoded of encodedList) { + const decoded = atob(encoded); + assertEquals(decoded, "hello world"); + } +}); + +unitTest(function atobThrows(): void { + let threw = false; + try { + atob("aGVsbG8gd29ybGQ=="); + } catch (e) { + threw = true; + } + assert(threw); +}); + +unitTest(function atobThrows2(): void { + let threw = false; + try { + atob("aGVsbG8gd29ybGQ==="); + } catch (e) { + threw = true; + } + assert(threw); +}); + +unitTest(function btoaFailed(): void { + const text = "你好"; + let err; + try { + btoa(text); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof TypeError); +}); + +unitTest(function textDecoder2(): void { + // prettier-ignore + const fixture = new Uint8Array([ + 0xf0, 0x9d, 0x93, 0xbd, + 0xf0, 0x9d, 0x93, 0xae, + 0xf0, 0x9d, 0x94, 0x81, + 0xf0, 0x9d, 0x93, 0xbd + ]); + const decoder = new TextDecoder(); + assertEquals(decoder.decode(fixture), "𝓽𝓮𝔁𝓽"); +}); + +unitTest(function textDecoderIgnoreBOM(): void { + // prettier-ignore + const fixture = new Uint8Array([ + 0xef, 0xbb, 0xbf, + 0xf0, 0x9d, 0x93, 0xbd, + 0xf0, 0x9d, 0x93, 0xae, + 0xf0, 0x9d, 0x94, 0x81, + 0xf0, 0x9d, 0x93, 0xbd + ]); + const decoder = new TextDecoder("utf-8", { ignoreBOM: true }); + assertEquals(decoder.decode(fixture), "𝓽𝓮𝔁𝓽"); +}); + +unitTest(function textDecoderNotBOM(): void { + // prettier-ignore + const fixture = new Uint8Array([ + 0xef, 0xbb, 0x89, + 0xf0, 0x9d, 0x93, 0xbd, + 0xf0, 0x9d, 0x93, 0xae, + 0xf0, 0x9d, 0x94, 0x81, + 0xf0, 0x9d, 0x93, 0xbd + ]); + const decoder = new TextDecoder("utf-8", { ignoreBOM: true }); + assertEquals(decoder.decode(fixture), "ﻉ𝓽𝓮𝔁𝓽"); +}); + +unitTest(function textDecoderASCII(): void { + const fixture = new Uint8Array([0x89, 0x95, 0x9f, 0xbf]); + const decoder = new TextDecoder("ascii"); + assertEquals(decoder.decode(fixture), "‰•Ÿ¿"); +}); + +unitTest(function textDecoderErrorEncoding(): void { + let didThrow = false; + try { + new TextDecoder("foo"); + } catch (e) { + didThrow = true; + assertEquals(e.message, "The encoding label provided ('foo') is invalid."); + } + assert(didThrow); +}); + +unitTest(function textEncoder(): void { + const fixture = "𝓽𝓮𝔁𝓽"; + const encoder = new TextEncoder(); + // prettier-ignore + assertEquals(Array.from(encoder.encode(fixture)), [ + 0xf0, 0x9d, 0x93, 0xbd, + 0xf0, 0x9d, 0x93, 0xae, + 0xf0, 0x9d, 0x94, 0x81, + 0xf0, 0x9d, 0x93, 0xbd + ]); +}); + +unitTest(function textEncodeInto(): void { + const fixture = "text"; + const encoder = new TextEncoder(); + const bytes = new Uint8Array(5); + const result = encoder.encodeInto(fixture, bytes); + assertEquals(result.read, 4); + assertEquals(result.written, 4); + // prettier-ignore + assertEquals(Array.from(bytes), [ + 0x74, 0x65, 0x78, 0x74, 0x00, + ]); +}); + +unitTest(function textEncodeInto2(): void { + const fixture = "𝓽𝓮𝔁𝓽"; + const encoder = new TextEncoder(); + const bytes = new Uint8Array(17); + const result = encoder.encodeInto(fixture, bytes); + assertEquals(result.read, 8); + assertEquals(result.written, 16); + // prettier-ignore + assertEquals(Array.from(bytes), [ + 0xf0, 0x9d, 0x93, 0xbd, + 0xf0, 0x9d, 0x93, 0xae, + 0xf0, 0x9d, 0x94, 0x81, + 0xf0, 0x9d, 0x93, 0xbd, 0x00, + ]); +}); + +unitTest(function textEncodeInto3(): void { + const fixture = "𝓽𝓮𝔁𝓽"; + const encoder = new TextEncoder(); + const bytes = new Uint8Array(5); + const result = encoder.encodeInto(fixture, bytes); + assertEquals(result.read, 2); + assertEquals(result.written, 4); + // prettier-ignore + assertEquals(Array.from(bytes), [ + 0xf0, 0x9d, 0x93, 0xbd, 0x00, + ]); +}); + +unitTest(function textDecoderSharedUint8Array(): void { + const ab = new SharedArrayBuffer(6); + const dataView = new DataView(ab); + const charCodeA = "A".charCodeAt(0); + for (let i = 0; i < ab.byteLength; i++) { + dataView.setUint8(i, charCodeA + i); + } + const ui8 = new Uint8Array(ab); + const decoder = new TextDecoder(); + const actual = decoder.decode(ui8); + assertEquals(actual, "ABCDEF"); +}); + +unitTest(function textDecoderSharedInt32Array(): void { + const ab = new SharedArrayBuffer(8); + const dataView = new DataView(ab); + const charCodeA = "A".charCodeAt(0); + for (let i = 0; i < ab.byteLength; i++) { + dataView.setUint8(i, charCodeA + i); + } + const i32 = new Int32Array(ab); + const decoder = new TextDecoder(); + const actual = decoder.decode(i32); + assertEquals(actual, "ABCDEFGH"); +}); + +unitTest(function toStringShouldBeWebCompatibility(): void { + const encoder = new TextEncoder(); + assertEquals(encoder.toString(), "[object TextEncoder]"); + + const decoder = new TextDecoder(); + assertEquals(decoder.toString(), "[object TextDecoder]"); +}); diff --git a/cli/tests/unit/timers_test.ts b/cli/tests/unit/timers_test.ts new file mode 100644 index 000000000..7adff0095 --- /dev/null +++ b/cli/tests/unit/timers_test.ts @@ -0,0 +1,368 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + unitTest, + createResolvable, + assert, + assertEquals, + assertNotEquals, +} from "./test_util.ts"; + +function deferred(): { + promise: Promise<{}>; + resolve: (value?: {} | PromiseLike<{}>) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reject: (reason?: any) => void; +} { + let resolve: (value?: {} | PromiseLike<{}>) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let reject: ((reason?: any) => void) | undefined = undefined; + const promise = new Promise<{}>((res, rej): void => { + resolve = res; + reject = rej; + }); + return { + promise, + resolve: resolve!, + reject: reject!, + }; +} + +function waitForMs(ms: number): Promise { + return new Promise((resolve: () => void): number => setTimeout(resolve, ms)); +} + +unitTest(async function timeoutSuccess(): Promise { + const { promise, resolve } = deferred(); + let count = 0; + setTimeout((): void => { + count++; + resolve(); + }, 500); + await promise; + // count should increment + assertEquals(count, 1); +}); + +unitTest(async function timeoutArgs(): Promise { + const { promise, resolve } = deferred(); + const arg = 1; + setTimeout( + (a, b, c): void => { + assertEquals(a, arg); + assertEquals(b, arg.toString()); + assertEquals(c, [arg]); + resolve(); + }, + 10, + arg, + arg.toString(), + [arg] + ); + await promise; +}); + +unitTest(async function timeoutCancelSuccess(): Promise { + let count = 0; + const id = setTimeout((): void => { + count++; + }, 1); + // Cancelled, count should not increment + clearTimeout(id); + await waitForMs(600); + assertEquals(count, 0); +}); + +unitTest(async function timeoutCancelMultiple(): Promise { + function uncalled(): never { + throw new Error("This function should not be called."); + } + + // Set timers and cancel them in the same order. + const t1 = setTimeout(uncalled, 10); + const t2 = setTimeout(uncalled, 10); + const t3 = setTimeout(uncalled, 10); + clearTimeout(t1); + clearTimeout(t2); + clearTimeout(t3); + + // Set timers and cancel them in reverse order. + const t4 = setTimeout(uncalled, 20); + const t5 = setTimeout(uncalled, 20); + const t6 = setTimeout(uncalled, 20); + clearTimeout(t6); + clearTimeout(t5); + clearTimeout(t4); + + // Sleep until we're certain that the cancelled timers aren't gonna fire. + await waitForMs(50); +}); + +unitTest(async function timeoutCancelInvalidSilentFail(): Promise { + // Expect no panic + const { promise, resolve } = deferred(); + let count = 0; + const id = setTimeout((): void => { + count++; + // Should have no effect + clearTimeout(id); + resolve(); + }, 500); + await promise; + assertEquals(count, 1); + + // Should silently fail (no panic) + clearTimeout(2147483647); +}); + +unitTest(async function intervalSuccess(): Promise { + const { promise, resolve } = deferred(); + let count = 0; + const id = setInterval((): void => { + count++; + clearInterval(id); + resolve(); + }, 100); + await promise; + // Clear interval + clearInterval(id); + // count should increment twice + assertEquals(count, 1); + // Similar false async leaking alarm. + // Force next round of polling. + await waitForMs(0); +}); + +unitTest(async function intervalCancelSuccess(): Promise { + let count = 0; + const id = setInterval((): void => { + count++; + }, 1); + clearInterval(id); + await waitForMs(500); + assertEquals(count, 0); +}); + +unitTest(async function intervalOrdering(): Promise { + const timers: number[] = []; + let timeouts = 0; + function onTimeout(): void { + ++timeouts; + for (let i = 1; i < timers.length; i++) { + clearTimeout(timers[i]); + } + } + for (let i = 0; i < 10; i++) { + timers[i] = setTimeout(onTimeout, 1); + } + await waitForMs(500); + assertEquals(timeouts, 1); +}); + +unitTest(function intervalCancelInvalidSilentFail(): void { + // Should silently fail (no panic) + clearInterval(2147483647); +}); + +unitTest(async function fireCallbackImmediatelyWhenDelayOverMaxValue(): Promise< + void +> { + let count = 0; + setTimeout((): void => { + count++; + }, 2 ** 31); + await waitForMs(1); + assertEquals(count, 1); +}); + +unitTest(async function timeoutCallbackThis(): Promise { + const { promise, resolve } = deferred(); + const obj = { + foo(): void { + assertEquals(this, window); + resolve(); + }, + }; + setTimeout(obj.foo, 1); + await promise; +}); + +unitTest(async function timeoutBindThis(): Promise { + const thisCheckPassed = [null, undefined, window, globalThis]; + + const thisCheckFailed = [ + 0, + "", + true, + false, + {}, + [], + "foo", + (): void => {}, + Object.prototype, + ]; + + for (const thisArg of thisCheckPassed) { + const resolvable = createResolvable(); + let hasThrown = 0; + try { + setTimeout.call(thisArg, () => resolvable.resolve(), 1); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + await resolvable; + assertEquals(hasThrown, 1); + } + + for (const thisArg of thisCheckFailed) { + let hasThrown = 0; + try { + setTimeout.call(thisArg, () => {}, 1); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); + } +}); + +unitTest(function clearTimeoutShouldConvertToNumber(): void { + let called = false; + const obj = { + valueOf(): number { + called = true; + return 1; + }, + }; + clearTimeout((obj as unknown) as number); + assert(called); +}); + +unitTest(function setTimeoutShouldThrowWithBigint(): void { + let hasThrown = 0; + try { + setTimeout((): void => {}, (1n as unknown) as number); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); +}); + +unitTest(function clearTimeoutShouldThrowWithBigint(): void { + let hasThrown = 0; + try { + clearTimeout((1n as unknown) as number); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); +}); + +unitTest(function testFunctionName(): void { + assertEquals(clearTimeout.name, "clearTimeout"); + assertEquals(clearInterval.name, "clearInterval"); +}); + +unitTest(function testFunctionParamsLength(): void { + assertEquals(setTimeout.length, 1); + assertEquals(setInterval.length, 1); + assertEquals(clearTimeout.length, 0); + assertEquals(clearInterval.length, 0); +}); + +unitTest(function clearTimeoutAndClearIntervalNotBeEquals(): void { + assertNotEquals(clearTimeout, clearInterval); +}); + +unitTest(async function timerMaxCpuBug(): Promise { + // There was a bug where clearing a timeout would cause Deno to use 100% CPU. + clearTimeout(setTimeout(() => {}, 1000)); + // We can check this by counting how many ops have triggered in the interim. + // Certainly less than 10 ops should have been dispatched in next 100 ms. + const { opsDispatched } = Deno.metrics(); + await waitForMs(100); + const opsDispatched_ = Deno.metrics().opsDispatched; + assert(opsDispatched_ - opsDispatched < 10); +}); + +unitTest(async function timerBasicMicrotaskOrdering(): Promise { + let s = ""; + let count = 0; + const { promise, resolve } = deferred(); + setTimeout(() => { + Promise.resolve().then(() => { + count++; + s += "de"; + if (count === 2) { + resolve(); + } + }); + }); + setTimeout(() => { + count++; + s += "no"; + if (count === 2) { + resolve(); + } + }); + await promise; + assertEquals(s, "deno"); +}); + +unitTest(async function timerNestedMicrotaskOrdering(): Promise { + let s = ""; + const { promise, resolve } = deferred(); + s += "0"; + setTimeout(() => { + s += "4"; + setTimeout(() => (s += "A")); + Promise.resolve() + .then(() => { + setTimeout(() => { + s += "B"; + resolve(); + }); + }) + .then(() => { + s += "5"; + }); + }); + setTimeout(() => (s += "6")); + Promise.resolve().then(() => (s += "2")); + Promise.resolve().then(() => + setTimeout(() => { + s += "7"; + Promise.resolve() + .then(() => (s += "8")) + .then(() => { + s += "9"; + }); + }) + ); + Promise.resolve().then(() => Promise.resolve().then(() => (s += "3"))); + s += "1"; + await promise; + assertEquals(s, "0123456789AB"); +}); + +unitTest(function testQueueMicrotask() { + assertEquals(typeof queueMicrotask, "function"); +}); diff --git a/cli/tests/unit/tls_test.ts b/cli/tests/unit/tls_test.ts new file mode 100644 index 000000000..3d071441d --- /dev/null +++ b/cli/tests/unit/tls_test.ts @@ -0,0 +1,262 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { + assert, + assertEquals, + createResolvable, + unitTest, +} from "./test_util.ts"; +import { BufWriter, BufReader } from "../../../std/io/bufio.ts"; +import { TextProtoReader } from "../../../std/textproto/mod.ts"; + +const encoder = new TextEncoder(); +const decoder = new TextDecoder(); + +unitTest(async function connectTLSNoPerm(): Promise { + let err; + try { + await Deno.connectTls({ hostname: "github.com", port: 443 }); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest(async function connectTLSCertFileNoReadPerm(): Promise { + let err; + try { + await Deno.connectTls({ + hostname: "github.com", + port: 443, + certFile: "cli/tests/tls/RootCA.crt", + }); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest( + { perms: { read: true, net: true } }, + function listenTLSNonExistentCertKeyFiles(): void { + let err; + const options = { + hostname: "localhost", + port: 3500, + certFile: "cli/tests/tls/localhost.crt", + keyFile: "cli/tests/tls/localhost.key", + }; + + try { + Deno.listenTls({ + ...options, + certFile: "./non/existent/file", + }); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); + + try { + Deno.listenTls({ + ...options, + keyFile: "./non/existent/file", + }); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.NotFound); + } +); + +unitTest({ perms: { net: true } }, function listenTLSNoReadPerm(): void { + let err; + try { + Deno.listenTls({ + hostname: "localhost", + port: 3500, + certFile: "cli/tests/tls/localhost.crt", + keyFile: "cli/tests/tls/localhost.key", + }); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest( + { + perms: { read: true, write: true, net: true }, + }, + function listenTLSEmptyKeyFile(): void { + let err; + const options = { + hostname: "localhost", + port: 3500, + certFile: "cli/tests/tls/localhost.crt", + keyFile: "cli/tests/tls/localhost.key", + }; + + const testDir = Deno.makeTempDirSync(); + const keyFilename = testDir + "/key.pem"; + Deno.writeFileSync(keyFilename, new Uint8Array([]), { + mode: 0o666, + }); + + try { + Deno.listenTls({ + ...options, + keyFile: keyFilename, + }); + } catch (e) { + err = e; + } + assert(err instanceof Error); + } +); + +unitTest( + { perms: { read: true, write: true, net: true } }, + function listenTLSEmptyCertFile(): void { + let err; + const options = { + hostname: "localhost", + port: 3500, + certFile: "cli/tests/tls/localhost.crt", + keyFile: "cli/tests/tls/localhost.key", + }; + + const testDir = Deno.makeTempDirSync(); + const certFilename = testDir + "/cert.crt"; + Deno.writeFileSync(certFilename, new Uint8Array([]), { + mode: 0o666, + }); + + try { + Deno.listenTls({ + ...options, + certFile: certFilename, + }); + } catch (e) { + err = e; + } + assert(err instanceof Error); + } +); + +unitTest( + { perms: { read: true, net: true } }, + async function dialAndListenTLS(): Promise { + const resolvable = createResolvable(); + const hostname = "localhost"; + const port = 3500; + + const listener = Deno.listenTls({ + hostname, + port, + certFile: "cli/tests/tls/localhost.crt", + keyFile: "cli/tests/tls/localhost.key", + }); + + const response = encoder.encode( + "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n" + ); + + listener.accept().then( + async (conn): Promise => { + assert(conn.remoteAddr != null); + assert(conn.localAddr != null); + await conn.write(response); + // TODO(bartlomieju): this might be a bug + setTimeout(() => { + conn.close(); + resolvable.resolve(); + }, 0); + } + ); + + const conn = await Deno.connectTls({ + hostname, + port, + certFile: "cli/tests/tls/RootCA.pem", + }); + assert(conn.rid > 0); + const w = new BufWriter(conn); + const r = new BufReader(conn); + const body = `GET / HTTP/1.1\r\nHost: ${hostname}:${port}\r\n\r\n`; + const writeResult = await w.write(encoder.encode(body)); + assertEquals(body.length, writeResult); + await w.flush(); + const tpr = new TextProtoReader(r); + const statusLine = await tpr.readLine(); + assert(statusLine !== null, `line must be read: ${String(statusLine)}`); + const m = statusLine.match(/^(.+?) (.+?) (.+?)$/); + assert(m !== null, "must be matched"); + const [_, proto, status, ok] = m; + assertEquals(proto, "HTTP/1.1"); + assertEquals(status, "200"); + assertEquals(ok, "OK"); + const headers = await tpr.readMIMEHeader(); + assert(headers !== null); + const contentLength = parseInt(headers.get("content-length")!); + const bodyBuf = new Uint8Array(contentLength); + await r.readFull(bodyBuf); + assertEquals(decoder.decode(bodyBuf), "Hello World\n"); + conn.close(); + listener.close(); + await resolvable; + } +); + +unitTest( + { perms: { read: true, net: true } }, + async function startTls(): Promise { + const hostname = "smtp.gmail.com"; + const port = 587; + const encoder = new TextEncoder(); + + let conn = await Deno.connect({ + hostname, + port, + }); + + let writer = new BufWriter(conn); + let reader = new TextProtoReader(new BufReader(conn)); + + let line: string | null = (await reader.readLine()) as string; + assert(line.startsWith("220")); + + await writer.write(encoder.encode(`EHLO ${hostname}\r\n`)); + await writer.flush(); + + while ((line = (await reader.readLine()) as string)) { + assert(line.startsWith("250")); + if (line.startsWith("250 ")) break; + } + + await writer.write(encoder.encode("STARTTLS\r\n")); + await writer.flush(); + + line = await reader.readLine(); + + // Received the message that the server is ready to establish TLS + assertEquals(line, "220 2.0.0 Ready to start TLS"); + + conn = await Deno.startTls(conn, { hostname }); + writer = new BufWriter(conn); + reader = new TextProtoReader(new BufReader(conn)); + + // After that use TLS communication again + await writer.write(encoder.encode(`EHLO ${hostname}\r\n`)); + await writer.flush(); + + while ((line = (await reader.readLine()) as string)) { + assert(line.startsWith("250")); + if (line.startsWith("250 ")) break; + } + + conn.close(); + } +); diff --git a/cli/tests/unit/truncate_test.ts b/cli/tests/unit/truncate_test.ts new file mode 100644 index 000000000..3fd85c990 --- /dev/null +++ b/cli/tests/unit/truncate_test.ts @@ -0,0 +1,80 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals, assert } from "./test_util.ts"; + +function readDataSync(name: string): string { + const data = Deno.readFileSync(name); + const decoder = new TextDecoder("utf-8"); + const text = decoder.decode(data); + return text; +} + +async function readData(name: string): Promise { + const data = await Deno.readFile(name); + const decoder = new TextDecoder("utf-8"); + const text = decoder.decode(data); + return text; +} + +unitTest( + { perms: { read: true, write: true } }, + function truncateSyncSuccess(): void { + const enc = new TextEncoder(); + const d = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test_truncateSync.txt"; + Deno.writeFileSync(filename, d); + Deno.truncateSync(filename, 20); + let data = readDataSync(filename); + assertEquals(data.length, 20); + Deno.truncateSync(filename, 5); + data = readDataSync(filename); + assertEquals(data.length, 5); + Deno.truncateSync(filename, -5); + data = readDataSync(filename); + assertEquals(data.length, 0); + Deno.removeSync(filename); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function truncateSuccess(): Promise { + const enc = new TextEncoder(); + const d = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test_truncate.txt"; + await Deno.writeFile(filename, d); + await Deno.truncate(filename, 20); + let data = await readData(filename); + assertEquals(data.length, 20); + await Deno.truncate(filename, 5); + data = await readData(filename); + assertEquals(data.length, 5); + await Deno.truncate(filename, -5); + data = await readData(filename); + assertEquals(data.length, 0); + await Deno.remove(filename); + } +); + +unitTest({ perms: { write: false } }, function truncateSyncPerm(): void { + let err; + try { + Deno.mkdirSync("/test_truncateSyncPermission.txt"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +unitTest({ perms: { write: false } }, async function truncatePerm(): Promise< + void +> { + let err; + try { + await Deno.mkdir("/test_truncatePermission.txt"); + } catch (e) { + err = e; + } + assert(err instanceof Deno.errors.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); diff --git a/cli/tests/unit/tty_test.ts b/cli/tests/unit/tty_test.ts new file mode 100644 index 000000000..116b0dfe9 --- /dev/null +++ b/cli/tests/unit/tty_test.ts @@ -0,0 +1,23 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +// Note tests for Deno.setRaw is in integration tests. + +unitTest({ perms: { read: true } }, function isatty(): void { + // CI not under TTY, so cannot test stdin/stdout/stderr. + const f = Deno.openSync("cli/tests/hello.txt"); + assert(!Deno.isatty(f.rid)); + f.close(); +}); + +unitTest(function isattyError(): void { + let caught = false; + try { + // Absurdly large rid. + Deno.isatty(0x7fffffff); + } catch (e) { + caught = true; + assert(e instanceof Deno.errors.BadResource); + } + assert(caught); +}); diff --git a/cli/tests/unit/umask_test.ts b/cli/tests/unit/umask_test.ts new file mode 100644 index 000000000..bfac65d52 --- /dev/null +++ b/cli/tests/unit/umask_test.ts @@ -0,0 +1,15 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals } from "./test_util.ts"; + +unitTest( + { + ignore: Deno.build.os === "windows", + }, + function umaskSuccess(): void { + const prevMask = Deno.umask(0o020); + const newMask = Deno.umask(prevMask); + const finalMask = Deno.umask(); + assertEquals(newMask, 0o020); + assertEquals(finalMask, prevMask); + } +); diff --git a/cli/tests/unit/unit_test_runner.ts b/cli/tests/unit/unit_test_runner.ts new file mode 100755 index 000000000..715dda500 --- /dev/null +++ b/cli/tests/unit/unit_test_runner.ts @@ -0,0 +1,309 @@ +#!/usr/bin/env -S deno run --reload --allow-run +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import "./unit_tests.ts"; +import { + readLines, + permissionCombinations, + Permissions, + registerUnitTests, + fmtPerms, + parseArgs, + reportToConn, +} from "./test_util.ts"; + +// @ts-ignore +const internalObj = Deno[Deno.internal]; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const reportToConsole = internalObj.reportToConsole as (message: any) => void; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const runTests = internalObj.runTests as (options: any) => Promise; + +interface PermissionSetTestResult { + perms: Permissions; + passed: boolean; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + endMessage: any; + permsStr: string; +} + +const PERMISSIONS: Deno.PermissionName[] = [ + "read", + "write", + "net", + "env", + "run", + "plugin", + "hrtime", +]; + +/** + * Take a list of permissions and revoke missing permissions. + */ +async function dropWorkerPermissions( + requiredPermissions: Deno.PermissionName[] +): Promise { + const permsToDrop = PERMISSIONS.filter((p): boolean => { + return !requiredPermissions.includes(p); + }); + + for (const perm of permsToDrop) { + await Deno.permissions.revoke({ name: perm }); + } +} + +async function workerRunnerMain( + addrStr: string, + permsStr: string, + filter?: string +): Promise { + const [hostname, port] = addrStr.split(":"); + const addr = { hostname, port: Number(port) }; + + let perms: Deno.PermissionName[] = []; + if (permsStr.length > 0) { + perms = permsStr.split(",") as Deno.PermissionName[]; + } + // Setup reporter + const conn = await Deno.connect(addr); + // Drop current process permissions to requested set + await dropWorkerPermissions(perms); + // Register unit tests that match process permissions + await registerUnitTests(); + // Execute tests + await runTests({ + exitOnFail: false, + filter, + reportToConsole: false, + onMessage: reportToConn.bind(null, conn), + }); +} + +function spawnWorkerRunner( + verbose: boolean, + addr: string, + perms: Permissions, + filter?: string +): Deno.Process { + // run subsequent tests using same deno executable + const permStr = Object.keys(perms) + .filter((permName): boolean => { + return perms[permName as Deno.PermissionName] === true; + }) + .join(","); + + const cmd = [ + Deno.execPath(), + "run", + "--unstable", // TODO(ry) be able to test stable vs unstable + "-A", + "cli/tests/unit/unit_test_runner.ts", + "--worker", + `--addr=${addr}`, + `--perms=${permStr}`, + ]; + + if (filter) { + cmd.push("--"); + cmd.push(filter); + } + + const ioMode = verbose ? "inherit" : "null"; + + const p = Deno.run({ + cmd, + stdin: ioMode, + stdout: ioMode, + stderr: ioMode, + }); + + return p; +} + +async function runTestsForPermissionSet( + listener: Deno.Listener, + addrStr: string, + verbose: boolean, + perms: Permissions, + filter?: string +): Promise { + const permsFmt = fmtPerms(perms); + console.log(`Running tests for: ${permsFmt}`); + const workerProcess = spawnWorkerRunner(verbose, addrStr, perms, filter); + // Wait for worker subprocess to go online + const conn = await listener.accept(); + + let expectedPassedTests; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let endMessage: any; + + try { + for await (const line of readLines(conn)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const message = JSON.parse(line) as any; + reportToConsole(message); + if (message.start != null) { + expectedPassedTests = message.start.tests.length; + } else if (message.end != null) { + endMessage = message.end; + } + } + } finally { + // Close socket to worker. + conn.close(); + } + + if (expectedPassedTests == null) { + throw new Error("Worker runner didn't report start"); + } + + if (endMessage == null) { + throw new Error("Worker runner didn't report end"); + } + + const workerStatus = await workerProcess.status(); + if (!workerStatus.success) { + throw new Error( + `Worker runner exited with status code: ${workerStatus.code}` + ); + } + + workerProcess.close(); + + const passed = expectedPassedTests === endMessage.passed + endMessage.ignored; + + return { + perms, + passed, + permsStr: permsFmt, + endMessage, + }; +} + +async function masterRunnerMain( + verbose: boolean, + filter?: string +): Promise { + console.log( + "Discovered permission combinations for tests:", + permissionCombinations.size + ); + + for (const perms of permissionCombinations.values()) { + console.log("\t" + fmtPerms(perms)); + } + + const testResults = new Set(); + const addr = { hostname: "127.0.0.1", port: 4510 }; + const addrStr = `${addr.hostname}:${addr.port}`; + const listener = Deno.listen(addr); + + for (const perms of permissionCombinations.values()) { + const result = await runTestsForPermissionSet( + listener, + addrStr, + verbose, + perms, + filter + ); + testResults.add(result); + } + + // if any run tests returned non-zero status then whole test + // run should fail + let testsPassed = true; + + for (const testResult of testResults) { + const { permsStr, endMessage } = testResult; + console.log(`Summary for ${permsStr}`); + reportToConsole({ end: endMessage }); + testsPassed = testsPassed && testResult.passed; + } + + if (!testsPassed) { + console.error("Unit tests failed"); + Deno.exit(1); + } + + console.log("Unit tests passed"); +} + +const HELP = `Unit test runner + +Run tests matching current process permissions: + + deno --allow-write unit_test_runner.ts + + deno --allow-net --allow-hrtime unit_test_runner.ts + + deno --allow-write unit_test_runner.ts -- testWriteFile + +Run "master" process that creates "worker" processes +for each discovered permission combination: + + deno -A unit_test_runner.ts --master + +Run worker process for given permissions: + + deno -A unit_test_runner.ts --worker --perms=net,read,write --addr=127.0.0.1:4500 + + +OPTIONS: + --master + Run in master mode, spawning worker processes for + each discovered permission combination + + --worker + Run in worker mode, requires "perms" and "addr" flags, + should be run with "-A" flag; after setup worker will + drop permissions to required set specified in "perms" + + --perms=... + Set of permissions this process should run tests with, + + --addr= + Address of TCP socket for reporting + +ARGS: + -- ... + Run only tests with names matching filter, must + be used after "--" +`; + +function assertOrHelp(expr: unknown): asserts expr { + if (!expr) { + console.log(HELP); + Deno.exit(1); + } +} + +async function main(): Promise { + const args = parseArgs(Deno.args, { + boolean: ["master", "worker", "verbose"], + "--": true, + }); + + if (args.help) { + console.log(HELP); + return; + } + + const filter = args["--"][0]; + + // Master mode + if (args.master) { + return masterRunnerMain(args.verbose, filter); + } + + // Worker mode + if (args.worker) { + assertOrHelp(typeof args.addr === "string"); + assertOrHelp(typeof args.perms === "string"); + return workerRunnerMain(args.addr, args.perms, filter); + } + + // Running tests matching current process permissions + await registerUnitTests(); + await runTests({ filter }); +} + +main(); diff --git a/cli/tests/unit/unit_tests.ts b/cli/tests/unit/unit_tests.ts new file mode 100644 index 000000000..7327bcc05 --- /dev/null +++ b/cli/tests/unit/unit_tests.ts @@ -0,0 +1,72 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +// This test is executed as part of unit test suite. +// +// Test runner automatically spawns subprocesses for each required permissions combination. + +import "./abort_controller_test.ts"; +import "./blob_test.ts"; +import "./body_test.ts"; +import "./buffer_test.ts"; +import "./build_test.ts"; +import "./chmod_test.ts"; +import "./chown_test.ts"; +import "./console_test.ts"; +import "./copy_file_test.ts"; +import "./custom_event_test.ts"; +import "./dir_test.ts"; +import "./dispatch_minimal_test.ts"; +import "./dispatch_json_test.ts"; +import "./dom_exception_test.ts"; +import "./error_stack_test.ts"; +import "./event_test.ts"; +import "./event_target_test.ts"; +import "./fetch_test.ts"; +import "./file_test.ts"; +import "./files_test.ts"; +import "./form_data_test.ts"; +import "./format_error_test.ts"; +import "./fs_events_test.ts"; +import "./get_random_values_test.ts"; +import "./globals_test.ts"; +import "./headers_test.ts"; +import "./internals_test.ts"; +import "./io_test.ts"; +import "./link_test.ts"; +import "./make_temp_test.ts"; +import "./metrics_test.ts"; +import "./dom_iterable_test.ts"; +import "./mkdir_test.ts"; +import "./net_test.ts"; +import "./os_test.ts"; +import "./permissions_test.ts"; +import "./process_test.ts"; +import "./real_path_test.ts"; +import "./read_dir_test.ts"; +import "./read_text_file_test.ts"; +import "./read_file_test.ts"; +import "./read_link_test.ts"; +import "./remove_test.ts"; +import "./rename_test.ts"; +import "./request_test.ts"; +import "./resources_test.ts"; +import "./signal_test.ts"; +import "./stat_test.ts"; +import "./streams_piping_test.ts"; +import "./streams_transform_test.ts"; +import "./streams_writable_test.ts"; +import "./symlink_test.ts"; +import "./text_encoding_test.ts"; +import "./testing_test.ts"; +import "./timers_test.ts"; +import "./tls_test.ts"; +import "./truncate_test.ts"; +import "./tty_test.ts"; +import "./umask_test.ts"; +import "./url_test.ts"; +import "./url_search_params_test.ts"; +import "./utime_test.ts"; +import "./write_file_test.ts"; +import "./write_text_file_test.ts"; +import "./performance_test.ts"; +import "./version_test.ts"; diff --git a/cli/tests/unit/url_search_params_test.ts b/cli/tests/unit/url_search_params_test.ts new file mode 100644 index 000000000..7b7dbab76 --- /dev/null +++ b/cli/tests/unit/url_search_params_test.ts @@ -0,0 +1,275 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest(function urlSearchParamsInitString(): void { + const init = "c=4&a=2&b=3&%C3%A1=1"; + const searchParams = new URLSearchParams(init); + assert( + init === searchParams.toString(), + "The init query string does not match" + ); +}); + +unitTest(function urlSearchParamsInitIterable(): void { + const init = [ + ["a", "54"], + ["b", "true"], + ]; + const searchParams = new URLSearchParams(init); + assertEquals(searchParams.toString(), "a=54&b=true"); +}); + +unitTest(function urlSearchParamsInitRecord(): void { + const init = { a: "54", b: "true" }; + const searchParams = new URLSearchParams(init); + assertEquals(searchParams.toString(), "a=54&b=true"); +}); + +unitTest(function urlSearchParamsInit(): void { + const params1 = new URLSearchParams("a=b"); + assertEquals(params1.toString(), "a=b"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const params2 = new URLSearchParams(params1 as any); + assertEquals(params2.toString(), "a=b"); +}); + +unitTest(function urlSearchParamsAppendSuccess(): void { + const searchParams = new URLSearchParams(); + searchParams.append("a", "true"); + assertEquals(searchParams.toString(), "a=true"); +}); + +unitTest(function urlSearchParamsDeleteSuccess(): void { + const init = "a=54&b=true"; + const searchParams = new URLSearchParams(init); + searchParams.delete("b"); + assertEquals(searchParams.toString(), "a=54"); +}); + +unitTest(function urlSearchParamsGetAllSuccess(): void { + const init = "a=54&b=true&a=true"; + const searchParams = new URLSearchParams(init); + assertEquals(searchParams.getAll("a"), ["54", "true"]); + assertEquals(searchParams.getAll("b"), ["true"]); + assertEquals(searchParams.getAll("c"), []); +}); + +unitTest(function urlSearchParamsGetSuccess(): void { + const init = "a=54&b=true&a=true"; + const searchParams = new URLSearchParams(init); + assertEquals(searchParams.get("a"), "54"); + assertEquals(searchParams.get("b"), "true"); + assertEquals(searchParams.get("c"), null); +}); + +unitTest(function urlSearchParamsHasSuccess(): void { + const init = "a=54&b=true&a=true"; + const searchParams = new URLSearchParams(init); + assert(searchParams.has("a")); + assert(searchParams.has("b")); + assert(!searchParams.has("c")); +}); + +unitTest(function urlSearchParamsSetReplaceFirstAndRemoveOthers(): void { + const init = "a=54&b=true&a=true"; + const searchParams = new URLSearchParams(init); + searchParams.set("a", "false"); + assertEquals(searchParams.toString(), "a=false&b=true"); +}); + +unitTest(function urlSearchParamsSetAppendNew(): void { + const init = "a=54&b=true&a=true"; + const searchParams = new URLSearchParams(init); + searchParams.set("c", "foo"); + assertEquals(searchParams.toString(), "a=54&b=true&a=true&c=foo"); +}); + +unitTest(function urlSearchParamsSortSuccess(): void { + const init = "c=4&a=2&b=3&a=1"; + const searchParams = new URLSearchParams(init); + searchParams.sort(); + assertEquals(searchParams.toString(), "a=2&a=1&b=3&c=4"); +}); + +unitTest(function urlSearchParamsForEachSuccess(): void { + const init = [ + ["a", "54"], + ["b", "true"], + ]; + const searchParams = new URLSearchParams(init); + let callNum = 0; + searchParams.forEach((value, key, parent): void => { + assertEquals(searchParams, parent); + assertEquals(value, init[callNum][1]); + assertEquals(key, init[callNum][0]); + callNum++; + }); + assertEquals(callNum, init.length); +}); + +unitTest(function urlSearchParamsMissingName(): void { + const init = "=4"; + const searchParams = new URLSearchParams(init); + assertEquals(searchParams.get(""), "4"); + assertEquals(searchParams.toString(), "=4"); +}); + +unitTest(function urlSearchParamsMissingValue(): void { + const init = "4="; + const searchParams = new URLSearchParams(init); + assertEquals(searchParams.get("4"), ""); + assertEquals(searchParams.toString(), "4="); +}); + +unitTest(function urlSearchParamsMissingEqualSign(): void { + const init = "4"; + const searchParams = new URLSearchParams(init); + assertEquals(searchParams.get("4"), ""); + assertEquals(searchParams.toString(), "4="); +}); + +unitTest(function urlSearchParamsMissingPair(): void { + const init = "c=4&&a=54&"; + const searchParams = new URLSearchParams(init); + assertEquals(searchParams.toString(), "c=4&a=54"); +}); + +// If pair does not contain exactly two items, then throw a TypeError. +// ref https://url.spec.whatwg.org/#interface-urlsearchparams +unitTest(function urlSearchParamsShouldThrowTypeError(): void { + let hasThrown = 0; + + try { + new URLSearchParams([["1"]]); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + + assertEquals(hasThrown, 2); + + try { + new URLSearchParams([["1", "2", "3"]]); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + + assertEquals(hasThrown, 2); +}); + +unitTest(function urlSearchParamsAppendArgumentsCheck(): void { + const methodRequireOneParam = ["delete", "getAll", "get", "has", "forEach"]; + + const methodRequireTwoParams = ["append", "set"]; + + methodRequireOneParam + .concat(methodRequireTwoParams) + .forEach((method: string): void => { + const searchParams = new URLSearchParams(); + let hasThrown = 0; + try { + // @ts-ignore + searchParams[method](); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); + }); + + methodRequireTwoParams.forEach((method: string): void => { + const searchParams = new URLSearchParams(); + let hasThrown = 0; + try { + // @ts-ignore + searchParams[method]("foo"); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; + } + } + assertEquals(hasThrown, 2); + }); +}); + +// ref: https://github.com/web-platform-tests/wpt/blob/master/url/urlsearchparams-delete.any.js +unitTest(function urlSearchParamsDeletingAppendedMultiple(): void { + const params = new URLSearchParams(); + params.append("first", (1 as unknown) as string); + assert(params.has("first")); + assertEquals(params.get("first"), "1"); + params.delete("first"); + assertEquals(params.has("first"), false); + params.append("first", (1 as unknown) as string); + params.append("first", (10 as unknown) as string); + params.delete("first"); + assertEquals(params.has("first"), false); +}); + +// ref: https://github.com/web-platform-tests/wpt/blob/master/url/urlsearchparams-constructor.any.js#L176-L182 +unitTest(function urlSearchParamsCustomSymbolIterator(): void { + const params = new URLSearchParams(); + params[Symbol.iterator] = function* (): IterableIterator<[string, string]> { + yield ["a", "b"]; + }; + const params1 = new URLSearchParams((params as unknown) as string[][]); + assertEquals(params1.get("a"), "b"); +}); + +unitTest( + function urlSearchParamsCustomSymbolIteratorWithNonStringParams(): void { + const params = {}; + // @ts-ignore + params[Symbol.iterator] = function* (): IterableIterator<[number, number]> { + yield [1, 2]; + }; + const params1 = new URLSearchParams((params as unknown) as string[][]); + assertEquals(params1.get("1"), "2"); + } +); + +// If a class extends URLSearchParams, override one method should not change another's behavior. +unitTest( + function urlSearchParamsOverridingAppendNotChangeConstructorAndSet(): void { + let overridedAppendCalled = 0; + class CustomSearchParams extends URLSearchParams { + append(name: string, value: string): void { + ++overridedAppendCalled; + super.append(name, value); + } + } + new CustomSearchParams("foo=bar"); + new CustomSearchParams([["foo", "bar"]]); + new CustomSearchParams(new CustomSearchParams({ foo: "bar" })); + new CustomSearchParams().set("foo", "bar"); + assertEquals(overridedAppendCalled, 0); + } +); + +unitTest(function urlSearchParamsOverridingEntriesNotChangeForEach(): void { + class CustomSearchParams extends URLSearchParams { + *entries(): IterableIterator<[string, string]> { + yield* []; + } + } + let loopCount = 0; + const params = new CustomSearchParams({ foo: "bar" }); + params.forEach(() => void ++loopCount); + assertEquals(loopCount, 1); +}); diff --git a/cli/tests/unit/url_test.ts b/cli/tests/unit/url_test.ts new file mode 100644 index 000000000..68fcbd95e --- /dev/null +++ b/cli/tests/unit/url_test.ts @@ -0,0 +1,407 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals, assertThrows } from "./test_util.ts"; + +unitTest(function urlParsing(): void { + const url = new URL( + "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" + ); + assertEquals(url.hash, "#qat"); + assertEquals(url.host, "baz.qat:8000"); + assertEquals(url.hostname, "baz.qat"); + assertEquals( + url.href, + "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" + ); + assertEquals(url.origin, "https://baz.qat:8000"); + assertEquals(url.password, "bar"); + assertEquals(url.pathname, "/qux/quux"); + assertEquals(url.port, "8000"); + assertEquals(url.protocol, "https:"); + assertEquals(url.search, "?foo=bar&baz=12"); + assertEquals(url.searchParams.getAll("foo"), ["bar"]); + assertEquals(url.searchParams.getAll("baz"), ["12"]); + assertEquals(url.username, "foo"); + assertEquals( + String(url), + "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" + ); + assertEquals( + JSON.stringify({ key: url }), + `{"key":"https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat"}` + ); +}); + +unitTest(function urlModifications(): void { + const url = new URL( + "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" + ); + url.hash = ""; + assertEquals( + url.href, + "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12" + ); + url.host = "qat.baz:8080"; + assertEquals( + url.href, + "https://foo:bar@qat.baz:8080/qux/quux?foo=bar&baz=12" + ); + url.hostname = "foo.bar"; + assertEquals( + url.href, + "https://foo:bar@foo.bar:8080/qux/quux?foo=bar&baz=12" + ); + url.password = "qux"; + assertEquals( + url.href, + "https://foo:qux@foo.bar:8080/qux/quux?foo=bar&baz=12" + ); + url.pathname = "/foo/bar%qat"; + assertEquals( + url.href, + "https://foo:qux@foo.bar:8080/foo/bar%qat?foo=bar&baz=12" + ); + url.port = ""; + assertEquals(url.href, "https://foo:qux@foo.bar/foo/bar%qat?foo=bar&baz=12"); + url.protocol = "http:"; + assertEquals(url.href, "http://foo:qux@foo.bar/foo/bar%qat?foo=bar&baz=12"); + url.search = "?foo=bar&foo=baz"; + assertEquals(url.href, "http://foo:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz"); + assertEquals(url.searchParams.getAll("foo"), ["bar", "baz"]); + url.username = "foo@bar"; + assertEquals( + url.href, + "http://foo%40bar:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz" + ); + url.searchParams.set("bar", "qat"); + assertEquals( + url.href, + "http://foo%40bar:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz&bar=qat" + ); + url.searchParams.delete("foo"); + assertEquals(url.href, "http://foo%40bar:qux@foo.bar/foo/bar%qat?bar=qat"); + url.searchParams.append("foo", "bar"); + assertEquals( + url.href, + "http://foo%40bar:qux@foo.bar/foo/bar%qat?bar=qat&foo=bar" + ); +}); + +unitTest(function urlModifyHref(): void { + const url = new URL("http://example.com/"); + url.href = "https://foo:bar@example.com:8080/baz/qat#qux"; + assertEquals(url.protocol, "https:"); + assertEquals(url.username, "foo"); + assertEquals(url.password, "bar"); + assertEquals(url.host, "example.com:8080"); + assertEquals(url.hostname, "example.com"); + assertEquals(url.pathname, "/baz/qat"); + assertEquals(url.hash, "#qux"); +}); + +unitTest(function urlNormalize(): void { + const url = new URL("http://example.com"); + assertEquals(url.pathname, "/"); + assertEquals(url.href, "http://example.com/"); +}); + +unitTest(function urlModifyPathname(): void { + const url = new URL("http://foo.bar/baz%qat/qux%quux"); + assertEquals(url.pathname, "/baz%qat/qux%quux"); + url.pathname = url.pathname; + assertEquals(url.pathname, "/baz%qat/qux%quux"); + url.pathname = "baz#qat qux"; + assertEquals(url.pathname, "/baz%23qat%20qux"); + url.pathname = url.pathname; + assertEquals(url.pathname, "/baz%23qat%20qux"); +}); + +unitTest(function urlModifyHash(): void { + const url = new URL("http://foo.bar"); + url.hash = "%foo bar/qat%qux#bar"; + assertEquals(url.hash, "#%foo%20bar/qat%qux#bar"); + url.hash = url.hash; + assertEquals(url.hash, "#%foo%20bar/qat%qux#bar"); +}); + +unitTest(function urlSearchParamsReuse(): void { + const url = new URL( + "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" + ); + const sp = url.searchParams; + url.host = "baz.qat"; + assert(sp === url.searchParams, "Search params should be reused."); +}); + +unitTest(function urlBackSlashes(): void { + const url = new URL( + "https:\\\\foo:bar@baz.qat:8000\\qux\\quux?foo=bar&baz=12#qat" + ); + assertEquals( + url.href, + "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" + ); +}); + +unitTest(function urlRequireHost(): void { + assertEquals(new URL("file:///").href, "file:///"); + assertThrows(() => { + new URL("ftp:///"); + }); + assertThrows(() => { + new URL("http:///"); + }); + assertThrows(() => { + new URL("https:///"); + }); + assertThrows(() => { + new URL("ws:///"); + }); + assertThrows(() => { + new URL("wss:///"); + }); +}); + +unitTest(function urlDriveLetter() { + assertEquals( + new URL("file:///C:").href, + Deno.build.os == "windows" ? "file:///C:/" : "file:///C:" + ); + assertEquals(new URL("http://example.com/C:").href, "http://example.com/C:"); +}); + +unitTest(function urlHostnameUpperCase() { + assertEquals(new URL("https://EXAMPLE.COM").href, "https://example.com/"); +}); + +unitTest(function urlTrim() { + assertEquals(new URL(" https://example.com ").href, "https://example.com/"); +}); + +unitTest(function urlEncoding() { + assertEquals( + new URL("https://a !$&*()=,;+'\"@example.com").username, + "a%20!$&*()%3D,%3B+%27%22" + ); + assertEquals( + new URL("https://:a !$&*()=,;+'\"@example.com").password, + "a%20!$&*()%3D,%3B+%27%22" + ); + // FIXME: https://url.spec.whatwg.org/#idna + // assertEquals( + // new URL("https://a !$&*()=,+'\"").hostname, + // "a%20%21%24%26%2A%28%29%3D%2C+%27%22" + // ); + assertEquals( + new URL("https://example.com/a ~!@$&*()=:/,;+'\"\\").pathname, + "/a%20~!@$&*()=:/,;+'%22/" + ); + assertEquals( + new URL("https://example.com?a ~!@$&*()=:/,;?+'\"\\").search, + "?a%20~!@$&*()=:/,;?+%27%22\\" + ); + assertEquals( + new URL("https://example.com#a ~!@#$&*()=:/,;?+'\"\\").hash, + "#a%20~!@#$&*()=:/,;?+'%22\\" + ); +}); + +unitTest(function urlBaseURL(): void { + const base = new URL( + "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" + ); + const url = new URL("/foo/bar?baz=foo#qux", base); + assertEquals(url.href, "https://foo:bar@baz.qat:8000/foo/bar?baz=foo#qux"); +}); + +unitTest(function urlBaseString(): void { + const url = new URL( + "/foo/bar?baz=foo#qux", + "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" + ); + assertEquals(url.href, "https://foo:bar@baz.qat:8000/foo/bar?baz=foo#qux"); +}); + +unitTest(function urlRelativeWithBase(): void { + assertEquals(new URL("", "file:///a/a/a").href, "file:///a/a/a"); + assertEquals(new URL(".", "file:///a/a/a").href, "file:///a/a/"); + assertEquals(new URL("..", "file:///a/a/a").href, "file:///a/"); + assertEquals(new URL("b", "file:///a/a/a").href, "file:///a/a/b"); + assertEquals(new URL("b", "file:///a/a/a/").href, "file:///a/a/a/b"); + assertEquals(new URL("b/", "file:///a/a/a").href, "file:///a/a/b/"); + assertEquals(new URL("../b", "file:///a/a/a").href, "file:///a/b"); +}); + +unitTest(function urlDriveLetterBase() { + assertEquals( + new URL("/b", "file:///C:/a/b").href, + Deno.build.os == "windows" ? "file:///C:/b" : "file:///b" + ); + assertEquals( + new URL("D:", "file:///C:/a/b").href, + Deno.build.os == "windows" ? "file:///D:/" : "file:///C:/a/D:" + ); + assertEquals( + new URL("/D:", "file:///C:/a/b").href, + Deno.build.os == "windows" ? "file:///D:/" : "file:///D:" + ); + assertEquals( + new URL("D:/b", "file:///C:/a/b").href, + Deno.build.os == "windows" ? "file:///D:/b" : "file:///C:/a/D:/b" + ); +}); + +unitTest(function emptyBasePath(): void { + assertEquals(new URL("", "http://example.com").href, "http://example.com/"); +}); + +unitTest(function deletingAllParamsRemovesQuestionMarkFromURL(): void { + const url = new URL("http://example.com/?param1¶m2"); + url.searchParams.delete("param1"); + url.searchParams.delete("param2"); + assertEquals(url.href, "http://example.com/"); + assertEquals(url.search, ""); +}); + +unitTest(function removingNonExistentParamRemovesQuestionMarkFromURL(): void { + const url = new URL("http://example.com/?"); + assertEquals(url.href, "http://example.com/?"); + url.searchParams.delete("param1"); + assertEquals(url.href, "http://example.com/"); + assertEquals(url.search, ""); +}); + +unitTest(function sortingNonExistentParamRemovesQuestionMarkFromURL(): void { + const url = new URL("http://example.com/?"); + assertEquals(url.href, "http://example.com/?"); + url.searchParams.sort(); + assertEquals(url.href, "http://example.com/"); + assertEquals(url.search, ""); +}); + +unitTest( + { + // FIXME(bartlomieju) + ignore: true, + }, + function customInspectFunction(): void { + const url = new URL("http://example.com/?"); + assertEquals( + Deno.inspect(url), + 'URL { href: "http://example.com/?", origin: "http://example.com", protocol: "http:", username: "", password: "", host: "example.com", hostname: "example.com", port: "", pathname: "/", hash: "", search: "?" }' + ); + } +); + +unitTest(function protocolNotHttpOrFile() { + const url = new URL("about:blank"); + assertEquals(url.href, "about:blank"); + assertEquals(url.protocol, "about:"); + assertEquals(url.origin, "null"); +}); + +unitTest(function createBadUrl(): void { + assertThrows(() => { + new URL("0.0.0.0:8080"); + }); +}); + +unitTest(function throwForInvalidPortConstructor(): void { + const urls = [ + // If port is greater than 2^16 − 1, validation error, return failure. + `https://baz.qat:${2 ** 16}`, + "https://baz.qat:-32", + "https://baz.qat:deno", + "https://baz.qat:9land", + "https://baz.qat:10.5", + ]; + + for (const url of urls) { + assertThrows(() => new URL(url), TypeError, "Invalid URL."); + } + + // Do not throw for 0 & 65535 + new URL("https://baz.qat:65535"); + new URL("https://baz.qat:0"); +}); + +unitTest(function throwForInvalidSchemeConstructor(): void { + assertThrows( + () => new URL("invalid_scheme://baz.qat"), + TypeError, + "Invalid URL." + ); +}); + +unitTest(function doNotOverridePortIfInvalid(): void { + const initialPort = "3000"; + const ports = [ + // If port is greater than 2^16 − 1, validation error, return failure. + `${2 ** 16}`, + "-32", + "deno", + "9land", + "10.5", + ]; + + for (const port of ports) { + const url = new URL(`https://deno.land:${initialPort}`); + url.port = port; + assertEquals(url.port, initialPort); + } +}); + +unitTest(function emptyPortForSchemeDefaultPort(): void { + const nonDefaultPort = "3500"; + const urls = [ + { url: "ftp://baz.qat:21", port: "21", protocol: "ftp:" }, + { url: "https://baz.qat:443", port: "443", protocol: "https:" }, + { url: "wss://baz.qat:443", port: "443", protocol: "wss:" }, + { url: "http://baz.qat:80", port: "80", protocol: "http:" }, + { url: "ws://baz.qat:80", port: "80", protocol: "ws:" }, + { url: "file://home/index.html", port: "", protocol: "file:" }, + { url: "/foo", baseUrl: "ftp://baz.qat:21", port: "21", protocol: "ftp:" }, + { + url: "/foo", + baseUrl: "https://baz.qat:443", + port: "443", + protocol: "https:", + }, + { + url: "/foo", + baseUrl: "wss://baz.qat:443", + port: "443", + protocol: "wss:", + }, + { + url: "/foo", + baseUrl: "http://baz.qat:80", + port: "80", + protocol: "http:", + }, + { url: "/foo", baseUrl: "ws://baz.qat:80", port: "80", protocol: "ws:" }, + { + url: "/foo", + baseUrl: "file://home/index.html", + port: "", + protocol: "file:", + }, + ]; + + for (const { url: urlString, baseUrl, port, protocol } of urls) { + const url = new URL(urlString, baseUrl); + assertEquals(url.port, ""); + + url.port = nonDefaultPort; + assertEquals(url.port, nonDefaultPort); + + url.port = port; + assertEquals(url.port, ""); + + // change scheme + url.protocol = "sftp:"; + assertEquals(url.port, port); + + url.protocol = protocol; + assertEquals(url.port, ""); + } +}); diff --git a/cli/tests/unit/utime_test.ts b/cli/tests/unit/utime_test.ts new file mode 100644 index 000000000..63727ae62 --- /dev/null +++ b/cli/tests/unit/utime_test.ts @@ -0,0 +1,230 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert } from "./test_util.ts"; + +// Allow 10 second difference. +// Note this might not be enough for FAT (but we are not testing on such fs). +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function assertFuzzyTimestampEquals(t1: Date | null, t2: Date): void { + assert(t1 instanceof Date); + assert(Math.abs(t1.valueOf() - t2.valueOf()) < 10_000); +} + +unitTest( + { perms: { read: true, write: true } }, + function utimeSyncFileSuccess(): void { + const testDir = Deno.makeTempDirSync(); + const filename = testDir + "/file.txt"; + Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { + mode: 0o666, + }); + + const atime = 1000; + const mtime = 50000; + Deno.utimeSync(filename, atime, mtime); + + const fileInfo = Deno.statSync(filename); + assertFuzzyTimestampEquals(fileInfo.atime, new Date(atime * 1000)); + assertFuzzyTimestampEquals(fileInfo.mtime, new Date(mtime * 1000)); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function utimeSyncDirectorySuccess(): void { + const testDir = Deno.makeTempDirSync(); + + const atime = 1000; + const mtime = 50000; + Deno.utimeSync(testDir, atime, mtime); + + const dirInfo = Deno.statSync(testDir); + assertFuzzyTimestampEquals(dirInfo.atime, new Date(atime * 1000)); + assertFuzzyTimestampEquals(dirInfo.mtime, new Date(mtime * 1000)); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function utimeSyncDateSuccess(): void { + const testDir = Deno.makeTempDirSync(); + + const atime = new Date(1000_000); + const mtime = new Date(50000_000); + Deno.utimeSync(testDir, atime, mtime); + + const dirInfo = Deno.statSync(testDir); + assertFuzzyTimestampEquals(dirInfo.atime, atime); + assertFuzzyTimestampEquals(dirInfo.mtime, mtime); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function utimeSyncFileDateSuccess() { + const testDir = Deno.makeTempDirSync(); + const filename = testDir + "/file.txt"; + Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { + mode: 0o666, + }); + const atime = new Date(); + const mtime = new Date(); + Deno.utimeSync(filename, atime, mtime); + + const fileInfo = Deno.statSync(filename); + assertFuzzyTimestampEquals(fileInfo.atime, atime); + assertFuzzyTimestampEquals(fileInfo.mtime, mtime); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function utimeSyncLargeNumberSuccess(): void { + const testDir = Deno.makeTempDirSync(); + + // There are Rust side caps (might be fs relate), + // so JUST make them slightly larger than UINT32_MAX. + const atime = 0x100000001; + const mtime = 0x100000002; + Deno.utimeSync(testDir, atime, mtime); + + const dirInfo = Deno.statSync(testDir); + assertFuzzyTimestampEquals(dirInfo.atime, new Date(atime * 1000)); + assertFuzzyTimestampEquals(dirInfo.mtime, new Date(mtime * 1000)); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function utimeSyncNotFound(): void { + const atime = 1000; + const mtime = 50000; + + let caughtError = false; + try { + Deno.utimeSync("/baddir", atime, mtime); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); + } +); + +unitTest( + { perms: { read: true, write: false } }, + function utimeSyncPerm(): void { + const atime = 1000; + const mtime = 50000; + + let caughtError = false; + try { + Deno.utimeSync("/some_dir", atime, mtime); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function utimeFileSuccess(): Promise { + const testDir = Deno.makeTempDirSync(); + const filename = testDir + "/file.txt"; + Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { + mode: 0o666, + }); + + const atime = 1000; + const mtime = 50000; + await Deno.utime(filename, atime, mtime); + + const fileInfo = Deno.statSync(filename); + assertFuzzyTimestampEquals(fileInfo.atime, new Date(atime * 1000)); + assertFuzzyTimestampEquals(fileInfo.mtime, new Date(mtime * 1000)); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function utimeDirectorySuccess(): Promise { + const testDir = Deno.makeTempDirSync(); + + const atime = 1000; + const mtime = 50000; + await Deno.utime(testDir, atime, mtime); + + const dirInfo = Deno.statSync(testDir); + assertFuzzyTimestampEquals(dirInfo.atime, new Date(atime * 1000)); + assertFuzzyTimestampEquals(dirInfo.mtime, new Date(mtime * 1000)); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function utimeDateSuccess(): Promise { + const testDir = Deno.makeTempDirSync(); + + const atime = new Date(100_000); + const mtime = new Date(5000_000); + await Deno.utime(testDir, atime, mtime); + + const dirInfo = Deno.statSync(testDir); + assertFuzzyTimestampEquals(dirInfo.atime, atime); + assertFuzzyTimestampEquals(dirInfo.mtime, mtime); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function utimeFileDateSuccess(): Promise { + const testDir = Deno.makeTempDirSync(); + const filename = testDir + "/file.txt"; + Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { + mode: 0o666, + }); + + const atime = new Date(); + const mtime = new Date(); + await Deno.utime(filename, atime, mtime); + + const fileInfo = Deno.statSync(filename); + assertFuzzyTimestampEquals(fileInfo.atime, atime); + assertFuzzyTimestampEquals(fileInfo.mtime, mtime); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function utimeNotFound(): Promise { + const atime = 1000; + const mtime = 50000; + + let caughtError = false; + try { + await Deno.utime("/baddir", atime, mtime); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); + } +); + +unitTest( + { perms: { read: true, write: false } }, + async function utimeSyncPerm(): Promise { + const atime = 1000; + const mtime = 50000; + + let caughtError = false; + try { + await Deno.utime("/some_dir", atime, mtime); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); + } +); diff --git a/cli/tests/unit/version_test.ts b/cli/tests/unit/version_test.ts new file mode 100644 index 000000000..0417b27de --- /dev/null +++ b/cli/tests/unit/version_test.ts @@ -0,0 +1,8 @@ +import { unitTest, assert } from "./test_util.ts"; + +unitTest(function version(): void { + const pattern = /^\d+\.\d+\.\d+/; + assert(pattern.test(Deno.version.deno)); + assert(pattern.test(Deno.version.v8)); + assert(pattern.test(Deno.version.typescript)); +}); diff --git a/cli/tests/unit/write_file_test.ts b/cli/tests/unit/write_file_test.ts new file mode 100644 index 000000000..80f711dbc --- /dev/null +++ b/cli/tests/unit/write_file_test.ts @@ -0,0 +1,228 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest( + { perms: { read: true, write: true } }, + function writeFileSyncSuccess(): void { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + Deno.writeFileSync(filename, data); + const dataRead = Deno.readFileSync(filename); + const dec = new TextDecoder("utf-8"); + const actual = dec.decode(dataRead); + assertEquals("Hello", actual); + } +); + +unitTest({ perms: { write: true } }, function writeFileSyncFail(): void { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = "/baddir/test.txt"; + // The following should fail because /baddir doesn't exist (hopefully). + let caughtError = false; + try { + Deno.writeFileSync(filename, data); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); +}); + +unitTest({ perms: { write: false } }, function writeFileSyncPerm(): void { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = "/baddir/test.txt"; + // The following should fail due to no write permission + let caughtError = false; + try { + Deno.writeFileSync(filename, data); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest( + { perms: { read: true, write: true } }, + function writeFileSyncUpdateMode(): void { + if (Deno.build.os !== "windows") { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + Deno.writeFileSync(filename, data, { mode: 0o755 }); + assertEquals(Deno.statSync(filename).mode! & 0o777, 0o755); + Deno.writeFileSync(filename, data, { mode: 0o666 }); + assertEquals(Deno.statSync(filename).mode! & 0o777, 0o666); + } + } +); + +unitTest( + { perms: { read: true, write: true } }, + function writeFileSyncCreate(): void { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + let caughtError = false; + // if create turned off, the file won't be created + try { + Deno.writeFileSync(filename, data, { create: false }); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); + + // Turn on create, should have no error + Deno.writeFileSync(filename, data, { create: true }); + Deno.writeFileSync(filename, data, { create: false }); + const dataRead = Deno.readFileSync(filename); + const dec = new TextDecoder("utf-8"); + const actual = dec.decode(dataRead); + assertEquals("Hello", actual); + } +); + +unitTest( + { perms: { read: true, write: true } }, + function writeFileSyncAppend(): void { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + Deno.writeFileSync(filename, data); + Deno.writeFileSync(filename, data, { append: true }); + let dataRead = Deno.readFileSync(filename); + const dec = new TextDecoder("utf-8"); + let actual = dec.decode(dataRead); + assertEquals("HelloHello", actual); + // Now attempt overwrite + Deno.writeFileSync(filename, data, { append: false }); + dataRead = Deno.readFileSync(filename); + actual = dec.decode(dataRead); + assertEquals("Hello", actual); + // append not set should also overwrite + Deno.writeFileSync(filename, data); + dataRead = Deno.readFileSync(filename); + actual = dec.decode(dataRead); + assertEquals("Hello", actual); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function writeFileSuccess(): Promise { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + await Deno.writeFile(filename, data); + const dataRead = Deno.readFileSync(filename); + const dec = new TextDecoder("utf-8"); + const actual = dec.decode(dataRead); + assertEquals("Hello", actual); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function writeFileNotFound(): Promise { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = "/baddir/test.txt"; + // The following should fail because /baddir doesn't exist (hopefully). + let caughtError = false; + try { + await Deno.writeFile(filename, data); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); + } +); + +unitTest( + { perms: { read: true, write: false } }, + async function writeFilePerm(): Promise { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = "/baddir/test.txt"; + // The following should fail due to no write permission + let caughtError = false; + try { + await Deno.writeFile(filename, data); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function writeFileUpdateMode(): Promise { + if (Deno.build.os !== "windows") { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + await Deno.writeFile(filename, data, { mode: 0o755 }); + assertEquals(Deno.statSync(filename).mode! & 0o777, 0o755); + await Deno.writeFile(filename, data, { mode: 0o666 }); + assertEquals(Deno.statSync(filename).mode! & 0o777, 0o666); + } + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function writeFileCreate(): Promise { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + let caughtError = false; + // if create turned off, the file won't be created + try { + await Deno.writeFile(filename, data, { create: false }); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); + + // Turn on create, should have no error + await Deno.writeFile(filename, data, { create: true }); + await Deno.writeFile(filename, data, { create: false }); + const dataRead = Deno.readFileSync(filename); + const dec = new TextDecoder("utf-8"); + const actual = dec.decode(dataRead); + assertEquals("Hello", actual); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function writeFileAppend(): Promise { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + await Deno.writeFile(filename, data); + await Deno.writeFile(filename, data, { append: true }); + let dataRead = Deno.readFileSync(filename); + const dec = new TextDecoder("utf-8"); + let actual = dec.decode(dataRead); + assertEquals("HelloHello", actual); + // Now attempt overwrite + await Deno.writeFile(filename, data, { append: false }); + dataRead = Deno.readFileSync(filename); + actual = dec.decode(dataRead); + assertEquals("Hello", actual); + // append not set should also overwrite + await Deno.writeFile(filename, data); + dataRead = Deno.readFileSync(filename); + actual = dec.decode(dataRead); + assertEquals("Hello", actual); + } +); diff --git a/cli/tests/unit/write_text_file_test.ts b/cli/tests/unit/write_text_file_test.ts new file mode 100644 index 000000000..321189b0e --- /dev/null +++ b/cli/tests/unit/write_text_file_test.ts @@ -0,0 +1,79 @@ +import { unitTest, assert, assertEquals } from "./test_util.ts"; + +unitTest( + { perms: { read: true, write: true } }, + function writeTextFileSyncSuccess(): void { + const filename = Deno.makeTempDirSync() + "/test.txt"; + Deno.writeTextFileSync(filename, "Hello"); + const dataRead = Deno.readTextFileSync(filename); + assertEquals("Hello", dataRead); + } +); + +unitTest({ perms: { write: true } }, function writeTextFileSyncFail(): void { + const filename = "/baddir/test.txt"; + // The following should fail because /baddir doesn't exist (hopefully). + let caughtError = false; + try { + Deno.writeTextFileSync(filename, "hello"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); +}); + +unitTest({ perms: { write: false } }, function writeTextFileSyncPerm(): void { + const filename = "/baddir/test.txt"; + // The following should fail due to no write permission + let caughtError = false; + try { + Deno.writeTextFileSync(filename, "Hello"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest( + { perms: { read: true, write: true } }, + async function writeTextFileSuccess(): Promise { + const filename = Deno.makeTempDirSync() + "/test.txt"; + await Deno.writeTextFile(filename, "Hello"); + const dataRead = Deno.readTextFileSync(filename); + assertEquals("Hello", dataRead); + } +); + +unitTest( + { perms: { read: true, write: true } }, + async function writeTextFileNotFound(): Promise { + const filename = "/baddir/test.txt"; + // The following should fail because /baddir doesn't exist (hopefully). + let caughtError = false; + try { + await Deno.writeTextFile(filename, "Hello"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.NotFound); + } + assert(caughtError); + } +); + +unitTest( + { perms: { write: false } }, + async function writeTextFilePerm(): Promise { + const filename = "/baddir/test.txt"; + // The following should fail due to no write permission + let caughtError = false; + try { + await Deno.writeTextFile(filename, "Hello"); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); + } +); -- cgit v1.2.3