From 62f79deb9e6a59013bfcb5e06973c8d53dca22f9 Mon Sep 17 00:00:00 2001 From: Jimmy Leung <43258070+hkleungai@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:04:14 +0800 Subject: [PATCH] doc,test: widen fsPromises.appendFile()'s data type, add missing tests From lib/internal/fs/promises.js, one can see fsPromises.appendFile() is simply a wrapper on fsPromises.writeFile(). Hence if writeFile() can take non-buffer data argument, so can appendFile() too. To justify the doc change, I have also restuctured the relevant tests, to enhance the test coverage. They now includes the below test cases consistently. - await do(Append|Write)Buffer(); - await do(Append|Write)BufferAndCancel(); - await do(Append|Write)String(); - await do(Append|Write)Stream(); - await do(Append|Write)StreamWithCancel(); - await do(Append|Write)Iterable(); - await do(Append|Write)InvalidIterable(); - await do(Append|Write)IterableWithEncoding(); - await do(Append|Write)BufferIterable(); - await do(Append|Write)AsyncIterable(); - await do(Append|Write)LargeIterable(); - await do(Append|Write)InvalidValues(); - await do(Append|Write)TypedArrays(); Note that test/parallel/test-fs-promises-writefile-typedarray.js is subsumed by test/parallel/test-fs-promises-file-handle-writeFile.js, and test/parallel/test-fs-promises-writefile-with-fd.js becomes a subcase in test/parallel/test-fs-promises-file-handle-writeFile.js. Refs: https://github.com/nodejs/node/pull/41677 Signed-off-by: Jimmy Leung --- doc/api/fs.md | 4 +- test/parallel/test-fs-promises-appendfile.js | 191 +++++++++++++ ...est-fs-promises-file-handle-append-file.js | 260 ++++++++++++++++-- .../test-fs-promises-file-handle-writeFile.js | 93 ++++++- .../test-fs-promises-writefile-typedarray.js | 24 -- .../test-fs-promises-writefile-with-fd.js | 35 --- test/parallel/test-fs-promises-writefile.js | 58 ++-- 7 files changed, 534 insertions(+), 131 deletions(-) create mode 100644 test/parallel/test-fs-promises-appendfile.js delete mode 100644 test/parallel/test-fs-promises-writefile-typedarray.js delete mode 100644 test/parallel/test-fs-promises-writefile-with-fd.js diff --git a/doc/api/fs.md b/doc/api/fs.md index 51e1a6f40b985a..ed7f824ed990e9 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -1241,7 +1241,7 @@ changes: --> * `path` {string|Buffer|URL|FileHandle} filename or {FileHandle} -* `data` {string|Buffer} +* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable|Stream} * `options` {Object|string} * `encoding` {string|null} **Default:** `'utf8'` * `mode` {integer} **Default:** `0o666` @@ -1251,7 +1251,7 @@ changes: * Returns: {Promise} Fulfills with `undefined` upon success. Asynchronously append data to a file, creating the file if it does not yet -exist. `data` can be a string or a {Buffer}. +`data` can be a string, a buffer, an {AsyncIterable}, or an {Iterable} object. If `options` is a string, then it specifies the `encoding`. diff --git a/test/parallel/test-fs-promises-appendfile.js b/test/parallel/test-fs-promises-appendfile.js new file mode 100644 index 00000000000000..04a3e4ac9072ec --- /dev/null +++ b/test/parallel/test-fs-promises-appendfile.js @@ -0,0 +1,191 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const fsPromises = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const tmpDir = tmpdir.path; +const { Readable } = require('stream'); + +tmpdir.refresh(); + +const buffer = Buffer.from('abc'.repeat(1000)); +const stream = Readable.from(['a', 'b', 'c']); +const stream2 = Readable.from(['ümlaut', ' ', 'sechzig']); +const iterable = { + expected: 'abc', + *[Symbol.iterator]() { + yield 'a'; + yield 'b'; + yield 'c'; + } +}; + +const veryLargeIterable = { + expected: 'dogs running'.repeat(512 * 1024), + *[Symbol.iterator]() { + yield Buffer.from('dogs running'.repeat(512 * 1024), 'utf8'); + } +}; + +function iterableWith(value) { + return { + *[Symbol.iterator]() { + yield value; + } + }; +} +const bufferIterable = { + expected: 'abc', + *[Symbol.iterator]() { + yield Buffer.from('a'); + yield Buffer.from('b'); + yield Buffer.from('c'); + } +}; +const asyncIterable = { + expected: 'abc', + async* [Symbol.asyncIterator]() { + yield 'a'; + yield 'b'; + yield 'c'; + } +}; + +let counter = 0; + +async function doAppendString() { + const string = 'x~yz'.repeat(100); + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await fsPromises.appendFile(dest, string); + const data = fs.readFileSync(dest); + const stringAsBuffer = Buffer.from(string, 'utf8'); + assert.deepStrictEqual(stringAsBuffer, data); +} + +async function doAppendBuffer() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await fsPromises.appendFile(dest, buffer); + const data = fs.readFileSync(dest); + assert.deepStrictEqual(data, buffer); +} + +async function doAppendStream() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await fsPromises.appendFile(dest, stream); + const expected = 'abc'; + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, expected); +} + +async function doAppendStreamWithCancel() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const controller = new AbortController(); + const { signal } = controller; + process.nextTick(() => controller.abort()); + await assert.rejects( + fsPromises.appendFile(dest, stream, { signal }), + { name: 'AbortError' } + ); +} + +async function doAppendIterable() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await fsPromises.appendFile(dest, iterable); + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, iterable.expected); +} + +async function doAppendInvalidIterable() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await Promise.all( + [42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) => + assert.rejects(fsPromises.appendFile(dest, iterableWith(value)), { + code: 'ERR_INVALID_ARG_TYPE', + }) + ) + ); +} + +async function doAppendIterableWithEncoding() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await fsPromises.appendFile(dest, stream2, 'latin1'); + const expected = 'ümlaut sechzig'; + const data = fs.readFileSync(dest, 'latin1'); + assert.deepStrictEqual(data, expected); +} + +async function doAppendBufferIterable() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await fsPromises.appendFile(dest, bufferIterable); + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, bufferIterable.expected); +} + +async function doAppendAsyncIterable() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await fsPromises.appendFile(dest, asyncIterable); + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, asyncIterable.expected); +} + +async function doAppendLargeIterable() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await fsPromises.appendFile(dest, veryLargeIterable); + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, veryLargeIterable.expected); +} + +async function doAppendInvalidValues() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + await Promise.all( + [42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) => + assert.rejects(fsPromises.appendFile(dest, value), { + code: 'ERR_INVALID_ARG_TYPE', + }) + ) + ); +} + +async function doAppendBufferAndCancel() { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const controller = new AbortController(); + const { signal } = controller; + process.nextTick(() => controller.abort()); + await assert.rejects( + fsPromises.appendFile(dest, buffer, { signal }), + { name: 'AbortError' } + ); +} + +async function doAppendTypedArrays() { + for (const Constructor of [Uint8Array, Uint16Array, Uint32Array]) { + const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`); + + // Use a file size larger than `kReadFileMaxChunkSize`. + const buffer = Buffer.from('012'.repeat(2 ** 14)); + + const array = new Constructor(buffer.buffer); + await fsPromises.appendFile(dest, array); + const data = await fsPromises.readFile(dest); + assert.deepStrictEqual(data, buffer); + } +} + +(async () => { + await doAppendBuffer(); + await doAppendBufferAndCancel(); + await doAppendString(); + await doAppendStream(); + await doAppendStreamWithCancel(); + await doAppendIterable(); + await doAppendInvalidIterable(); + await doAppendIterableWithEncoding(); + await doAppendBufferIterable(); + await doAppendAsyncIterable(); + await doAppendLargeIterable(); + await doAppendInvalidValues(); + await doAppendTypedArrays(); +})().then(common.mustCall()); diff --git a/test/parallel/test-fs-promises-file-handle-append-file.js b/test/parallel/test-fs-promises-file-handle-append-file.js index 42b6dbee309cec..4c476a6c36ca29 100644 --- a/test/parallel/test-fs-promises-file-handle-append-file.js +++ b/test/parallel/test-fs-promises-file-handle-append-file.js @@ -8,52 +8,256 @@ const common = require('../common'); const fs = require('fs'); const { open } = fs.promises; const path = require('path'); +const { Readable } = require('stream'); const tmpdir = require('../common/tmpdir'); const assert = require('assert'); const tmpDir = tmpdir.path; tmpdir.refresh(); -async function validateAppendBuffer() { - const filePath = path.resolve(tmpDir, 'tmp-append-file-buffer.txt'); +const stream = Readable.from(['a', 'b', 'c']); +const stream2 = Readable.from(['ümlaut', ' ', 'sechzig']); +const iterable = { + expected: 'abc', + *[Symbol.iterator]() { + yield 'a'; + yield 'b'; + yield 'c'; + } +}; +function iterableWith(value) { + return { + *[Symbol.iterator]() { + yield value; + } + }; +} +const bufferIterable = { + expected: 'abc', + *[Symbol.iterator]() { + yield Buffer.from('a'); + yield Buffer.from('b'); + yield Buffer.from('c'); + } +}; +const asyncIterable = { + expected: 'abc', + async* [Symbol.asyncIterator]() { + yield 'a'; + yield 'b'; + yield 'c'; + } +}; +const veryLargeIterable = { + expected: 'dogs running'.repeat(512 * 1024), + *[Symbol.iterator]() { + yield Buffer.from('dogs running'.repeat(512 * 1024), 'utf8'); + } +}; + +let counter = 0; + +async function doAppendBuffer() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); const fileHandle = await open(filePath, 'a'); - const buffer = Buffer.from('a&Dp'.repeat(100), 'utf8'); + try { + const buffer = Buffer.from('a&Dp'.repeat(100), 'utf8'); - await fileHandle.appendFile(buffer); - const appendedFileData = fs.readFileSync(filePath); - assert.deepStrictEqual(appendedFileData, buffer); + await fileHandle.appendFile(buffer); + const appendedFileData = fs.readFileSync(filePath); + assert.deepStrictEqual(appendedFileData, buffer); - await fileHandle.close(); + } finally { + await fileHandle.close(); + } } -async function validateAppendString() { - const filePath = path.resolve(tmpDir, 'tmp-append-file-string.txt'); +async function doAppendString() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); const fileHandle = await open(filePath, 'a'); - const string = 'x~yz'.repeat(100); + try { + const string = 'x~yz'.repeat(100); - await fileHandle.appendFile(string); - const stringAsBuffer = Buffer.from(string, 'utf8'); - const appendedFileData = fs.readFileSync(filePath); - assert.deepStrictEqual(appendedFileData, stringAsBuffer); + await fileHandle.appendFile(string); + const stringAsBuffer = Buffer.from(string, 'utf8'); + const appendedFileData = fs.readFileSync(filePath); + assert.deepStrictEqual(appendedFileData, stringAsBuffer); + + } finally { + await fileHandle.close(); + } +} - await fileHandle.close(); +async function doAppendBufferAndCancel() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + const buffer = Buffer.from('dogs running'.repeat(512 * 1024), 'utf8'); + const controller = new AbortController(); + const { signal } = controller; + process.nextTick(() => controller.abort()); + await assert.rejects(fileHandle.appendFile(buffer, { signal }), { + name: 'AbortError' + }); + } finally { + await fileHandle.close(); + } +} + +async function doAppendStream() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + await fileHandle.appendFile(stream); + const expected = 'abc'; + const data = fs.readFileSync(filePath, 'utf-8'); + assert.deepStrictEqual(data, expected); + } finally { + await fileHandle.close(); + } } -async function doAppendAndCancel() { - const filePathForHandle = path.resolve(tmpDir, 'dogs-running.txt'); - const fileHandle = await open(filePathForHandle, 'w+'); - const buffer = Buffer.from('dogs running'.repeat(512 * 1024), 'utf8'); +async function doAppendStreamWithCancel() { const controller = new AbortController(); const { signal } = controller; process.nextTick(() => controller.abort()); - await assert.rejects(fileHandle.appendFile(buffer, { signal }), { - name: 'AbortError' - }); - await fileHandle.close(); + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + await assert.rejects( + fileHandle.appendFile(stream, { signal }), + { name: 'AbortError' } + ); + } finally { + await fileHandle.close(); + } +} + +async function doAppendIterable() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + await fileHandle.appendFile(iterable); + const data = fs.readFileSync(filePath, 'utf-8'); + assert.deepStrictEqual(data, iterable.expected); + } finally { + await fileHandle.close(); + } +} + +async function doAppendInvalidIterable() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + await Promise.all( + [42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) => + assert.rejects( + fileHandle.appendFile(iterableWith(value)), + { code: 'ERR_INVALID_ARG_TYPE' } + ) + ) + ); + } finally { + await fileHandle.close(); + } +} + +async function doAppendIterableWithEncoding() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + await fileHandle.appendFile(stream2, 'latin1'); + const expected = 'ümlaut sechzig'; + const data = fs.readFileSync(filePath, 'latin1'); + assert.deepStrictEqual(data, expected); + } finally { + await fileHandle.close(); + } +} + +async function doAppendBufferIterable() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + await fileHandle.appendFile(bufferIterable); + const data = fs.readFileSync(filePath, 'utf-8'); + assert.deepStrictEqual(data, bufferIterable.expected); + } finally { + await fileHandle.close(); + } +} + +async function doAppendAsyncIterable() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + await fileHandle.appendFile(asyncIterable); + const data = fs.readFileSync(filePath, 'utf-8'); + assert.deepStrictEqual(data, asyncIterable.expected); + } finally { + await fileHandle.close(); + } +} + +async function doAppendLargeIterable() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + await fileHandle.appendFile(veryLargeIterable); + const data = fs.readFileSync(filePath, 'utf-8'); + assert.deepStrictEqual(data, veryLargeIterable.expected); + } finally { + await fileHandle.close(); + } +} + +async function doAppendInvalidValues() { + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + try { + await Promise.all( + [42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) => + assert.rejects( + fileHandle.appendFile(value), + { code: 'ERR_INVALID_ARG_TYPE' } + ) + ) + ); + } finally { + await fileHandle.close(); + } +} + +async function doAppendTypedArrays() { + for (const Constructor of [Uint8Array, Uint16Array, Uint32Array]) { + // Use a file size larger than `kReadFileMaxChunkSize`. + const buffer = Buffer.from('012'.repeat(2 ** 14)); + const array = new Constructor(buffer.buffer); + const filePath = path.resolve(tmpDir, `tmp-${counter++}.txt`); + const fileHandle = await open(filePath, 'a'); + + try { + await fileHandle.writeFile(array); + const data = fs.readFileSync(filePath); + assert.deepStrictEqual(data, buffer); + } finally { + await fileHandle.close(); + } + } } -Promise.all([ - validateAppendBuffer(), - validateAppendString(), - doAppendAndCancel(), -]).then(common.mustCall()); +(async () => { + await doAppendBuffer(); + await doAppendBufferAndCancel(); + await doAppendString(); + await doAppendStream(); + await doAppendStreamWithCancel(); + await doAppendIterable(); + await doAppendInvalidIterable(); + await doAppendIterableWithEncoding(); + await doAppendBufferIterable(); + await doAppendAsyncIterable(); + await doAppendLargeIterable(); + await doAppendInvalidValues(); + await doAppendTypedArrays(); +})().then(common.mustCall()); diff --git a/test/parallel/test-fs-promises-file-handle-writeFile.js b/test/parallel/test-fs-promises-file-handle-writeFile.js index 2c1a80e4f52d49..b08200d4f78c94 100644 --- a/test/parallel/test-fs-promises-file-handle-writeFile.js +++ b/test/parallel/test-fs-promises-file-handle-writeFile.js @@ -1,12 +1,8 @@ 'use strict'; const common = require('../common'); - -// The following tests validate base functionality for the fs.promises -// FileHandle.writeFile method. - const fs = require('fs'); -const { open, writeFile } = fs.promises; +const { open } = fs.promises; const path = require('path'); const { Readable } = require('stream'); const tmpdir = require('../common/tmpdir'); @@ -15,7 +11,7 @@ const tmpDir = tmpdir.path; tmpdir.refresh(); -async function validateWriteFile() { +async function doWriteBuffer() { const filePathForHandle = path.resolve(tmpDir, 'tmp-write-file2.txt'); const fileHandle = await open(filePathForHandle, 'w+'); try { @@ -29,8 +25,22 @@ async function validateWriteFile() { } } -// Signal aborted while writing file -async function doWriteAndCancel() { +async function doWriteString() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-write-string.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + try { + const string = 'x~yz'.repeat(100); + + await fileHandle.writeFile(string); + const readFileData = fs.readFileSync(filePathForHandle); + const stringAsBuffer = Buffer.from(string, 'utf8'); + assert.deepStrictEqual(stringAsBuffer, readFileData); + } finally { + await fileHandle.close(); + } +} + +async function doWriteBufferAndCancel() { const filePathForHandle = path.resolve(tmpDir, 'dogs-running.txt'); const fileHandle = await open(filePathForHandle, 'w+'); try { @@ -38,7 +48,7 @@ async function doWriteAndCancel() { const controller = new AbortController(); const { signal } = controller; process.nextTick(() => controller.abort()); - await assert.rejects(writeFile(fileHandle, buffer, { signal }), { + await assert.rejects(fileHandle.writeFile(buffer, { signal }), { name: 'AbortError' }); } finally { @@ -58,6 +68,13 @@ const iterable = { yield 'c'; } }; +const veryLargeIterable = { + expected: 'dogs running'.repeat(512 * 1024), + *[Symbol.iterator]() { + yield Buffer.from('dogs running'.repeat(512 * 1024), 'utf8'); + } +}; + function iterableWith(value) { return { *[Symbol.iterator]() { @@ -75,7 +92,7 @@ const bufferIterable = { }; const asyncIterable = { expected: 'abc', - async* [Symbol.asyncIterator]() { + async*[Symbol.asyncIterator]() { yield 'a'; yield 'b'; yield 'c'; @@ -170,6 +187,17 @@ async function doWriteAsyncIterable() { } } +async function doWriteLargeIterable() { + const fileHandle = await open(dest, 'w+'); + try { + await fileHandle.writeFile(veryLargeIterable); + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, veryLargeIterable.expected); + } finally { + await fileHandle.close(); + } +} + async function doWriteInvalidValues() { const fileHandle = await open(dest, 'w+'); try { @@ -186,9 +214,47 @@ async function doWriteInvalidValues() { } } +async function doWriteTypedArrays() { + for (const Constructor of [Uint8Array, Uint16Array, Uint32Array]) { + // Use a file size larger than `kReadFileMaxChunkSize`. + const buffer = Buffer.from('012'.repeat(2 ** 14)); + const array = new Constructor(buffer.buffer); + const fileHandle = await open(dest, 'w+'); + + try { + await fileHandle.writeFile(array); + const data = fs.readFileSync(dest); + assert.deepStrictEqual(data, buffer); + } finally { + await fileHandle.close(); + } + } +} + +async function doWriteFromCurrentPosition() { + const fileHandle = await open(dest, 'w+'); + try { + /* Write only five bytes, so that the position moves to five. */ + const buf = Buffer.from('Hello'); + const { bytesWritten } = await fileHandle.write(buf, 0, 5, null); + assert.strictEqual(bytesWritten, 5); + + /* Write some more with writeFile(). */ + await fileHandle.writeFile('World'); + + const data = fs.readFileSync(dest, 'utf-8'); + + /* New content should be written at position five, instead of zero. */ + assert.strictEqual(data, 'HelloWorld'); + } finally { + await fileHandle.close(); + } +} + (async () => { - await validateWriteFile(); - await doWriteAndCancel(); + await doWriteBuffer(); + await doWriteBufferAndCancel(); + await doWriteString(); await doWriteStream(); await doWriteStreamWithCancel(); await doWriteIterable(); @@ -196,5 +262,8 @@ async function doWriteInvalidValues() { await doWriteIterableWithEncoding(); await doWriteBufferIterable(); await doWriteAsyncIterable(); + await doWriteLargeIterable(); await doWriteInvalidValues(); + await doWriteTypedArrays(); + await doWriteFromCurrentPosition(); })().then(common.mustCall()); diff --git a/test/parallel/test-fs-promises-writefile-typedarray.js b/test/parallel/test-fs-promises-writefile-typedarray.js deleted file mode 100644 index 32d9cffa236e57..00000000000000 --- a/test/parallel/test-fs-promises-writefile-typedarray.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -const common = require('../common'); -const fs = require('fs'); -const fsPromises = fs.promises; -const path = require('path'); -const tmpdir = require('../common/tmpdir'); -const assert = require('assert'); -const tmpDir = tmpdir.path; - -tmpdir.refresh(); - -const dest = path.resolve(tmpDir, 'tmp.txt'); -// Use a file size larger than `kReadFileMaxChunkSize`. -const buffer = Buffer.from('012'.repeat(2 ** 14)); - -(async () => { - for (const Constructor of [Uint8Array, Uint16Array, Uint32Array]) { - const array = new Constructor(buffer.buffer); - await fsPromises.writeFile(dest, array); - const data = await fsPromises.readFile(dest); - assert.deepStrictEqual(data, buffer); - } -})().then(common.mustCall()); diff --git a/test/parallel/test-fs-promises-writefile-with-fd.js b/test/parallel/test-fs-promises-writefile-with-fd.js deleted file mode 100644 index 7cb9eeeca9a9f2..00000000000000 --- a/test/parallel/test-fs-promises-writefile-with-fd.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -// This test makes sure that `writeFile()` always writes from the current -// position of the file, instead of truncating the file. - -const common = require('../common'); -const assert = require('assert'); -const { readFileSync } = require('fs'); -const { open } = require('fs').promises; - -const tmpdir = require('../common/tmpdir'); -tmpdir.refresh(); - -const fn = tmpdir.resolve('test.txt'); - -async function writeFileTest() { - const handle = await open(fn, 'w'); - - /* Write only five bytes, so that the position moves to five. */ - const buf = Buffer.from('Hello'); - const { bytesWritten } = await handle.write(buf, 0, 5, null); - assert.strictEqual(bytesWritten, 5); - - /* Write some more with writeFile(). */ - await handle.writeFile('World'); - - /* New content should be written at position five, instead of zero. */ - assert.strictEqual(readFileSync(fn).toString(), 'HelloWorld'); - - await handle.close(); -} - - -writeFileTest() - .then(common.mustCall()); diff --git a/test/parallel/test-fs-promises-writefile.js b/test/parallel/test-fs-promises-writefile.js index 25df61b2b48414..da7e345a70791e 100644 --- a/test/parallel/test-fs-promises-writefile.js +++ b/test/parallel/test-fs-promises-writefile.js @@ -14,7 +14,6 @@ tmpdir.refresh(); const dest = path.resolve(tmpDir, 'tmp.txt'); const otherDest = path.resolve(tmpDir, 'tmp-2.txt'); const buffer = Buffer.from('abc'.repeat(1000)); -const buffer2 = Buffer.from('xyz'.repeat(1000)); const stream = Readable.from(['a', 'b', 'c']); const stream2 = Readable.from(['ümlaut', ' ', 'sechzig']); const iterable = { @@ -26,7 +25,7 @@ const iterable = { } }; -const veryLargeBuffer = { +const veryLargeIterable = { expected: 'dogs running'.repeat(512 * 1024), *[Symbol.iterator]() { yield Buffer.from('dogs running'.repeat(512 * 1024), 'utf8'); @@ -57,7 +56,15 @@ const asyncIterable = { } }; -async function doWrite() { +async function doWriteString() { + const string = 'x~yz'.repeat(100); + await fsPromises.writeFile(dest, string); + const data = fs.readFileSync(dest); + const stringAsBuffer = Buffer.from(string, 'utf8'); + assert.deepStrictEqual(stringAsBuffer, data); +} + +async function doWriteBuffer() { await fsPromises.writeFile(dest, buffer); const data = fs.readFileSync(dest); assert.deepStrictEqual(data, buffer); @@ -115,10 +122,10 @@ async function doWriteAsyncIterable() { assert.deepStrictEqual(data, asyncIterable.expected); } -async function doWriteAsyncLargeIterable() { - await fsPromises.writeFile(dest, veryLargeBuffer); +async function doWriteLargeIterable() { + await fsPromises.writeFile(dest, veryLargeIterable); const data = fs.readFileSync(dest, 'utf-8'); - assert.deepStrictEqual(data, veryLargeBuffer.expected); + assert.deepStrictEqual(data, veryLargeIterable.expected); } async function doWriteInvalidValues() { @@ -131,7 +138,7 @@ async function doWriteInvalidValues() { ); } -async function doWriteWithCancel() { +async function doWriteBufferAndCancel() { const controller = new AbortController(); const { signal } = controller; process.nextTick(() => controller.abort()); @@ -141,32 +148,22 @@ async function doWriteWithCancel() { ); } -async function doAppend() { - await fsPromises.appendFile(dest, buffer2, { flag: null }); - const data = fs.readFileSync(dest); - const buf = Buffer.concat([buffer, buffer2]); - assert.deepStrictEqual(buf, data); -} - -async function doRead() { - const data = await fsPromises.readFile(dest); - const buf = fs.readFileSync(dest); - assert.deepStrictEqual(buf, data); -} +async function doWriteTypedArrays() { + for (const Constructor of [Uint8Array, Uint16Array, Uint32Array]) { + // Use a file size larger than `kReadFileMaxChunkSize`. + const buffer = Buffer.from('012'.repeat(2 ** 14)); -async function doReadWithEncoding() { - const data = await fsPromises.readFile(dest, 'utf-8'); - const syncData = fs.readFileSync(dest, 'utf-8'); - assert.strictEqual(typeof data, 'string'); - assert.deepStrictEqual(data, syncData); + const array = new Constructor(buffer.buffer); + await fsPromises.writeFile(dest, array); + const data = await fsPromises.readFile(dest); + assert.deepStrictEqual(data, buffer); + } } (async () => { - await doWrite(); - await doWriteWithCancel(); - await doAppend(); - await doRead(); - await doReadWithEncoding(); + await doWriteBuffer(); + await doWriteBufferAndCancel(); + await doWriteString(); await doWriteStream(); await doWriteStreamWithCancel(); await doWriteIterable(); @@ -174,6 +171,7 @@ async function doReadWithEncoding() { await doWriteIterableWithEncoding(); await doWriteBufferIterable(); await doWriteAsyncIterable(); - await doWriteAsyncLargeIterable(); + await doWriteLargeIterable(); await doWriteInvalidValues(); + await doWriteTypedArrays(); })().then(common.mustCall());