// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { test, testPerm, assert, assertEquals, assertStrContains } from "./test_util.ts"; test(function filesStdioFileDescriptors(): void { assertEquals(Deno.stdin.rid, 0); assertEquals(Deno.stdout.rid, 1); assertEquals(Deno.stderr.rid, 2); }); testPerm({ read: true }, async function filesCopyToStdout(): Promise { 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); }); testPerm({ read: true }, async function filesToAsyncIterator(): Promise { const filename = "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); }); test(async function readerToAsyncIterator(): Promise { // 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 { 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); }); testPerm({ write: false }, async function writePermFailure(): Promise { 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); assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); assertEquals(err.name, "PermissionDenied"); } }); test(async function openOptions(): Promise { const filename = "cli/tests/fixture.json"; let err; try { await Deno.open(filename, { write: false }); } catch (e) { err = e; } assert(!!err); assertStrContains( err.message, "OpenOptions requires at least one option to be true" ); try { await Deno.open(filename, { truncate: true, write: false }); } catch (e) { err = e; } assert(!!err); assertStrContains(err.message, "'truncate' option requires 'write' option"); try { await Deno.open(filename, { create: true, write: false }); } catch (e) { err = e; } assert(!!err); assertStrContains( err.message, "'create' or 'createNew' options require 'write' or 'append' option" ); try { await Deno.open(filename, { createNew: true, append: false }); } catch (e) { err = e; } assert(!!err); assertStrContains( err.message, "'create' or 'createNew' options require 'write' or 'append' option" ); }); testPerm({ read: false }, async function readPermFailure(): Promise { let caughtError = false; try { await Deno.open("package.json", "r"); await Deno.open("cli/tests/fixture.json", "r"); } catch (e) { caughtError = true; assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); assertEquals(e.name, "PermissionDenied"); } assert(caughtError); }); testPerm({ 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 { 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 }); }); testPerm( { write: true, read: true }, async function readNullBufferFailure(): Promise { 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 { 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 }); } ); testPerm( { write: false, read: false }, async function readWritePermFailure(): Promise { 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); assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); assertEquals(err.name, "PermissionDenied"); } } ); testPerm({ 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 }); }); testPerm({ 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 }); }); testPerm( { read: true, write: true }, async function openModeWriteRead(): Promise { const tempDir = Deno.makeTempDirSync(); const encoder = new TextEncoder(); const filename = tempDir + "hello.txt"; const data = encoder.encode("Hello world!\n"); const file = await Deno.open(filename, "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); const buf = new Uint8Array(20); await file.seek(0, Deno.SeekMode.SEEK_START); const result = await file.read(buf); assertEquals(result, 13); file.close(); await Deno.remove(tempDir, { recursive: true }); } ); testPerm({ read: true }, async function seekStart(): Promise { const filename = "tests/hello.txt"; const file = await Deno.open(filename); // Deliberately move 1 step forward await file.read(new Uint8Array(1)); // "H" // Skipping "Hello " await file.seek(6, Deno.SeekMode.SEEK_START); const buf = new Uint8Array(6); await file.read(buf); const decoded = new TextDecoder().decode(buf); assertEquals(decoded, "world!"); }); testPerm({ read: true }, function seekSyncStart(): void { const filename = "tests/hello.txt"; const file = Deno.openSync(filename); // Deliberately move 1 step forward file.readSync(new Uint8Array(1)); // "H" // Skipping "Hello " file.seekSync(6, Deno.SeekMode.SEEK_START); const buf = new Uint8Array(6); file.readSync(buf); const decoded = new TextDecoder().decode(buf); assertEquals(decoded, "world!"); }); testPerm({ read: true }, async function seekCurrent(): Promise { const filename = "tests/hello.txt"; const file = await Deno.open(filename); // Deliberately move 1 step forward await file.read(new Uint8Array(1)); // "H" // Skipping "ello " await file.seek(5, Deno.SeekMode.SEEK_CURRENT); const buf = new Uint8Array(6); await file.read(buf); const decoded = new TextDecoder().decode(buf); assertEquals(decoded, "world!"); }); testPerm({ read: true }, function seekSyncCurrent(): void { const filename = "tests/hello.txt"; const file = Deno.openSync(filename); // Deliberately move 1 step forward file.readSync(new Uint8Array(1)); // "H" // Skipping "ello " file.seekSync(5, Deno.SeekMode.SEEK_CURRENT); const buf = new Uint8Array(6); file.readSync(buf); const decoded = new TextDecoder().decode(buf); assertEquals(decoded, "world!"); }); testPerm({ read: true }, async function seekEnd(): Promise { const filename = "tests/hello.txt"; const file = await Deno.open(filename); await file.seek(-6, Deno.SeekMode.SEEK_END); const buf = new Uint8Array(6); await file.read(buf); const decoded = new TextDecoder().decode(buf); assertEquals(decoded, "world!"); }); testPerm({ read: true }, function seekSyncEnd(): void { const filename = "tests/hello.txt"; const file = Deno.openSync(filename); file.seekSync(-6, Deno.SeekMode.SEEK_END); const buf = new Uint8Array(6); file.readSync(buf); const decoded = new TextDecoder().decode(buf); assertEquals(decoded, "world!"); }); testPerm({ read: true }, async function seekMode(): Promise { const filename = "tests/hello.txt"; const file = await Deno.open(filename); let err; try { await file.seek(1, -1); } catch (e) { err = e; } assert(!!err); assertEquals(err.kind, Deno.ErrorKind.InvalidSeekMode); assertEquals(err.name, "InvalidSeekMode"); // 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"); });