fs: add flush option to appendFile() functions

This commit adds documentation and tests for the 'flush' option
of the fs.appendFile family of functions. Technically, support
was indirectly added in #50009, but this makes it more official.

Refs: https://github.com/nodejs/node/issues/49886
Refs: https://github.com/nodejs/node/pull/50009
PR-URL: https://github.com/nodejs/node/pull/50095
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
Colin Ihrig 2023-10-17 21:51:41 -04:00 committed by GitHub
parent 09f80a9f64
commit f971106072
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 135 additions and 0 deletions

View File

@ -180,6 +180,9 @@ longer be used.
<!-- YAML
added: v10.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/50095
description: The `flush` option is now supported.
- version:
- v15.14.0
- v14.18.0
@ -194,6 +197,8 @@ changes:
* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable|Stream}
* `options` {Object|string}
* `encoding` {string|null} **Default:** `'utf8'`
* `flush` {boolean} If `true`, the underlying file descriptor is flushed
prior to closing it. **Default:** `false`.
* Returns: {Promise} Fulfills with `undefined` upon success.
Alias of [`filehandle.writeFile()`][].
@ -896,6 +901,10 @@ the error raised if the file is not accessible.
<!-- YAML
added: v10.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/50095
description: The `flush` option is now supported.
-->
* `path` {string|Buffer|URL|FileHandle} filename or {FileHandle}
@ -904,6 +913,8 @@ added: v10.0.0
* `encoding` {string|null} **Default:** `'utf8'`
* `mode` {integer} **Default:** `0o666`
* `flag` {string} See [support of file system `flags`][]. **Default:** `'a'`.
* `flush` {boolean} If `true`, the underlying file descriptor is flushed
prior to closing it. **Default:** `false`.
* Returns: {Promise} Fulfills with `undefined` upon success.
Asynchronously append data to a file, creating the file if it does not yet
@ -2052,6 +2063,9 @@ the user from reading or writing to it.
<!-- YAML
added: v0.6.7
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/50095
description: The `flush` option is now supported.
- version: v18.0.0
pr-url: https://github.com/nodejs/node/pull/41678
description: Passing an invalid callback to the `callback` argument
@ -2079,6 +2093,8 @@ changes:
* `encoding` {string|null} **Default:** `'utf8'`
* `mode` {integer} **Default:** `0o666`
* `flag` {string} See [support of file system `flags`][]. **Default:** `'a'`.
* `flush` {boolean} If `true`, the underlying file descriptor is flushed
prior to closing it. **Default:** `false`.
* `callback` {Function}
* `err` {Error}
@ -5142,6 +5158,9 @@ try {
<!-- YAML
added: v0.6.7
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/50095
description: The `flush` option is now supported.
- version: v7.0.0
pr-url: https://github.com/nodejs/node/pull/7831
description: The passed `options` object will never be modified.
@ -5156,6 +5175,8 @@ changes:
* `encoding` {string|null} **Default:** `'utf8'`
* `mode` {integer} **Default:** `0o666`
* `flag` {string} See [support of file system `flags`][]. **Default:** `'a'`.
* `flush` {boolean} If `true`, the underlying file descriptor is flushed
prior to closing it. **Default:** `false`.
Synchronously append data to a file, creating the file if it does not yet
exist. `data` can be a string or a {Buffer}.

View File

@ -0,0 +1,114 @@
'use strict';
const common = require('../common');
const tmpdir = require('../common/tmpdir');
const assert = require('node:assert');
const fs = require('node:fs');
const fsp = require('node:fs/promises');
const test = require('node:test');
const data = 'foo';
let cnt = 0;
function nextFile() {
return tmpdir.resolve(`${cnt++}.out`);
}
tmpdir.refresh();
test('synchronous version', async (t) => {
await t.test('validation', (t) => {
for (const v of ['true', '', 0, 1, [], {}, Symbol()]) {
assert.throws(() => {
fs.appendFileSync(nextFile(), data, { flush: v });
}, { code: 'ERR_INVALID_ARG_TYPE' });
}
});
await t.test('performs flush', (t) => {
const spy = t.mock.method(fs, 'fsyncSync');
const file = nextFile();
fs.appendFileSync(file, data, { flush: true });
const calls = spy.mock.calls;
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].result, undefined);
assert.strictEqual(calls[0].error, undefined);
assert.strictEqual(calls[0].arguments.length, 1);
assert.strictEqual(typeof calls[0].arguments[0], 'number');
assert.strictEqual(fs.readFileSync(file, 'utf8'), data);
});
await t.test('does not perform flush', (t) => {
const spy = t.mock.method(fs, 'fsyncSync');
for (const v of [undefined, null, false]) {
const file = nextFile();
fs.appendFileSync(file, data, { flush: v });
assert.strictEqual(fs.readFileSync(file, 'utf8'), data);
}
assert.strictEqual(spy.mock.calls.length, 0);
});
});
test('callback version', async (t) => {
await t.test('validation', (t) => {
for (const v of ['true', '', 0, 1, [], {}, Symbol()]) {
assert.throws(() => {
fs.appendFileSync(nextFile(), data, { flush: v });
}, { code: 'ERR_INVALID_ARG_TYPE' });
}
});
await t.test('performs flush', (t, done) => {
const spy = t.mock.method(fs, 'fsync');
const file = nextFile();
fs.appendFile(file, data, { flush: true }, common.mustSucceed(() => {
const calls = spy.mock.calls;
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].result, undefined);
assert.strictEqual(calls[0].error, undefined);
assert.strictEqual(calls[0].arguments.length, 2);
assert.strictEqual(typeof calls[0].arguments[0], 'number');
assert.strictEqual(typeof calls[0].arguments[1], 'function');
assert.strictEqual(fs.readFileSync(file, 'utf8'), data);
done();
}));
});
await t.test('does not perform flush', (t, done) => {
const values = [undefined, null, false];
const spy = t.mock.method(fs, 'fsync');
let cnt = 0;
for (const v of values) {
const file = nextFile();
fs.appendFile(file, data, { flush: v }, common.mustSucceed(() => {
assert.strictEqual(fs.readFileSync(file, 'utf8'), data);
cnt++;
if (cnt === values.length) {
assert.strictEqual(spy.mock.calls.length, 0);
done();
}
}));
}
});
});
test('promise based version', async (t) => {
await t.test('validation', async (t) => {
for (const v of ['true', '', 0, 1, [], {}, Symbol()]) {
await assert.rejects(() => {
return fsp.appendFile(nextFile(), data, { flush: v });
}, { code: 'ERR_INVALID_ARG_TYPE' });
}
});
await t.test('success path', async (t) => {
for (const v of [undefined, null, false, true]) {
const file = nextFile();
await fsp.appendFile(file, data, { flush: v });
assert.strictEqual(await fsp.readFile(file, 'utf8'), data);
}
});
});