diff options
author | Matt Mastracci <matthew@mastracci.com> | 2024-02-10 13:22:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-10 20:22:13 +0000 |
commit | f5e46c9bf2f50d66a953fa133161fc829cecff06 (patch) | |
tree | 8faf2f5831c1c7b11d842cd9908d141082c869a5 /tests/unit/buffer_test.ts | |
parent | d2477f780630a812bfd65e3987b70c0d309385bb (diff) |
chore: move cli/tests/ -> tests/ (#22369)
This looks like a massive PR, but it's only a move from cli/tests ->
tests, and updates of relative paths for files.
This is the first step towards aggregate all of the integration test
files under tests/, which will lead to a set of integration tests that
can run without the CLI binary being built.
While we could leave these tests under `cli`, it would require us to
keep a more complex directory structure for the various test runners. In
addition, we have a lot of complexity to ignore various test files in
the `cli` project itself (cargo publish exclusion rules, autotests =
false, etc).
And finally, the `tests/` folder will eventually house the `test_ffi`,
`test_napi` and other testing code, reducing the size of the root repo
directory.
For easier review, the extremely large and noisy "move" is in the first
commit (with no changes -- just a move), while the remainder of the
changes to actual files is in the second commit.
Diffstat (limited to 'tests/unit/buffer_test.ts')
-rw-r--r-- | tests/unit/buffer_test.ts | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/tests/unit/buffer_test.ts b/tests/unit/buffer_test.ts new file mode 100644 index 000000000..9d7e51a95 --- /dev/null +++ b/tests/unit/buffer_test.ts @@ -0,0 +1,461 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file no-deprecated-deno-api + +// 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 { + assert, + assertEquals, + assertRejects, + assertThrows, +} from "./test_util.ts"; + +const MAX_SIZE = 2 ** 32 - 2; +// N controls how many iterations of certain checks are performed. +const N = 100; +let testBytes: Uint8Array | null; +let testString: string | null; + +const ignoreMaxSizeTests = true; + +function init() { + 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) { + const bytes = buf.bytes(); + assertEquals(buf.length, bytes.byteLength); + const decoder = new TextDecoder(); + const bytesStr = decoder.decode(bytes); + assertEquals(bytesStr, s); + assertEquals(buf.length, s.length); +} + +// Fill buf through n writes of byte slice fub. +// The initial contents of buf corresponds to the string s; +// the result is the final contents of buf returned as a string. +async function fillBytes( + buf: Deno.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: Deno.Buffer, + s: string, + fub: Uint8Array, +) { + check(buf, s); + while (true) { + const r = await buf.read(fub); + if (r === null) { + break; + } + s = s.slice(r); + check(buf, s); + } + check(buf, ""); +} + +function repeat(c: string, bytes: number): Uint8Array { + assertEquals(c.length, 1); + const ui8 = new Uint8Array(bytes); + ui8.fill(c.charCodeAt(0)); + return ui8; +} + +Deno.test(function bufferNewBuffer() { + init(); + assert(testBytes); + assert(testString); + const buf = new Deno.Buffer(testBytes.buffer as ArrayBuffer); + check(buf, testString); +}); + +Deno.test(async function bufferBasicOperations() { + init(); + assert(testBytes); + assert(testString); + const buf = new Deno.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(bartlomieju): buf.writeByte() + // TODO(bartlomieju): buf.readByte() + } +}); + +Deno.test(async function bufferReadEmptyAtEOF() { + // 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 Deno.Buffer(); + const zeroLengthTmp = new Uint8Array(0); + const result = await buf.read(zeroLengthTmp); + assertEquals(result, 0); +}); + +Deno.test(async function bufferLargeByteWrites() { + init(); + const buf = new Deno.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, ""); +}); + +Deno.test(async function bufferTooLargeByteWrites() { + init(); + const tmp = new Uint8Array(72); + const growLen = Number.MAX_VALUE; + const xBytes = repeat("x", 0); + const buf = new Deno.Buffer(xBytes.buffer as ArrayBuffer); + await buf.read(tmp); + + assertThrows( + () => { + buf.grow(growLen); + }, + Error, + "grown beyond the maximum size", + ); +}); + +Deno.test( + { ignore: ignoreMaxSizeTests }, + function bufferGrowWriteMaxBuffer() { + const bufSize = 16 * 1024; + const capacities = [MAX_SIZE, MAX_SIZE - 1]; + for (const capacity of capacities) { + let written = 0; + const buf = new Deno.Buffer(); + const writes = Math.floor(capacity / bufSize); + for (let i = 0; i < writes; i++) { + written += buf.writeSync(repeat("x", bufSize)); + } + + if (written < capacity) { + written += buf.writeSync(repeat("x", capacity - written)); + } + + assertEquals(written, capacity); + } + }, +); + +Deno.test( + { ignore: ignoreMaxSizeTests }, + async function bufferGrowReadCloseMaxBufferPlus1() { + const reader = new Deno.Buffer(new ArrayBuffer(MAX_SIZE + 1)); + const buf = new Deno.Buffer(); + + await assertRejects( + async () => { + await buf.readFrom(reader); + }, + Error, + "grown beyond the maximum size", + ); + }, +); + +Deno.test( + { ignore: ignoreMaxSizeTests }, + function bufferGrowReadSyncCloseMaxBufferPlus1() { + const reader = new Deno.Buffer(new ArrayBuffer(MAX_SIZE + 1)); + const buf = new Deno.Buffer(); + + assertThrows( + () => { + buf.readFromSync(reader); + }, + Error, + "grown beyond the maximum size", + ); + }, +); + +Deno.test( + { ignore: ignoreMaxSizeTests }, + function bufferGrowReadSyncCloseToMaxBuffer() { + const capacities = [MAX_SIZE, MAX_SIZE - 1]; + for (const capacity of capacities) { + const reader = new Deno.Buffer(new ArrayBuffer(capacity)); + const buf = new Deno.Buffer(); + buf.readFromSync(reader); + + assertEquals(buf.length, capacity); + } + }, +); + +Deno.test( + { ignore: ignoreMaxSizeTests }, + async function bufferGrowReadCloseToMaxBuffer() { + const capacities = [MAX_SIZE, MAX_SIZE - 1]; + for (const capacity of capacities) { + const reader = new Deno.Buffer(new ArrayBuffer(capacity)); + const buf = new Deno.Buffer(); + await buf.readFrom(reader); + assertEquals(buf.length, capacity); + } + }, +); + +Deno.test( + { ignore: ignoreMaxSizeTests }, + async function bufferReadCloseToMaxBufferWithInitialGrow() { + const capacities = [MAX_SIZE, MAX_SIZE - 1, MAX_SIZE - 512]; + for (const capacity of capacities) { + const reader = new Deno.Buffer(new ArrayBuffer(capacity)); + const buf = new Deno.Buffer(); + buf.grow(MAX_SIZE); + await buf.readFrom(reader); + assertEquals(buf.length, capacity); + } + }, +); + +Deno.test(async function bufferLargeByteReads() { + init(); + assert(testBytes); + assert(testString); + const buf = new Deno.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, ""); +}); + +Deno.test(function bufferCapWithPreallocatedSlice() { + const buf = new Deno.Buffer(new ArrayBuffer(10)); + assertEquals(buf.capacity, 10); +}); + +Deno.test(async function bufferReadFrom() { + init(); + assert(testBytes); + assert(testString); + const buf = new Deno.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 Deno.Buffer(); + await b.readFrom(buf); + const fub = new Uint8Array(testString.length); + await empty(b, s, fub); + } + await assertRejects(async function () { + await new Deno.Buffer().readFrom(null!); + }); +}); + +Deno.test(async function bufferReadFromSync() { + init(); + assert(testBytes); + assert(testString); + const buf = new Deno.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 Deno.Buffer(); + b.readFromSync(buf); + const fub = new Uint8Array(testString.length); + await empty(b, s, fub); + } + assertThrows(function () { + new Deno.Buffer().readFromSync(null!); + }); +}); + +Deno.test(async function bufferTestGrow() { + const tmp = new Uint8Array(72); + for (const startLen of [0, 100, 1000, 10000]) { + const xBytes = repeat("x", startLen); + for (const growLen of [0, 100, 1000, 10000]) { + const buf = new Deno.Buffer(xBytes.buffer as ArrayBuffer); + // If we read, this affects buf.off, which is good to test. + const nread = (await buf.read(tmp)) ?? 0; + buf.grow(growLen); + const yBytes = repeat("y", growLen); + await buf.write(yBytes); + // Check that buffer has correct data. + assertEquals( + buf.bytes().subarray(0, startLen - nread), + xBytes.subarray(nread), + ); + assertEquals( + buf.bytes().subarray(startLen - nread, startLen - nread + growLen), + yBytes, + ); + } + } +}); + +Deno.test(async function testReadAll() { + init(); + assert(testBytes); + const reader = new Deno.Buffer(testBytes.buffer as ArrayBuffer); + const actualBytes = await Deno.readAll(reader); + assertEquals(testBytes.byteLength, actualBytes.byteLength); + for (let i = 0; i < testBytes.length; ++i) { + assertEquals(testBytes[i], actualBytes[i]); + } +}); + +Deno.test(function testReadAllSync() { + init(); + assert(testBytes); + const reader = new Deno.Buffer(testBytes.buffer as ArrayBuffer); + const actualBytes = Deno.readAllSync(reader); + assertEquals(testBytes.byteLength, actualBytes.byteLength); + for (let i = 0; i < testBytes.length; ++i) { + assertEquals(testBytes[i], actualBytes[i]); + } +}); + +Deno.test(async function testWriteAll() { + init(); + assert(testBytes); + const writer = new Deno.Buffer(); + await Deno.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]); + } +}); + +Deno.test(function testWriteAllSync() { + init(); + assert(testBytes); + const writer = new Deno.Buffer(); + Deno.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]); + } +}); + +Deno.test(function testBufferBytesArrayBufferLength() { + // defaults to copy + const args = [{}, { copy: undefined }, undefined, { copy: true }]; + for (const arg of args) { + const bufSize = 64 * 1024; + const bytes = new TextEncoder().encode("a".repeat(bufSize)); + const reader = new Deno.Buffer(); + Deno.writeAllSync(reader, bytes); + + const writer = new Deno.Buffer(); + writer.readFromSync(reader); + const actualBytes = writer.bytes(arg); + + assertEquals(actualBytes.byteLength, bufSize); + assert(actualBytes.buffer !== writer.bytes(arg).buffer); + assertEquals(actualBytes.byteLength, actualBytes.buffer.byteLength); + } +}); + +Deno.test(function testBufferBytesCopyFalse() { + const bufSize = 64 * 1024; + const bytes = new TextEncoder().encode("a".repeat(bufSize)); + const reader = new Deno.Buffer(); + Deno.writeAllSync(reader, bytes); + + const writer = new Deno.Buffer(); + writer.readFromSync(reader); + const actualBytes = writer.bytes({ copy: false }); + + assertEquals(actualBytes.byteLength, bufSize); + assertEquals(actualBytes.buffer, writer.bytes({ copy: false }).buffer); + assert(actualBytes.buffer.byteLength > actualBytes.byteLength); +}); + +Deno.test(function testBufferBytesCopyFalseGrowExactBytes() { + const bufSize = 64 * 1024; + const bytes = new TextEncoder().encode("a".repeat(bufSize)); + const reader = new Deno.Buffer(); + Deno.writeAllSync(reader, bytes); + + const writer = new Deno.Buffer(); + writer.grow(bufSize); + writer.readFromSync(reader); + const actualBytes = writer.bytes({ copy: false }); + + assertEquals(actualBytes.byteLength, bufSize); + assertEquals(actualBytes.buffer.byteLength, actualBytes.byteLength); +}); + +Deno.test(function testThrowsErrorWhenBufferExceedsMaxLength() { + const kStringMaxLengthPlusOne = 536870888 + 1; + const bytes = new Uint8Array(kStringMaxLengthPlusOne); + + assertThrows( + () => { + new TextDecoder().decode(bytes); + }, + TypeError, + "buffer exceeds maximum length", + ); +}); |