-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fs: make sure readFile[Sync] reads from the beginning #9699
Conversation
It seems you will be asked to add some tests. Here are the possible ones even without writing and additional files to read: 'use strict';
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const fd = fs.openSync(__filename, 'r');
const fileLength = fs.readFileSync(fd).length;
assert.strictEqual(fileLength, fs.readFileSync(fd).length); 'use strict';
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const fd = fs.openSync(__filename, 'r');
const fileLength = fs.readFileSync(fd).length;
fs.readFile(fd, common.mustCall((err, buf) => {
if (err) throw err;
assert.strictEqual(fileLength, buf.length);
})); |
To the two tests proposed above I would add an |
Marking this semver-major to be cautious. Adding tests is definitely a good thing |
I'd also add a test that checks it works with file descriptors opened in 'a+' mode (append + read/write.) |
Added test. @sam-github I think the test should test just one thing, namely that doing @jasnell IMO it shouldn't be semver-major, it's a bug fix. I mean there's a possibility that someone out there is relying on it, but the the same could be said about any bug. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test looks like it contains duplicated logic. You could probably extract it into a function that is called twice.
const fileLengthR = fs.readFileSync(fdr).length; | ||
const fileLengthA = fs.readFileSync(fda).length; | ||
|
||
// reading again should result in the same length |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you capitalize and punctuate this comment.
assert.strictEqual(fileLengthA, fs.readFileSync(fda).length); | ||
|
||
fs.readFile(fdr, common.mustCall((err, buf) => { | ||
if (err) throw err; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert.ifError(err);
})); | ||
|
||
fs.readFile(fda, common.mustCall((err, buf) => { | ||
if (err) throw err; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing here.
@cjihrig Agreed, done. I think this way is better than a function. |
@seishun Is this test not intended to test the API contract of readFile? If so, the API contract is not that it returns the same data every time, but that it returns the entire file contents every time - the test as written doesn't do that. Calling stat will at least guarantee that what you read from disk is the same size as what is on disk. Right now if a bug was introduced so that only the first block of a data was returned when reading from a fd, your test looks like it would pass. To be more thorough, you could readFile, change the data on disk, and readFile again... but since that isn't a suspected regression its overkill at this moment. |
I don't think this is semver-major. Its hard to see how the current behaviour would be depended on. Possible - sure, like any bug can be depended on, but not likely. Marking semver-major would block back-porting. |
@sam-github Alright, I guess you're right, although now the test depends on fs.stat working correctly. What do others think? |
The test is always going to depend on some other fs APIs if it needs a fixture, and this does. You avoided having to create the file to read by using your own js file, which is clever, but at the price of not knowing what the file's contents are, so you no longer know if you've read the whole file contents. I don't think its likely that readFile will read incorrect data if the size of what it read is at least identical to the size on disk, but some kind of assertion that there is at least a relationship between the data you are trying to read and the data you did read is important. |
const fileLength = fs.statSync(__filename).size; | ||
|
||
['r', 'a+'].forEach(mode => { | ||
const fd = fs.openSync(__filename, mode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use a file in common.tmpDir
for a+ because __filename
is not going to work when the file is owned by a different user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that used for new files? We need an already existing file here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, just create a file upfront with fs.writeFileSync()
.
@bnoordhuis like this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM when the style nits are fixed.
const path = require('path'); | ||
|
||
const filename = path.join(common.tmpDir, 'readfile.txt'); | ||
const dataExpected = new Array(100).join('a'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'a'.repeat(100)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm still not up to speed with ES6. In my defense, I copied from test-fs-readfilesync-pipe-large.js
.
fs.writeFileSync(filename, dataExpected); | ||
const fileLength = dataExpected.length; | ||
|
||
['r', 'a+'].forEach(mode => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the linter complaining that it should be (mode) => {
?
|
||
['r', 'a+'].forEach(mode => { | ||
const fd = fs.openSync(filename, mode); | ||
assert.strictEqual(fileLength, fs.readFileSync(fd).length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tiny nit: it should be assert.strictEqual(actual, expected)
here and below.
@bnoordhuis nits are fixed. |
@nodejs/ctc ... we need some discussion on whether this change is semver-major or not. |
@jasnell I'd say a patch. Current behavior contradicts official documentation, and this PR aligns the two. |
Works for me |
@jasnell so you're okay with removing |
@nodejs/collaborators Merging in a week from now if there are no objections. |
It would previously read from the current file position, which can be non-zero if the `fd` has been read from or written to. This contradicts the documentation which states that it "reads the entire contents of a file". PR-URL: nodejs#9699 Fixes: nodejs#9671 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
It would previously read from the current file position, which can be non-zero if the `fd` has been read from or written to. This contradicts the documentation which states that it "reads the entire contents of a file". PR-URL: nodejs#9699 Fixes: nodejs#9671 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Since this has been reverted, should we add all the dont-land-on labels? |
Checklist
make -j8 test
(UNIX), orvcbuild test nosign
(Windows) passesAffected core subsystem(s)
fs
Description of change
Currently,
fs.readFile[Sync]
starts reading from the current file position, which can be non-zero if thefd
was previously read or written from/to. This conflicts with the documentation which states that it "reads the entire contents of a file".Fixes: #9671
Provisional testcase for
fs.readFile
:For
fs.readFileSync
: