Skip to content

Commit 62f79de

Browse files
committed
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: #41677 Signed-off-by: Jimmy Leung <mingfamily@hotmail.com.hk>
1 parent 0e2126d commit 62f79de

7 files changed

Lines changed: 534 additions & 131 deletions

doc/api/fs.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,7 @@ changes:
12411241
-->
12421242
12431243
* `path` {string|Buffer|URL|FileHandle} filename or {FileHandle}
1244-
* `data` {string|Buffer}
1244+
* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable|Stream}
12451245
* `options` {Object|string}
12461246
* `encoding` {string|null} **Default:** `'utf8'`
12471247
* `mode` {integer} **Default:** `0o666`
@@ -1251,7 +1251,7 @@ changes:
12511251
* Returns: {Promise} Fulfills with `undefined` upon success.
12521252
12531253
Asynchronously append data to a file, creating the file if it does not yet
1254-
exist. `data` can be a string or a {Buffer}.
1254+
`data` can be a string, a buffer, an {AsyncIterable}, or an {Iterable} object.
12551255
12561256
If `options` is a string, then it specifies the `encoding`.
12571257
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const fs = require('fs');
5+
const fsPromises = fs.promises;
6+
const path = require('path');
7+
const tmpdir = require('../common/tmpdir');
8+
const assert = require('assert');
9+
const tmpDir = tmpdir.path;
10+
const { Readable } = require('stream');
11+
12+
tmpdir.refresh();
13+
14+
const buffer = Buffer.from('abc'.repeat(1000));
15+
const stream = Readable.from(['a', 'b', 'c']);
16+
const stream2 = Readable.from(['ümlaut', ' ', 'sechzig']);
17+
const iterable = {
18+
expected: 'abc',
19+
*[Symbol.iterator]() {
20+
yield 'a';
21+
yield 'b';
22+
yield 'c';
23+
}
24+
};
25+
26+
const veryLargeIterable = {
27+
expected: 'dogs running'.repeat(512 * 1024),
28+
*[Symbol.iterator]() {
29+
yield Buffer.from('dogs running'.repeat(512 * 1024), 'utf8');
30+
}
31+
};
32+
33+
function iterableWith(value) {
34+
return {
35+
*[Symbol.iterator]() {
36+
yield value;
37+
}
38+
};
39+
}
40+
const bufferIterable = {
41+
expected: 'abc',
42+
*[Symbol.iterator]() {
43+
yield Buffer.from('a');
44+
yield Buffer.from('b');
45+
yield Buffer.from('c');
46+
}
47+
};
48+
const asyncIterable = {
49+
expected: 'abc',
50+
async* [Symbol.asyncIterator]() {
51+
yield 'a';
52+
yield 'b';
53+
yield 'c';
54+
}
55+
};
56+
57+
let counter = 0;
58+
59+
async function doAppendString() {
60+
const string = 'x~yz'.repeat(100);
61+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
62+
await fsPromises.appendFile(dest, string);
63+
const data = fs.readFileSync(dest);
64+
const stringAsBuffer = Buffer.from(string, 'utf8');
65+
assert.deepStrictEqual(stringAsBuffer, data);
66+
}
67+
68+
async function doAppendBuffer() {
69+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
70+
await fsPromises.appendFile(dest, buffer);
71+
const data = fs.readFileSync(dest);
72+
assert.deepStrictEqual(data, buffer);
73+
}
74+
75+
async function doAppendStream() {
76+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
77+
await fsPromises.appendFile(dest, stream);
78+
const expected = 'abc';
79+
const data = fs.readFileSync(dest, 'utf-8');
80+
assert.deepStrictEqual(data, expected);
81+
}
82+
83+
async function doAppendStreamWithCancel() {
84+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
85+
const controller = new AbortController();
86+
const { signal } = controller;
87+
process.nextTick(() => controller.abort());
88+
await assert.rejects(
89+
fsPromises.appendFile(dest, stream, { signal }),
90+
{ name: 'AbortError' }
91+
);
92+
}
93+
94+
async function doAppendIterable() {
95+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
96+
await fsPromises.appendFile(dest, iterable);
97+
const data = fs.readFileSync(dest, 'utf-8');
98+
assert.deepStrictEqual(data, iterable.expected);
99+
}
100+
101+
async function doAppendInvalidIterable() {
102+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
103+
await Promise.all(
104+
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
105+
assert.rejects(fsPromises.appendFile(dest, iterableWith(value)), {
106+
code: 'ERR_INVALID_ARG_TYPE',
107+
})
108+
)
109+
);
110+
}
111+
112+
async function doAppendIterableWithEncoding() {
113+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
114+
await fsPromises.appendFile(dest, stream2, 'latin1');
115+
const expected = 'ümlaut sechzig';
116+
const data = fs.readFileSync(dest, 'latin1');
117+
assert.deepStrictEqual(data, expected);
118+
}
119+
120+
async function doAppendBufferIterable() {
121+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
122+
await fsPromises.appendFile(dest, bufferIterable);
123+
const data = fs.readFileSync(dest, 'utf-8');
124+
assert.deepStrictEqual(data, bufferIterable.expected);
125+
}
126+
127+
async function doAppendAsyncIterable() {
128+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
129+
await fsPromises.appendFile(dest, asyncIterable);
130+
const data = fs.readFileSync(dest, 'utf-8');
131+
assert.deepStrictEqual(data, asyncIterable.expected);
132+
}
133+
134+
async function doAppendLargeIterable() {
135+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
136+
await fsPromises.appendFile(dest, veryLargeIterable);
137+
const data = fs.readFileSync(dest, 'utf-8');
138+
assert.deepStrictEqual(data, veryLargeIterable.expected);
139+
}
140+
141+
async function doAppendInvalidValues() {
142+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
143+
await Promise.all(
144+
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
145+
assert.rejects(fsPromises.appendFile(dest, value), {
146+
code: 'ERR_INVALID_ARG_TYPE',
147+
})
148+
)
149+
);
150+
}
151+
152+
async function doAppendBufferAndCancel() {
153+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
154+
const controller = new AbortController();
155+
const { signal } = controller;
156+
process.nextTick(() => controller.abort());
157+
await assert.rejects(
158+
fsPromises.appendFile(dest, buffer, { signal }),
159+
{ name: 'AbortError' }
160+
);
161+
}
162+
163+
async function doAppendTypedArrays() {
164+
for (const Constructor of [Uint8Array, Uint16Array, Uint32Array]) {
165+
const dest = path.resolve(tmpDir, `tmp-${counter++}.txt`);
166+
167+
// Use a file size larger than `kReadFileMaxChunkSize`.
168+
const buffer = Buffer.from('012'.repeat(2 ** 14));
169+
170+
const array = new Constructor(buffer.buffer);
171+
await fsPromises.appendFile(dest, array);
172+
const data = await fsPromises.readFile(dest);
173+
assert.deepStrictEqual(data, buffer);
174+
}
175+
}
176+
177+
(async () => {
178+
await doAppendBuffer();
179+
await doAppendBufferAndCancel();
180+
await doAppendString();
181+
await doAppendStream();
182+
await doAppendStreamWithCancel();
183+
await doAppendIterable();
184+
await doAppendInvalidIterable();
185+
await doAppendIterableWithEncoding();
186+
await doAppendBufferIterable();
187+
await doAppendAsyncIterable();
188+
await doAppendLargeIterable();
189+
await doAppendInvalidValues();
190+
await doAppendTypedArrays();
191+
})().then(common.mustCall());

0 commit comments

Comments
 (0)