diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 26ec260e..cb373658 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,11 @@ jobs: - 8 - 10 - 12 + exclude: + # Temporarily excludes node 12 on windows + # Wait for https://github.com/nodejs/node/issues/34514 + - os: windows-latest + node: 12 steps: - name: Clone repository diff --git a/lib/binding.js b/lib/binding.js index 0e34b9e4..0f70343c 100644 --- a/lib/binding.js +++ b/lib/binding.js @@ -240,19 +240,6 @@ Stats.prototype.isSocket = function() { return this._checkModeProperty(constants.S_IFSOCK); }; -// I don't know exactly what is going on. -// If _openFiles is a property of binding instance, there is a strange -// bug in nodejs v10+ that something cleaned up this._openFiles from -// nowhere. It happens after second mockfs(), after first mockfs()+restore(). - -// So I moved _openFiles to a private var. The other two vars (_system, -// _counter) do not hurt. -// This fixed https://github.com/tschaub/mock-fs/issues/254 -// But I did not dig deep enough to understand what exactly happened. -let _system; -let _openFiles = {}; -let _counter = 0; - /** * Create a new binding with the given file system. * @param {FileSystem} system Mock file system. @@ -263,7 +250,7 @@ function Binding(system) { * Mock file system. * @type {FileSystem} */ - _system = system; + this._system = system; /** * Stats constructor. @@ -275,13 +262,13 @@ function Binding(system) { * Lookup of open files. * @type {Object.} */ - _openFiles = {}; + this._openFiles = {}; /** * Counter for file descriptors. * @type {number} */ - _counter = 0; + this._counter = 0; } /** @@ -289,7 +276,7 @@ function Binding(system) { * @return {FileSystem} The underlying file system. */ Binding.prototype.getSystem = function() { - return _system; + return this._system; }; /** @@ -297,7 +284,7 @@ Binding.prototype.getSystem = function() { * @param {FileSystem} system The new file system. */ Binding.prototype.setSystem = function(system) { - _system = system; + this._system = system; }; /** @@ -305,34 +292,34 @@ Binding.prototype.setSystem = function(system) { * @param {number} fd File descriptor identifier. * @return {FileDescriptor} File descriptor. */ -function getDescriptorById(fd) { - if (!_openFiles.hasOwnProperty(fd)) { +Binding.prototype.getDescriptorById = function(fd) { + if (!this._openFiles.hasOwnProperty(fd)) { throw new FSError('EBADF'); } - return _openFiles[fd]; -} + return this._openFiles[fd]; +}; /** * Keep track of a file descriptor as open. * @param {FileDescriptor} descriptor The file descriptor. * @return {number} Identifier for file descriptor. */ -function trackDescriptor(descriptor) { - const fd = ++_counter; - _openFiles[fd] = descriptor; +Binding.prototype.trackDescriptor = function(descriptor) { + const fd = ++this._counter; + this._openFiles[fd] = descriptor; return fd; -} +}; /** * Stop tracking a file descriptor as open. * @param {number} fd Identifier for file descriptor. */ -function untrackDescriptorById(fd) { - if (!_openFiles.hasOwnProperty(fd)) { +Binding.prototype.untrackDescriptorById = function(fd) { + if (!this._openFiles.hasOwnProperty(fd)) { throw new FSError('EBADF'); } - delete _openFiles[fd]; -} + delete this._openFiles[fd]; +}; /** * Resolve the canonicalized absolute pathname. @@ -349,14 +336,14 @@ Binding.prototype.realpath = function(filepath, encoding, callback, ctx) { filepath = deBuffer(filepath); const resolved = path.resolve(filepath); const parts = getPathParts(resolved); - let item = _system.getRoot(); + let item = this._system.getRoot(); let itemPath = '/'; let name, i, ii; for (i = 0, ii = parts.length; i < ii; ++i) { name = parts[i]; while (item instanceof SymbolicLink) { itemPath = path.resolve(path.dirname(itemPath), item.getPath()); - item = _system.getItem(itemPath); + item = this._system.getItem(itemPath); } if (!item) { throw new FSError('ENOENT', filepath); @@ -371,7 +358,7 @@ Binding.prototype.realpath = function(filepath, encoding, callback, ctx) { if (item) { while (item instanceof SymbolicLink) { itemPath = path.resolve(path.dirname(itemPath), item.getPath()); - item = _system.getItem(itemPath); + item = this._system.getItem(itemPath); } realPath = itemPath; } else { @@ -453,9 +440,9 @@ Binding.prototype.stat = function(filepath, options, callback, ctx) { return maybeCallback(wrapStatsCallback(callback), ctx, this, function() { filepath = deBuffer(filepath); - let item = _system.getItem(filepath); + let item = this._system.getItem(filepath); if (item instanceof SymbolicLink) { - item = _system.getItem( + item = this._system.getItem( path.resolve(path.dirname(filepath), item.getPath()) ); } @@ -496,7 +483,7 @@ Binding.prototype.fstat = function(fd, options, callback, ctx) { markSyscall(ctx, 'fstat'); return maybeCallback(wrapStatsCallback(callback), ctx, this, function() { - const descriptor = getDescriptorById(fd); + const descriptor = this.getDescriptorById(fd); const item = descriptor.getItem(); const stats = item.getStats(); @@ -525,7 +512,7 @@ Binding.prototype.close = function(fd, callback, ctx) { markSyscall(ctx, 'close'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - untrackDescriptorById(fd); + this.untrackDescriptorById(fd); }); }; @@ -544,9 +531,9 @@ Binding.prototype.open = function(pathname, flags, mode, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { pathname = deBuffer(pathname); const descriptor = new FileDescriptor(flags); - let item = _system.getItem(pathname); + let item = this._system.getItem(pathname); while (item instanceof SymbolicLink) { - item = _system.getItem( + item = this._system.getItem( path.resolve(path.dirname(pathname), item.getPath()) ); } @@ -554,7 +541,7 @@ Binding.prototype.open = function(pathname, flags, mode, callback, ctx) { throw new FSError('EEXIST', pathname); } if (descriptor.isCreate() && !item) { - const parent = _system.getItem(path.dirname(pathname)); + const parent = this._system.getItem(path.dirname(pathname)); if (!parent) { throw new FSError('ENOENT', pathname); } @@ -594,7 +581,7 @@ Binding.prototype.open = function(pathname, flags, mode, callback, ctx) { descriptor.setPosition(item.getContent().length); } descriptor.setItem(item); - return trackDescriptor(descriptor); + return this.trackDescriptor(descriptor); }); }; @@ -607,6 +594,7 @@ Binding.prototype.open = function(pathname, flags, mode, callback, ctx) { */ Binding.prototype.openFileHandle = function(pathname, flags, mode, callback) { const self = this; + return this.open(pathname, flags, mode, kUsePromises).then(function(fd) { // nodejs v10+ fs.promises FileHandler constructor only ask these three properties. return { @@ -644,7 +632,7 @@ Binding.prototype.read = function( markSyscall(ctx, 'read'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - const descriptor = getDescriptorById(fd); + const descriptor = this.getDescriptorById(fd); if (!descriptor.isRead()) { throw new FSError('EBADF'); } @@ -686,7 +674,7 @@ Binding.prototype.copyFile = function(src, dest, flags, callback, ctx) { const srcFd = this.open(src, constants.O_RDONLY); try { - const srcDescriptor = getDescriptorById(srcFd); + const srcDescriptor = this.getDescriptorById(srcFd); if (!srcDescriptor.isRead()) { throw new FSError('EBADF'); } @@ -737,7 +725,7 @@ Binding.prototype.writeBuffers = function( markSyscall(ctx, 'write'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - const descriptor = getDescriptorById(fd); + const descriptor = this.getDescriptorById(fd); if (!descriptor.isWrite()) { throw new FSError('EBADF'); } @@ -789,7 +777,7 @@ Binding.prototype.writeBuffer = function( markSyscall(ctx, 'write'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - const descriptor = getDescriptorById(fd); + const descriptor = this.getDescriptorById(fd); if (!descriptor.isWrite()) { throw new FSError('EBADF'); } @@ -884,14 +872,14 @@ Binding.prototype.rename = function(oldPath, newPath, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { oldPath = deBuffer(oldPath); newPath = deBuffer(newPath); - const oldItem = _system.getItem(oldPath); + const oldItem = this._system.getItem(oldPath); if (!oldItem) { throw new FSError('ENOENT', oldPath); } - const oldParent = _system.getItem(path.dirname(oldPath)); + const oldParent = this._system.getItem(path.dirname(oldPath)); const oldName = path.basename(oldPath); - const newItem = _system.getItem(newPath); - const newParent = _system.getItem(path.dirname(newPath)); + const newItem = this._system.getItem(newPath); + const newParent = this._system.getItem(path.dirname(newPath)); const newName = path.basename(newPath); if (newItem) { // make sure they are the same type @@ -951,10 +939,10 @@ Binding.prototype.readdir = function( return maybeCallback(normalizeCallback(callback), ctx, this, function() { dirpath = deBuffer(dirpath); let dpath = dirpath; - let dir = _system.getItem(dirpath); + let dir = this._system.getItem(dirpath); while (dir instanceof SymbolicLink) { dpath = path.resolve(path.dirname(dpath), dir.getPath()); - dir = _system.getItem(dpath); + dir = this._system.getItem(dpath); } if (!dir) { throw new FSError('ENOENT', dirpath); @@ -1006,7 +994,7 @@ Binding.prototype.mkdir = function(pathname, mode, recursive, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { pathname = deBuffer(pathname); - const item = _system.getItem(pathname); + const item = this._system.getItem(pathname); if (item) { if (recursive && item instanceof Directory) { // silently pass existing folder in recursive mode @@ -1017,7 +1005,7 @@ Binding.prototype.mkdir = function(pathname, mode, recursive, callback, ctx) { const _mkdir = function(_pathname) { const parentDir = path.dirname(_pathname); - let parent = _system.getItem(parentDir); + let parent = this._system.getItem(parentDir); if (!parent) { if (!recursive) { throw new FSError('ENOENT', _pathname); @@ -1047,7 +1035,7 @@ Binding.prototype.rmdir = function(pathname, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { pathname = deBuffer(pathname); - const item = _system.getItem(pathname); + const item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } @@ -1058,7 +1046,7 @@ Binding.prototype.rmdir = function(pathname, callback, ctx) { throw new FSError('ENOTEMPTY', pathname); } this.access(path.dirname(pathname), parseInt('0002', 8)); - const parent = _system.getItem(path.dirname(pathname)); + const parent = this._system.getItem(path.dirname(pathname)); parent.removeItem(path.basename(pathname)); }); }; @@ -1087,7 +1075,7 @@ Binding.prototype.mkdtemp = function(prefix, encoding, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { prefix = prefix.replace(/X{0,6}$/, 'XXXXXX'); const parentPath = path.dirname(prefix); - const parent = _system.getItem(parentPath); + const parent = this._system.getItem(parentPath); if (!parent) { throw new FSError('ENOENT', prefix); } @@ -1139,7 +1127,7 @@ Binding.prototype.ftruncate = function(fd, len, callback, ctx) { markSyscall(ctx, 'ftruncate'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - const descriptor = getDescriptorById(fd); + const descriptor = this.getDescriptorById(fd); if (!descriptor.isWrite()) { throw new FSError('EINVAL'); } @@ -1176,7 +1164,7 @@ Binding.prototype.chown = function(pathname, uid, gid, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { pathname = deBuffer(pathname); - const item = _system.getItem(pathname); + const item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } @@ -1197,7 +1185,7 @@ Binding.prototype.fchown = function(fd, uid, gid, callback, ctx) { markSyscall(ctx, 'fchown'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - const descriptor = getDescriptorById(fd); + const descriptor = this.getDescriptorById(fd); const item = descriptor.getItem(); item.setUid(uid); item.setGid(gid); @@ -1216,7 +1204,7 @@ Binding.prototype.chmod = function(pathname, mode, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { pathname = deBuffer(pathname); - const item = _system.getItem(pathname); + const item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } @@ -1235,7 +1223,7 @@ Binding.prototype.fchmod = function(fd, mode, callback, ctx) { markSyscall(ctx, 'fchmod'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - const descriptor = getDescriptorById(fd); + const descriptor = this.getDescriptorById(fd); const item = descriptor.getItem(); item.setMode(mode); }); @@ -1252,14 +1240,14 @@ Binding.prototype.unlink = function(pathname, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { pathname = deBuffer(pathname); - const item = _system.getItem(pathname); + const item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } if (item instanceof Directory) { throw new FSError('EPERM', pathname); } - const parent = _system.getItem(path.dirname(pathname)); + const parent = this._system.getItem(path.dirname(pathname)); parent.removeItem(path.basename(pathname)); }); }; @@ -1277,7 +1265,7 @@ Binding.prototype.utimes = function(pathname, atime, mtime, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { pathname = deBuffer(pathname); - const item = _system.getItem(pathname); + const item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } @@ -1298,7 +1286,7 @@ Binding.prototype.futimes = function(fd, atime, mtime, callback, ctx) { markSyscall(ctx, 'futimes'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - const descriptor = getDescriptorById(fd); + const descriptor = this.getDescriptorById(fd); const item = descriptor.getItem(); item.setATime(new Date(atime * 1000)); item.setMTime(new Date(mtime * 1000)); @@ -1315,7 +1303,7 @@ Binding.prototype.fsync = function(fd, callback, ctx) { markSyscall(ctx, 'fsync'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - getDescriptorById(fd); + this.getDescriptorById(fd); }); }; @@ -1329,7 +1317,7 @@ Binding.prototype.fdatasync = function(fd, callback, ctx) { markSyscall(ctx, 'fdatasync'); return maybeCallback(normalizeCallback(callback), ctx, this, function() { - getDescriptorById(fd); + this.getDescriptorById(fd); }); }; @@ -1346,17 +1334,17 @@ Binding.prototype.link = function(srcPath, destPath, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { srcPath = deBuffer(srcPath); destPath = deBuffer(destPath); - const item = _system.getItem(srcPath); + const item = this._system.getItem(srcPath); if (!item) { throw new FSError('ENOENT', srcPath); } if (item instanceof Directory) { throw new FSError('EPERM', srcPath); } - if (_system.getItem(destPath)) { + if (this._system.getItem(destPath)) { throw new FSError('EEXIST', destPath); } - const parent = _system.getItem(path.dirname(destPath)); + const parent = this._system.getItem(path.dirname(destPath)); if (!parent) { throw new FSError('ENOENT', destPath); } @@ -1381,10 +1369,10 @@ Binding.prototype.symlink = function(srcPath, destPath, type, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { srcPath = deBuffer(srcPath); destPath = deBuffer(destPath); - if (_system.getItem(destPath)) { + if (this._system.getItem(destPath)) { throw new FSError('EEXIST', destPath); } - const parent = _system.getItem(path.dirname(destPath)); + const parent = this._system.getItem(path.dirname(destPath)); if (!parent) { throw new FSError('ENOENT', destPath); } @@ -1416,7 +1404,7 @@ Binding.prototype.readlink = function(pathname, encoding, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { pathname = deBuffer(pathname); - const link = _system.getItem(pathname); + const link = this._system.getItem(pathname); if (!link) { throw new FSError('ENOENT', pathname); } @@ -1450,7 +1438,7 @@ Binding.prototype.lstat = function(filepath, options, callback, ctx) { return maybeCallback(wrapStatsCallback(callback), ctx, this, function() { filepath = deBuffer(filepath); - const item = _system.getItem(filepath); + const item = this._system.getItem(filepath); if (!item) { throw new FSError('ENOENT', filepath); } @@ -1483,14 +1471,14 @@ Binding.prototype.access = function(filepath, mode, callback, ctx) { return maybeCallback(normalizeCallback(callback), ctx, this, function() { filepath = deBuffer(filepath); - let item = _system.getItem(filepath); + let item = this._system.getItem(filepath); let links = 0; while (item instanceof SymbolicLink) { if (links > MAX_LINKS) { throw new FSError('ELOOP', filepath); } filepath = path.resolve(path.dirname(filepath), item.getPath()); - item = _system.getItem(filepath); + item = this._system.getItem(filepath); ++links; } if (!item) { diff --git a/lib/index.js b/lib/index.js index 0f78d734..2f6b5ac6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -34,7 +34,7 @@ function patch(key) { const existingMethod = realBinding[key]; realBinding[key] = function() { if (this._mockedBinding) { - return this._mockedBinding[key].apply(this, arguments); + return this._mockedBinding[key].apply(this._mockedBinding, arguments); } else { return existingMethod.apply(this, arguments); } @@ -52,17 +52,6 @@ for (const key in Binding.prototype) { function overrideBinding(binding) { realBinding._mockedBinding = binding; - - for (const key in binding) { - if (typeof realBinding[key] === 'function') { - // Stats and StatWatcher are constructors - if (key === 'Stats' || key === 'StatWatcher') { - realBinding[key] = binding[key]; - } - } else if (typeof realBinding[key] === 'undefined') { - realBinding[key] = binding[key]; - } - } } function overrideProcess(cwd, chdir) { @@ -156,7 +145,7 @@ exports = module.exports = function mock(config, options) { */ exports.getMockRoot = function() { if (realBinding._mockedBinding) { - return realBinding.getSystem().getRoot(); + return realBinding._mockedBinding.getSystem().getRoot(); } else { return {}; }