-
Notifications
You must be signed in to change notification settings - Fork 29.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fs: make promises/write and writeSync params optional
This change allows passing objects as "named parameters" Fixes: #41666
- Loading branch information
1 parent
0c9273d
commit 334eabc
Showing
5 changed files
with
232 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
'use strict'; | ||
|
||
const common = require('../common'); | ||
|
||
// This test ensures that filehandle.write accepts "named parameters" object | ||
// and doesn't interpret objects as strings | ||
|
||
const assert = require('assert'); | ||
const fsPromises = require('fs').promises; | ||
const path = require('path'); | ||
const tmpdir = require('../common/tmpdir'); | ||
|
||
tmpdir.refresh(); | ||
|
||
const dest = path.resolve(tmpdir.path, 'tmp.txt'); | ||
const buffer = Buffer.from('zyx'); | ||
|
||
async function testInvalid(dest, expectedCode, params) { | ||
let fh; | ||
try { | ||
fh = await fsPromises.open(dest, 'w+'); | ||
await assert.rejects( | ||
async () => fh.write(params), | ||
{ code: expectedCode }); | ||
} finally { | ||
await fh?.close(); | ||
} | ||
} | ||
|
||
async function testValid(dest, params) { | ||
let fh; | ||
try { | ||
fh = await fsPromises.open(dest, 'w+'); | ||
const writeResult = await fh.write(params); | ||
const writeBufCopy = Uint8Array.prototype.slice.call(writeResult.buffer); | ||
const readResult = await fh.read(params); | ||
const readBufCopy = Uint8Array.prototype.slice.call(readResult.buffer); | ||
|
||
assert.ok(writeResult.bytesWritten >= readResult.bytesRead); | ||
if (params.length !== undefined && params.length !== null) { | ||
assert.strictEqual(writeResult.bytesWritten, params.length); | ||
} | ||
if (params.offset === undefined || params.offset === 0) { | ||
assert.deepStrictEqual(writeBufCopy, readBufCopy); | ||
} | ||
assert.deepStrictEqual(writeResult.buffer, readResult.buffer); | ||
} finally { | ||
await fh?.close(); | ||
} | ||
} | ||
|
||
(async () => { | ||
// Test if first argument is not wrongly interpreted as ArrayBufferView|string | ||
for (const badParams of [ | ||
undefined, null, true, 42, 42n, Symbol('42'), NaN, [], | ||
{}, | ||
{ buffer: 'amNotParam' }, | ||
{ string: 'amNotParam' }, | ||
{ buffer: new Uint8Array(1).buffer }, | ||
new Date(), | ||
new String('notPrimitive'), | ||
{ toString() { return 'amObject'; } }, | ||
{ [Symbol.toPrimitive]: (hint) => 'amObject' }, | ||
]) { | ||
await testInvalid(dest, 'ERR_INVALID_ARG_TYPE', badParams); | ||
} | ||
|
||
// Various invalid params | ||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', { buffer, length: 5 }); | ||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', { buffer, offset: 5 }); | ||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', { buffer, length: 1, offset: 3 }); | ||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', { buffer, length: -1 }); | ||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', { buffer, offset: -1 }); | ||
|
||
// Test compatibility with filehandle.read counterpart with reused params | ||
for (const params of [ | ||
{ buffer }, | ||
{ buffer, length: 1 }, | ||
{ buffer, position: 5 }, | ||
{ buffer, length: 1, position: 5 }, | ||
{ buffer, length: 1, position: -1, offset: 2 }, | ||
{ buffer, length: null }, | ||
{ buffer, offset: 1 }, | ||
]) { | ||
await testValid(dest, params); | ||
} | ||
})().then(common.mustCall()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
'use strict'; | ||
|
||
require('../common'); | ||
|
||
// This test ensures that fs.writeSync accepts "named parameters" object | ||
// and doesn't interpret objects as strings | ||
|
||
const assert = require('assert'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const tmpdir = require('../common/tmpdir'); | ||
|
||
tmpdir.refresh(); | ||
|
||
const dest = path.resolve(tmpdir.path, 'tmp.txt'); | ||
const buffer = Buffer.from('zyx'); | ||
|
||
function testInvalid(dest, expectedCode, ...bufferAndParams) { | ||
let fd; | ||
try { | ||
fd = fs.openSync(dest, 'w+'); | ||
assert.throws( | ||
() => fs.writeSync(fd, ...bufferAndParams), | ||
{ code: expectedCode }); | ||
} finally { | ||
if (fd != null) fs.closeSync(fd); | ||
} | ||
} | ||
|
||
function testValid(dest, buffer, params) { | ||
let fd; | ||
try { | ||
fd = fs.openSync(dest, 'w+'); | ||
const bytesWritten = fs.writeSync(fd, buffer, params); | ||
const bytesRead = fs.readSync(fd, buffer, params); | ||
|
||
assert.ok(bytesWritten >= bytesRead); | ||
if (params.length !== undefined && params.length !== null) { | ||
assert.strictEqual(bytesWritten, params.length); | ||
} | ||
} finally { | ||
if (fd != null) fs.closeSync(fd); | ||
} | ||
} | ||
|
||
{ | ||
// Test if second argument is not wrongly interpreted as string or params | ||
for (const badBuffer of [ | ||
undefined, null, true, 42, 42n, Symbol('42'), NaN, [], | ||
{}, | ||
{ buffer: 'amNotParam' }, | ||
{ string: 'amNotParam' }, | ||
{ buffer: new Uint8Array(1) }, | ||
{ buffer: new Uint8Array(1).buffer }, | ||
new Date(), | ||
new String('notPrimitive'), | ||
{ toString() { return 'amObject'; } }, | ||
{ [Symbol.toPrimitive]: (hint) => 'amObject' }, | ||
]) { | ||
testInvalid(dest, 'ERR_INVALID_ARG_TYPE', badBuffer); | ||
} | ||
|
||
// Various invalid params | ||
testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { length: 5 }); | ||
testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { offset: 5 }); | ||
testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { length: 1, offset: 3 }); | ||
testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { length: -1 }); | ||
testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { offset: -1 }); | ||
|
||
// Test compatibility with fs.readSync counterpart with reused params | ||
for (const params of [ | ||
{}, | ||
{ length: 1 }, | ||
{ position: 5 }, | ||
{ length: 1, position: 5 }, | ||
{ length: 1, position: -1, offset: 2 }, | ||
{ length: null }, | ||
{ offset: 1 }, | ||
]) { | ||
testValid(dest, buffer, params); | ||
} | ||
} |