diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index 82841f0acf..5f96b10763 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -577,7 +577,10 @@ If you want to be notified when the file was modified, not just accessed you need to compare `curr.mtime` and `prev.mtime`. _Note: when an `fs.watchFile` operation results in an `ENOENT` error, it will -invoke the callback once. This is a change in functionality since v0.10._ + invoke the listener once, with all the fields zeroed (or, for dates, the Unix + Epoch). In Windows, `blksize` and `blocks` fields will be `undefined`, instead + of zero. If the file is created later on, the listener will be called again, + with the latest stat objects. This is a change in functionality since v0.10._ _Note: `fs.watch` is more efficient than `fs.watchFile` and `fs.unwatchFile`. `fs.watch` should be used instead of `fs.watchFile` and `fs.unwatchFile` diff --git a/test/parallel/test-fs-watchfile.js b/test/parallel/test-fs-watchfile.js index eacb2f9d82..35712741f7 100644 --- a/test/parallel/test-fs-watchfile.js +++ b/test/parallel/test-fs-watchfile.js @@ -1,9 +1,9 @@ 'use strict'; +const common = require('../common'); const fs = require('fs'); const path = require('path'); const assert = require('assert'); -const common = require('../common'); const fixtures = path.join(__dirname, '..', 'fixtures'); // Basic usage tests. @@ -19,8 +19,60 @@ assert.throws(function() { fs.watchFile(new Object(), function() {}); }, /Path must be a string/); -// Test ENOENT. Should fire once. -const enoentFile = path.join(fixtures, 'empty', 'non-existent-file'); +const enoentFile = path.join(fixtures, 'non-existent-file'); +const expectedStatObject = new fs.Stats( + 0, // dev + 0, // mode + 0, // nlink + 0, // uid + 0, // gid + 0, // rdev + common.isWindows ? undefined : 0, // blksize + 0, // ino + 0, // size + common.isWindows ? undefined : 0, // blocks + Date.UTC(1970, 0, 1, 0, 0, 0), // atime + Date.UTC(1970, 0, 1, 0, 0, 0), // mtime + Date.UTC(1970, 0, 1, 0, 0, 0), // ctime + Date.UTC(1970, 0, 1, 0, 0, 0) // birthtime +); + +function removeTestFile() { + try { + fs.unlinkSync(enoentFile); + } catch (ex) { + if (ex.code !== 'ENOENT') { + throw ex; + } + } +} + +// Make sure that the file does not exist, when the test starts +removeTestFile(); + +// If the file initially didn't exist, and gets created at a later point of +// time, the callback should be invoked again with proper values in stat object +var fileExists = false; + fs.watchFile(enoentFile, common.mustCall(function(curr, prev) { - fs.unwatchFile(enoentFile); -})); + if (!fileExists) { + // If the file does not exist, all the fields should be zero and the date + // fields should be UNIX EPOCH time + assert.deepStrictEqual(curr, expectedStatObject); + assert.deepStrictEqual(prev, expectedStatObject); + // Create the file now, so that the callback will be called back once the + // event loop notices it. + fs.closeSync(fs.openSync(enoentFile, 'w')); + fileExists = true; + } else { + // If the ino (inode) value is greater than zero, it means that the file is + // present in the filesystem and it has a valid inode number. + assert(curr.ino > 0); + // As the file just got created, previous ino value should be lesser than + // or equal to zero (non-existent file). + assert(prev.ino <= 0); + // Stop watching the file and delete it + fs.unwatchFile(enoentFile); + removeTestFile(); + } +}, 2));