summaryrefslogtreecommitdiff
path: root/cli/js/tests
diff options
context:
space:
mode:
Diffstat (limited to 'cli/js/tests')
-rw-r--r--cli/js/tests/README.md47
-rw-r--r--cli/js/tests/blob_test.ts62
-rw-r--r--cli/js/tests/body_test.ts74
-rw-r--r--cli/js/tests/buffer_test.ts296
-rw-r--r--cli/js/tests/build_test.ts10
-rw-r--r--cli/js/tests/chmod_test.ts159
-rw-r--r--cli/js/tests/chown_test.ts147
-rw-r--r--cli/js/tests/compiler_api_test.ts153
-rw-r--r--cli/js/tests/console_test.ts706
-rw-r--r--cli/js/tests/copy_file_test.ts176
-rw-r--r--cli/js/tests/custom_event_test.ts27
-rw-r--r--cli/js/tests/dir_test.ts54
-rw-r--r--cli/js/tests/dispatch_json_test.ts32
-rw-r--r--cli/js/tests/dispatch_minimal_test.ts43
-rw-r--r--cli/js/tests/dom_iterable_test.ts87
-rw-r--r--cli/js/tests/error_stack_test.ts108
-rw-r--r--cli/js/tests/event_target_test.ts231
-rw-r--r--cli/js/tests/event_test.ts96
-rw-r--r--cli/js/tests/fetch_test.ts497
-rw-r--r--cli/js/tests/file_test.ts105
-rw-r--r--cli/js/tests/files_test.ts446
-rw-r--r--cli/js/tests/form_data_test.ts189
-rw-r--r--cli/js/tests/format_error_test.ts37
-rw-r--r--cli/js/tests/fs_events_test.ts51
-rw-r--r--cli/js/tests/get_random_values_test.ts51
-rw-r--r--cli/js/tests/globals_test.ts112
-rw-r--r--cli/js/tests/headers_test.ts355
-rw-r--r--cli/js/tests/internals_test.ts10
-rw-r--r--cli/js/tests/link_test.ts147
-rw-r--r--cli/js/tests/location_test.ts7
-rw-r--r--cli/js/tests/make_temp_test.ts134
-rw-r--r--cli/js/tests/metrics_test.ts58
-rw-r--r--cli/js/tests/mkdir_test.ts76
-rw-r--r--cli/js/tests/net_test.ts307
-rw-r--r--cli/js/tests/os_test.ts337
-rw-r--r--cli/js/tests/performance_test.ts15
-rw-r--r--cli/js/tests/permissions_test.ts27
-rw-r--r--cli/js/tests/process_test.ts383
-rw-r--r--cli/js/tests/read_dir_test.ts86
-rw-r--r--cli/js/tests/read_file_test.ts59
-rw-r--r--cli/js/tests/read_link_test.ts75
-rw-r--r--cli/js/tests/realpath_test.ts108
-rw-r--r--cli/js/tests/remove_test.ts481
-rw-r--r--cli/js/tests/rename_test.ts84
-rw-r--r--cli/js/tests/request_test.ts49
-rw-r--r--cli/js/tests/resources_test.ts51
-rw-r--r--cli/js/tests/signal_test.ts201
-rw-r--r--cli/js/tests/stat_test.ts229
-rw-r--r--cli/js/tests/symbols_test.ts7
-rw-r--r--cli/js/tests/symlink_test.ts85
-rw-r--r--cli/js/tests/test_util.ts417
-rw-r--r--cli/js/tests/testing_test.ts37
-rw-r--r--cli/js/tests/text_encoding_test.ts193
-rw-r--r--cli/js/tests/timers_test.ts353
-rw-r--r--cli/js/tests/tls_test.ts214
-rw-r--r--cli/js/tests/truncate_test.ts80
-rw-r--r--cli/js/tests/tty_test.ts23
-rwxr-xr-xcli/js/tests/unit_test_runner.ts108
-rw-r--r--cli/js/tests/unit_tests.ts69
-rw-r--r--cli/js/tests/url_search_params_test.ts245
-rw-r--r--cli/js/tests/url_test.ts208
-rw-r--r--cli/js/tests/utime_test.ts193
-rw-r--r--cli/js/tests/version_test.ts8
-rw-r--r--cli/js/tests/write_file_test.ts228
64 files changed, 9743 insertions, 0 deletions
diff --git a/cli/js/tests/README.md b/cli/js/tests/README.md
new file mode 100644
index 000000000..5809224f7
--- /dev/null
+++ b/cli/js/tests/README.md
@@ -0,0 +1,47 @@
+# 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({
+ skip: Deno.build.os === "win",
+ 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
+
+`unit_test_runner.ts` is main script used to run unit tests.
+
+Runner discoveres required permissions combinations by loading
+`cli/js/tests/unit_tests.ts` and going through all registered instances of
+`unitTest`. For each discovered permission combination a new Deno process is
+created with respective `--allow-*` flags which loads
+`cli/js/tests/unit_tests.ts` and executes all `unitTest` that match runtime
+permissions.
diff --git a/cli/js/tests/blob_test.ts b/cli/js/tests/blob_test.ts
new file mode 100644
index 000000000..54071a11e
--- /dev/null
+++ b/cli/js/tests/blob_test.ts
@@ -0,0 +1,62 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert, assertEquals } from "./test_util.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 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 === "win" ? 12 : 11);
+});
+
+// TODO(qti3e) Test the stored data in a Blob after implementing FileReader API.
diff --git a/cli/js/tests/body_test.ts b/cli/js/tests/body_test.ts
new file mode 100644
index 000000000..23f6d89e4
--- /dev/null
+++ b/cli/js/tests/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<void> {
+ 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<void> {
+ 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<void> {
+ 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(), "<Deno>");
+ }
+);
diff --git a/cli/js/tests/buffer_test.ts b/cli/js/tests/buffer_test.ts
new file mode 100644
index 000000000..163ed0a30
--- /dev/null
+++ b/cli/js/tests/buffer_test.ts
@@ -0,0 +1,296 @@
+// 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, buf.toString().length);
+ 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<string> {
+ 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<void> {
+ check(buf, s);
+ while (true) {
+ const r = await buf.read(fub);
+ if (r === Deno.EOF) {
+ 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<void> {
+ 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<void> {
+ // 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<void> {
+ 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<void> {
+ 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<void> {
+ 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<void> {
+ 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<void> {
+ 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<void> {
+ 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 result = await buf.read(tmp);
+ const nread = result === Deno.EOF ? 0 : result;
+ 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<void> {
+ 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<void> {
+ 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
new file mode 100644
index 000000000..50c7b519c
--- /dev/null
+++ b/cli/js/tests/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 === "x64");
+ assert(os === "mac" || os === "win" || os === "linux");
+});
diff --git a/cli/js/tests/chmod_test.ts b/cli/js/tests/chmod_test.ts
new file mode 100644
index 000000000..8731fad22
--- /dev/null
+++ b/cli/js/tests/chmod_test.ts
@@ -0,0 +1,159 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert, assertEquals } from "./test_util.ts";
+
+const isNotWindows = Deno.build.os !== "win";
+
+unitTest(
+ { 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 });
+
+ // On windows no effect, but should not crash
+ Deno.chmodSync(filename, 0o777);
+
+ // Check success when not on windows
+ if (isNotWindows) {
+ const fileInfo = Deno.statSync(filename);
+ assert(fileInfo.mode);
+ assertEquals(fileInfo.mode & 0o777, 0o777);
+ }
+ }
+);
+
+// Check symlink when not on windows
+unitTest(
+ {
+ skip: Deno.build.os === "win",
+ 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(
+ { perms: { read: true, write: true } },
+ async function chmodSuccess(): Promise<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 });
+
+ // On windows no effect, but should not crash
+ await Deno.chmod(filename, 0o777);
+
+ // Check success when not on windows
+ if (isNotWindows) {
+ const fileInfo = Deno.statSync(filename);
+ assert(fileInfo.mode);
+ assertEquals(fileInfo.mode & 0o777, 0o777);
+ }
+ }
+);
+
+// Check symlink when not on windows
+
+unitTest(
+ {
+ skip: Deno.build.os === "win",
+ perms: { read: true, write: true }
+ },
+ async function chmodSymlinkSuccess(): Promise<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
+
+ 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
new file mode 100644
index 000000000..50dcd6dc1
--- /dev/null
+++ b/cli/js/tests/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 !== "win") {
+ 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",
+ args: ["python", "-c", "import os; print(os.getuid())"]
+ });
+ const gidProc = Deno.run({
+ stdout: "piped",
+ args: ["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<void> {
+ 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<void> {
+ 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<void> {
+ 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<void> {
+ 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<void> {
+ // 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 priviledge, 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<void> {
+ // 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/compiler_api_test.ts b/cli/js/tests/compiler_api_test.ts
new file mode 100644
index 000000000..3b0449f9a
--- /dev/null
+++ b/cli/js/tests/compiler_api_test.ts
@@ -0,0 +1,153 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+import { assert, assertEquals, unitTest } from "./test_util.ts";
+
+const { compile, transpileOnly, bundle } = Deno;
+
+unitTest(async function compilerApiCompileSources() {
+ const [diagnostics, actual] = await compile("/foo.ts", {
+ "/foo.ts": `import * as bar from "./bar.ts";\n\nconsole.log(bar);\n`,
+ "/bar.ts": `export const bar = "bar";\n`
+ });
+ assert(diagnostics == null);
+ assert(actual);
+ assertEquals(Object.keys(actual), [
+ "/bar.js.map",
+ "/bar.js",
+ "/foo.js.map",
+ "/foo.js"
+ ]);
+});
+
+unitTest(async function compilerApiCompileNoSources() {
+ const [diagnostics, actual] = await compile("./cli/tests/subdir/mod1.ts");
+ assert(diagnostics == null);
+ assert(actual);
+ const keys = Object.keys(actual);
+ assertEquals(keys.length, 6);
+ assert(keys[0].endsWith("print_hello.js.map"));
+ assert(keys[1].endsWith("print_hello.js"));
+});
+
+unitTest(async function compilerApiCompileOptions() {
+ const [diagnostics, actual] = await compile(
+ "/foo.ts",
+ {
+ "/foo.ts": `export const foo = "foo";`
+ },
+ {
+ module: "amd",
+ sourceMap: false
+ }
+ );
+ assert(diagnostics == null);
+ assert(actual);
+ assertEquals(Object.keys(actual), ["/foo.js"]);
+ assert(actual["/foo.js"].startsWith("define("));
+});
+
+unitTest(async function compilerApiCompileLib() {
+ const [diagnostics, actual] = await compile(
+ "/foo.ts",
+ {
+ "/foo.ts": `console.log(document.getElementById("foo"));
+ console.log(Deno.args);`
+ },
+ {
+ lib: ["dom", "es2018", "deno.ns"]
+ }
+ );
+ assert(diagnostics == null);
+ assert(actual);
+ assertEquals(Object.keys(actual), ["/foo.js.map", "/foo.js"]);
+});
+
+unitTest(async function compilerApiCompileTypes() {
+ const [diagnostics, actual] = await compile(
+ "/foo.ts",
+ {
+ "/foo.ts": `console.log(Foo.bar);`
+ },
+ {
+ types: ["./cli/tests/subdir/foo_types.d.ts"]
+ }
+ );
+ assert(diagnostics == null);
+ assert(actual);
+ assertEquals(Object.keys(actual), ["/foo.js.map", "/foo.js"]);
+});
+
+unitTest(async function transpileOnlyApi() {
+ const actual = await transpileOnly({
+ "foo.ts": `export enum Foo { Foo, Bar, Baz };\n`
+ });
+ assert(actual);
+ assertEquals(Object.keys(actual), ["foo.ts"]);
+ assert(actual["foo.ts"].source.startsWith("export var Foo;"));
+ assert(actual["foo.ts"].map);
+});
+
+unitTest(async function transpileOnlyApiConfig() {
+ const actual = await transpileOnly(
+ {
+ "foo.ts": `export enum Foo { Foo, Bar, Baz };\n`
+ },
+ {
+ sourceMap: false,
+ module: "amd"
+ }
+ );
+ assert(actual);
+ assertEquals(Object.keys(actual), ["foo.ts"]);
+ assert(actual["foo.ts"].source.startsWith("define("));
+ assert(actual["foo.ts"].map == null);
+});
+
+unitTest(async function bundleApiSources() {
+ const [diagnostics, actual] = await bundle("/foo.ts", {
+ "/foo.ts": `export * from "./bar.ts";\n`,
+ "/bar.ts": `export const bar = "bar";\n`
+ });
+ assert(diagnostics == null);
+ assert(actual.includes(`__instantiate("foo")`));
+ assert(actual.includes(`__exp["bar"]`));
+});
+
+unitTest(async function bundleApiNoSources() {
+ const [diagnostics, actual] = await bundle("./cli/tests/subdir/mod1.ts");
+ assert(diagnostics == null);
+ assert(actual.includes(`__instantiate("mod1")`));
+ assert(actual.includes(`__exp["printHello3"]`));
+});
+
+unitTest(async function bundleApiConfig() {
+ const [diagnostics, actual] = await bundle(
+ "/foo.ts",
+ {
+ "/foo.ts": `// random comment\nexport * from "./bar.ts";\n`,
+ "/bar.ts": `export const bar = "bar";\n`
+ },
+ {
+ removeComments: true
+ }
+ );
+ assert(diagnostics == null);
+ assert(!actual.includes(`random`));
+});
+
+unitTest(async function bundleApiJsModules() {
+ const [diagnostics, actual] = await bundle("/foo.js", {
+ "/foo.js": `export * from "./bar.js";\n`,
+ "/bar.js": `export const bar = "bar";\n`
+ });
+ assert(diagnostics == null);
+ assert(actual.includes(`System.register("bar",`));
+});
+
+unitTest(async function diagnosticsTest() {
+ const [diagnostics] = await compile("/foo.ts", {
+ "/foo.ts": `document.getElementById("foo");`
+ });
+ assert(Array.isArray(diagnostics));
+ assert(diagnostics.length === 1);
+});
diff --git a/cli/js/tests/console_test.ts b/cli/js/tests/console_test.ts
new file mode 100644
index 000000000..34a61c25f
--- /dev/null
+++ b/cli/js/tests/console_test.ts
@@ -0,0 +1,706 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { assert, assertEquals, unitTest } from "./test_util.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,
+ writeSync,
+ stdout
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+} = Deno as any;
+
+const customInspect = Deno.symbols.customInspect;
+const {
+ Console,
+ stringifyArgs
+ // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol
+} = Deno[Deno.symbols.internal];
+
+function stringify(...args: unknown[]): string {
+ return 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, bool, str, method, asyncMethod, generatorMethod, un, nu, arrowFunc, extendedClass, nFunc, extendedCstr, o }`;
+
+ 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<void> {}),
+ "[AsyncFunction: af]"
+ );
+ assertEquals(
+ stringify(function* gf() {}),
+ "[GeneratorFunction: gf]"
+ );
+ assertEquals(
+ stringify(async function* agf() {}),
+ "[AsyncGeneratorFunction: agf]"
+ );
+ assertEquals(stringify(new Uint8Array([1, 2, 3])), "Uint8Array [ 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), "{}");
+ assertEquals(
+ stringify(console),
+ "{ printFunc, log, debug, info, dir, dirxml, warn, error, assert, count, countReset, table, time, timeLog, timeEnd, group, groupCollapsed, groupEnd, clear, trace, indentLevel }"
+ );
+ // test inspect is working the same
+ assertEquals(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(
+ stringifyArgs([nestedObj], { depth: 3 }),
+ "{ a: { b: { c: [Object] } } }"
+ );
+ assertEquals(
+ stringifyArgs([nestedObj], { depth: 4 }),
+ "{ a: { b: { c: { d: [Object] } } } }"
+ );
+ assertEquals(stringifyArgs([nestedObj], { depth: 0 }), "[Object]");
+ assertEquals(
+ stringifyArgs([nestedObj], { depth: null }),
+ "{ a: { b: { c: { d: [Object] } } } }"
+ );
+ // test inspect is working the same way
+ assertEquals(
+ inspect(nestedObj, { depth: 4 }),
+ "{ a: { b: { c: { d: [Object] } } } }"
+ );
+});
+
+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), "{}");
+});
+
+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 {
+ const stdoutWriteSync = stdout.writeSync;
+ const uint8 = new TextEncoder().encode("\x1b[1;1H" + "\x1b[0J");
+ let buffer = new Uint8Array(0);
+
+ stdout.writeSync = (u8: Uint8Array): Promise<number> => {
+ const tmp = new Uint8Array(buffer.length + u8.length);
+ tmp.set(buffer, 0);
+ tmp.set(u8, buffer.length);
+ buffer = tmp;
+
+ return writeSync(stdout.rid, u8);
+ };
+ console.clear();
+ stdout.writeSync = stdoutWriteSync;
+ assertEquals(buffer, uint8);
+});
+
+// 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(
+ out.toString(),
+ `┌─────────┬────────┐
+│ (index) │ Values │
+├─────────┼────────┤
+│ a │ "test" │
+│ b │ 1 │
+└─────────┴────────┘
+`
+ );
+ });
+ mockConsole((console, out): void => {
+ console.table({ a: { b: 10 }, b: { b: 20, c: 30 } }, ["c"]);
+ assertEquals(
+ out.toString(),
+ `┌─────────┬────┐
+│ (index) │ c │
+├─────────┼────┤
+│ a │ │
+│ b │ 30 │
+└─────────┴────┘
+`
+ );
+ });
+ mockConsole((console, out): void => {
+ console.table([1, 2, [3, [4]], [5, 6], [[7], [8]]]);
+ assertEquals(
+ out.toString(),
+ `┌─────────┬───────┬───────┬────────┐
+│ (index) │ 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(
+ out.toString(),
+ `┌───────────────────┬────────┐
+│ (iteration index) │ Values │
+├───────────────────┼────────┤
+│ 0 │ 1 │
+│ 1 │ 2 │
+│ 2 │ 3 │
+│ 3 │ "test" │
+└───────────────────┴────────┘
+`
+ );
+ });
+ mockConsole((console, out): void => {
+ console.table(
+ new Map([
+ [1, "one"],
+ [2, "two"]
+ ])
+ );
+ assertEquals(
+ out.toString(),
+ `┌───────────────────┬─────┬────────┐
+│ (iteration index) │ 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(
+ out.toString(),
+ `┌─────────┬───────────┬───────────────────┬────────┐
+│ (index) │ 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(
+ out.toString(),
+ `┌─────────┬────────┬──────────────────────┬────┬────────┐
+│ (index) │ 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(
+ out.toString(),
+ `┌─────────┐
+│ (index) │
+├─────────┤
+└─────────┘
+`
+ );
+ });
+ mockConsole((console, out): void => {
+ console.table({});
+ assertEquals(
+ out.toString(),
+ `┌─────────┐
+│ (index) │
+├─────────┤
+└─────────┘
+`
+ );
+ });
+ mockConsole((console, out): void => {
+ console.table(new Set());
+ assertEquals(
+ out.toString(),
+ `┌───────────────────┐
+│ (iteration index) │
+├───────────────────┤
+└───────────────────┘
+`
+ );
+ });
+ mockConsole((console, out): void => {
+ console.table(new Map());
+ assertEquals(
+ out.toString(),
+ `┌───────────────────┐
+│ (iteration index) │
+├───────────────────┤
+└───────────────────┘
+`
+ );
+ });
+ mockConsole((console, out): void => {
+ console.table("test");
+ assertEquals(out.toString(), "test\n");
+ });
+});
+
+// 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.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
new file mode 100644
index 000000000..986bab53b
--- /dev/null
+++ b/cli/js/tests/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<void> {
+ 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<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 {
+ 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<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!");
+ 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<void> {
+ 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<void> {
+ 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
new file mode 100644
index 000000000..7a5cc44ca
--- /dev/null
+++ b/cli/js/tests/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/js/tests/dir_test.ts b/cli/js/tests/dir_test.ts
new file mode 100644
index 000000000..75184587b
--- /dev/null
+++ b/cli/js/tests/dir_test.ts
@@ -0,0 +1,54 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert, assertEquals } from "./test_util.ts";
+
+unitTest(function dirCwdNotNull(): void {
+ assert(Deno.cwd() != null);
+});
+
+unitTest({ perms: { write: true } }, function dirCwdChdirSuccess(): void {
+ const initialdir = Deno.cwd();
+ const path = Deno.makeTempDirSync();
+ Deno.chdir(path);
+ const current = Deno.cwd();
+ if (Deno.build.os === "mac") {
+ assertEquals(current, "/private" + path);
+ } else {
+ assertEquals(current, path);
+ }
+ Deno.chdir(initialdir);
+});
+
+unitTest({ perms: { write: true } }, function dirCwdError(): void {
+ // excluding windows since it throws resource busy, while removeSync
+ if (["linux", "mac"].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: { 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
new file mode 100644
index 000000000..a2306dda8
--- /dev/null
+++ b/cli/js/tests/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<void> {
+ await Deno.open("nonexistent.txt")
+ .then(unreachable)
+ .catch((error): void => {
+ assertMatch(error.stack, openErrorStackPattern);
+ });
+ }
+);
+
+unitTest(async function malformedJsonControlBuffer(): Promise<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
new file mode 100644
index 000000000..724a41698
--- /dev/null
+++ b/cli/js/tests/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<void> {
+ const buf = new Uint8Array(10);
+ const rid = 10;
+ try {
+ await Deno.read(rid, buf);
+ unreachable();
+ } catch (error) {
+ assertMatch(error.stack, readErrorStackPattern);
+ }
+});
+
+unitTest(async function malformedMinimalControlBuffer(): Promise<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_iterable_test.ts b/cli/js/tests/dom_iterable_test.ts
new file mode 100644
index 000000000..e16378945
--- /dev/null
+++ b/cli/js/tests/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 {
+ private [dataSymbol] = new Map<string, number>();
+
+ 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.symbols.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
new file mode 100644
index 000000000..7acbd3a3d
--- /dev/null
+++ b/cli/js/tests/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.symbols.internal];
+
+interface CallSite {
+ getThis(): unknown;
+ getTypeName(): string;
+ getFunction(): Function;
+ getFunctionName(): string;
+ getMethodName(): string;
+ getFileName(): string;
+ getLineNumber(): number | null;
+ getColumnNumber(): number | null;
+ getEvalOrigin(): string | null;
+ isToplevel(): boolean;
+ isEval(): boolean;
+ isNative(): boolean;
+ isConstructor(): boolean;
+ isAsync(): boolean;
+ isPromiseAll(): boolean;
+ getPromiseIndex(): number | null;
+}
+
+function getMockCallSite(
+ filename: string,
+ line: number | null,
+ column: 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 line;
+ },
+ getColumnNumber(): number | null {
+ return column;
+ },
+ 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",
+ line: 23,
+ column: 0
+ });
+ assert(result.filename.endsWith(".ts"));
+ assert(result.line != null);
+ assert(result.column != null);
+});
diff --git a/cli/js/tests/event_target_test.ts b/cli/js/tests/event_target_test.ts
new file mode 100644
index 000000000..09d47f704
--- /dev/null
+++ b/cli/js/tests/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?: __domTypes.AddEventListenerOptions
+ ): void {
+ this.addEventListener(type, callback, options);
+ }
+
+ off(
+ type: string,
+ callback: ((e: Event) => void) | null,
+ options?: __domTypes.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 = async (e: Event): Promise<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 = {
+ async handleEvent(e: Event): Promise<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
new file mode 100644
index 000000000..05a9ed577
--- /dev/null
+++ b/cli/js/tests/event_test.ts
@@ -0,0 +1,96 @@
+// 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);
+ assertEquals(event.cancelBubbleImmediately, false);
+ event.stopImmediatePropagation();
+ assertEquals(event.cancelBubble, true);
+ assertEquals(event.cancelBubbleImmediately, 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
new file mode 100644
index 000000000..5ebef92d9
--- /dev/null
+++ b/cli/js/tests/fetch_test.ts
@@ -0,0 +1,497 @@
+// 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<void> {
+ 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<void> {
+ 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<void> {
+ const response = await fetch("http://localhost:4545/cli/tests/fixture.json");
+ assertEquals(response.url, "http://localhost:4545/cli/tests/fixture.json");
+ response.body.close();
+});
+
+unitTest({ perms: { net: true } }, async function fetchURL(): Promise<void> {
+ const response = await fetch(
+ new URL("http://localhost:4545/cli/tests/fixture.json")
+ );
+ assertEquals(response.url, "http://localhost:4545/cli/tests/fixture.json");
+ response.body.close();
+});
+
+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"));
+ response.body.close();
+});
+
+unitTest({ perms: { net: true } }, async function fetchBlob(): Promise<void> {
+ 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.
+ response.bodyUsed = true;
+ });
+ await response.blob();
+ assertEquals(response.bodyUsed, true);
+});
+
+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")));
+ response.body.close();
+});
+
+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<void> {
+ 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<void> {
+ 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(), "<Deno>");
+ }
+);
+
+unitTest(
+ {
+ // TODO(bartlomieju): leaking resources
+ skip: true,
+ perms: { net: true }
+ },
+ async function fetchWithRedirection(): Promise<void> {
+ 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("<title>Directory listing for /</title>"));
+ }
+);
+
+unitTest(
+ {
+ // TODO: leaking resources
+ skip: true,
+ perms: { net: true }
+ },
+ async function fetchWithRelativeRedirection(): Promise<void> {
+ 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("<title>Directory listing for /cli/tests/</title>"));
+ }
+);
+
+// The feature below is not implemented, but the test should work after implementation
+/*
+unitTest({ perms: { net: true} }, async function fetchWithInfRedirection(): Promise<
+ void
+> {
+ 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<void> {
+ 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<void> {
+ 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<void> {
+ 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<void> {
+ const data = "param1=value1&param2=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 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 listener = Deno.listen(addr);
+ const buf = new Deno.Buffer();
+ listener.accept().then(async 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({ perms: { net: true} }, async function fetchRequest():Promise<void> {
+ 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({ perms: { net: true} }, async function fetchPostBodyString():Promise<void> {
+ 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({ perms: { net: true} }, async function fetchPostBodyTypedArray():Promise<void> {
+ 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(
+ {
+ // TODO: leaking resources
+ skip: true,
+ perms: { net: true }
+ },
+ async function fetchWithManualRedirection(): Promise<void> {
+ 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(
+ {
+ // TODO: leaking resources
+ skip: true,
+ perms: { net: true }
+ },
+ async function fetchWithErrorRedirection(): Promise<void> {
+ 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 response = new Response(
+ "example.com/beforeredirect",
+ 200,
+ "OK",
+ [["This-Should", "Disappear"]],
+ -1,
+ false,
+ null
+ );
+ 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
new file mode 100644
index 000000000..1a7a5f88b
--- /dev/null
+++ b/cli/js/tests/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/js/tests/files_test.ts b/cli/js/tests/files_test.ts
new file mode 100644
index 000000000..49fecabe0
--- /dev/null
+++ b/cli/js/tests/files_test.ts
@@ -0,0 +1,446 @@
+// 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(Deno.stdout, file);
+ const fileSize = Deno.statSync(filename).len;
+ assertEquals(bytesWritten, fileSize);
+ console.log("bytes written", bytesWritten);
+ file.close();
+});
+
+unitTest(
+ { perms: { read: true } },
+ async function filesToAsyncIterator(): Promise<void> {
+ const filename = "cli/tests/hello.txt";
+ const file = await Deno.open(filename);
+
+ let totalSize = 0;
+ for await (const buf of Deno.toAsyncIterator(file)) {
+ totalSize += buf.byteLength;
+ }
+
+ assertEquals(totalSize, 12);
+ file.close();
+ }
+);
+
+unitTest(async function readerToAsyncIterator(): Promise<void> {
+ // ref: https://github.com/denoland/deno/issues/2330
+ const encoder = new TextEncoder();
+
+ class TestReader implements Deno.Reader {
+ private offset = 0;
+ private buf = new Uint8Array(encoder.encode(this.s));
+
+ constructor(private readonly s: string) {}
+
+ async read(p: Uint8Array): Promise<number | Deno.EOF> {
+ 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 Deno.EOF;
+ }
+
+ return n;
+ }
+ }
+
+ const reader = new TestReader("hello world!");
+
+ let totalSize = 0;
+ for await (const buf of Deno.toAsyncIterator(reader)) {
+ totalSize += buf.byteLength;
+ }
+
+ assertEquals(totalSize, 12);
+});
+
+unitTest(
+ { perms: { write: false } },
+ async function writePermFailure(): Promise<void> {
+ const filename = "tests/hello.txt";
+ const writeModes: Deno.OpenMode[] = ["w", "a", "x"];
+ for (const mode of writeModes) {
+ let err;
+ try {
+ await Deno.open(filename, mode);
+ } catch (e) {
+ err = e;
+ }
+ assert(!!err);
+ assert(err instanceof Deno.errors.PermissionDenied);
+ assertEquals(err.name, "PermissionDenied");
+ }
+ }
+);
+
+unitTest(async function openOptions(): Promise<void> {
+ 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", "r");
+ await Deno.open("cli/tests/fixture.json", "r");
+ } catch (e) {
+ caughtError = true;
+ assert(e instanceof Deno.errors.PermissionDenied);
+ }
+ assert(caughtError);
+});
+
+unitTest(
+ { perms: { write: true } },
+ async function writeNullBufferFailure(): Promise<void> {
+ 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<void> {
+ const tempDir = Deno.makeTempDirSync();
+ const filename = tempDir + "hello.txt";
+ const file = await Deno.open(filename, "w+");
+
+ // 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<void> {
+ const filename = "tests/hello.txt";
+ const writeModes: Deno.OpenMode[] = ["r+", "w+", "a+", "x+"];
+ for (const mode of writeModes) {
+ let err;
+ try {
+ await Deno.open(filename, mode);
+ } 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<void> {
+ 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.len === 0);
+ const enc = new TextEncoder();
+ const data = enc.encode("Hello");
+ await f.write(data);
+ fileInfo = Deno.statSync(filename);
+ assert(fileInfo.len === 5);
+ f.close();
+
+ // TODO: test different modes
+ await Deno.remove(tempDir, { recursive: true });
+ }
+);
+
+unitTest(
+ { perms: { read: true, write: true } },
+ async function openModeWrite(): Promise<void> {
+ 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, "w");
+ // assert file was created
+ let fileInfo = Deno.statSync(filename);
+ assert(fileInfo.isFile());
+ assertEquals(fileInfo.len, 0);
+ // write some data
+ await file.write(data);
+ fileInfo = Deno.statSync(filename);
+ assertEquals(fileInfo.len, 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, "w");
+ file.close();
+ const fileSize = Deno.statSync(filename).len;
+ assertEquals(fileSize, 0);
+ await Deno.remove(tempDir, { recursive: true });
+ }
+);
+
+unitTest(
+ { perms: { read: true, write: true } },
+ async function openModeWriteRead(): Promise<void> {
+ 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, "w+");
+ const seekPosition = 0;
+ // assert file was created
+ let fileInfo = Deno.statSync(filename);
+ assert(fileInfo.isFile());
+ assertEquals(fileInfo.len, 0);
+ // write some data
+ await file.write(data);
+ fileInfo = Deno.statSync(filename);
+ assertEquals(fileInfo.len, 13);
+
+ const buf = new Uint8Array(20);
+ // seeking from beginning of a file
+ const cursorPosition = await file.seek(
+ seekPosition,
+ Deno.SeekMode.SEEK_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<void> {
+ 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.SEEK_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.SEEK_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.SEEK_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.SEEK_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<void> {
+ 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.SEEK_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.SEEK_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<void> {
+ 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
new file mode 100644
index 000000000..9b218547c
--- /dev/null
+++ b/cli/js/tests/form_data_test.ts
@@ -0,0 +1,189 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert, assertEquals } from "./test_util.ts";
+
+unitTest(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 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);
+ assertEquals(
+ errMsg,
+ `FormData.${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);
+ assertEquals(
+ errMsg,
+ `FormData.${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);
+ assertEquals(
+ errMsg,
+ `FormData.${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
new file mode 100644
index 000000000..9831ef160
--- /dev/null
+++ b/cli/js/tests/format_error_test.ts
@@ -0,0 +1,37 @@
+// 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 TypeError);
+ thrown = true;
+ }
+ assert(thrown);
+});
+
+if (import.meta.main) {
+ Deno.runTests();
+}
diff --git a/cli/js/tests/fs_events_test.ts b/cli/js/tests/fs_events_test.ts
new file mode 100644
index 000000000..b1697971a
--- /dev/null
+++ b/cli/js/tests/fs_events_test.ts
@@ -0,0 +1,51 @@
+// 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 fsEventsPermissions() {
+ let thrown = false;
+ try {
+ Deno.fsEvents(".");
+ } catch (err) {
+ assert(err instanceof Deno.errors.PermissionDenied);
+ thrown = true;
+ }
+ assert(thrown);
+});
+
+async function getTwoEvents(
+ iter: AsyncIterableIterator<Deno.FsEvent>
+): Promise<Deno.FsEvent[]> {
+ 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 fsEventsBasic(): Promise<void> {
+ const testDir = await Deno.makeTempDir();
+ const iter = Deno.fsEvents(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
new file mode 100644
index 000000000..76fa732ea
--- /dev/null
+++ b/cli/js/tests/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/js/tests/globals_test.ts b/cli/js/tests/globals_test.ts
new file mode 100644
index 000000000..aa8b4f46e
--- /dev/null
+++ b/cli/js/tests/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<void> {
+ 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
new file mode 100644
index 000000000..fcb5385a5
--- /dev/null
+++ b/cli/js/tests/headers_test.ts
@@ -0,0 +1,355 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert, assertEquals } from "./test_util.ts";
+const {
+ stringifyArgs
+ // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol
+} = Deno[Deno.symbols.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<string, string> = {
+ 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<string, string> = {
+ 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);
+ assertEquals(
+ errMsg,
+ `Headers.${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);
+ assertEquals(
+ errMsg,
+ `Headers.${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);
+ assertEquals(
+ errMsg,
+ `Headers.${method} requires at least 2 arguments, but only 1 present`
+ );
+ });
+});
+
+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
new file mode 100644
index 000000000..fb712707c
--- /dev/null
+++ b/cli/js/tests/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.symbols.internal];
+ assert(!!stringifyArgs);
+});
diff --git a/cli/js/tests/link_test.ts b/cli/js/tests/link_test.ts
new file mode 100644
index 000000000..e9f72ebef
--- /dev/null
+++ b/cli/js/tests/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<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.
+ 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/location_test.ts b/cli/js/tests/location_test.ts
new file mode 100644
index 000000000..78ecb55b3
--- /dev/null
+++ b/cli/js/tests/location_test.ts
@@ -0,0 +1,7 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert } from "./test_util.ts";
+
+unitTest(function locationBasic(): void {
+ // location example: file:///Users/rld/src/deno/js/unit_tests.ts
+ assert(window.location.toString().endsWith("unit_tests.ts"));
+});
diff --git a/cli/js/tests/make_temp_test.ts b/cli/js/tests/make_temp_test.ts
new file mode 100644
index 000000000..9804a7043
--- /dev/null
+++ b/cli/js/tests/make_temp_test.ts
@@ -0,0 +1,134 @@
+// 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(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<void> {
+ 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: { 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(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<void> {
+ 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);
+ }
+);
diff --git a/cli/js/tests/metrics_test.ts b/cli/js/tests/metrics_test.ts
new file mode 100644
index 000000000..9b7d83887
--- /dev/null
+++ b/cli/js/tests/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<void> {
+ 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<void> {
+ 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
new file mode 100644
index 000000000..2921177eb
--- /dev/null
+++ b/cli/js/tests/mkdir_test.ts
@@ -0,0 +1,76 @@
+// 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 mkdirSyncSuccess(): void {
+ const path = Deno.makeTempDirSync() + "/dir";
+ Deno.mkdirSync(path);
+ const pathInfo = Deno.statSync(path);
+ assert(pathInfo.isDirectory());
+ }
+);
+
+unitTest(
+ { perms: { read: true, write: true } },
+ function mkdirSyncMode(): void {
+ const path = Deno.makeTempDirSync() + "/dir";
+ Deno.mkdirSync(path, { mode: 0o755 }); // no perm for x
+ const pathInfo = Deno.statSync(path);
+ if (pathInfo.mode !== null) {
+ // Skip windows
+ assertEquals(pathInfo.mode & 0o777, 0o755);
+ }
+ }
+);
+
+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<void> {
+ const path = Deno.makeTempDirSync() + "/dir";
+ await Deno.mkdir(path);
+ const pathInfo = Deno.statSync(path);
+ assert(pathInfo.isDirectory());
+ }
+);
+
+unitTest({ perms: { write: true } }, function mkdirErrIfExists(): void {
+ let err;
+ try {
+ Deno.mkdirSync(".");
+ } 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 });
+ const pathInfo = Deno.statSync(path);
+ assert(pathInfo.isDirectory());
+ }
+);
+
+unitTest(
+ { perms: { read: true, write: true } },
+ async function mkdirRecursive(): Promise<void> {
+ const path = Deno.makeTempDirSync() + "/nested/directory";
+ await Deno.mkdir(path, { recursive: true });
+ const pathInfo = Deno.statSync(path);
+ assert(pathInfo.isDirectory());
+ }
+);
diff --git a/cli/js/tests/net_test.ts b/cli/js/tests/net_test.ts
new file mode 100644
index 000000000..46543acdf
--- /dev/null
+++ b/cli/js/tests/net_test.ts
@@ -0,0 +1,307 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert, assertEquals } from "./test_util.ts";
+
+unitTest({ perms: { net: true } }, function netTcpListenClose(): void {
+ const listener = Deno.listen({ hostname: "127.0.0.1", port: 4500 });
+ assertEquals(listener.addr.transport, "tcp");
+ assertEquals(listener.addr.hostname, "127.0.0.1");
+ assertEquals(listener.addr.port, 4500);
+ listener.close();
+});
+
+unitTest(
+ {
+ perms: { net: true },
+ // TODO:
+ skip: Deno.build.os === "win"
+ },
+ function netUdpListenClose(): void {
+ const socket = Deno.listen({
+ hostname: "127.0.0.1",
+ port: 4500,
+ transport: "udp"
+ });
+ assertEquals(socket.addr.transport, "udp");
+ assertEquals(socket.addr.hostname, "127.0.0.1");
+ assertEquals(socket.addr.port, 4500);
+ socket.close();
+ }
+);
+
+unitTest(
+ {
+ perms: { net: true }
+ },
+ async function netTcpCloseWhileAccept(): Promise<void> {
+ 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(
+ { perms: { net: true } },
+ async function netTcpConcurrentAccept(): Promise<void> {
+ 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);
+ }
+);
+
+unitTest({ perms: { net: true } }, async function netTcpDialListen(): Promise<
+ void
+> {
+ const listener = Deno.listen({ port: 4500 });
+ listener.accept().then(
+ async (conn): Promise<void> => {
+ assert(conn.remoteAddr != null);
+ assertEquals(conn.localAddr.hostname, "127.0.0.1");
+ assertEquals(conn.localAddr.port, 4500);
+ await conn.write(new Uint8Array([1, 2, 3]));
+ conn.close();
+ }
+ );
+ const conn = await Deno.connect({ hostname: "127.0.0.1", port: 4500 });
+ assertEquals(conn.remoteAddr.hostname, "127.0.0.1");
+ assertEquals(conn.remoteAddr.port, 4500);
+ 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 !== Deno.EOF);
+
+ const readResult2 = await conn.read(buf);
+ assertEquals(Deno.EOF, readResult2);
+
+ listener.close();
+ conn.close();
+});
+
+unitTest(
+ { skip: Deno.build.os === "win", perms: { net: true } },
+ async function netUdpSendReceive(): Promise<void> {
+ const alice = Deno.listen({ port: 4500, transport: "udp" });
+ assertEquals(alice.addr.port, 4500);
+ assertEquals(alice.addr.hostname, "0.0.0.0");
+ assertEquals(alice.addr.transport, "udp");
+
+ const bob = Deno.listen({ port: 4501, transport: "udp" });
+ assertEquals(bob.addr.port, 4501);
+ assertEquals(bob.addr.hostname, "0.0.0.0");
+ assertEquals(bob.addr.transport, "udp");
+
+ const sent = new Uint8Array([1, 2, 3]);
+ await alice.send(sent, bob.addr);
+
+ const [recvd, remote] = await bob.receive();
+ assertEquals(remote.port, 4500);
+ 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 netTcpListenCloseWhileIterating(): Promise<void> {
+ 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(
+ { skip: Deno.build.os === "win", perms: { net: true } },
+ async function netUdpListenCloseWhileIterating(): Promise<void> {
+ const socket = Deno.listen({ 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 });
+ }
+);
+
+/* TODO(ry) Re-enable this test.
+unitTest({ perms: { net: true } }, async function netListenAsyncIterator(): Promise<void> {
+ const listener = Deno.listen(":4500");
+ const runAsyncIterator = async (): Promise<void> => {
+ for await (let conn of listener) {
+ await conn.write(new Uint8Array([1, 2, 3]));
+ conn.close();
+ }
+ };
+ runAsyncIterator();
+ const conn = await Deno.connect("127.0.0.1:4500");
+ 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 !== Deno.EOF);
+
+ const readResult2 = await conn.read(buf);
+ assertEquals(Deno.EOF, readResult2);
+
+ listener.close();
+ conn.close();
+});
+ */
+
+/* TODO Fix broken test.
+unitTest({ perms: { net: true } }, async function netCloseReadSuccess() {
+ const addr = "127.0.0.1:4500";
+ const listener = Deno.listen(addr);
+ const closeDeferred = deferred();
+ const closeReadDeferred = deferred();
+ listener.accept().then(async conn => {
+ await closeReadDeferred.promise;
+ await conn.write(new Uint8Array([1, 2, 3]));
+ const buf = new Uint8Array(1024);
+ const readResult = await conn.read(buf);
+ assertEquals(3, readResult);
+ assertEquals(4, buf[0]);
+ assertEquals(5, buf[1]);
+ assertEquals(6, buf[2]);
+ conn.close();
+ closeDeferred.resolve();
+ });
+ const conn = await Deno.connect(addr);
+ conn.closeRead(); // closing read
+ closeReadDeferred.resolve();
+ const buf = new Uint8Array(1024);
+ const readResult = await conn.read(buf);
+ assertEquals(Deno.EOF, readResult); // with immediate EOF
+ // Ensure closeRead does not impact write
+ await conn.write(new Uint8Array([4, 5, 6]));
+ await closeDeferred.promise;
+ listener.close();
+ conn.close();
+});
+*/
+
+/* TODO Fix broken test.
+unitTest({ perms: { net: true } }, async function netDoubleCloseRead() {
+ const addr = "127.0.0.1:4500";
+ const listener = Deno.listen(addr);
+ const closeDeferred = deferred();
+ listener.accept().then(async conn => {
+ await conn.write(new Uint8Array([1, 2, 3]));
+ await closeDeferred.promise;
+ conn.close();
+ });
+ const conn = await Deno.connect(addr);
+ conn.closeRead(); // closing read
+ let err;
+ try {
+ // Duplicated close should throw error
+ conn.closeRead();
+ } catch (e) {
+ err = e;
+ }
+ assert(!!err);
+ assert(err instanceof Deno.errors.NotConnected);
+ closeDeferred.resolve();
+ listener.close();
+ conn.close();
+});
+*/
+
+/* TODO Fix broken test.
+unitTest({ perms: { net: true } }, async function netCloseWriteSuccess() {
+ const addr = "127.0.0.1:4500";
+ const listener = Deno.listen(addr);
+ const closeDeferred = deferred();
+ listener.accept().then(async conn => {
+ await conn.write(new Uint8Array([1, 2, 3]));
+ await closeDeferred.promise;
+ 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();
+});
+*/
+
+/* TODO Fix broken test.
+unitTest({ perms: { net: true } }, async function netDoubleCloseWrite() {
+ const addr = "127.0.0.1:4500";
+ const listener = Deno.listen(addr);
+ const closeDeferred = deferred();
+ listener.accept().then(async conn => {
+ await closeDeferred.promise;
+ 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();
+});
+*/
diff --git a/cli/js/tests/os_test.ts b/cli/js/tests/os_test.ts
new file mode 100644
index 000000000..0c851be51
--- /dev/null
+++ b/cli/js/tests/os_test.ts
@@ -0,0 +1,337 @@
+// 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 {
+ const env = Deno.env();
+ assert(env !== null);
+ // eslint-disable-next-line @typescript-eslint/camelcase
+ env.test_var = "Hello World";
+ const newEnv = Deno.env();
+ assertEquals(env.test_var, newEnv.test_var);
+ assertEquals(Deno.env("test_var"), env.test_var);
+});
+
+unitTest({ perms: { env: true } }, function envNotFound(): void {
+ const r = Deno.env("env_var_does_not_exist!");
+ assertEquals(r, undefined);
+});
+
+unitTest(function envPermissionDenied1(): void {
+ let err;
+ try {
+ Deno.env();
+ } 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("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(
+ { skip: Deno.build.os !== "win", perms: { 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<string, string>,
+ expectedEnv: Record<string, string>
+ ): Promise<void> => {
+ const src = `
+ console.log(
+ ${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env(k))
+ )`;
+ const proc = Deno.run({
+ args: [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("path"), Deno.env("PATH"));
+ assertEquals(Deno.env("Path"), Deno.env("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 = "mac" | "win" | "linux";
+
+ interface Runtime {
+ os: supportOS;
+ shouldHaveValue: boolean;
+ }
+
+ interface Scenes {
+ kind: Deno.DirKind;
+ runtime: Runtime[];
+ }
+
+ const scenes: Scenes[] = [
+ {
+ kind: "config",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: true }
+ ]
+ },
+ {
+ kind: "cache",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: true }
+ ]
+ },
+ {
+ kind: "executable",
+ runtime: [
+ { os: "mac", shouldHaveValue: false },
+ { os: "win", shouldHaveValue: false },
+ { os: "linux", shouldHaveValue: true }
+ ]
+ },
+ {
+ kind: "data",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: true }
+ ]
+ },
+ {
+ kind: "data_local",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: true }
+ ]
+ },
+ {
+ kind: "audio",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: false }
+ ]
+ },
+ {
+ kind: "desktop",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: false }
+ ]
+ },
+ {
+ kind: "document",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: false }
+ ]
+ },
+ {
+ kind: "download",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: false }
+ ]
+ },
+ {
+ kind: "font",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: false },
+ { os: "linux", shouldHaveValue: true }
+ ]
+ },
+ {
+ kind: "picture",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: false }
+ ]
+ },
+ {
+ kind: "public",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: false }
+ ]
+ },
+ {
+ kind: "template",
+ runtime: [
+ { os: "mac", shouldHaveValue: false },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: false }
+ ]
+ },
+ {
+ kind: "tmp",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", shouldHaveValue: true },
+ { os: "linux", shouldHaveValue: true }
+ ]
+ },
+ {
+ kind: "video",
+ runtime: [
+ { os: "mac", shouldHaveValue: true },
+ { os: "win", 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: { env: true } }, function execPath(): void {
+ assertNotEquals(Deno.execPath(), "");
+});
+
+unitTest({ perms: { env: 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
new file mode 100644
index 000000000..89b7cad8b
--- /dev/null
+++ b/cli/js/tests/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/js/tests/permissions_test.ts b/cli/js/tests/permissions_test.ts
new file mode 100644
index 000000000..7528768a1
--- /dev/null
+++ b/cli/js/tests/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<void> {
+ 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<void> {
+ 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
new file mode 100644
index 000000000..2411b5134
--- /dev/null
+++ b/cli/js/tests/process_test.ts
@@ -0,0 +1,383 @@
+// 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 } = Deno;
+
+unitTest(function runPermissions(): void {
+ let caughtError = false;
+ try {
+ Deno.run({ args: ["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<void> {
+ const p = run({
+ args: ["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<void> {
+ const p = run({
+ args: ["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.
+ skip: Deno.build.os === "win",
+ perms: { run: true }
+ },
+ async function runCommandFailedWithSignal(): Promise<void> {
+ const p = run({
+ args: ["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({ args: ["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<void> {
+ 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
+`;
+
+ Deno.writeFileSync(`${cwd}/${pyProgramFile}.py`, enc.encode(pyProgram));
+ const p = run({
+ cwd,
+ args: ["python", `${pyProgramFile}.py`]
+ });
+
+ // Write the expected exit code *after* starting python.
+ // This is how we verify that `run()` is actually asynchronous.
+ const code = 84;
+ Deno.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({
+ args: ["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({
+ args: ["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 === Deno.EOF) {
+ throw new Error("p.stdout.read(...) should not be EOF");
+ }
+ assertEquals(r, 5);
+ const s = new TextDecoder().decode(data.subarray(0, r));
+ assertEquals(s, "hello");
+ r = await p.stdout!.read(data);
+ assertEquals(r, Deno.EOF);
+ 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({
+ args: ["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 === Deno.EOF) {
+ throw new Error("p.stderr.read should not return EOF here");
+ }
+ assertEquals(r, 5);
+ const s = new TextDecoder().decode(data.subarray(0, r));
+ assertEquals(s, "hello");
+ r = await p.stderr!.read(data);
+ assertEquals(r, Deno.EOF);
+ 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<void> {
+ const p = run({
+ args: ["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({
+ args: ["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<void> {
+ const tempDir = await makeTempDir();
+ const fileName = tempDir + "/redirected_stdio.txt";
+ const file = await open(fileName, "w");
+
+ const p = run({
+ args: [
+ "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<void> {
+ 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, "r");
+
+ const p = run({
+ args: ["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<void> {
+ const p = run({
+ args: [
+ "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<void> {
+ const p = run({
+ args: [
+ "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, Deno.EOF);
+ p.stderr!.close();
+});
+
+unitTest(function signalNumbers(): void {
+ if (Deno.build.os === "mac") {
+ assertEquals(Deno.Signal.SIGSTOP, 17);
+ } else if (Deno.build.os === "linux") {
+ assertEquals(Deno.Signal.SIGSTOP, 19);
+ }
+});
+
+// Ignore signal tests on windows for now...
+if (Deno.build.os !== "win") {
+ 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.
+ Deno.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<
+ void
+ > {
+ const p = run({
+ args: ["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 } }, async function killFailed(): Promise<
+ void
+ > {
+ const p = run({
+ args: ["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
new file mode 100644
index 000000000..95936c645
--- /dev/null
+++ b/cli/js/tests/read_dir_test.ts
@@ -0,0 +1,86 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert, assertEquals } from "./test_util.ts";
+
+type FileInfo = Deno.FileInfo;
+
+function assertSameContent(files: FileInfo[]): void {
+ let counter = 0;
+
+ for (const file of files) {
+ if (file.name === "subdir") {
+ assert(file.isDirectory());
+ counter++;
+ }
+
+ if (file.name === "002_hello.ts") {
+ assertEquals(file.mode!, Deno.statSync(`cli/tests/${file.name}`).mode!);
+ counter++;
+ }
+ }
+
+ assertEquals(counter, 2);
+}
+
+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 = await Deno.readdir("cli/tests/");
+ assertSameContent(files);
+});
+
+unitTest({ perms: { read: false } }, async function readdirPerm(): Promise<
+ void
+> {
+ let caughtError = false;
+ try {
+ await Deno.readdir("tests/");
+ } 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
new file mode 100644
index 000000000..1b709b1f4
--- /dev/null
+++ b/cli/js/tests/read_file_test.ts
@@ -0,0 +1,59 @@
+// 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);
+});
diff --git a/cli/js/tests/read_link_test.ts b/cli/js/tests/read_link_test.ts
new file mode 100644
index 000000000..409f37110
--- /dev/null
+++ b/cli/js/tests/read_link_test.ts
@@ -0,0 +1,75 @@
+// 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 !== "win") {
+ Deno.symlinkSync(target, symlink);
+ const targetPath = Deno.readlinkSync(symlink);
+ assertEquals(targetPath, target);
+ }
+ }
+);
+
+unitTest({ perms: { read: false } }, async function readlinkSyncPerm(): Promise<
+ 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<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 !== "win") {
+ 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/realpath_test.ts b/cli/js/tests/realpath_test.ts
new file mode 100644
index 000000000..cda7ddaf1
--- /dev/null
+++ b/cli/js/tests/realpath_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 !== "win") {
+ assert(realPath.startsWith("/"));
+ } else {
+ assert(/^[A-Z]/.test(realPath));
+ }
+ assert(realPath.endsWith(incompletePath));
+});
+
+unitTest(
+ {
+ skip: Deno.build.os === "win",
+ 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 !== "win") {
+ assert(realPath.startsWith("/"));
+ } else {
+ assert(/^[A-Z]/.test(realPath));
+ }
+ assert(realPath.endsWith(incompletePath));
+});
+
+unitTest(
+ {
+ skip: Deno.build.os === "win",
+ perms: { read: true, write: true }
+ },
+ async function realpathSymlink(): Promise<void> {
+ 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
new file mode 100644
index 000000000..fac5ba303
--- /dev/null
+++ b/cli/js/tests/remove_test.ts
@@ -0,0 +1,481 @@
+// 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";
+ // TODO(#3832): Remove "Not Implemented" error checking when symlink creation is implemented for Windows
+ let errOnWindows;
+ try {
+ Deno.symlinkSync("unexistent_file", danglingSymlinkPath);
+ } catch (err) {
+ errOnWindows = err;
+ }
+ if (Deno.build.os === "win") {
+ assertEquals(errOnWindows.message, "Not implemented");
+ } else {
+ 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 });
+ // TODO(#3832): Remove "Not Implemented" error checking when symlink creation is implemented for Windows
+ let errOnWindows;
+ try {
+ Deno.symlinkSync(filePath, validSymlinkPath);
+ } catch (err) {
+ errOnWindows = err;
+ }
+ if (Deno.build.os === "win") {
+ assertEquals(errOnWindows.message, "Not implemented");
+ } else {
+ 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<void> {
+ // 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<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
+ 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<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
+ 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<void> {
+ const danglingSymlinkPath = Deno.makeTempDirSync() + "/dangling_symlink";
+ // TODO(#3832): Remove "Not Implemented" error checking when symlink creation is implemented for Windows
+ let errOnWindows;
+ try {
+ Deno.symlinkSync("unexistent_file", danglingSymlinkPath);
+ } catch (e) {
+ errOnWindows = e;
+ }
+ if (Deno.build.os === "win") {
+ assertEquals(errOnWindows.message, "Not implemented");
+ } else {
+ 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<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 });
+ // TODO(#3832): Remove "Not Implemented" error checking when symlink creation is implemented for Windows
+ let errOnWindows;
+ try {
+ Deno.symlinkSync(filePath, validSymlinkPath);
+ } catch (e) {
+ errOnWindows = e;
+ }
+ if (Deno.build.os === "win") {
+ assertEquals(errOnWindows.message, "Not implemented");
+ } else {
+ 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<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
+ 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<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
+ 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");
+});
diff --git a/cli/js/tests/rename_test.ts b/cli/js/tests/rename_test.ts
new file mode 100644
index 000000000..288f24bd7
--- /dev/null
+++ b/cli/js/tests/rename_test.ts
@@ -0,0 +1,84 @@
+// 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 renameSyncSuccess(): void {
+ const testDir = Deno.makeTempDirSync();
+ const oldpath = testDir + "/oldpath";
+ const newpath = testDir + "/newpath";
+ Deno.mkdirSync(oldpath);
+ Deno.renameSync(oldpath, newpath);
+ const newPathInfo = Deno.statSync(newpath);
+ assert(newPathInfo.isDirectory());
+
+ let caughtErr = false;
+ let oldPathInfo;
+
+ try {
+ oldPathInfo = Deno.statSync(oldpath);
+ } catch (e) {
+ caughtErr = true;
+ assert(e instanceof Deno.errors.NotFound);
+ }
+ assert(caughtErr);
+ assertEquals(oldPathInfo, undefined);
+ }
+);
+
+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<void> {
+ const testDir = Deno.makeTempDirSync();
+ const oldpath = testDir + "/oldpath";
+ const newpath = testDir + "/newpath";
+ Deno.mkdirSync(oldpath);
+ await Deno.rename(oldpath, newpath);
+ const newPathInfo = Deno.statSync(newpath);
+ assert(newPathInfo.isDirectory());
+
+ let caughtErr = false;
+ let oldPathInfo;
+
+ try {
+ oldPathInfo = Deno.statSync(oldpath);
+ } catch (e) {
+ caughtErr = true;
+ assert(e instanceof Deno.errors.NotFound);
+ }
+ assert(caughtErr);
+ assertEquals(oldPathInfo, undefined);
+ }
+);
diff --git a/cli/js/tests/request_test.ts b/cli/js/tests/request_test.ts
new file mode 100644
index 000000000..15e19e285
--- /dev/null
+++ b/cli/js/tests/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<void> {
+ // 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
new file mode 100644
index 000000000..84b713a6d
--- /dev/null
+++ b/cli/js/tests/resources_test.ts
@@ -0,0 +1,51 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assertEquals } from "./test_util.ts";
+
+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
+ );
+ assertEquals(
+ Object.values(res).filter((r): boolean => r === "tcpStream").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
new file mode 100644
index 000000000..e1d00c669
--- /dev/null
+++ b/cli/js/tests/signal_test.ts
@@ -0,0 +1,201 @@
+// 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<void> {
+ return new Promise((resolve: () => void, _) => {
+ setTimeout(resolve, n);
+ });
+}
+
+unitTest(
+ { skip: Deno.build.os !== "win" },
+ async function signalsNotImplemented(): Promise<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(
+ { skip: Deno.build.os === "win", perms: { run: true, net: true } },
+ async function signalStreamTest(): Promise<void> {
+ 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);
+ // Defer for a moment to allow async op from `setInterval` to resolve;
+ // for more explanation see `FIXME` in `cli/js/timers.ts::setGlobalTimeout`
+ await defer(20);
+ await resolvable;
+ }
+);
+
+unitTest(
+ { skip: Deno.build.os === "win", perms: { run: true } },
+ async function signalPromiseTest(): Promise<void> {
+ 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);
+ // Defer for a moment to allow async op from `setInterval` to resolve;
+ // for more explanation see `FIXME` in `cli/js/timers.ts::setGlobalTimeout`
+ await defer(20);
+ await resolvable;
+ }
+);
+
+unitTest(
+ { skip: Deno.build.os === "win", perms: { run: true } },
+ async function signalShorthandsTest(): Promise<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
new file mode 100644
index 000000000..0a33901b7
--- /dev/null
+++ b/cli/js/tests/stat_test.ts
@@ -0,0 +1,229 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert, assertEquals } from "./test_util.ts";
+
+// TODO Add tests for modified, accessed, and created fields once there is a way
+// to create temp files.
+unitTest({ perms: { read: true } }, async function statSyncSuccess(): Promise<
+ 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());
+});
+
+unitTest({ perms: { read: false } }, async function statSyncPerm(): Promise<
+ 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 } }, async function statSyncNotFound(): Promise<
+ 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 } }, async function lstatSyncSuccess(): Promise<
+ 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 } }, async function lstatSyncPerm(): Promise<
+ 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 } }, async function lstatSyncNotFound(): Promise<
+ 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 } }, async function statSuccess(): Promise<
+ void
+> {
+ 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());
+});
+
+unitTest({ perms: { read: false } }, async function statPerm(): Promise<void> {
+ 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<void> {
+ 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(
+ { skip: Deno.build.os !== "win", perms: { read: true, write: true } },
+ async function statNoUnixFields(): Promise<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(
+ { skip: Deno.build.os === "win", perms: { read: true, write: true } },
+ async function statUnixFields(): Promise<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/symbols_test.ts b/cli/js/tests/symbols_test.ts
new file mode 100644
index 000000000..ad05e9a70
--- /dev/null
+++ b/cli/js/tests/symbols_test.ts
@@ -0,0 +1,7 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assert } from "./test_util.ts";
+
+unitTest(function symbolsExists(): void {
+ assert("internal" in Deno.symbols);
+ assert("customInspect" in Deno.symbols);
+});
diff --git a/cli/js/tests/symlink_test.ts b/cli/js/tests/symlink_test.ts
new file mode 100644
index 000000000..221960bb0
--- /dev/null
+++ b/cli/js/tests/symlink_test.ts
@@ -0,0 +1,85 @@
+// 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);
+ let errOnWindows;
+ // Just for now, until we implement symlink for Windows.
+ try {
+ Deno.symlinkSync(oldname, newname);
+ } catch (e) {
+ errOnWindows = e;
+ }
+ if (errOnWindows) {
+ assertEquals(Deno.build.os, "win");
+ assertEquals(errOnWindows.message, "Not implemented");
+ } else {
+ 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");
+});
+
+// Just for now, until we implement symlink for Windows.
+// Symlink with type should succeed on other platforms with type ignored
+unitTest(
+ { perms: { write: true } },
+ function symlinkSyncNotImplemented(): void {
+ const testDir = Deno.makeTempDirSync();
+ const oldname = testDir + "/oldname";
+ const newname = testDir + "/newname";
+ let err;
+ try {
+ Deno.symlinkSync(oldname, newname, "dir");
+ } catch (e) {
+ err = e;
+ }
+ if (err) {
+ assertEquals(Deno.build.os, "win");
+ assertEquals(err.message, "Not implemented");
+ }
+ }
+);
+
+unitTest(
+ { perms: { read: true, write: true } },
+ async function symlinkSuccess(): Promise<void> {
+ const testDir = Deno.makeTempDirSync();
+ const oldname = testDir + "/oldname";
+ const newname = testDir + "/newname";
+ Deno.mkdirSync(oldname);
+ let errOnWindows;
+ // Just for now, until we implement symlink for Windows.
+ try {
+ await Deno.symlink(oldname, newname);
+ } catch (e) {
+ errOnWindows = e;
+ }
+ if (errOnWindows) {
+ assertEquals(errOnWindows.message, "Not implemented");
+ } else {
+ const newNameInfoLStat = Deno.lstatSync(newname);
+ const newNameInfoStat = Deno.statSync(newname);
+ assert(newNameInfoLStat.isSymlink());
+ assert(newNameInfoStat.isDirectory());
+ }
+ }
+);
diff --git a/cli/js/tests/test_util.ts b/cli/js/tests/test_util.ts
new file mode 100644
index 000000000..c8f28437d
--- /dev/null
+++ b/cli/js/tests/test_util.ts
@@ -0,0 +1,417 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+//
+// We want to test many ops in deno which have different behavior depending on
+// the permissions set. These tests can specify which permissions they expect,
+// which appends a special string like "permW1N0" to the end of the test name.
+// Here we run several copies of deno with different permissions, filtering the
+// tests by the special string. permW1N0 means allow-write but not allow-net.
+// See tools/unit_tests.py for more details.
+
+import { readLines } from "../../../std/io/bufio.ts";
+import { assert, assertEquals } from "../../../std/testing/asserts.ts";
+export {
+ assert,
+ assertThrows,
+ assertEquals,
+ assertMatch,
+ assertNotEquals,
+ assertStrictEq,
+ assertStrContains,
+ unreachable,
+ fail
+} from "../../../std/testing/asserts.ts";
+
+interface TestPermissions {
+ read?: boolean;
+ write?: boolean;
+ net?: boolean;
+ env?: boolean;
+ run?: boolean;
+ plugin?: boolean;
+ hrtime?: boolean;
+}
+
+export interface Permissions {
+ read: boolean;
+ write: boolean;
+ net: boolean;
+ env: boolean;
+ run: boolean;
+ plugin: boolean;
+ hrtime: boolean;
+}
+
+const isGranted = async (name: Deno.PermissionName): Promise<boolean> =>
+ (await Deno.permissions.query({ name })).state === "granted";
+
+async function getProcessPermissions(): Promise<Permissions> {
+ 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")
+ };
+}
+
+const processPerms = await getProcessPermissions();
+
+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<string, Permissions> = 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);
+ }
+}
+
+function normalizeTestPermissions(perms: TestPermissions): Permissions {
+ return {
+ read: !!perms.read,
+ write: !!perms.write,
+ net: !!perms.net,
+ run: !!perms.run,
+ env: !!perms.env,
+ plugin: !!perms.plugin,
+ hrtime: !!perms.hrtime
+ };
+}
+
+// Wrap `TestFunction` in additional assertion that makes sure
+// the test case does not leak async "ops" - ie. number of async
+// completed ops after the test is the same as number of dispatched
+// ops. Note that "unref" ops are ignored since in nature that are
+// optional.
+function assertOps(fn: Deno.TestFunction): Deno.TestFunction {
+ return async function asyncOpSanitizer(): Promise<void> {
+ const pre = Deno.metrics();
+ await fn();
+ const post = Deno.metrics();
+ // We're checking diff because one might spawn HTTP server in the background
+ // that will be a pending async op before test starts.
+ assertEquals(
+ post.opsDispatchedAsync - pre.opsDispatchedAsync,
+ post.opsCompletedAsync - pre.opsCompletedAsync,
+ `Test case is leaking async ops.
+ Before:
+ - dispatched: ${pre.opsDispatchedAsync}
+ - completed: ${pre.opsCompletedAsync}
+ After:
+ - dispatched: ${post.opsDispatchedAsync}
+ - completed: ${post.opsCompletedAsync}`
+ );
+ };
+}
+
+// Wrap `TestFunction` in additional assertion that makes sure
+// the test case does not "leak" resources - ie. resource table after
+// the test has exactly the same contents as before the test.
+function assertResources(fn: Deno.TestFunction): Deno.TestFunction {
+ return async function resourceSanitizer(): Promise<void> {
+ const pre = Deno.resources();
+ await fn();
+ const post = Deno.resources();
+ const msg = `Test case is leaking resources.
+ Before: ${JSON.stringify(pre, null, 2)}
+ After: ${JSON.stringify(post, null, 2)}`;
+ assertEquals(pre, post, msg);
+ };
+}
+
+interface UnitTestOptions {
+ skip?: boolean;
+ perms?: TestPermissions;
+}
+
+export function unitTest(fn: Deno.TestFunction): void;
+export function unitTest(options: UnitTestOptions, fn: Deno.TestFunction): void;
+export function unitTest(
+ optionsOrFn: UnitTestOptions | Deno.TestFunction,
+ maybeFn?: Deno.TestFunction
+): void {
+ assert(optionsOrFn, "At least one argument is required");
+
+ let options: UnitTestOptions;
+ let name: string;
+ let fn: Deno.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");
+ }
+
+ if (options.skip) {
+ return;
+ }
+
+ const normalizedPerms = normalizeTestPermissions(options.perms || {});
+ registerPermCombination(normalizedPerms);
+ if (!permissionsMatch(processPerms, normalizedPerms)) {
+ return;
+ }
+
+ const testDefinition: Deno.TestDefinition = {
+ name,
+ fn: assertResources(assertOps(fn))
+ };
+ Deno.test(testDefinition);
+}
+
+function extractNumber(re: RegExp, str: string): number | undefined {
+ const match = str.match(re);
+
+ if (match) {
+ return Number.parseInt(match[1]);
+ }
+}
+
+export async function parseUnitTestOutput(
+ reader: Deno.Reader,
+ print: boolean
+): Promise<{ actual?: number; expected?: number; resultOutput?: string }> {
+ let expected, actual, result;
+
+ for await (const line of readLines(reader)) {
+ if (!expected) {
+ // expect "running 30 tests"
+ expected = extractNumber(/running (\d+) tests/, line);
+ } else if (line.indexOf("test result:") !== -1) {
+ result = line;
+ }
+
+ if (print) {
+ console.log(line);
+ }
+ }
+
+ // Check that the number of expected tests equals what was reported at the
+ // bottom.
+ if (result) {
+ // result should be a string like this:
+ // "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; ..."
+ actual = extractNumber(/(\d+) passed/, result);
+ }
+
+ return { actual, expected, resultOutput: result };
+}
+
+export interface ResolvableMethods<T> {
+ resolve: (value?: T | PromiseLike<T>) => void;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ reject: (reason?: any) => void;
+}
+
+export type Resolvable<T> = Promise<T> & ResolvableMethods<T>;
+
+export function createResolvable<T>(): Resolvable<T> {
+ let methods: ResolvableMethods<T>;
+ const promise = new Promise<T>((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<T>;
+}
+
+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
+ }
+ )
+ );
+});
+
+unitTest(
+ { perms: { read: true } },
+ async function parsingUnitTestOutput(): Promise<void> {
+ const cwd = Deno.cwd();
+ const testDataPath = `${cwd}/tools/testdata/`;
+
+ let result;
+
+ // This is an example of a successful unit test output.
+ const f1 = await Deno.open(`${testDataPath}/unit_test_output1.txt`);
+ result = await parseUnitTestOutput(f1, false);
+ assertEquals(result.actual, 96);
+ assertEquals(result.expected, 96);
+ f1.close();
+
+ // This is an example of a silently dying unit test.
+ const f2 = await Deno.open(`${testDataPath}/unit_test_output2.txt`);
+ result = await parseUnitTestOutput(f2, false);
+ assertEquals(result.actual, undefined);
+ assertEquals(result.expected, 96);
+ f2.close();
+
+ // This is an example of compiling before successful unit tests.
+ const f3 = await Deno.open(`${testDataPath}/unit_test_output3.txt`);
+ result = await parseUnitTestOutput(f3, false);
+ assertEquals(result.actual, 96);
+ assertEquals(result.expected, 96);
+ f3.close();
+
+ // Check what happens on empty output.
+ const f = new Deno.Buffer(new TextEncoder().encode("\n\n\n"));
+ result = await parseUnitTestOutput(f, false);
+ assertEquals(result.actual, undefined);
+ assertEquals(result.expected, undefined);
+ }
+);
+
+/*
+ * 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 } },
+ async function assertAllUnitTestFilesImported(): Promise<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
new file mode 100644
index 000000000..b47eb03e2
--- /dev/null
+++ b/cli/js/tests/testing_test.ts
@@ -0,0 +1,37 @@
+// 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("", () => {});
+ },
+ Error,
+ "The name of test case can't be empty"
+ );
+ assertThrows(
+ () => {
+ Deno.test({
+ name: "",
+ fn: () => {}
+ });
+ },
+ Error,
+ "The name of test case can't be empty"
+ );
+});
+
+unitTest(function testFnCantBeAnonymous(): void {
+ assertThrows(
+ () => {
+ Deno.test(function() {});
+ },
+ Error,
+ "Test function can't be anonymous"
+ );
+});
diff --git a/cli/js/tests/text_encoding_test.ts b/cli/js/tests/text_encoding_test.ts
new file mode 100644
index 000000000..e85655feb
--- /dev/null
+++ b/cli/js/tests/text_encoding_test.ts
@@ -0,0 +1,193 @@
+// 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 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
new file mode 100644
index 000000000..429e42692
--- /dev/null
+++ b/cli/js/tests/timers_test.ts
@@ -0,0 +1,353 @@
+// 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!
+ };
+}
+
+async function waitForMs(ms: number): Promise<number> {
+ return new Promise((resolve: () => void): number => setTimeout(resolve, ms));
+}
+
+unitTest(async function timeoutSuccess(): Promise<void> {
+ 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<void> {
+ 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<void> {
+ 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<void> {
+ 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<void> {
+ // 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<void> {
+ 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);
+});
+
+unitTest(async function intervalCancelSuccess(): Promise<void> {
+ let count = 0;
+ const id = setInterval((): void => {
+ count++;
+ }, 1);
+ clearInterval(id);
+ await waitForMs(500);
+ assertEquals(count, 0);
+});
+
+unitTest(async function intervalOrdering(): Promise<void> {
+ 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(async function intervalCancelInvalidSilentFail(): Promise<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<void> {
+ const { promise, resolve } = deferred();
+ const obj = {
+ foo(): void {
+ assertEquals(this, window);
+ resolve();
+ }
+ };
+ setTimeout(obj.foo, 1);
+ await promise;
+});
+
+unitTest(async function timeoutBindThis(): Promise<void> {
+ 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(async function clearTimeoutShouldConvertToNumber(): Promise<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<void> {
+ // 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<void> {
+ 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<void> {
+ let s = "";
+ const { promise, resolve } = deferred();
+ s += "0";
+ setTimeout(() => {
+ s += "4";
+ setTimeout(() => (s += "8"));
+ Promise.resolve().then(() => {
+ setTimeout(() => {
+ s += "9";
+ resolve();
+ });
+ });
+ });
+ setTimeout(() => (s += "5"));
+ Promise.resolve().then(() => (s += "2"));
+ Promise.resolve().then(() =>
+ setTimeout(() => {
+ s += "6";
+ Promise.resolve().then(() => (s += "7"));
+ })
+ );
+ Promise.resolve().then(() => Promise.resolve().then(() => (s += "3")));
+ s += "1";
+ await promise;
+ assertEquals(s, "0123456789");
+});
diff --git a/cli/js/tests/tls_test.ts b/cli/js/tests/tls_test.ts
new file mode 100644
index 000000000..b405a94dd
--- /dev/null
+++ b/cli/js/tests/tls_test.ts
@@ -0,0 +1,214 @@
+// 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<void> {
+ 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<void> {
+ 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 } },
+ async function listenTLSNonExistentCertKeyFiles(): Promise<void> {
+ let err;
+ const options = {
+ hostname: "localhost",
+ port: 4500,
+ 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 } },
+ async function listenTLSNoReadPerm(): Promise<void> {
+ let err;
+ try {
+ Deno.listenTLS({
+ hostname: "localhost",
+ port: 4500,
+ 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 }
+ },
+ async function listenTLSEmptyKeyFile(): Promise<void> {
+ let err;
+ const options = {
+ hostname: "localhost",
+ port: 4500,
+ 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 } },
+ async function listenTLSEmptyCertFile(): Promise<void> {
+ let err;
+ const options = {
+ hostname: "localhost",
+ port: 4500,
+ 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<void> {
+ const resolvable = createResolvable();
+ const hostname = "localhost";
+ const port = 4500;
+
+ 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<void> => {
+ 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 !== Deno.EOF, `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 !== Deno.EOF);
+ 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;
+ }
+);
diff --git a/cli/js/tests/truncate_test.ts b/cli/js/tests/truncate_test.ts
new file mode 100644
index 000000000..3fd85c990
--- /dev/null
+++ b/cli/js/tests/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<string> {
+ 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<void> {
+ 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
new file mode 100644
index 000000000..116b0dfe9
--- /dev/null
+++ b/cli/js/tests/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/js/tests/unit_test_runner.ts b/cli/js/tests/unit_test_runner.ts
new file mode 100755
index 000000000..a5b7c3a48
--- /dev/null
+++ b/cli/js/tests/unit_test_runner.ts
@@ -0,0 +1,108 @@
+#!/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 {
+ permissionCombinations,
+ parseUnitTestOutput,
+ Permissions
+} from "./test_util.ts";
+
+interface TestResult {
+ perms: string;
+ output?: string;
+ result: number;
+}
+
+function permsToCliFlags(perms: Permissions): string[] {
+ return Object.keys(perms)
+ .map(key => {
+ if (!perms[key as keyof Permissions]) return "";
+
+ const cliFlag = key.replace(
+ /\.?([A-Z])/g,
+ (x, y): string => `-${y.toLowerCase()}`
+ );
+ return `--allow-${cliFlag}`;
+ })
+ .filter((e): boolean => e.length > 0);
+}
+
+function fmtPerms(perms: Permissions): string {
+ let fmt = permsToCliFlags(perms).join(" ");
+
+ if (!fmt) {
+ fmt = "<no permissions>";
+ }
+
+ return fmt;
+}
+
+async function main(): Promise<void> {
+ console.log(
+ "Discovered permission combinations for tests:",
+ permissionCombinations.size
+ );
+
+ for (const perms of permissionCombinations.values()) {
+ console.log("\t" + fmtPerms(perms));
+ }
+
+ const testResults = new Set<TestResult>();
+
+ for (const perms of permissionCombinations.values()) {
+ const permsFmt = fmtPerms(perms);
+ console.log(`Running tests for: ${permsFmt}`);
+ const cliPerms = permsToCliFlags(perms);
+ // run subsequent tests using same deno executable
+ const args = [
+ Deno.execPath(),
+ "run",
+ ...cliPerms,
+ "cli/js/tests/unit_tests.ts"
+ ];
+
+ const p = Deno.run({
+ args,
+ stdout: "piped"
+ });
+
+ const { actual, expected, resultOutput } = await parseUnitTestOutput(
+ p.stdout!,
+ true
+ );
+
+ let result = 0;
+
+ if (!actual && !expected) {
+ console.error("Bad cli/js/tests/unit_test.ts output");
+ result = 1;
+ } else if (expected !== actual) {
+ result = 1;
+ }
+
+ testResults.add({
+ perms: permsFmt,
+ output: resultOutput,
+ result
+ });
+ }
+
+ // if any run tests returned non-zero status then whole test
+ // run should fail
+ let testsFailed = false;
+
+ for (const testResult of testResults) {
+ console.log(`Summary for ${testResult.perms}`);
+ console.log(testResult.output + "\n");
+ testsFailed = testsFailed || Boolean(testResult.result);
+ }
+
+ if (testsFailed) {
+ console.error("Unit tests failed");
+ Deno.exit(1);
+ }
+
+ console.log("Unit tests passed");
+}
+
+main();
diff --git a/cli/js/tests/unit_tests.ts b/cli/js/tests/unit_tests.ts
new file mode 100644
index 000000000..cc51e6ade
--- /dev/null
+++ b/cli/js/tests/unit_tests.ts
@@ -0,0 +1,69 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+// This test is executed as part of tools/test.py
+// But it can also be run manually: ./target/debug/deno cli/js/tests/unit_tests.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 "./compiler_api_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 "./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 "./link_test.ts";
+import "./location_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 "./realpath_test.ts";
+import "./read_dir_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 "./symbols_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 "./url_test.ts";
+import "./url_search_params_test.ts";
+import "./utime_test.ts";
+import "./write_file_test.ts";
+import "./performance_test.ts";
+import "./version_test.ts";
+
+if (import.meta.main) {
+ await Deno.runTests();
+}
diff --git a/cli/js/tests/url_search_params_test.ts b/cli/js/tests/url_search_params_test.ts
new file mode 100644
index 000000000..b256395a0
--- /dev/null
+++ b/cli/js/tests/url_search_params_test.ts
@@ -0,0 +1,245 @@
+// 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");
+ }
+);
diff --git a/cli/js/tests/url_test.ts b/cli/js/tests/url_test.ts
new file mode 100644
index 000000000..46468c8ae
--- /dev/null
+++ b/cli/js/tests/url_test.ts
@@ -0,0 +1,208 @@
+// 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 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 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 emptyBasePath(): void {
+ assertEquals(new URL("", "http://example.com").href, "http://example.com/");
+});
+
+unitTest(function deletingAllParamsRemovesQuestionMarkFromURL(): void {
+ const url = new URL("http://example.com/?param1&param2");
+ 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(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");
+ });
+});
+
+if (import.meta.main) {
+ Deno.runTests();
+}
diff --git a/cli/js/tests/utime_test.ts b/cli/js/tests/utime_test.ts
new file mode 100644
index 000000000..8cd34d39a
--- /dev/null
+++ b/cli/js/tests/utime_test.ts
@@ -0,0 +1,193 @@
+// 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: any, t2: number): void {
+ assert(typeof t1 === "number");
+ assert(Math.abs(t1 - t2) < 10);
+}
+
+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.accessed, atime);
+ assertFuzzyTimestampEquals(fileInfo.modified, mtime);
+ }
+);
+
+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.accessed, atime);
+ assertFuzzyTimestampEquals(dirInfo.modified, mtime);
+ }
+);
+
+unitTest(
+ { perms: { read: true, write: true } },
+ function utimeSyncDateSuccess(): void {
+ const testDir = Deno.makeTempDirSync();
+
+ const atime = 1000;
+ const mtime = 50000;
+ Deno.utimeSync(testDir, new Date(atime * 1000), new Date(mtime * 1000));
+
+ const dirInfo = Deno.statSync(testDir);
+ assertFuzzyTimestampEquals(dirInfo.accessed, atime);
+ assertFuzzyTimestampEquals(dirInfo.modified, 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.accessed, atime);
+ assertFuzzyTimestampEquals(dirInfo.modified, mtime);
+ }
+);
+
+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<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;
+ await Deno.utime(filename, atime, mtime);
+
+ const fileInfo = Deno.statSync(filename);
+ assertFuzzyTimestampEquals(fileInfo.accessed, atime);
+ assertFuzzyTimestampEquals(fileInfo.modified, mtime);
+ }
+);
+
+unitTest(
+ { perms: { read: true, write: true } },
+ async function utimeDirectorySuccess(): Promise<void> {
+ const testDir = Deno.makeTempDirSync();
+
+ const atime = 1000;
+ const mtime = 50000;
+ await Deno.utime(testDir, atime, mtime);
+
+ const dirInfo = Deno.statSync(testDir);
+ assertFuzzyTimestampEquals(dirInfo.accessed, atime);
+ assertFuzzyTimestampEquals(dirInfo.modified, mtime);
+ }
+);
+
+unitTest(
+ { perms: { read: true, write: true } },
+ async function utimeDateSuccess(): Promise<void> {
+ const testDir = Deno.makeTempDirSync();
+
+ const atime = 1000;
+ const mtime = 50000;
+ await Deno.utime(testDir, new Date(atime * 1000), new Date(mtime * 1000));
+
+ const dirInfo = Deno.statSync(testDir);
+ assertFuzzyTimestampEquals(dirInfo.accessed, atime);
+ assertFuzzyTimestampEquals(dirInfo.modified, mtime);
+ }
+);
+
+unitTest(
+ { perms: { read: true, write: true } },
+ async function utimeNotFound(): Promise<void> {
+ 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<void> {
+ 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
new file mode 100644
index 000000000..0417b27de
--- /dev/null
+++ b/cli/js/tests/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/js/tests/write_file_test.ts b/cli/js/tests/write_file_test.ts
new file mode 100644
index 000000000..c06c5b330
--- /dev/null
+++ b/cli/js/tests/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 !== "win") {
+ 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<void> {
+ 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<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 {
+ 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<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 {
+ 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<void> {
+ if (Deno.build.os !== "win") {
+ 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<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 {
+ 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<void> {
+ 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);
+ }
+);