[Pkg-javascript-commits] [node-graceful-fs] 04/08: New upstream version 4.1.9
Julien Puydt
julien.puydt at laposte.net
Sun Oct 30 07:27:00 UTC 2016
This is an automated email from the git hooks/post-receive script.
jpuydt-guest pushed a commit to branch master
in repository node-graceful-fs.
commit 4667eb53d7ab68160c22da1b13c35c93c08de805
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Sun Oct 16 11:08:43 2016 +0200
New upstream version 4.1.9
---
.gitignore | 2 +
.travis.yml | 7 +
LICENSE | 36 ++---
README.md | 103 +++++++++++++-
fs.js | 32 +++--
graceful-fs.js | 334 ++++++++++++++++++++++++++++++----------------
legacy-streams.js | 118 ++++++++++++++++
package.json | 20 ++-
polyfills.js | 333 ++++++++++++++++++++++++++-------------------
test.js | 24 ++++
test/chown-er-ok.js | 53 ++++++++
test/close.js | 10 ++
test/enoent.js | 46 +++++++
test/max-open.js | 68 ++++++++++
test/open.js | 7 +-
test/read-write-stream.js | 51 +++++++
test/readdir-options.js | 61 +++++++++
test/readdir-sort.js | 3 +-
test/readfile.js | 47 +++++++
test/stats-uid-gid.js | 44 ++++++
test/stats.js | 12 ++
test/write-then-read.js | 43 ++++++
22 files changed, 1149 insertions(+), 305 deletions(-)
diff --git a/.gitignore b/.gitignore
index c2658d7..2f24c57 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
node_modules/
+coverage/
+.nyc_output/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c070d37
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,7 @@
+sudo: false
+language: node_js
+node_js:
+ - '0.10'
+ - '4'
+ - '5'
+ - '6'
diff --git a/LICENSE b/LICENSE
index 0c44ae7..9d2c803 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,27 +1,15 @@
-Copyright (c) Isaac Z. Schlueter ("Author")
-All rights reserved.
+The ISC License
-The BSD License
+Copyright (c) Isaac Z. Schlueter, Ben Noordhuis, and Contributors
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
index 13a2e86..5273a50 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,7 @@ The improvements are meant to normalize behavior across different
platforms and environments, and to make filesystem access more
resilient to errors.
-## Improvements over [fs module](http://api.nodejs.org/fs.html)
-
-graceful-fs:
+## Improvements over [fs module](https://nodejs.org/api/fs.html)
* Queues up `open` and `readdir` calls, and retries them once
something closes if there is an EMFILE error from too many file
@@ -34,3 +32,102 @@ var fs = require('graceful-fs')
// now go and do stuff with it...
fs.readFileSync('some-file-or-whatever')
```
+
+## Global Patching
+
+If you want to patch the global fs module (or any other fs-like
+module) you can do this:
+
+```javascript
+// Make sure to read the caveat below.
+var realFs = require('fs')
+var gracefulFs = require('graceful-fs')
+gracefulFs.gracefulify(realFs)
+```
+
+This should only ever be done at the top-level application layer, in
+order to delay on EMFILE errors from any fs-using dependencies. You
+should **not** do this in a library, because it can cause unexpected
+delays in other parts of the program.
+
+## Changes
+
+This module is fairly stable at this point, and used by a lot of
+things. That being said, because it implements a subtle behavior
+change in a core part of the node API, even modest changes can be
+extremely breaking, and the versioning is thus biased towards
+bumping the major when in doubt.
+
+The main change between major versions has been switching between
+providing a fully-patched `fs` module vs monkey-patching the node core
+builtin, and the approach by which a non-monkey-patched `fs` was
+created.
+
+The goal is to trade `EMFILE` errors for slower fs operations. So, if
+you try to open a zillion files, rather than crashing, `open`
+operations will be queued up and wait for something else to `close`.
+
+There are advantages to each approach. Monkey-patching the fs means
+that no `EMFILE` errors can possibly occur anywhere in your
+application, because everything is using the same core `fs` module,
+which is patched. However, it can also obviously cause undesirable
+side-effects, especially if the module is loaded multiple times.
+
+Implementing a separate-but-identical patched `fs` module is more
+surgical (and doesn't run the risk of patching multiple times), but
+also imposes the challenge of keeping in sync with the core module.
+
+The current approach loads the `fs` module, and then creates a
+lookalike object that has all the same methods, except a few that are
+patched. It is safe to use in all versions of Node from 0.8 through
+7.0.
+
+### v4
+
+* Do not monkey-patch the fs module. This module may now be used as a
+ drop-in dep, and users can opt into monkey-patching the fs builtin
+ if their app requires it.
+
+### v3
+
+* Monkey-patch fs, because the eval approach no longer works on recent
+ node.
+* fixed possible type-error throw if rename fails on windows
+* verify that we *never* get EMFILE errors
+* Ignore ENOSYS from chmod/chown
+* clarify that graceful-fs must be used as a drop-in
+
+### v2.1.0
+
+* Use eval rather than monkey-patching fs.
+* readdir: Always sort the results
+* win32: requeue a file if error has an OK status
+
+### v2.0
+
+* A return to monkey patching
+* wrap process.cwd
+
+### v1.1
+
+* wrap readFile
+* Wrap fs.writeFile.
+* readdir protection
+* Don't clobber the fs builtin
+* Handle fs.read EAGAIN errors by trying again
+* Expose the curOpen counter
+* No-op lchown/lchmod if not implemented
+* fs.rename patch only for win32
+* Patch fs.rename to handle AV software on Windows
+* Close #4 Chown should not fail on einval or eperm if non-root
+* Fix isaacs/fstream#1 Only wrap fs one time
+* Fix #3 Start at 1024 max files, then back off on EMFILE
+* lutimes that doens't blow up on Linux
+* A full on-rewrite using a queue instead of just swallowing the EMFILE error
+* Wrap Read/Write streams as well
+
+### 1.0
+
+* Update engines for node 0.6
+* Be lstat-graceful on Windows
+* first
diff --git a/fs.js b/fs.js
index ae9fd6f..8ad4a38 100644
--- a/fs.js
+++ b/fs.js
@@ -1,11 +1,21 @@
-// eeeeeevvvvviiiiiiillllll
-// more evil than monkey-patching the native builtin?
-// Not sure.
-
-var mod = require("module")
-var pre = '(function (exports, require, module, __filename, __dirname) { '
-var post = '});'
-var src = pre + process.binding('natives').fs + post
-var vm = require('vm')
-var fn = vm.runInThisContext(src)
-return fn(exports, require, module, __filename, __dirname)
+'use strict'
+
+var fs = require('fs')
+
+module.exports = clone(fs)
+
+function clone (obj) {
+ if (obj === null || typeof obj !== 'object')
+ return obj
+
+ if (obj instanceof Object)
+ var copy = { __proto__: obj.__proto__ }
+ else
+ var copy = Object.create(null)
+
+ Object.getOwnPropertyNames(obj).forEach(function (key) {
+ Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key))
+ })
+
+ return copy
+}
diff --git a/graceful-fs.js b/graceful-fs.js
index 77fc702..33b30d2 100644
--- a/graceful-fs.js
+++ b/graceful-fs.js
@@ -1,11 +1,7 @@
-// Monkey-patching the fs module.
-// It's ugly, but there is simply no other way to do this.
-var fs = module.exports = require('./fs.js')
-
-var assert = require('assert')
-
-// fix up some busted stuff, mostly on windows and old nodes
-require('./polyfills.js')
+var fs = require('fs')
+var polyfills = require('./polyfills.js')
+var legacy = require('./legacy-streams.js')
+var queue = []
var util = require('util')
@@ -13,146 +9,254 @@ function noop () {}
var debug = noop
if (util.debuglog)
- debug = util.debuglog('gfs')
-else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || ''))
+ debug = util.debuglog('gfs4')
+else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))
debug = function() {
var m = util.format.apply(util, arguments)
- m = 'GFS: ' + m.split(/\n/).join('\nGFS: ')
+ m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ')
console.error(m)
}
-if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) {
+if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {
process.on('exit', function() {
- debug('fds', fds)
debug(queue)
- assert.equal(queue.length, 0)
+ require('assert').equal(queue.length, 0)
})
}
-
-var originalOpen = fs.open
-fs.open = open
-
-function open(path, flags, mode, cb) {
- if (typeof mode === "function") cb = mode, mode = null
- if (typeof cb !== "function") cb = noop
- new OpenReq(path, flags, mode, cb)
-}
-
-function OpenReq(path, flags, mode, cb) {
- this.path = path
- this.flags = flags
- this.mode = mode
- this.cb = cb
- Req.call(this)
-}
-
-util.inherits(OpenReq, Req)
-
-OpenReq.prototype.process = function() {
- originalOpen.call(fs, this.path, this.flags, this.mode, this.done)
-}
-
-var fds = {}
-OpenReq.prototype.done = function(er, fd) {
- debug('open done', er, fd)
- if (fd)
- fds['fd' + fd] = this.path
- Req.prototype.done.call(this, er, fd)
+module.exports = patch(require('./fs.js'))
+if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH) {
+ module.exports = patch(fs)
}
+// Always patch fs.close/closeSync, because we want to
+// retry() whenever a close happens *anywhere* in the program.
+// This is essential when multiple graceful-fs instances are
+// in play at the same time.
+module.exports.close =
+fs.close = (function (fs$close) { return function (fd, cb) {
+ return fs$close.call(fs, fd, function (err) {
+ if (!err)
+ retry()
+
+ if (typeof cb === 'function')
+ cb.apply(this, arguments)
+ })
+}})(fs.close)
+
+module.exports.closeSync =
+fs.closeSync = (function (fs$closeSync) { return function (fd) {
+ // Note that graceful-fs also retries when fs.closeSync() fails.
+ // Looks like a bug to me, although it's probably a harmless one.
+ var rval = fs$closeSync.apply(fs, arguments)
+ retry()
+ return rval
+}})(fs.closeSync)
+
+function patch (fs) {
+ // Everything that references the open() function needs to be in here
+ polyfills(fs)
+ fs.gracefulify = patch
+ fs.FileReadStream = ReadStream; // Legacy name.
+ fs.FileWriteStream = WriteStream; // Legacy name.
+ fs.createReadStream = createReadStream
+ fs.createWriteStream = createWriteStream
+ var fs$readFile = fs.readFile
+ fs.readFile = readFile
+ function readFile (path, options, cb) {
+ if (typeof options === 'function')
+ cb = options, options = null
+
+ return go$readFile(path, options, cb)
+
+ function go$readFile (path, options, cb) {
+ return fs$readFile(path, options, function (err) {
+ if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
+ enqueue([go$readFile, [path, options, cb]])
+ else {
+ if (typeof cb === 'function')
+ cb.apply(this, arguments)
+ retry()
+ }
+ })
+ }
+ }
-var originalReaddir = fs.readdir
-fs.readdir = readdir
-
-function readdir(path, cb) {
- if (typeof cb !== "function") cb = noop
- new ReaddirReq(path, cb)
-}
+ var fs$writeFile = fs.writeFile
+ fs.writeFile = writeFile
+ function writeFile (path, data, options, cb) {
+ if (typeof options === 'function')
+ cb = options, options = null
+
+ return go$writeFile(path, data, options, cb)
+
+ function go$writeFile (path, data, options, cb) {
+ return fs$writeFile(path, data, options, function (err) {
+ if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
+ enqueue([go$writeFile, [path, data, options, cb]])
+ else {
+ if (typeof cb === 'function')
+ cb.apply(this, arguments)
+ retry()
+ }
+ })
+ }
+ }
-function ReaddirReq(path, cb) {
- this.path = path
- this.cb = cb
- Req.call(this)
-}
+ var fs$appendFile = fs.appendFile
+ if (fs$appendFile)
+ fs.appendFile = appendFile
+ function appendFile (path, data, options, cb) {
+ if (typeof options === 'function')
+ cb = options, options = null
+
+ return go$appendFile(path, data, options, cb)
+
+ function go$appendFile (path, data, options, cb) {
+ return fs$appendFile(path, data, options, function (err) {
+ if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
+ enqueue([go$appendFile, [path, data, options, cb]])
+ else {
+ if (typeof cb === 'function')
+ cb.apply(this, arguments)
+ retry()
+ }
+ })
+ }
+ }
-util.inherits(ReaddirReq, Req)
+ var fs$readdir = fs.readdir
+ fs.readdir = readdir
+ function readdir (path, options, cb) {
+ var args = [path]
+ if (typeof options !== 'function') {
+ args.push(options)
+ } else {
+ cb = options
+ }
+ args.push(go$readdir$cb)
+
+ return go$readdir(args)
+
+ function go$readdir$cb (err, files) {
+ if (files && files.sort)
+ files.sort()
+
+ if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
+ enqueue([go$readdir, [args]])
+ else {
+ if (typeof cb === 'function')
+ cb.apply(this, arguments)
+ retry()
+ }
+ }
+ }
-ReaddirReq.prototype.process = function() {
- originalReaddir.call(fs, this.path, this.done)
-}
+ function go$readdir (args) {
+ return fs$readdir.apply(fs, args)
+ }
-ReaddirReq.prototype.done = function(er, files) {
- if (files && files.sort)
- files = files.sort()
- Req.prototype.done.call(this, er, files)
- onclose()
-}
+ if (process.version.substr(0, 4) === 'v0.8') {
+ var legStreams = legacy(fs)
+ ReadStream = legStreams.ReadStream
+ WriteStream = legStreams.WriteStream
+ }
+ var fs$ReadStream = fs.ReadStream
+ ReadStream.prototype = Object.create(fs$ReadStream.prototype)
+ ReadStream.prototype.open = ReadStream$open
-var originalClose = fs.close
-fs.close = close
+ var fs$WriteStream = fs.WriteStream
+ WriteStream.prototype = Object.create(fs$WriteStream.prototype)
+ WriteStream.prototype.open = WriteStream$open
-function close (fd, cb) {
- debug('close', fd)
- if (typeof cb !== "function") cb = noop
- delete fds['fd' + fd]
- originalClose.call(fs, fd, function(er) {
- onclose()
- cb(er)
- })
-}
+ fs.ReadStream = ReadStream
+ fs.WriteStream = WriteStream
+ function ReadStream (path, options) {
+ if (this instanceof ReadStream)
+ return fs$ReadStream.apply(this, arguments), this
+ else
+ return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
+ }
-var originalCloseSync = fs.closeSync
-fs.closeSync = closeSync
+ function ReadStream$open () {
+ var that = this
+ open(that.path, that.flags, that.mode, function (err, fd) {
+ if (err) {
+ if (that.autoClose)
+ that.destroy()
+
+ that.emit('error', err)
+ } else {
+ that.fd = fd
+ that.emit('open', fd)
+ that.read()
+ }
+ })
+ }
-function closeSync (fd) {
- try {
- return originalCloseSync(fd)
- } finally {
- onclose()
+ function WriteStream (path, options) {
+ if (this instanceof WriteStream)
+ return fs$WriteStream.apply(this, arguments), this
+ else
+ return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
}
-}
+ function WriteStream$open () {
+ var that = this
+ open(that.path, that.flags, that.mode, function (err, fd) {
+ if (err) {
+ that.destroy()
+ that.emit('error', err)
+ } else {
+ that.fd = fd
+ that.emit('open', fd)
+ }
+ })
+ }
-// Req class
-function Req () {
- // start processing
- this.done = this.done.bind(this)
- this.failures = 0
- this.process()
-}
+ function createReadStream (path, options) {
+ return new ReadStream(path, options)
+ }
-Req.prototype.done = function (er, result) {
- var tryAgain = false
- if (er) {
- var code = er.code
- var tryAgain = code === "EMFILE"
- if (process.platform === "win32")
- tryAgain = tryAgain || code === "OK"
+ function createWriteStream (path, options) {
+ return new WriteStream(path, options)
}
- if (tryAgain) {
- this.failures ++
- enqueue(this)
- } else {
- var cb = this.cb
- cb(er, result)
+ var fs$open = fs.open
+ fs.open = open
+ function open (path, flags, mode, cb) {
+ if (typeof mode === 'function')
+ cb = mode, mode = null
+
+ return go$open(path, flags, mode, cb)
+
+ function go$open (path, flags, mode, cb) {
+ return fs$open(path, flags, mode, function (err, fd) {
+ if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
+ enqueue([go$open, [path, flags, mode, cb]])
+ else {
+ if (typeof cb === 'function')
+ cb.apply(this, arguments)
+ retry()
+ }
+ })
+ }
}
-}
-var queue = []
+ return fs
+}
-function enqueue(req) {
- queue.push(req)
- debug('enqueue %d %s', queue.length, req.constructor.name, req)
+function enqueue (elem) {
+ debug('ENQUEUE', elem[0].name, elem[1])
+ queue.push(elem)
}
-function onclose() {
- var req = queue.shift()
- if (req) {
- debug('process', req.constructor.name, req)
- req.process()
+function retry () {
+ var elem = queue.shift()
+ if (elem) {
+ debug('RETRY', elem[0].name, elem[1])
+ elem[0].apply(null, elem[1])
}
}
diff --git a/legacy-streams.js b/legacy-streams.js
new file mode 100644
index 0000000..d617b50
--- /dev/null
+++ b/legacy-streams.js
@@ -0,0 +1,118 @@
+var Stream = require('stream').Stream
+
+module.exports = legacy
+
+function legacy (fs) {
+ return {
+ ReadStream: ReadStream,
+ WriteStream: WriteStream
+ }
+
+ function ReadStream (path, options) {
+ if (!(this instanceof ReadStream)) return new ReadStream(path, options);
+
+ Stream.call(this);
+
+ var self = this;
+
+ this.path = path;
+ this.fd = null;
+ this.readable = true;
+ this.paused = false;
+
+ this.flags = 'r';
+ this.mode = 438; /*=0666*/
+ this.bufferSize = 64 * 1024;
+
+ options = options || {};
+
+ // Mixin options into this
+ var keys = Object.keys(options);
+ for (var index = 0, length = keys.length; index < length; index++) {
+ var key = keys[index];
+ this[key] = options[key];
+ }
+
+ if (this.encoding) this.setEncoding(this.encoding);
+
+ if (this.start !== undefined) {
+ if ('number' !== typeof this.start) {
+ throw TypeError('start must be a Number');
+ }
+ if (this.end === undefined) {
+ this.end = Infinity;
+ } else if ('number' !== typeof this.end) {
+ throw TypeError('end must be a Number');
+ }
+
+ if (this.start > this.end) {
+ throw new Error('start must be <= end');
+ }
+
+ this.pos = this.start;
+ }
+
+ if (this.fd !== null) {
+ process.nextTick(function() {
+ self._read();
+ });
+ return;
+ }
+
+ fs.open(this.path, this.flags, this.mode, function (err, fd) {
+ if (err) {
+ self.emit('error', err);
+ self.readable = false;
+ return;
+ }
+
+ self.fd = fd;
+ self.emit('open', fd);
+ self._read();
+ })
+ }
+
+ function WriteStream (path, options) {
+ if (!(this instanceof WriteStream)) return new WriteStream(path, options);
+
+ Stream.call(this);
+
+ this.path = path;
+ this.fd = null;
+ this.writable = true;
+
+ this.flags = 'w';
+ this.encoding = 'binary';
+ this.mode = 438; /*=0666*/
+ this.bytesWritten = 0;
+
+ options = options || {};
+
+ // Mixin options into this
+ var keys = Object.keys(options);
+ for (var index = 0, length = keys.length; index < length; index++) {
+ var key = keys[index];
+ this[key] = options[key];
+ }
+
+ if (this.start !== undefined) {
+ if ('number' !== typeof this.start) {
+ throw TypeError('start must be a Number');
+ }
+ if (this.start < 0) {
+ throw new Error('start must be >= zero');
+ }
+
+ this.pos = this.start;
+ }
+
+ this.busy = false;
+ this._queue = [];
+
+ if (this.fd === null) {
+ this._open = fs.open;
+ this._queue.push([this._open, this.path, this.flags, this.mode, undefined]);
+ this.flush();
+ }
+ }
+}
diff --git a/package.json b/package.json
index b15f91f..d8e11a5 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,10 @@
{
- "author": "Isaac Z. Schlueter <i at izs.me> (http://blog.izs.me)",
"name": "graceful-fs",
"description": "A drop-in replacement for fs, making various improvements.",
- "version": "3.0.2",
+ "version": "4.1.9",
"repository": {
"type": "git",
- "url": "git://github.com/isaacs/node-graceful-fs.git"
+ "url": "https://github.com/isaacs/node-graceful-fs"
},
"main": "graceful-fs.js",
"engines": {
@@ -15,7 +14,7 @@
"test": "test"
},
"scripts": {
- "test": "tap test/*.js"
+ "test": "node test.js | tap -"
},
"keywords": [
"fs",
@@ -33,5 +32,16 @@
"EPERM",
"EACCESS"
],
- "license": "BSD"
+ "license": "ISC",
+ "devDependencies": {
+ "mkdirp": "^0.5.0",
+ "rimraf": "^2.2.8",
+ "tap": "^5.4.2"
+ },
+ "files": [
+ "fs.js",
+ "graceful-fs.js",
+ "legacy-streams.js",
+ "polyfills.js"
+ ]
}
diff --git a/polyfills.js b/polyfills.js
index 9d62af5..cf474df 100644
--- a/polyfills.js
+++ b/polyfills.js
@@ -8,33 +8,140 @@ process.cwd = function() {
cwd = origCwd.call(process)
return cwd
}
+try {
+ process.cwd()
+} catch (er) {}
+
var chdir = process.chdir
process.chdir = function(d) {
cwd = null
chdir.call(process, d)
}
-// (re-)implement some things that are known busted or missing.
+module.exports = patch
+
+function patch (fs) {
+ // (re-)implement some things that are known busted or missing.
+
+ // lchmod, broken prior to 0.6.2
+ // back-port the fix here.
+ if (constants.hasOwnProperty('O_SYMLINK') &&
+ process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
+ patchLchmod(fs)
+ }
+
+ // lutimes implementation, or no-op
+ if (!fs.lutimes) {
+ patchLutimes(fs)
+ }
+
+ // https://github.com/isaacs/node-graceful-fs/issues/4
+ // Chown should not fail on einval or eperm if non-root.
+ // It should not fail on enosys ever, as this just indicates
+ // that a fs doesn't support the intended operation.
+
+ fs.chown = chownFix(fs.chown)
+ fs.fchown = chownFix(fs.fchown)
+ fs.lchown = chownFix(fs.lchown)
+
+ fs.chmod = chmodFix(fs.chmod)
+ fs.fchmod = chmodFix(fs.fchmod)
+ fs.lchmod = chmodFix(fs.lchmod)
+
+ fs.chownSync = chownFixSync(fs.chownSync)
+ fs.fchownSync = chownFixSync(fs.fchownSync)
+ fs.lchownSync = chownFixSync(fs.lchownSync)
+
+ fs.chmodSync = chmodFixSync(fs.chmodSync)
+ fs.fchmodSync = chmodFixSync(fs.fchmodSync)
+ fs.lchmodSync = chmodFixSync(fs.lchmodSync)
+
+ fs.stat = statFix(fs.stat)
+ fs.fstat = statFix(fs.fstat)
+ fs.lstat = statFix(fs.lstat)
+
+ fs.statSync = statFixSync(fs.statSync)
+ fs.fstatSync = statFixSync(fs.fstatSync)
+ fs.lstatSync = statFixSync(fs.lstatSync)
+
+ // if lchmod/lchown do not exist, then make them no-ops
+ if (!fs.lchmod) {
+ fs.lchmod = function (path, mode, cb) {
+ if (cb) process.nextTick(cb)
+ }
+ fs.lchmodSync = function () {}
+ }
+ if (!fs.lchown) {
+ fs.lchown = function (path, uid, gid, cb) {
+ if (cb) process.nextTick(cb)
+ }
+ fs.lchownSync = function () {}
+ }
+
+ // on Windows, A/V software can lock the directory, causing this
+ // to fail with an EACCES or EPERM if the directory contains newly
+ // created files. Try again on failure, for up to 1 second.
+ if (process.platform === "win32") {
+ fs.rename = (function (fs$rename) { return function (from, to, cb) {
+ var start = Date.now()
+ fs$rename(from, to, function CB (er) {
+ if (er
+ && (er.code === "EACCES" || er.code === "EPERM")
+ && Date.now() - start < 1000) {
+ return fs$rename(from, to, CB)
+ }
+ if (cb) cb(er)
+ })
+ }})(fs.rename)
+ }
+
+ // if read() returns EAGAIN, then just try it again.
+ fs.read = (function (fs$read) { return function (fd, buffer, offset, length, position, callback_) {
+ var callback
+ if (callback_ && typeof callback_ === 'function') {
+ var eagCounter = 0
+ callback = function (er, _, __) {
+ if (er && er.code === 'EAGAIN' && eagCounter < 10) {
+ eagCounter ++
+ return fs$read.call(fs, fd, buffer, offset, length, position, callback)
+ }
+ callback_.apply(this, arguments)
+ }
+ }
+ return fs$read.call(fs, fd, buffer, offset, length, position, callback)
+ }})(fs.read)
+
+ fs.readSync = (function (fs$readSync) { return function (fd, buffer, offset, length, position) {
+ var eagCounter = 0
+ while (true) {
+ try {
+ return fs$readSync.call(fs, fd, buffer, offset, length, position)
+ } catch (er) {
+ if (er.code === 'EAGAIN' && eagCounter < 10) {
+ eagCounter ++
+ continue
+ }
+ throw er
+ }
+ }
+ }})(fs.readSync)
+}
-// lchmod, broken prior to 0.6.2
-// back-port the fix here.
-if (constants.hasOwnProperty('O_SYMLINK') &&
- process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
+function patchLchmod (fs) {
fs.lchmod = function (path, mode, callback) {
- callback = callback || noop
fs.open( path
, constants.O_WRONLY | constants.O_SYMLINK
, mode
, function (err, fd) {
if (err) {
- callback(err)
+ if (callback) callback(err)
return
}
// prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
fs.fchmod(fd, mode, function (err) {
fs.close(fd, function(err2) {
- callback(err || err2)
+ if (callback) callback(err || err2)
})
})
})
@@ -45,33 +152,35 @@ if (constants.hasOwnProperty('O_SYMLINK') &&
// prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
- var err, err2
+ var threw = true
+ var ret
try {
- var ret = fs.fchmodSync(fd, mode)
- } catch (er) {
- err = er
- }
- try {
- fs.closeSync(fd)
- } catch (er) {
- err2 = er
+ ret = fs.fchmodSync(fd, mode)
+ threw = false
+ } finally {
+ if (threw) {
+ try {
+ fs.closeSync(fd)
+ } catch (er) {}
+ } else {
+ fs.closeSync(fd)
+ }
}
- if (err || err2) throw (err || err2)
return ret
}
}
-
-// lutimes implementation, or no-op
-if (!fs.lutimes) {
+function patchLutimes (fs) {
if (constants.hasOwnProperty("O_SYMLINK")) {
fs.lutimes = function (path, at, mt, cb) {
fs.open(path, constants.O_SYMLINK, function (er, fd) {
- cb = cb || noop
- if (er) return cb(er)
+ if (er) {
+ if (cb) cb(er)
+ return
+ }
fs.futimes(fd, at, mt, function (er) {
fs.close(fd, function (er2) {
- return cb(er || er2)
+ if (cb) cb(er || er2)
})
})
})
@@ -79,68 +188,57 @@ if (!fs.lutimes) {
fs.lutimesSync = function (path, at, mt) {
var fd = fs.openSync(path, constants.O_SYMLINK)
- , err
- , err2
- , ret
-
+ var ret
+ var threw = true
try {
- var ret = fs.futimesSync(fd, at, mt)
- } catch (er) {
- err = er
+ ret = fs.futimesSync(fd, at, mt)
+ threw = false
+ } finally {
+ if (threw) {
+ try {
+ fs.closeSync(fd)
+ } catch (er) {}
+ } else {
+ fs.closeSync(fd)
+ }
}
- try {
- fs.closeSync(fd)
- } catch (er) {
- err2 = er
- }
- if (err || err2) throw (err || err2)
return ret
}
- } else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) {
- // maybe utimensat will be bound soonish?
- fs.lutimes = function (path, at, mt, cb) {
- fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb)
- }
-
- fs.lutimesSync = function (path, at, mt) {
- return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW)
- }
-
} else {
- fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) }
+ fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb) }
fs.lutimesSync = function () {}
}
}
+function chmodFix (orig) {
+ if (!orig) return orig
+ return function (target, mode, cb) {
+ return orig.call(fs, target, mode, function (er) {
+ if (chownErOk(er)) er = null
+ if (cb) cb.apply(this, arguments)
+ })
+ }
+}
-// https://github.com/isaacs/node-graceful-fs/issues/4
-// Chown should not fail on einval or eperm if non-root.
-// It should not fail on enosys ever, as this just indicates
-// that a fs doesn't support the intended operation.
-
-fs.chown = chownFix(fs.chown)
-fs.fchown = chownFix(fs.fchown)
-fs.lchown = chownFix(fs.lchown)
-
-fs.chmod = chownFix(fs.chmod)
-fs.fchmod = chownFix(fs.fchmod)
-fs.lchmod = chownFix(fs.lchmod)
-
-fs.chownSync = chownFixSync(fs.chownSync)
-fs.fchownSync = chownFixSync(fs.fchownSync)
-fs.lchownSync = chownFixSync(fs.lchownSync)
+function chmodFixSync (orig) {
+ if (!orig) return orig
+ return function (target, mode) {
+ try {
+ return orig.call(fs, target, mode)
+ } catch (er) {
+ if (!chownErOk(er)) throw er
+ }
+ }
+}
-fs.chmodSync = chownFix(fs.chmodSync)
-fs.fchmodSync = chownFix(fs.fchmodSync)
-fs.lchmodSync = chownFix(fs.lchmodSync)
function chownFix (orig) {
if (!orig) return orig
return function (target, uid, gid, cb) {
- return orig.call(fs, target, uid, gid, function (er, res) {
+ return orig.call(fs, target, uid, gid, function (er) {
if (chownErOk(er)) er = null
- cb(er, res)
+ if (cb) cb.apply(this, arguments)
})
}
}
@@ -156,6 +254,33 @@ function chownFixSync (orig) {
}
}
+
+function statFix (orig) {
+ if (!orig) return orig
+ // Older versions of Node erroneously returned signed integers for
+ // uid + gid.
+ return function (target, cb) {
+ return orig.call(fs, target, function (er, stats) {
+ if (!stats) return cb.apply(this, arguments)
+ if (stats.uid < 0) stats.uid += 0x100000000
+ if (stats.gid < 0) stats.gid += 0x100000000
+ if (cb) cb.apply(this, arguments)
+ })
+ }
+}
+
+function statFixSync (orig) {
+ if (!orig) return orig
+ // Older versions of Node erroneously returned signed integers for
+ // uid + gid.
+ return function (target) {
+ var stats = orig.call(fs, target)
+ if (stats.uid < 0) stats.uid += 0x100000000
+ if (stats.gid < 0) stats.gid += 0x100000000
+ return stats;
+ }
+}
+
// ENOSYS means that the fs doesn't support the op. Just ignore
// that, because it doesn't matter.
//
@@ -183,73 +308,3 @@ function chownErOk (er) {
return false
}
-
-
-// if lchmod/lchown do not exist, then make them no-ops
-if (!fs.lchmod) {
- fs.lchmod = function (path, mode, cb) {
- process.nextTick(cb)
- }
- fs.lchmodSync = function () {}
-}
-if (!fs.lchown) {
- fs.lchown = function (path, uid, gid, cb) {
- process.nextTick(cb)
- }
- fs.lchownSync = function () {}
-}
-
-
-
-// on Windows, A/V software can lock the directory, causing this
-// to fail with an EACCES or EPERM if the directory contains newly
-// created files. Try again on failure, for up to 1 second.
-if (process.platform === "win32") {
- var rename_ = fs.rename
- fs.rename = function rename (from, to, cb) {
- var start = Date.now()
- rename_(from, to, function CB (er) {
- if (er
- && (er.code === "EACCES" || er.code === "EPERM")
- && Date.now() - start < 1000) {
- return rename_(from, to, CB)
- }
- cb(er)
- })
- }
-}
-
-
-// if read() returns EAGAIN, then just try it again.
-var read = fs.read
-fs.read = function (fd, buffer, offset, length, position, callback_) {
- var callback
- if (callback_ && typeof callback_ === 'function') {
- var eagCounter = 0
- callback = function (er, _, __) {
- if (er && er.code === 'EAGAIN' && eagCounter < 10) {
- eagCounter ++
- return read.call(fs, fd, buffer, offset, length, position, callback)
- }
- callback_.apply(this, arguments)
- }
- }
- return read.call(fs, fd, buffer, offset, length, position, callback)
-}
-
-var readSync = fs.readSync
-fs.readSync = function (fd, buffer, offset, length, position) {
- var eagCounter = 0
- while (true) {
- try {
- return readSync.call(fs, fd, buffer, offset, length, position)
- } catch (er) {
- if (er.code === 'EAGAIN' && eagCounter < 10) {
- eagCounter ++
- continue
- }
- throw er
- }
- }
-}
-
diff --git a/test.js b/test.js
new file mode 100644
index 0000000..59e8fe8
--- /dev/null
+++ b/test.js
@@ -0,0 +1,24 @@
+var spawn = require('child_process').spawn
+var fs = require('fs')
+var tap = require('tap')
+var dir = __dirname + '/test'
+var node = process.execPath
+
+var files = fs.readdirSync(dir)
+var env = Object.keys(process.env).reduce(function (env, k) {
+ env[k] = process.env[k]
+ return env
+}, {
+ TEST_GRACEFUL_FS_GLOBAL_PATCH: 1
+})
+
+files.filter(function (f) {
+ if (/\.js$/.test(f) && fs.statSync(dir + '/' + f).isFile()) {
+ tap.spawn(node, ['test/' + f])
+ return true
+ }
+}).forEach(function (f) {
+ tap.spawn(node, ['test/' + f], {
+ env: env
+ }, '🐵 test/' + f)
+})
diff --git a/test/chown-er-ok.js b/test/chown-er-ok.js
new file mode 100644
index 0000000..3096652
--- /dev/null
+++ b/test/chown-er-ok.js
@@ -0,0 +1,53 @@
+var t = require('tap')
+var realFs = require('fs')
+
+var methods = ['chown', 'chownSync', 'chmod', 'chmodSync']
+methods.forEach(function (method) {
+ causeErr(method, realFs[method])
+})
+
+function causeErr (method, original) {
+ realFs[method] = function (path) {
+ var err = makeErr(path, method)
+ if (!/Sync$/.test(method)) {
+ var cb = arguments[arguments.length - 1]
+ process.nextTick(cb.bind(null, err))
+ } else {
+ throw err
+ }
+ }
+}
+
+function makeErr (path, method) {
+ var err = new Error('this is fine')
+ err.syscall = method.replace(/Sync$/, '')
+ err.code = path.toUpperCase()
+ return err
+}
+
+var fs = require('../')
+
+var errs = ['ENOSYS', 'EINVAL', 'EPERM']
+t.plan(errs.length * methods.length)
+
+errs.forEach(function (err) {
+ methods.forEach(function (method) {
+ var args = [err]
+ if (/chmod/.test(method)) {
+ args.push('some mode')
+ } else {
+ args.push('some uid', 'some gid')
+ }
+
+ if (method.match(/Sync$/)) {
+ t.doesNotThrow(function () {
+ fs[method].apply(fs, args)
+ })
+ } else {
+ args.push(function (err) {
+ t.notOk(err)
+ })
+ fs[method].apply(fs, args)
+ }
+ })
+})
diff --git a/test/close.js b/test/close.js
new file mode 100644
index 0000000..848e2c4
--- /dev/null
+++ b/test/close.js
@@ -0,0 +1,10 @@
+var test = require('tap').test
+var fs$close = require('fs').close;
+var fs$closeSync = require('fs').closeSync;
+var fs = require('../');
+
+test('`close` is patched correctly', function(t) {
+ t.notEqual(fs.close, fs$close, 'patch close');
+ t.notEqual(fs.closeSync, fs$closeSync, 'patch closeSync');
+ t.end();
+})
diff --git a/test/enoent.js b/test/enoent.js
new file mode 100644
index 0000000..10ba417
--- /dev/null
+++ b/test/enoent.js
@@ -0,0 +1,46 @@
+// this test makes sure that various things get enoent, instead of
+// some other kind of throw.
+
+var t = require('tap')
+var g = require('../')
+var file = 'this file does not exist even a little bit'
+var methods = [
+ ['open', 'r'],
+ ['readFile'],
+ ['stat'],
+ ['lstat'],
+ ['utimes', new Date(), new Date()],
+ ['readdir']
+]
+
+// any version > v6 can do readdir(path, options, cb)
+if (process.version.match(/^v([6-9]|[1-9][0-9])\./)) {
+ methods.push(['readdir', {}])
+}
+
+t.plan(methods.length)
+methods.forEach(function (method) {
+ t.test(method[0], runTest(method))
+})
+
+function runTest (args) { return function (t) {
+ var method = args.shift()
+ args.unshift(file)
+ var methodSync = method + 'Sync'
+ t.isa(g[methodSync], 'function')
+ t.throws(function () {
+ g[methodSync].apply(g, args)
+ }, { code: 'ENOENT' })
+ // add the callback
+ args.push(verify(t))
+ t.isa(g[method], 'function')
+ t.doesNotThrow(function () {
+ g[method].apply(g, args)
+ })
+}}
+
+function verify (t) { return function (er) {
+ t.isa(er, Error)
+ t.equal(er.code, 'ENOENT')
+ t.end()
+}}
diff --git a/test/max-open.js b/test/max-open.js
new file mode 100644
index 0000000..5c3babc
--- /dev/null
+++ b/test/max-open.js
@@ -0,0 +1,68 @@
+var test = require('tap').test
+var fs = require('../')
+
+test('open lots of stuff', function (t) {
+ // Get around EBADF from libuv by making sure that stderr is opened
+ // Otherwise Darwin will refuse to give us a FD for stderr!
+ process.stderr.write('')
+
+ // How many parallel open()'s to do
+ var n = 1024
+ var opens = 0
+ var fds = []
+ var going = true
+ var closing = false
+ var doneCalled = 0
+
+ for (var i = 0; i < n; i++) {
+ go()
+ }
+
+ function go() {
+ opens++
+ fs.open(__filename, 'r', function (er, fd) {
+ if (er) throw er
+ fds.push(fd)
+ if (going) go()
+ })
+ }
+
+ // should hit ulimit pretty fast
+ setTimeout(function () {
+ going = false
+ t.equal(opens - fds.length, n)
+ done()
+ }, 100)
+
+
+ function done () {
+ if (closing) return
+ doneCalled++
+
+ if (fds.length === 0) {
+ // First because of the timeout
+ // Then to close the fd's opened afterwards
+ // Then this time, to complete.
+ // Might take multiple passes, depending on CPU speed
+ // and ulimit, but at least 3 in every case.
+ t.ok(doneCalled >= 2)
+ return t.end()
+ }
+
+ closing = true
+ setTimeout(function () {
+ // console.error('do closing again')
+ closing = false
+ done()
+ }, 100)
+
+ // console.error('closing time')
+ var closes = fds.slice(0)
+ fds.length = 0
+ closes.forEach(function (fd) {
+ fs.close(fd, function (er) {
+ if (er) throw er
+ })
+ })
+ }
+})
diff --git a/test/open.js b/test/open.js
index 85732f2..b76014d 100644
--- a/test/open.js
+++ b/test/open.js
@@ -1,10 +1,5 @@
var test = require('tap').test
-var fs = require('../graceful-fs.js')
-
-test('graceful fs is monkeypatched fs', function (t) {
- t.equal(fs, require('../fs.js'))
- t.end()
-})
+var fs = require('../')
test('open an existing file works', function (t) {
var fd = fs.openSync(__filename, 'r')
diff --git a/test/read-write-stream.js b/test/read-write-stream.js
new file mode 100644
index 0000000..c6511cb
--- /dev/null
+++ b/test/read-write-stream.js
@@ -0,0 +1,51 @@
+'use strict'
+
+var fs = require('../')
+var rimraf = require('rimraf')
+var mkdirp = require('mkdirp')
+var test = require('tap').test
+var p = require('path').resolve(__dirname, 'files')
+
+process.chdir(__dirname)
+
+// Make sure to reserve the stderr fd
+process.stderr.write('')
+
+var num = 4097
+var paths = new Array(num)
+
+test('write files', function (t) {
+ rimraf.sync(p)
+ mkdirp.sync(p)
+
+ t.plan(num)
+ for (var i = 0; i < num; ++i) {
+ paths[i] = 'files/file-' + i
+ var stream = fs.createWriteStream(paths[i])
+ stream.on('finish', function () {
+ t.pass('success')
+ })
+ stream.write('content')
+ stream.end()
+ }
+})
+
+test('read files', function (t) {
+ // now read them
+ t.plan(num)
+ for (var i = 0; i < num; ++i) (function (i) {
+ var stream = fs.createReadStream(paths[i])
+ var data = ''
+ stream.on('data', function (c) {
+ data += c
+ })
+ stream.on('end', function () {
+ t.equal(data, 'content')
+ })
+ })(i)
+})
+
+test('cleanup', function (t) {
+ rimraf.sync(p)
+ t.end()
+})
diff --git a/test/readdir-options.js b/test/readdir-options.js
new file mode 100644
index 0000000..b937901
--- /dev/null
+++ b/test/readdir-options.js
@@ -0,0 +1,61 @@
+var t = require("tap")
+var fs = require("fs")
+
+var currentTest
+
+var strings = ['b', 'z', 'a']
+var buffs = strings.map(function (s) { return new Buffer(s) })
+var hexes = buffs.map(function (b) { return b.toString('hex') })
+
+function getRet (encoding) {
+ switch (encoding) {
+ case 'hex':
+ return hexes
+ case 'buffer':
+ return buffs
+ default:
+ return strings
+ }
+}
+
+var readdir = fs.readdir
+var failed = false
+fs.readdir = function(path, options, cb) {
+ if (!failed) {
+ // simulate an EMFILE and then open and close a thing to retry
+ failed = true
+ process.nextTick(function () {
+ var er = new Error('synthetic emfile')
+ er.code = 'EMFILE'
+ cb(er)
+ process.nextTick(function () {
+ fs.closeSync(fs.openSync(__filename, 'r'))
+ })
+ })
+ return
+ }
+
+ failed = false
+ currentTest.isa(cb, 'function')
+ currentTest.isa(options, 'object')
+ currentTest.ok(options)
+ process.nextTick(function() {
+ var ret = getRet(options.encoding)
+ cb(null, ret)
+ })
+}
+
+var g = require("../")
+
+var encodings = ['buffer', 'hex', 'utf8', null]
+encodings.forEach(function (enc) {
+ t.test('encoding=' + enc, function (t) {
+ currentTest = t
+ g.readdir("whatevers", { encoding: enc }, function (er, files) {
+ if (er)
+ throw er
+ t.same(files, getRet(enc).sort())
+ t.end()
+ })
+ })
+})
diff --git a/test/readdir-sort.js b/test/readdir-sort.js
index fe005aa..89d3e77 100644
--- a/test/readdir-sort.js
+++ b/test/readdir-sort.js
@@ -1,5 +1,5 @@
var test = require("tap").test
-var fs = require("../fs.js")
+var fs = require("fs")
var readdir = fs.readdir
fs.readdir = function(path, cb) {
@@ -14,7 +14,6 @@ test("readdir reorder", function (t) {
g.readdir("whatevers", function (er, files) {
if (er)
throw er
- console.error(files)
t.same(files, [ "a", "b", "z" ])
t.end()
})
diff --git a/test/readfile.js b/test/readfile.js
new file mode 100644
index 0000000..ce4f04f
--- /dev/null
+++ b/test/readfile.js
@@ -0,0 +1,47 @@
+'use strict'
+
+var fs = require('../')
+var rimraf = require('rimraf')
+var mkdirp = require('mkdirp')
+var test = require('tap').test
+var p = require('path').resolve(__dirname, 'files')
+
+process.chdir(__dirname)
+
+// Make sure to reserve the stderr fd
+process.stderr.write('')
+
+var num = 4097
+var paths = new Array(num)
+
+test('write files', function (t) {
+ rimraf.sync(p)
+ mkdirp.sync(p)
+
+ t.plan(num)
+ for (var i = 0; i < num; ++i) {
+ paths[i] = 'files/file-' + i
+ fs.writeFile(paths[i], 'content', 'ascii', function (er) {
+ if (er)
+ throw er
+ t.pass('written')
+ })
+ }
+})
+
+test('read files', function (t) {
+ // now read them
+ t.plan(num)
+ for (var i = 0; i < num; ++i) {
+ fs.readFile(paths[i], 'ascii', function (er, data) {
+ if (er)
+ throw er
+ t.equal(data, 'content')
+ })
+ }
+})
+
+test('cleanup', function (t) {
+ rimraf.sync(p)
+ t.end()
+})
diff --git a/test/stats-uid-gid.js b/test/stats-uid-gid.js
new file mode 100644
index 0000000..58ce661
--- /dev/null
+++ b/test/stats-uid-gid.js
@@ -0,0 +1,44 @@
+'use strict';
+var test = require('tap').test
+var util = require('util')
+var fs = require('fs')
+
+// mock fs.statSync to return signed uids/gids
+var realStatSync = fs.statSync
+fs.statSync = function(path) {
+ var stats = realStatSync.call(fs, path)
+ stats.uid = -2
+ stats.gid = -2
+ return stats
+}
+
+var gfs = require('../graceful-fs.js')
+
+test('graceful fs uses same stats constructor as fs', function (t) {
+ t.equal(gfs.Stats, fs.Stats, 'should reference the same constructor')
+
+ if (!process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH) {
+ t.equal(fs.statSync(__filename).uid, -2)
+ t.equal(fs.statSync(__filename).gid, -2)
+ }
+
+ t.equal(gfs.statSync(__filename).uid, 0xfffffffe)
+ t.equal(gfs.statSync(__filename).gid, 0xfffffffe)
+
+ t.end()
+})
+
+test('does not throw when async stat fails', function (t) {
+ gfs.stat(__filename + ' this does not exist', function (er, stats) {
+ t.ok(er)
+ t.notOk(stats)
+ t.end()
+ })
+})
+
+test('throws ENOENT when sync stat fails', function (t) {
+ t.throws(function() {
+ gfs.statSync(__filename + ' this does not exist')
+ }, /ENOENT/)
+ t.end()
+})
diff --git a/test/stats.js b/test/stats.js
new file mode 100644
index 0000000..4519012
--- /dev/null
+++ b/test/stats.js
@@ -0,0 +1,12 @@
+var test = require('tap').test
+var fs = require('fs')
+var gfs = require('../graceful-fs.js')
+
+test('graceful fs uses same stats constructor as fs', function (t) {
+ t.equal(gfs.Stats, fs.Stats, 'should reference the same constructor')
+ t.ok(fs.statSync(__filename) instanceof fs.Stats,
+ 'should be instance of fs.Stats')
+ t.ok(gfs.statSync(__filename) instanceof fs.Stats,
+ 'should be instance of fs.Stats')
+ t.end()
+})
diff --git a/test/write-then-read.js b/test/write-then-read.js
new file mode 100644
index 0000000..3a66df3
--- /dev/null
+++ b/test/write-then-read.js
@@ -0,0 +1,43 @@
+var fs = require('../');
+var rimraf = require('rimraf');
+var mkdirp = require('mkdirp');
+var test = require('tap').test;
+var p = require('path').resolve(__dirname, 'files');
+
+process.chdir(__dirname)
+
+// Make sure to reserve the stderr fd
+process.stderr.write('');
+
+var num = 4097;
+var paths = new Array(num);
+
+test('make files', function (t) {
+ rimraf.sync(p);
+ mkdirp.sync(p);
+
+ for (var i = 0; i < num; ++i) {
+ paths[i] = 'files/file-' + i;
+ fs.writeFileSync(paths[i], 'content');
+ }
+
+ t.end();
+})
+
+test('read files', function (t) {
+ // now read them
+ t.plan(num)
+ for (var i = 0; i < num; ++i) {
+ fs.readFile(paths[i], 'ascii', function(err, data) {
+ if (err)
+ throw err;
+
+ t.equal(data, 'content')
+ });
+ }
+});
+
+test('cleanup', function (t) {
+ rimraf.sync(p);
+ t.end();
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-graceful-fs.git
More information about the Pkg-javascript-commits
mailing list