From 7fd64d689afd2dfd1c37ec8b66544816c58b29b7 Mon Sep 17 00:00:00 2001 From: Matt Henkes Date: Thu, 7 Sep 2017 22:16:42 -0500 Subject: [PATCH] initial commit --- lib/MemoryFileSystem.js | 62 ++++++++++++++++++++++++++-------------- test/MemoryFileSystem.js | 23 +++++++++++++-- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/lib/MemoryFileSystem.js b/lib/MemoryFileSystem.js index 1d9d3d2..45ee213 100644 --- a/lib/MemoryFileSystem.js +++ b/lib/MemoryFileSystem.js @@ -29,7 +29,7 @@ function pathToArray(path) { const nix = /^\//.test(path); if(!nix) { if(!/^[A-Za-z]:/.test(path)) { - throw new MemoryFileSystemError(errors.code.EINVAL, path); + return null; } path = path.replace(/[\\\/]+/g, "\\"); // multi slashs path = path.split(/[\\\/]/); @@ -55,6 +55,9 @@ class MemoryFileSystem { meta(_path) { const path = pathToArray(_path); + if (path === null) { + return this.throwError('meta', [_path], [errors.code.EINVAL, _path, "meta"]); + } let current = this.data; let i = 0; for(; i < path.length - 1; i++) { @@ -92,24 +95,27 @@ class MemoryFileSystem { isSocket: falseFn }; } else { - throw new MemoryFileSystemError(errors.code.ENOENT, _path, "stat"); + return this.throwError('statSync', [_path], [errors.code.ENOENT, _path, "stat"]); } } readFileSync(_path, optionsOrEncoding) { const path = pathToArray(_path); + if (path === null) { + return this.throwError('readFileSync', [_path, optionsOrEncoding], [errors.code.EINVAL, _path, "meta"]); + } let current = this.data; let i = 0 for(; i < path.length - 1; i++) { if(!isDir(current[path[i]])) - throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readFile"); + return this.throwError('readFileSync', [_path, optionsOrEncoding], [errors.code.ENOENT, _path, "readFile"]); current = current[path[i]]; } if(!isFile(current[path[i]])) { if(isDir(current[path[i]])) - throw new MemoryFileSystemError(errors.code.EISDIR, _path, "readFile"); + return this.throwError('readFileSync', [_path, optionsOrEncoding], [errors.code.EISDIR, _path, "readFile"]); else - throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readFile"); + return this.throwError('readFileSync', [_path, optionsOrEncoding], [errors.code.ENOENT, _path, "readFile"]); } current = current[path[i]]; const encoding = typeof optionsOrEncoding === "object" ? optionsOrEncoding.encoding : optionsOrEncoding; @@ -119,29 +125,35 @@ class MemoryFileSystem { readdirSync(_path) { if(_path === "/") return Object.keys(this.data).filter(Boolean); const path = pathToArray(_path); + if (path === null) { + return this.throwError('readdirSync', [_path], [errors.code.EINVAL, _path, "meta"]); + } let current = this.data; let i = 0; for(; i < path.length - 1; i++) { if(!isDir(current[path[i]])) - throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readdir"); + return this.throwError('readdirSync', [_path], [errors.code.ENOENT, _path, "readdir"]); current = current[path[i]]; } if(!isDir(current[path[i]])) { if(isFile(current[path[i]])) - throw new MemoryFileSystemError(errors.code.ENOTDIR, _path, "readdir"); + return this.throwError('readdirSync', [_path], [errors.code.ENOTDIR, _path, "readdir"]); else - throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readdir"); + return this.throwError('readdirSync', [_path], [errors.code.ENOENT, _path, "readdir"]); } return Object.keys(current[path[i]]).filter(Boolean); } mkdirpSync(_path) { const path = pathToArray(_path); + if (path === null) { + return this.throwError('mkdirpSync', [_path], [errors.code.EINVAL, _path, "mkdirp"]); + } if(path.length === 0) return; let current = this.data; for(let i = 0; i < path.length; i++) { if(isFile(current[path[i]])) - throw new MemoryFileSystemError(errors.code.ENOTDIR, _path, "mkdirp"); + return this.throwError('mkdirpSync', [_path], [errors.code.ENOTDIR, _path, "mkdirp"]); else if(!isDir(current[path[i]])) current[path[i]] = {"":true}; current = current[path[i]]; @@ -151,18 +163,21 @@ class MemoryFileSystem { mkdirSync(_path) { const path = pathToArray(_path); + if (path === null) { + return this.throwError('mkdirSync', [_path], [errors.code.EINVAL, _path, "mkdir"]); + } if(path.length === 0) return; let current = this.data; let i = 0; for(; i < path.length - 1; i++) { if(!isDir(current[path[i]])) - throw new MemoryFileSystemError(errors.code.ENOENT, _path, "mkdir"); + return this.throwError('mkdirSync', [_path], [errors.code.ENOENT, _path, "mkdir"]); current = current[path[i]]; } if(isDir(current[path[i]])) - throw new MemoryFileSystemError(errors.code.EEXIST, _path, "mkdir"); + return this.throwError('mkdirSync', [_path], [errors.code.EEXIST, _path, "mkdir"]); else if(isFile(current[path[i]])) - throw new MemoryFileSystemError(errors.code.ENOTDIR, _path, "mkdir"); + return this.throwError('mkdirSync', [_path], [errors.code.ENOTDIR, _path, "mkdir"]); current[path[i]] = {"":true}; return; } @@ -170,18 +185,19 @@ class MemoryFileSystem { _remove(_path, name, testFn) { const path = pathToArray(_path); const operation = name === "File" ? "unlink" : "rmdir"; - if(path.length === 0) { - throw new MemoryFileSystemError(errors.code.EPERM, _path, operation); + const fn = name === "File" ? "unlinkSync" : "rmdirSync"; + if(path === null || path.length === 0) { + return this.throwError(fn, [_path], [errors.code.EPERM, _path, operation]); } let current = this.data; let i = 0; for(; i < path.length - 1; i++) { if(!isDir(current[path[i]])) - throw new MemoryFileSystemError(errors.code.ENOENT, _path, operation); + return this.throwError(fn, [_path], [errors.code.ENOENT, _path, operation]); current = current[path[i]]; } if(!testFn(current[path[i]])) - throw new MemoryFileSystemError(errors.code.ENOENT, _path, operation); + return this.throwError(fn, [_path], [errors.code.ENOENT, _path, operation]); delete current[path[i]]; return; } @@ -195,24 +211,24 @@ class MemoryFileSystem { } readlinkSync(_path) { - throw new MemoryFileSystemError(errors.code.ENOSYS, _path, "readlink"); + return this.throwError('readlinkSync', [_path], [errors.code.ENOSYS, _path, "readlink"]); } writeFileSync(_path, content, optionsOrEncoding) { if(!content && !optionsOrEncoding) throw new Error("No content"); const path = pathToArray(_path); - if(path.length === 0) { - throw new MemoryFileSystemError(errors.code.EISDIR, _path, "writeFile"); + if(path === null || path.length === 0) { + return this.throwError('writeFileSync', [_path, content, optionsOrEncoding], [errors.code.EISDIR, _path, "writeFile"]); } let current = this.data; let i = 0 for(; i < path.length - 1; i++) { if(!isDir(current[path[i]])) - throw new MemoryFileSystemError(errors.code.ENOENT, _path, "writeFile"); + return this.throwError('writeFileSync', [_path, content, optionsOrEncoding], [errors.code.ENOENT, _path, "writeFile"]); current = current[path[i]]; } if(isDir(current[path[i]])) - throw new MemoryFileSystemError(errors.code.EISDIR, _path, "writeFile"); + return this.throwError('writeFileSync', [_path, content, optionsOrEncoding], [errors.code.EISDIR, _path, "writeFile"]); const encoding = typeof optionsOrEncoding === "object" ? optionsOrEncoding.encoding : optionsOrEncoding; current[path[i]] = optionsOrEncoding || typeof content === "string" ? new Buffer(content, encoding) : content; return; @@ -288,6 +304,10 @@ class MemoryFileSystem { } return callback(); } + + throwError(fn, args, error) { + throw new MemoryFileSystemError(...error); + } } // async functions diff --git a/test/MemoryFileSystem.js b/test/MemoryFileSystem.js index 7eceffb..d684276 100644 --- a/test/MemoryFileSystem.js +++ b/test/MemoryFileSystem.js @@ -205,6 +205,19 @@ describe("errors", function() { fs.readlinkSync("/test/dir/link"); }).should.throw(); }); + it("should throw on throwError", function () { + var fs = new MemoryFileSystem(); + (function() { + fs.throwError("fn", [], [errors.code.ENOSYS, _path, "fn"]); + }).should.throw(); + }); + it("should allow overridable errors", function() { + var fs = new MemoryFileSystem(); + fs.throwError = function(fn, args, error) { + return 'fallback file'; + }; + fs.readFileSync("/test/dir").should.be.eql("fallback file"); + }); }); describe("async", function() { ["stat", "readdir", "mkdirp", "rmdir", "unlink", "readlink"].forEach(function(methodName) { @@ -367,11 +380,15 @@ describe("pathToArray", function() { fs.pathToArray("/a/b/c").should.be.eql(["a", "b", "c"]); fs.pathToArray("C:/a/b").should.be.eql(["C:", "a", "b"]); fs.pathToArray("C:\\a\\b").should.be.eql(["C:", "a", "b"]); + fs.pathToArray("/a").should.be.eql(["a"]); + fs.pathToArray("/").should.be.eql([]); }); it("should fail on invalid paths", function() { - (function() { - fs.pathToArray("0:/"); - }).should.throw(); + var fs = new MemoryFileSystem(); + // eslint-disable-next-line no-unused-expressions + should(fs.pathToArray("0:/")).not.be.ok; + // eslint-disable-next-line no-unused-expressions + should(fs.pathToArray("")).not.be.ok; }); }); describe("join", function() {