├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github └── workflows │ └── node-js-ci.yml ├── .gitignore ├── .npmrc ├── .release-it.json ├── .travis.yml ├── AUTHORS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── dist ├── filer.js ├── filer.js.map ├── filer.map ├── filer.min.js ├── filer.min.js.map └── filer.min.map ├── env.sample ├── karma.conf.js ├── lib ├── async.js ├── eventemitter.js └── intercom.js ├── package-lock.json ├── package.json ├── perf ├── index.html ├── index.js └── simple-statistics │ ├── .gitignore │ ├── .jshintrc │ ├── .travis.yml │ ├── API.md │ ├── CHANGELOG.md │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── README.test.md │ ├── SEEALSO.md │ ├── api.js │ ├── bower.json │ ├── component.json │ ├── docs │ ├── docco.css │ └── simple_statistics.html │ ├── index.html │ ├── package.json │ ├── src │ └── simple_statistics.js │ └── test │ ├── bayes.test.js │ ├── bernoulli_distribution.test.js │ ├── binomial_distribution.test.js │ ├── chi_squared_goodness_of_fit.test.js │ ├── chunks.test.js │ ├── cumulative.js │ ├── factorial.test.js │ ├── geometric_mean.test.js │ ├── harmonic_mean.test.js │ ├── iqr.test.js │ ├── jenks.test.js │ ├── linear_regression.test.js │ ├── mad.test.js │ ├── mean.test.js │ ├── median.test.js │ ├── minmax.test.js │ ├── mixin.test.js │ ├── mode.test.js │ ├── normal_distribution.test.js │ ├── poisson_distribution.test.js │ ├── quantile.test.js │ ├── quantilesorted.test.js │ ├── r_squared.test.js │ ├── root_mean_square.test.js │ ├── sample.test.js │ ├── sample_correlation.test.js │ ├── sample_covariance.test.js │ ├── sample_skewness.test.js │ ├── sample_standard_deviation.test.js │ ├── sample_variance.test.js │ ├── shuffle.test.js │ ├── standard_deviation.test.js │ ├── standard_normal_table.js │ ├── sum.test.js │ ├── t_test.test.js │ └── variance.test.js ├── shims ├── fs.js ├── path.js └── providers │ ├── default.js │ ├── indexeddb.js │ └── memory.js ├── src ├── constants.js ├── directory-entry.js ├── dirent.js ├── errors.js ├── filesystem │ ├── implementation.js │ └── interface.js ├── fs-watcher.js ├── index.js ├── node.js ├── open-file-description.js ├── open-files.js ├── path.js ├── providers │ ├── index.js │ ├── indexeddb.js │ └── memory.js ├── shared.js ├── shell │ ├── environment.js │ └── shell.js ├── stats.js ├── super-node.js └── webpack-plugin │ ├── index.js │ ├── processors.js │ ├── schema.js │ └── utils.js ├── tests ├── bugs │ ├── issue105.js │ ├── issue106.js │ ├── issue239.js │ ├── issue247.js │ ├── issue249.js │ ├── issue254.js │ ├── issue258.js │ ├── issue267.js │ ├── issue270.js │ ├── issue357.js │ ├── issue773.js │ ├── issue775.js │ ├── issue776.js │ ├── ls-depth-bug.js │ └── rename-dir-trailing-slash.js ├── filesystems │ ├── images │ │ ├── README.md │ │ └── tiny-fs.0.43.json │ ├── migrations │ │ └── from-0.43.test.js │ └── tiny-fs │ │ ├── dir │ │ └── file2.txt │ │ └── file.txt ├── index.html ├── index.js ├── lib │ ├── indexeddb.js │ ├── memory.js │ ├── serializable-memory-provider.js │ └── test-utils.js ├── spec │ ├── errors.spec.js │ ├── filer.buffer.spec.js │ ├── filer.filesystem.spec.js │ ├── filer.spec.js │ ├── fs.access.spec.js │ ├── fs.appendFile.spec.js │ ├── fs.chmod.spec.js │ ├── fs.chown.spec.js │ ├── fs.close.spec.js │ ├── fs.copyFile.spec.js │ ├── fs.exists.spec.js │ ├── fs.fsync.spec.js │ ├── fs.ftruncate.spec.js │ ├── fs.link.spec.js │ ├── fs.lseek.spec.js │ ├── fs.lstat.spec.js │ ├── fs.mkdir.spec.js │ ├── fs.mkdtemp.spec.js │ ├── fs.mknod.spec.js │ ├── fs.open.spec.js │ ├── fs.read.spec.js │ ├── fs.readdir.spec.js │ ├── fs.readlink.spec.js │ ├── fs.rename.spec.js │ ├── fs.rmdir.spec.js │ ├── fs.shell.spec.js │ ├── fs.spec.js │ ├── fs.stat.spec.js │ ├── fs.symlink.spec.js │ ├── fs.truncate.spec.js │ ├── fs.unlink.spec.js │ ├── fs.unwatchFile.spec.js │ ├── fs.utimes.spec.js │ ├── fs.watch.spec.js │ ├── fs.write.spec.js │ ├── fs.writeFile-readFile.spec.js │ ├── fs.xattr.spec.js │ ├── node-js │ │ └── simple │ │ │ ├── test-fs-mkdir.js │ │ │ ├── test-fs-null-bytes.js │ │ │ ├── test-fs-watch-recursive.js │ │ │ └── test-fs-watch.js │ ├── path-resolution.spec.js │ ├── providers │ │ ├── providers.base.js │ │ ├── providers.indexeddb.spec.js │ │ ├── providers.memory.spec.js │ │ ├── providers.spec.js │ │ └── serializable-memory-provider.spec.js │ ├── readme.example.spec.js │ ├── shell │ │ ├── cat.spec.js │ │ ├── cd.spec.js │ │ ├── env.spec.js │ │ ├── exec.spec.js │ │ ├── find.spec.js │ │ ├── ls.spec.js │ │ ├── mkdirp.spec.js │ │ ├── rm.spec.js │ │ └── touch.spec.js │ ├── shims │ │ ├── fs.spec.js │ │ └── path.spec.js │ ├── time-flags.spec.js │ ├── times.spec.js │ ├── trailing-slashes.spec.js │ └── webpack-plugin │ │ └── webpack-plugin.spec.js ├── test-dir.zip ├── test-file.txt └── test-file.txt.zip ├── tools ├── fs-image.js └── get-filer-version.js └── webpack └── index.js /.eslintignore: -------------------------------------------------------------------------------- 1 | tests/dist/ -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "mocha": true, 7 | "node": true 8 | }, 9 | "extends": "eslint:recommended", 10 | "parserOptions": { 11 | "ecmaVersion": 2017, 12 | "sourceType": "module" 13 | }, 14 | "rules": { 15 | "indent": [ 16 | "error", 17 | 2 18 | ], 19 | "linebreak-style": [ 20 | "error", 21 | "unix" 22 | ], 23 | "quotes": [ 24 | "error", 25 | "single" 26 | ], 27 | "semi": [ 28 | "error", 29 | "always" 30 | ], 31 | "eqeqeq": [ 32 | "error", 33 | "always" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/node-js-ci.yml: -------------------------------------------------------------------------------- 1 | name: node-js-ci 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: ${{matrix.os}} 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, macos-latest, windows-latest] 17 | node: ['14', '16'] 18 | 19 | name: Node ${{ matrix.node }} on ${{ matrix.os }} 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Test 23 | uses: actions/setup-node@v2.4.1 24 | with: 25 | node-version: ${{ matrix.node }} 26 | - run: npm install 27 | - run: npm test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | *~ 4 | .vscode 5 | .idea 6 | 7 | # Parcel build dirs 8 | .cache 9 | tests/dist 10 | 11 | # nyc code coverage 12 | .nyc_output 13 | coverage 14 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | loglevel=silent 2 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "before:init": ["npm run test"], 4 | "before:bump": ["npm run build"] 5 | }, 6 | "git": { 7 | "pushRepo": "git@github.com:filerjs/filer.git", 8 | "tagName": "v${version}" 9 | }, 10 | "npm": { 11 | "publish": true 12 | }, 13 | "github": { 14 | "pushRepo": "git@github.com:filerjs/filer.git", 15 | "release": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | 4 | node_js: 5 | - "lts/*" 6 | 7 | cache: 8 | directories: 9 | - "node_modules" 10 | 11 | os: 12 | - linux 13 | - osx 14 | 15 | # Setup headless Firefox and Chrome support 16 | # https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-the-Chrome-addon-in-the-headless-mode 17 | env: 18 | - MOZ_HEADLESS=1 19 | addons: 20 | chrome: stable 21 | firefox: latest 22 | before_install: 23 | - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost & 24 | 25 | after_success: 26 | - npm install -g codecov 27 | - npm run coverage 28 | - codecov 29 | 30 | notifications: 31 | email: false 32 | irc: "irc.mozilla.org#filer" 33 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Alan K (blog.modeswitch.org) 2 | David Humphrey (@humphd) 3 | Abir Viqar 4 | Barry Tulchinsky (@btulchinsky) 5 | Kieran Sedgwick (@sedge) 6 | Yoav Gurevich 7 | Gideon Thomas 8 | Abdirahman Guled 9 | Ben Heidemann -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 - 2019 Alan Kligman and the Filer contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 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. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. 10 | -------------------------------------------------------------------------------- /env.sample: -------------------------------------------------------------------------------- 1 | ### 2 | # Dev ENVIRONMENT file 3 | # 4 | # Copy to .env to use defaults when releasing via `npm release` 5 | ### 6 | 7 | # GitHub Personal Access Token (to push releases) 8 | # https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ 9 | GITHUB_TOKEN= 10 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | singleRun: true, 4 | basePath: '', 5 | files: [ 6 | 'node_modules/regenerator-runtime/runtime.js', 7 | 'tests/dist/index.js' 8 | ], 9 | frameworks: ['mocha', 'chai'], 10 | reporters: ['mocha', 'summary'], 11 | client: { 12 | captureConsole: true, 13 | mocha: { 14 | ui: 'bdd', 15 | timeout: 5000, 16 | slow: 250 17 | } 18 | }, 19 | summaryReporter: { 20 | // 'failed', 'skipped' or 'all' 21 | show: 'failed', 22 | // Limit the spec label to this length 23 | specLength: 50, 24 | // Show an 'all' column as a summary 25 | overviewColumn: true 26 | } 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /lib/async.js: -------------------------------------------------------------------------------- 1 | /*global setImmediate: false, setTimeout: false, console: false */ 2 | 3 | /** 4 | * async.js shim, based on https://raw.github.com/caolan/async/master/lib/async.js Feb 18, 2014 5 | * Used under MIT - https://github.com/caolan/async/blob/master/LICENSE 6 | */ 7 | 8 | (function () { 9 | 10 | var async = {}; 11 | 12 | // async.js functions used in Filer 13 | 14 | //// nextTick implementation with browser-compatible fallback //// 15 | if (typeof process === 'undefined' || !(process.nextTick)) { 16 | if (typeof setImmediate === 'function') { 17 | async.nextTick = function (fn) { 18 | // not a direct alias for IE10 compatibility 19 | setImmediate(fn); 20 | }; 21 | async.setImmediate = async.nextTick; 22 | } 23 | else { 24 | async.nextTick = function (fn) { 25 | setTimeout(fn, 0); 26 | }; 27 | async.setImmediate = async.nextTick; 28 | } 29 | } 30 | else { 31 | async.nextTick = process.nextTick; 32 | if (typeof setImmediate !== 'undefined') { 33 | async.setImmediate = function (fn) { 34 | // not a direct alias for IE10 compatibility 35 | setImmediate(fn); 36 | }; 37 | } 38 | else { 39 | async.setImmediate = async.nextTick; 40 | } 41 | } 42 | 43 | async.eachSeries = function (arr, iterator, callback) { 44 | callback = callback || function () {}; 45 | if (!arr.length) { 46 | return callback(); 47 | } 48 | var completed = 0; 49 | var iterate = function () { 50 | iterator(arr[completed], function (err) { 51 | if (err) { 52 | callback(err); 53 | callback = function () {}; 54 | } 55 | else { 56 | completed += 1; 57 | if (completed >= arr.length) { 58 | callback(); 59 | } 60 | else { 61 | iterate(); 62 | } 63 | } 64 | }); 65 | }; 66 | iterate(); 67 | }; 68 | async.forEachSeries = async.eachSeries; 69 | 70 | // AMD / RequireJS 71 | if (typeof define !== 'undefined' && define.amd) { 72 | define([], function () { 73 | return async; 74 | }); 75 | } 76 | // Node.js 77 | else if (typeof module !== 'undefined' && module.exports) { 78 | module.exports = async; 79 | } 80 | // included directly via 12 | 13 | 14 | -------------------------------------------------------------------------------- /perf/index.js: -------------------------------------------------------------------------------- 1 | var util = require('../tests/lib/test-utils.js'); 2 | 3 | function setImmediate(cb) { 4 | setTimeout(cb, 0); 5 | } 6 | 7 | function parse_query() { 8 | var query = window.location.search.substring(1); 9 | var parsed = {}; 10 | query.split('&').forEach(function(pair) { 11 | pair = pair.split('='); 12 | var key = decodeURIComponent(pair[0]); 13 | var value = decodeURIComponent(pair[1]); 14 | parsed[key] = value; 15 | }); 16 | return parsed; 17 | } 18 | 19 | var query = parse_query(); 20 | 21 | function time(test, cb) { 22 | var start = performance.now(); 23 | function done() { 24 | var end = performance.now(); 25 | cb(end - start); 26 | } 27 | test(done); 28 | } 29 | 30 | var random_data = new Buffer(1024); // 1kB buffer 31 | var read_buffer = new Buffer(1024); 32 | 33 | function run(iter) { 34 | iter = (undefined == iter) ? 0 : iter; 35 | 36 | function before() { 37 | util.setup(function() { 38 | setImmediate(during); 39 | }); 40 | } 41 | 42 | function during() { 43 | var fs = util.fs(); 44 | 45 | window.crypto.getRandomValues(random_data); 46 | time(function(done) { 47 | fs.mkdir('/tmp', function(err) { 48 | fs.stat('/tmp', function(err, stats) { 49 | fs.open('/tmp/test', 'w', function(err, fd) { 50 | fs.write(fd, random_data, null, null, null, function(err, nbytes) { 51 | fs.close(fd, function(err) { 52 | fs.stat('/tmp/test', function(err, stats) { 53 | fs.open('/tmp/test', 'r', function(err, fd) { 54 | fs.read(fd, read_buffer, null, null, null, function(err, nbytes) { 55 | fs.close(fd, function(err) { 56 | fs.unlink('/tmp/test', function(err) { 57 | done(); 58 | });});});});});});});});});}); 59 | }, after); 60 | } 61 | 62 | function after(dt) { 63 | util.cleanup(complete.bind(null, iter, dt)); 64 | } 65 | 66 | before(); 67 | } 68 | 69 | var results = []; 70 | function complete(iter, result) { 71 | results.push(result); 72 | 73 | if(++iter < iterations) { 74 | setImmediate(run.bind(null, iter)); 75 | } else { 76 | do_stats(); 77 | } 78 | 79 | progress.value = iter; 80 | } 81 | 82 | function do_stats() { 83 | var output = document.getElementById("output"); 84 | var stats = { 85 | mean: ss.mean(results) + " ms", 86 | min: ss.min(results), 87 | max: ss.max(results), 88 | med_abs_dev: ss.median_absolute_deviation(results), 89 | }; 90 | 91 | var t = document.createElement("table"); 92 | var tbody = document.createElement("tbody"); 93 | var keys = Object.keys(stats); 94 | keys.forEach(function(key) { 95 | var row = document.createElement("tr"); 96 | 97 | var key_cell = document.createElement("td"); 98 | var key_cell_text = document.createTextNode(key); 99 | key_cell.appendChild(key_cell_text); 100 | row.appendChild(key_cell); 101 | 102 | var val_cell = document.createElement("td"); 103 | var val_cell_text = document.createTextNode(stats[key]); 104 | val_cell.appendChild(val_cell_text); 105 | row.appendChild(val_cell); 106 | 107 | tbody.appendChild(row); 108 | }); 109 | 110 | t.appendChild(tbody); 111 | output.appendChild(t); 112 | } 113 | 114 | var query = parse_query(); 115 | var iterations = query.iterations || 10; 116 | var progress = document.getElementById("progress"); 117 | progress.max = iterations; 118 | 119 | run(); 120 | -------------------------------------------------------------------------------- /perf/simple-statistics/.gitignore: -------------------------------------------------------------------------------- 1 | components 2 | build 3 | node_modules -------------------------------------------------------------------------------- /perf/simple-statistics/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent": 4, 3 | "undef": true, 4 | "unused": true, 5 | "globals": { 6 | "require": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /perf/simple-statistics/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | script: 5 | - npm install 6 | - npm test 7 | - npm run cov 8 | -------------------------------------------------------------------------------- /perf/simple-statistics/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.9.0 4 | 5 | * Adds `.sample` for simple random sampling 6 | * Adds `.shuffle` and `.shuffle_in_place` for random permutations 7 | * Adds `.chunk` for splitting arrays into chunked subsets 8 | 9 | ## 0.8.1 10 | 11 | * fixes a bug in `mode` that favored the last new number 12 | 13 | ## 0.8.0 14 | 15 | * `mixin` can now take an array in order to mixin functions into a single array 16 | instance rather than the global Array prototype. 17 | 18 | ## 0.7.0 19 | 20 | * Adds `simple_statistics.harmonic_mean` thanks to [jseppi](https://github.com/jseppi) 21 | 22 | ## 0.6.0 23 | 24 | * Adds `simple_statistics.quantile_sorted` thanks to [rluta](http://github.com/rluta) 25 | * `simple_statistics.quantile` now accepts a sorted list of quantiles as a second argument 26 | * Improved test coverage 27 | 28 | ## 0.5.0 29 | 30 | * Adds `simple_statistics.cumulative_std_normal_probability` by [doronlinder](https://github.com/doronlinder) 31 | * Adds `simple_statistics.z_score` by doronlinder 32 | * Adds `simple_statistics.standard_normal_table` 33 | 34 | ## 0.4.0 35 | 36 | * Adds `simple_statistics.median_absolute_deviation()` by siculars 37 | * Adds `simple_statistics.iqr()` by siculars 38 | * Adds `simple_statistics.skewness()` by Doron Linder 39 | * Lower-level accessors for linear regression allow users to do the line 40 | equation themselves 41 | 42 | ## 0.3.0 43 | 44 | * Adds `simple_statistics.jenks()` 45 | * Adds `simple_statistics.jenksMatrices()` 46 | * Improves test coverage and validation 47 | 48 | ## 0.2.0 49 | 50 | * Adds `simple_statistics.quantile()` 51 | * Adds `simple_statistics.mixin()` 52 | * Adds `simple_statistics.geometric_mean()` 53 | * Adds `simple_statistics.sample_variance()` 54 | * Adds `simple_statistics.sample_covariance()` 55 | 56 | ## 0.1.0 57 | 58 | * Adds `simple_statistics.t_test()` 59 | * Adds `simple_statistics.min()` 60 | * Adds `simple_statistics.max()` 61 | -------------------------------------------------------------------------------- /perf/simple-statistics/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to simple-statistics 2 | 3 | Simple statistics is a statistics library that can be both used and read. 4 | It should help programmers learn statistics and statisticians learn programming. 5 | In order to achieve this goal, it must be **simple** and **explanatory**. 6 | 7 | ## Simple 8 | 9 | `simple-statistics` is written in a subset of JavaScript. Unused features 10 | include: 11 | 12 | * [Conditional Operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) 13 | * [ES5 Array methods](http://ie.microsoft.com/TestDrive/HTML5/ECMAScript5Array/Default.html) 14 | * `with`, `eval`, and other forms of `eval` 15 | * Most micro-optimizations, like [alternative for loop forms](http://jsperf.com/loops/70) 16 | * [Shortcut branching](http://javascriptweblog.wordpress.com/2010/07/26/no-more-ifs-alternatives-to-statement-branching-in-javascript/) 17 | 18 | ## Explanatory 19 | 20 | Example: 21 | 22 | ```js 23 | // # harmonic mean 24 | // 25 | // a mean function typically used to find the average of rates 26 | // 27 | // this is the reciprocal of the arithmetic mean of the reciprocals 28 | // of the input numbers 29 | // 30 | // This runs on `O(n)`, linear time in respect to the array 31 | ``` 32 | 33 | `simple-statistics` tries to stay away from speaking only in the language of math: 34 | for instance, while JavaScript supports UTF8 characters like π, they are not used 35 | in the source: 36 | 37 | * UTF8 in JavaScript on pages without specific meta-tag or Content-Type encodings will fail 38 | * UTF8 can be hard to type, since users need to memorize key combinations or code points 39 | * Mathematical symbols have meanings that are often better communicated by words: 40 | in the form of code, we do not run out of space on the paper, and can afford 41 | to call a variable `reciprocal_sum` instead of `r`. 42 | 43 | Every function has a comment that ideally includes: 44 | 45 | * The English, long-form name of the method 46 | * What the method does 47 | * What purpose the method typically serves 48 | * A link to a longer description on Wikipedia, Mathematica, or another 49 | web-accessible, non-paywalled source 50 | * The efficiency of the function in terms of Big-O notation, if appropriate 51 | * If the function depends on another function in the library, a note of this, like 52 | `depends on mean()` 53 | 54 | ## Tests 55 | 56 | `simple-statistics` has a testsuite located in `test/spec/`. Each test file 57 | covers a specific topic and tries to test against known values: 58 | 59 | * Values produced by trusted statistics software like R or scipy 60 | * Common-sense results 61 | 62 | Tests can be run in [node.js](http://nodejs.org/) and are run on every commit 63 | to GitHub by Travis-CI. 64 | 65 | To run tests: 66 | 67 | ```sh 68 | npm install 69 | npm test 70 | ``` 71 | 72 | ## Documentation 73 | 74 | While the code is meant to readable, it is not documentation. We maintain 75 | documentation in `API.md`, which has the simple form: 76 | 77 | ```md 78 | ### .geometric_mean(x) 79 | 80 | [Geometric mean](http://en.wikipedia.org/wiki/Geometric_mean) of a single-dimensional array of **positive** numbers. 81 | ``` 82 | 83 | This file is written in [Markdown](https://daringfireball.net/projects/markdown/) and 84 | specifies which functions are available, what type of arguments they receive, 85 | what they compute, and what type of answer they return. 86 | 87 | ## Code Style 88 | 89 | We use the [Airbnb style for Javascript](https://github.com/airbnb/javascript) with 90 | only one difference: 91 | 92 | **4 space soft tabs always for Javascript, not 2.** 93 | 94 | No aligned `=`, no aligned arguments, spaces are either indents or the 1 95 | space between expressions. No hard tabs. 96 | 97 | * All comparisons should be as strict and obvious as possible: prefer `(foo === 0)` to 98 | `(!foo)`. 99 | * Straightforward code is more important than most optimizations. 100 | -------------------------------------------------------------------------------- /perf/simple-statistics/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Tom MacWright 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /perf/simple-statistics/Makefile: -------------------------------------------------------------------------------- 1 | docs: 2 | docco src/*.js 3 | 4 | test: 5 | mocha -R spec test/spec/*.js 6 | 7 | .PHONY: docs test 8 | -------------------------------------------------------------------------------- /perf/simple-statistics/SEEALSO.md: -------------------------------------------------------------------------------- 1 | ## See Also 2 | 3 | * [stream-statistics](https://github.com/tmcw/stream-statistics), a sister project that implements 4 | many of the same measures for streaming data - as online algorithms 5 | 6 | ### Javascript 7 | 8 | * [science.js](https://github.com/jasondavies/science.js) 9 | * [atoll.js](https://github.com/nsfmc/atoll.js) 10 | * [descriptive_statistics](https://github.com/thirtysixthspan/descriptive_statistics) 11 | * [jStat](http://www.jstat.org/) 12 | * [classifier](https://github.com/harthur/classifier) is a naive bayesian classifier (though specialized for the words-spam case) 13 | * [underscore.math](https://github.com/syntagmatic/underscore.math/blob/master/underscore.math.js) 14 | 15 | ### Python 16 | 17 | * [Pandas](http://pandas.pydata.org/) 18 | * [SciPy](http://www.scipy.org/) 19 | 20 | ### Their Own Language 21 | 22 | * [Julia Language](http://julialang.org/) 23 | * [R language](http://www.r-project.org/) 24 | -------------------------------------------------------------------------------- /perf/simple-statistics/api.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var readme = fs.readFileSync('README.md', 'utf8') 4 | .split('\n'); 5 | 6 | var a = true, b = true; 7 | 8 | fs.writeFileSync('README.md', readme.filter(function(f) { 9 | if (f === '---') { 10 | a = !a; 11 | return true; 12 | } 13 | return a; 14 | }).map(function(f) { 15 | if (f === '---' && b) { 16 | f = f + '\n\n' + fs.readFileSync('API.md', 'utf8') + '\n\n'; 17 | b = false; 18 | } 19 | return f; 20 | }).join('\n')); 21 | -------------------------------------------------------------------------------- /perf/simple-statistics/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-statistics", 3 | "version": "0.9.0", 4 | "description": "Simple Statistics", 5 | "repo": "tmcw/simple-statistics", 6 | "keywords": [], 7 | "license": "ISC", 8 | "dependencies": {}, 9 | "development": {}, 10 | "main": "src/simple_statistics.js" 11 | } -------------------------------------------------------------------------------- /perf/simple-statistics/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-statistics", 3 | "version": "0.9.0", 4 | "description": "Simple Statistics", 5 | "repo": "tmcw/simple-statistics", 6 | "keywords": [], 7 | "license": "ISC", 8 | "dependencies": {}, 9 | "development": {}, 10 | "scripts": [ 11 | "src/simple_statistics.js" 12 | ] 13 | } -------------------------------------------------------------------------------- /perf/simple-statistics/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /perf/simple-statistics/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-statistics", 3 | "version": "0.9.0", 4 | "description": "Simple Statistics", 5 | "author": "Tom MacWright (http://macwright.org/)", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/tmcw/simple-statistics.git" 9 | }, 10 | "dependencies": {}, 11 | "devDependencies": { 12 | "jshint": "2.5.3", 13 | "coveralls": "~2.11.1", 14 | "istanbul": "~0.3.0", 15 | "tape": "~2.14.0", 16 | "random-js": "~1.0.4" 17 | }, 18 | "scripts": { 19 | "test": "tape test/*.js", 20 | "cov": "istanbul cover ./node_modules/.bin/tape test/*.js && coveralls < ./coverage/lcov.info", 21 | "api": "node api.js" 22 | }, 23 | "main": "src/simple_statistics.js", 24 | "engines": { 25 | "node": "*" 26 | }, 27 | "license": "ISC" 28 | } 29 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/bayes.test.js: -------------------------------------------------------------------------------- 1 | var ss = require('../'); 2 | var test = require('tape'); 3 | 4 | test('bayes', function(t) { 5 | test('makes an easy call with one training round', function(t) { 6 | var bayes = ss.bayesian(); 7 | bayes.train({ 8 | species: 'Cat' 9 | }, 'animal'); 10 | t.deepEqual(bayes.score({ 11 | species: 'Cat' 12 | }), { 13 | animal: 1 14 | }); 15 | t.end(); 16 | }); 17 | 18 | test('makes fify-fifty call', function(t) { 19 | var bayes = ss.bayesian(); 20 | bayes.train({ 21 | species: 'Cat' 22 | }, 'animal'); 23 | bayes.train({ 24 | species: 'Cat' 25 | }, 'chair'); 26 | t.deepEqual(bayes.score({ 27 | species: 'Cat' 28 | }), { 29 | animal: 0.5, 30 | chair: 0.5 31 | }); 32 | t.end(); 33 | }); 34 | 35 | test('makes seventy-five/twenty-five call', function(t) { 36 | var bayes = ss.bayesian(); 37 | bayes.train({ 38 | species: 'Cat' 39 | }, 'animal'); 40 | bayes.train({ 41 | species: 'Cat' 42 | }, 'animal'); 43 | bayes.train({ 44 | species: 'Cat' 45 | }, 'animal'); 46 | bayes.train({ 47 | species: 'Cat' 48 | }, 'chair'); 49 | t.deepEqual(bayes.score({ 50 | species: 'Cat' 51 | }), { 52 | animal: 0.75, 53 | chair: 0.25 54 | }); 55 | t.end(); 56 | }); 57 | 58 | test('tests multiple properties', function(t) { 59 | var bayes = ss.bayesian(); 60 | bayes.train({ 61 | species: 'Cat' 62 | }, 'animal'); 63 | bayes.train({ 64 | species: 'Cat' 65 | }, 'animal'); 66 | bayes.train({ 67 | species: 'Cat' 68 | }, 'animal'); 69 | bayes.train({ 70 | species: 'Cat' 71 | }, 'chair'); 72 | bayes.train({ 73 | species: 'Cat', 74 | color: 'white' 75 | }, 'chair'); 76 | t.deepEqual(bayes.score({ 77 | color: 'white' 78 | }), { 79 | animal: 0, 80 | chair: 0.2 81 | }); 82 | t.end(); 83 | }); 84 | 85 | test('classifies multiple things', function(t) { 86 | var bayes = ss.bayesian(); 87 | bayes.train({ 88 | species: 'Cat' 89 | }, 'animal'); 90 | bayes.train({ 91 | species: 'Dog' 92 | }, 'animal'); 93 | bayes.train({ 94 | species: 'Dog' 95 | }, 'animal'); 96 | bayes.train({ 97 | species: 'Cat' 98 | }, 'chair'); 99 | t.deepEqual(bayes.score({ 100 | species: 'Cat' 101 | }), { 102 | animal: 0.25, 103 | chair: 0.25 104 | }); 105 | t.deepEqual(bayes.score({ 106 | species: 'Dog' 107 | }), { 108 | animal: 0.5, 109 | chair: 0 110 | }); 111 | t.end(); 112 | }); 113 | t.end(); 114 | }); 115 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/bernoulli_distribution.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('bernoulli_distribution', function(t) { 5 | test('can return generate probability and cumulative probability distributions for p = 0.3', function(t) { 6 | t.equal('object', typeof ss.bernoulli_distribution(0.3)); 7 | t.equal(ss.bernoulli_distribution(0.3)[0], 0.7, ss.epsilon); 8 | t.equal(ss.bernoulli_distribution(0.3)[1], 0.3, ss.epsilon); 9 | t.end(); 10 | }); 11 | test('can return null when p is not a valid probability', function(t) { 12 | t.equal(null, ss.bernoulli_distribution(-0.01), 'p should be greater than 0.0'); 13 | t.equal(null, ss.bernoulli_distribution(1.5), 'p should be less than 1.0'); 14 | t.end(); 15 | }); 16 | t.end(); 17 | }); 18 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/binomial_distribution.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(n) { 5 | return parseFloat(n.toFixed(4)); 6 | } 7 | 8 | test('binomial_distribution', function(t) { 9 | // Data given in the [Wikipedia example](http://en.wikipedia.org/wiki/Binomial_distribution#Example) retrieved 29 Mar 2014 10 | // Cumulative probabilities worked by hand to mitigate accumulated rounding errors. 11 | test('can return generate probability and cumulative probability distributions for n = 6, p = 0.3', function(t) { 12 | t.equal('object', typeof ss.binomial_distribution(6, 0.3)); 13 | t.equal(rnd(ss.binomial_distribution(6, 0.3)[0]), 0.1176, ss.epsilon); 14 | t.equal(rnd(ss.binomial_distribution(6, 0.3)[1]), 0.3025, ss.epsilon); 15 | t.equal(rnd(ss.binomial_distribution(6, 0.3)[2]), 0.3241, ss.epsilon); 16 | t.equal(rnd(ss.binomial_distribution(6, 0.3)[3]), 0.1852, ss.epsilon); 17 | t.equal(rnd(ss.binomial_distribution(6, 0.3)[4]), 0.0595, ss.epsilon); 18 | t.equal(rnd(ss.binomial_distribution(6, 0.3)[5]), 0.0102, ss.epsilon); 19 | t.equal(rnd(ss.binomial_distribution(6, 0.3)[6]), 0.0007, ss.epsilon); 20 | t.end(); 21 | }); 22 | 23 | test('can return null when p or n are not valid parameters', function(t) { 24 | t.equal(null, ss.binomial_distribution(0, 0.5), 'n should be strictly positive'); 25 | t.equal(null, ss.binomial_distribution(1.5, 0.5), 'n should be an integer'); 26 | t.equal(null, ss.binomial_distribution(2, -0.01), 'p should be greater than 0.0'); 27 | t.equal(null, ss.binomial_distribution(2, 1.5), 'p should be less than 1.0'); 28 | t.end(); 29 | }); 30 | t.end(); 31 | }); 32 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/chi_squared_goodness_of_fit.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | // Data from Poisson goodness-of-fit example 10-19 in William W. Hines & Douglas C. Montgomery, 5 | // "Probability and Statistics in Engineering and Management Science", Wiley (1980). 6 | var data_10_19 = [ 7 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 10 | 3, 3, 3, 3 11 | ]; 12 | 13 | test('chi_squared_goodness_of_fit', function(t) { 14 | test('can reject the null hypothesis with level of confidence specified at 0.05', function(t) { 15 | t.equal(false, ss.chi_squared_goodness_of_fit(data_10_19, ss.poisson_distribution, 0.05)); 16 | t.end(); 17 | }); 18 | test('can accept the null hypothesis with level of confidence specified at 0.10', function(t) { 19 | t.equal(true, ss.chi_squared_goodness_of_fit(data_10_19, ss.poisson_distribution, 0.10)); 20 | t.end(); 21 | }); 22 | t.end(); 23 | }); 24 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/chunks.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('chunks', function(t) { 5 | test('can get chunks of an array', function(t) { 6 | t.deepEqual(ss.chunk([1, 2], 1), [[1], [2]]); 7 | t.deepEqual(ss.chunk([1, 2], 2), [[1, 2]]); 8 | t.deepEqual(ss.chunk([1, 2, 3, 4], 4), [[1, 2, 3, 4]]); 9 | t.deepEqual(ss.chunk([1, 2, 3, 4], 2), [[1, 2], [3, 4]]); 10 | t.deepEqual(ss.chunk([1, 2, 3, 4], 3), [[1, 2, 3], [4]]); 11 | t.deepEqual(ss.chunk([1, 2, 3, 4, 5, 6, 7], 2), [[1, 2], [3, 4], [5, 6], [7]]); 12 | t.deepEqual(ss.chunk([], 2), []); 13 | t.deepEqual(ss.chunk([], 0), null); 14 | t.deepEqual(ss.chunk([1, 2], 0), null); 15 | t.end(); 16 | }); 17 | t.end(); 18 | }); 19 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/cumulative.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('cumulative_std_normal_probability', function(t) { 5 | // https://en.wikipedia.org/wiki/Standard_normal_table#Examples_of_use 6 | test('wikipedia test example works', function(t) { 7 | for (var i = 0; i < ss.standard_normal_table.length; i++) { 8 | t.equal(ss.cumulative_std_normal_probability(0.4), 0.6554); 9 | } 10 | t.end(); 11 | }); 12 | t.end(); 13 | }); 14 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/factorial.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('factorial', function(t) { 5 | test('can return null given a negative number', function(t) { 6 | t.equal(null, ss.factorial(-1)); 7 | t.end(); 8 | }); 9 | test('can calculate 0! = 1', function(t) { 10 | t.equal(ss.factorial(0), 1); 11 | t.end(); 12 | }); 13 | test('can calculate 1! = 1', function(t) { 14 | t.equal(ss.factorial(1), 1); 15 | t.end(); 16 | }); 17 | test('can calculate 100! = 1', function(t) { 18 | t.equal(ss.factorial(100), 9.33262154439441e+157); 19 | t.end(); 20 | }); 21 | t.end(); 22 | }); 23 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/geometric_mean.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('geometric mean', function(t) { 5 | // From http://en.wikipedia.org/wiki/Geometric_mean 6 | test('can get the mean of two numbers', function(t) { 7 | t.equal(ss.geometric_mean([2, 8]), 4); 8 | t.equal(ss.geometric_mean([4, 1, 1 / 32]), 0.5); 9 | t.equal(Math.round(ss.geometric_mean([2, 32, 1])), 4); 10 | t.end(); 11 | }); 12 | 13 | test('returns null for empty lists', function(t) { 14 | t.equal(ss.geometric_mean([]), null); 15 | t.end(); 16 | }); 17 | 18 | test('returns null for lists with negative numbers', function(t) { 19 | t.equal(ss.geometric_mean([-1]), null); 20 | t.end(); 21 | }); 22 | t.end(); 23 | }); 24 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/harmonic_mean.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(x) { 5 | return Math.round(x * 1000) / 1000; 6 | } 7 | 8 | test('harmonic_mean', function(t) { 9 | // From http://en.wikipedia.org/wiki/Harmonic_mean 10 | test('can get the mean of two or more numbers', function(t) { 11 | t.equal(ss.harmonic_mean([1, 1]), 1); 12 | t.equal(rnd(ss.harmonic_mean([2, 3])), 2.4); 13 | t.equal(ss.harmonic_mean([1, 2, 4]), 12 / 7); 14 | t.end(); 15 | }); 16 | 17 | test('returns null for empty lists', function(t) { 18 | t.equal(ss.harmonic_mean([]), null); 19 | t.end(); 20 | }); 21 | 22 | test('returns null for lists with negative numbers', function(t) { 23 | t.equal(ss.harmonic_mean([-1]), null); 24 | t.end(); 25 | }); 26 | t.end(); 27 | }); 28 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/iqr.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('interquartile range (iqr)', function(t) { 5 | // Data and results from 6 | // [Wikipedia](http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population) 7 | test('can get proper iqr of an even-length list', function(t) { 8 | var even = [3, 6, 7, 8, 8, 10, 13, 15, 16, 20]; 9 | t.equal(ss.quantile(even, 0.75) - ss.quantile(even, 0.25), ss.iqr(even)); 10 | t.end(); 11 | }); 12 | 13 | test('can get proper iqr of an odd-length list', function(t) { 14 | var odd = [3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20]; 15 | t.equal(ss.quantile(odd, 0.75) - ss.quantile(odd, 0.25), ss.iqr(odd)); 16 | t.end(); 17 | }); 18 | 19 | test('an iqr of a zero-length list produces null', function(t) { 20 | t.equal(ss.iqr([]), null); 21 | t.end(); 22 | }); 23 | t.end(); 24 | }); 25 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/jenks.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('jenks', function(t) { 5 | test('will not try to assign more classes than datapoints', function(t) { 6 | t.equal(ss.jenks([1, 2], 3), null); 7 | t.end(); 8 | }); 9 | test('assigns correct breaks', function(t) { 10 | t.deepEqual(ss.jenks([1, 2, 4, 5, 7, 9, 10, 20], 3), [1, 2, 5, 20]); 11 | t.end(); 12 | }); 13 | t.end(); 14 | }); 15 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/linear_regression.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('linear regression', function(t) { 5 | test('correctly generates a line for a 0, 0 to 1, 1 dataset', function(t) { 6 | var l = ss.linear_regression().data([[0, 0], [1, 1]]); 7 | t.equal(l.line()(0), 0); 8 | t.equal(l.line()(0.5), 0.5); 9 | t.equal(l.line()(1), 1); 10 | t.end(); 11 | }); 12 | 13 | test('correctly generates a line for a 0, 0 to 1, 0 dataset', function(t) { 14 | var l = ss.linear_regression().data([[0, 0], [1, 0]]); 15 | t.equal(l.line()(0), 0); 16 | t.equal(l.line()(0.5), 0); 17 | t.equal(l.line()(1), 0); 18 | t.end(); 19 | }); 20 | 21 | test('returns the data assigned to it', function(t) { 22 | var l = ss.linear_regression().data([[0, 0], [1, 0]]); 23 | t.deepEqual(l.data(), [[0, 0], [1, 0]]); 24 | t.end(); 25 | }); 26 | 27 | test('handles a single-point sample', function(t) { 28 | var l = ss.linear_regression().data([[0, 0]]).line(); 29 | t.deepEqual(l(10), 0); 30 | t.end(); 31 | }); 32 | 33 | test('a straight line will have a slope of 0', function(t) { 34 | var l = ss.linear_regression().data([[0, 0], [1, 0]]); 35 | t.equal(l.m(), 0); 36 | t.equal(l.b(), 0); 37 | t.end(); 38 | }); 39 | 40 | test('a line at 50% grade', function(t) { 41 | var l = ss.linear_regression().data([[0, 0], [1, 0.5]]); 42 | t.equal(l.m(), 0.5); 43 | t.equal(l.b(), 0); 44 | t.end(); 45 | }); 46 | 47 | test('a line with a high y-intercept', function(t) { 48 | var l = ss.linear_regression().data([[0, 20], [1, 10]]); 49 | t.equal(l.m(), -10); 50 | t.equal(l.b(), 20); 51 | t.end(); 52 | }); 53 | t.end(); 54 | }); 55 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/mad.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('median absolute deviation (mad)', function(t) { 5 | test('median absolute deviation of an example on wikipedia', function(t) { 6 | t.equal(ss.mad([1, 1, 2, 2, 4, 6, 9]), 1); 7 | t.end(); 8 | }); 9 | 10 | // wolfram alpha: median absolute deviation {0,1,2,3,4,5,6,7,8,9,10} 11 | test('median absolute deviation of 0-10', function(t) { 12 | t.equal(ss.mad([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 3); 13 | t.end(); 14 | }); 15 | 16 | test('median absolute deviation of one number is zero', function(t) { 17 | t.equal(ss.mad([1]), 0); 18 | t.end(); 19 | }); 20 | 21 | test('zero-length corner case', function(t) { 22 | t.equal(ss.mad([]), null); 23 | t.end(); 24 | }); 25 | t.end(); 26 | }); 27 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/mean.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('mean', function(t) { 5 | test('can get the mean of two numbers', function(t) { 6 | t.equal(ss.mean([1, 2]), 1.5); 7 | t.end(); 8 | }); 9 | test('can get the mean of one number', function(t) { 10 | t.equal(ss.mean([1]), 1); 11 | t.end(); 12 | }); 13 | test('an empty list has no average', function(t) { 14 | t.equal(ss.mean([]), null); 15 | t.end(); 16 | }); 17 | t.end(); 18 | }); 19 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/median.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('median', function(t) { 5 | test('can get the median of three numbers', function(t) { 6 | t.equal(ss.median([1, 2, 3]), 2); 7 | t.end(); 8 | }); 9 | 10 | test('can get the median of two numbers', function(t) { 11 | t.equal(ss.median([1, 2]), 1.5); 12 | t.end(); 13 | }); 14 | 15 | test('can get the median of four numbers', function(t) { 16 | t.equal(ss.median([1, 2, 3, 4]), 2.5); 17 | t.end(); 18 | }); 19 | 20 | test('gives null for the median of an empty list', function(t) { 21 | t.equal(ss.median([]), null); 22 | t.end(); 23 | }); 24 | 25 | test('sorts numbers numerically', function(t) { 26 | t.equal(ss.median([8, 9, 10]), 9); 27 | t.end(); 28 | }); 29 | 30 | test('does not change the sorting order of its input', function(t) { 31 | var x = [1, 0]; 32 | t.equal(ss.median(x), 0.5); 33 | t.equal(x[0], 1); 34 | t.equal(x[1], 0); 35 | t.end(); 36 | }); 37 | t.end(); 38 | }); 39 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/minmax.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('min', function(t) { 5 | test('can get the minimum of one number', function(t) { 6 | t.equal(ss.min([1]), 1); 7 | t.end(); 8 | }); 9 | 10 | test('can get the minimum of three numbers', function(t) { 11 | t.equal(ss.min([1, 7, -1000]), -1000); 12 | t.end(); 13 | }); 14 | t.end(); 15 | }); 16 | 17 | test('max', function(t) { 18 | test('can get the maximum of three numbers', function(t) { 19 | t.equal(ss.max([1, 7, -1000]), 7); 20 | t.end(); 21 | }); 22 | t.end(); 23 | }); 24 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/mixin.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('mixin', function(t) { 5 | test('can mix into a single array', function(t) { 6 | var even = ss.mixin([2, 4, 6, 8]); 7 | t.equal(even.sum(), 20); 8 | t.equal(even.mean(), 5); 9 | t.equal(even.max(), 8); 10 | t.equal(even.min(), 2); 11 | t.equal(even.sample_skewness(), 0); 12 | t.end(); 13 | }); 14 | 15 | test('can mix into Array.prototype', function(t) { 16 | ss.mixin(); 17 | var even = [2, 4, 6, 8]; 18 | t.equal(even.sum(), 20); 19 | t.equal(even.mean(), 5); 20 | t.equal(even.max(), 8); 21 | t.equal(even.min(), 2); 22 | t.equal(even.sample_skewness(), 0); 23 | t.end(); 24 | }); 25 | 26 | test('mixins can take arguments', function(t) { 27 | ss.mixin(); 28 | var even = [2, 4, 6, 8]; 29 | t.equal(even.quantile(0.2), 2); 30 | t.equal(even.quantile(0.8), 8); 31 | t.end(); 32 | }); 33 | t.end(); 34 | }); 35 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/mode.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('mode', function(t) { 5 | test('the mode of a single-number array is that one number', function(t) { 6 | t.equal(ss.mode([1]), 1); 7 | t.end(); 8 | }); 9 | 10 | test('the mode of a two-number array is that one number', function(t) { 11 | t.equal(ss.mode([1, 1]), 1); 12 | t.end(); 13 | }); 14 | 15 | test('other cases', function(t) { 16 | t.equal(ss.mode([1, 1, 2]), 1); 17 | t.equal(ss.mode([1, 1, 2, 3]), 1); 18 | t.equal(ss.mode([1, 1, 2, 3, 3]), 1); 19 | t.equal(ss.mode([1, 1, 2, 3, 3, 3]), 3); 20 | t.equal(ss.mode([1, 1, 2, 2, 2, 2, 3, 3, 3]), 2); 21 | t.equal(ss.mode([1, 2, 3, 4, 5]), 1); 22 | t.equal(ss.mode([1, 2, 3, 4, 5, 5]), 5); 23 | t.equal(ss.mode([1, 1, 1, 2, 2, 3, 3, 4, 4]), 1); 24 | t.end(); 25 | }); 26 | 27 | test('the mode of an empty array is null', function(t) { 28 | t.equal(ss.mode([]), null); 29 | t.end(); 30 | }); 31 | 32 | test('the mode of a three-number array with two same numbers is the repeated one', function(t) { 33 | t.equal(ss.mode([1, 2, 2]), 2); 34 | t.end(); 35 | }); 36 | t.end(); 37 | }); 38 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/normal_distribution.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('natural distribution and z-score', function(t) { 5 | 6 | test('normal table is exposed in the API', function(t) { 7 | t.equal(ss.standard_normal_table.length, 310); 8 | t.equal(ss.standard_normal_table[0], 0.5); 9 | t.end(); 10 | }); 11 | 12 | test('P(Z <= 0.4) is 0.6554', function(t) { 13 | // Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table 14 | t.equal(ss.cumulative_std_normal_probability(0.4), 0.6554); 15 | t.end(); 16 | }); 17 | 18 | test('P(Z <= -1.20) is 0.1151', function(t) { 19 | // Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table 20 | t.equal(ss.cumulative_std_normal_probability(-1.20), 0.1151); 21 | t.end(); 22 | }); 23 | 24 | test('P(X <= 82) when X ~ N (80, 25) is 0.6554', function(t) { 25 | // Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table 26 | // A professor's exam scores are approximately distributed normally with mean 80 and standard deviation 5. 27 | // What is the probability that a student scores an 82 or less? 28 | t.equal(ss.cumulative_std_normal_probability(ss.z_score(82, 80, 5)), 0.6554); 29 | t.end(); 30 | }); 31 | 32 | test('P(X >= 90) when X ~ N (80, 25) is 0.0228', function(t) { 33 | // Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table 34 | // A professor's exam scores are approximately distributed normally with mean 80 and standard deviation 5. 35 | // What is the probability that a student scores a 90 or more? 36 | t.equal(+(1 - ss.cumulative_std_normal_probability(ss.z_score(90, 80, 5))).toPrecision(5), 0.0228); 37 | t.end(); 38 | }); 39 | 40 | test('P(X <= 74) when X ~ N (80, 25) is 0.1151', function(t) { 41 | // Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table 42 | // A professor's exam scores are approximately distributed normally with mean 80 and standard deviation 5. 43 | // What is the probability that a student scores a 74 or less? 44 | t.equal(ss.cumulative_std_normal_probability(ss.z_score(74, 80, 5)), 0.1151); 45 | t.end(); 46 | }); 47 | 48 | test('P(78 <= X <= 88) when X ~ N (80, 25) is 0.6006', function(t) { 49 | // Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table 50 | // A professor's exam scores are approximately distributed normally with mean 80 and standard deviation 5. 51 | // What is the probability that a student scores between 78 and 88? 52 | var prob88 = ss.cumulative_std_normal_probability(ss.z_score(88, 80, 5)), 53 | prob78 = ss.cumulative_std_normal_probability(ss.z_score(78, 80, 5)); 54 | 55 | t.equal(+(prob88 - prob78).toPrecision(5), 0.6006); 56 | t.end(); 57 | }); 58 | 59 | t.end(); 60 | }); 61 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/poisson_distribution.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(n) { 5 | return parseFloat(n.toFixed(4)); 6 | } 7 | 8 | // expected cumulative probabilities taken from Appendix 1, Table I of William W. Hines & Douglas C. 9 | // Montgomery, "Probability and Statistics in Engineering and Management Science", Wiley (1980). 10 | test('poisson_distribution', function(t) { 11 | test('can return generate probability and cumulative probability distributions for lambda = 3.0', function(t) { 12 | t.equal('object', typeof ss.poisson_distribution(3.0)); 13 | t.equal(rnd(ss.poisson_distribution(3.0)[3]), 0.2240, ss.epsilon); 14 | t.end(); 15 | }); 16 | test('can generate probability and cumulative probability distributions for lambda = 4.0', function(t) { 17 | t.equal('object', typeof ss.poisson_distribution(4.0)); 18 | t.equal(rnd(ss.poisson_distribution(4.0)[2]), 0.1465, ss.epsilon); 19 | t.end(); 20 | }); 21 | test('can generate probability and cumulative probability distributions for lambda = 5.5', function(t) { 22 | t.equal('object', typeof ss.poisson_distribution(5.5)); 23 | t.equal(rnd(ss.poisson_distribution(5.5)[7]), 0.1234, ss.epsilon); 24 | t.end(); 25 | }); 26 | test('can generate probability and cumulative probability distributions for lambda = 9.5', function(t) { 27 | t.equal('object', typeof ss.poisson_distribution(9.5)); 28 | t.equal(rnd(ss.poisson_distribution(9.5)[17]), 0.0088, ss.epsilon); 29 | t.end(); 30 | }); 31 | test('can return null when lambda <= 0', function(t) { 32 | t.equal(null, ss.poisson_distribution(0)); 33 | t.equal(null, ss.poisson_distribution(-10)); 34 | t.end(); 35 | }); 36 | t.end(); 37 | }); 38 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/quantile.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('quantile', function(t) { 5 | // Data and results from 6 | // [Wikipedia](http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population) 7 | test('can get proper quantiles of an even-length list', function(t) { 8 | var even = [3, 6, 7, 8, 8, 10, 13, 15, 16, 20]; 9 | t.equal(ss.quantile(even, 0.25), 7); 10 | t.equal(ss.quantile(even, 0.5), 9); 11 | t.equal(ss.quantile(even, 0.75), 15); 12 | t.end(); 13 | }); 14 | 15 | test('can get proper quantiles of an odd-length list', function(t) { 16 | var odd = [3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20]; 17 | t.equal(ss.quantile(odd, 0.25), 7); 18 | t.equal(ss.quantile(odd, 0.5), 9); 19 | t.equal(ss.quantile(odd, 0.75), 15); 20 | t.end(); 21 | }); 22 | 23 | test('the median quantile is equal to the median', function(t) { 24 | var rand = [1, 4, 5, 8]; 25 | t.equal(ss.quantile(rand, 0.5), ss.median(rand)); 26 | var rand2 = [10, 50, 2, 4, 4, 5, 8]; 27 | t.equal(ss.quantile(rand2, 0.5), ss.median(rand2)); 28 | t.end(); 29 | }); 30 | 31 | test('a zero-length list produces null', function(t) { 32 | t.equal(ss.quantile([], 0.5), null); 33 | t.end(); 34 | }); 35 | 36 | test('test odd-value case', function(t) { 37 | t.equal(ss.quantile([0, 1, 2, 3, 4], 0.2), 1); 38 | t.end(); 39 | }); 40 | 41 | test('bad bounds produce null', function(t) { 42 | t.equal(ss.quantile([1, 2, 3], 1.1), null); 43 | t.equal(ss.quantile([1, 2, 3], -0.5), null); 44 | t.end(); 45 | }); 46 | 47 | test('max quantile is equal to the max', function(t) { 48 | t.equal(ss.quantile([1, 2, 3], 1), ss.max([1, 2, 3])); 49 | t.end(); 50 | }); 51 | 52 | test('min quantile is equal to the min', function(t) { 53 | t.equal(ss.quantile([1, 2, 3], 0), ss.min([1, 2, 3])); 54 | t.end(); 55 | }); 56 | 57 | test('if quantile arg is an array, response is an array of quantiles', function(t) { 58 | var odd = [3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20]; 59 | t.deepEqual(ss.quantile(odd, [0, 0.25, 0.5, 0.75, 1]), [3, 7, 9, 15, 20]); 60 | t.deepEqual(ss.quantile(odd, [0.75, 0.5]), [15, 9]); 61 | t.end(); 62 | }); 63 | t.end(); 64 | }); 65 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/quantilesorted.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('quantile_sorted', function(t) { 5 | // Data and results from 6 | // [Wikipedia](http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population) 7 | test('can get proper quantiles of an even-length list', function(t) { 8 | var even = [3, 6, 7, 8, 8, 10, 13, 15, 16, 20]; 9 | t.equal(ss.quantile_sorted(even, 0.25), 7); 10 | t.equal(ss.quantile_sorted(even, 0.5), 9); 11 | t.equal(ss.quantile_sorted(even, 0.75), 15); 12 | t.end(); 13 | }); 14 | t.end(); 15 | }); 16 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/r_squared.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('r-squared', function(t) { 5 | test('says that the r squared of a two-point line is perfect', function(t) { 6 | var d = [[0, 0], [1, 1]]; 7 | var l = ss.linear_regression().data(d); 8 | t.equal(ss.r_squared(d, l.line()), 1); 9 | t.end(); 10 | }); 11 | 12 | test('says that the r squared of a three-point line is not perfect', function(t) { 13 | var d = [[0, 0], [0.5, 0.2], [1, 1]]; 14 | var l = ss.linear_regression().data(d); 15 | t.notEqual(ss.r_squared(d, l.line()), 1); 16 | t.end(); 17 | }); 18 | 19 | test('r-squared of single sample is 1', function(t) { 20 | var d = [[0, 0]]; 21 | var l = ss.linear_regression().data(d); 22 | t.equal(ss.r_squared(d, l.line()), 1); 23 | t.end(); 24 | }); 25 | t.end(); 26 | }); 27 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/root_mean_square.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(x) { 5 | return Math.round(x * 1000) / 1000; 6 | } 7 | 8 | test('root_mean_square', function(t) { 9 | // From http://en.wikipedia.org/wiki/Root_mean_square 10 | test('can get the RMS of two or more numbers', function(t) { 11 | t.equal(ss.root_mean_square([1, 1]), 1); 12 | t.equal(rnd(ss.root_mean_square([3, 4, 5])), 4.082); 13 | t.equal(rnd(ss.root_mean_square([-0.1, 5, -2, 10])), 5.679); 14 | t.end(); 15 | }); 16 | 17 | test('returns null for empty lists', function(t) { 18 | t.equal(ss.root_mean_square([]), null); 19 | t.end(); 20 | }); 21 | 22 | t.end(); 23 | }); 24 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/sample.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var Random = require('random-js'); 3 | var random = new Random(Random.engines.mt19937().seed(0)); 4 | var ss = require('../'); 5 | 6 | function rng() { return random.real(0, 1); } 7 | 8 | test('sample', function(t) { 9 | t.deepEqual(ss.sample([], 0, rng), [], 'edge case - zero array'); 10 | t.deepEqual(ss.sample([], 2, rng), [], 'edge case - zero array'); 11 | t.deepEqual(ss.sample([1,2,3], 0, rng, 0), [], 'edge case - zero array'); 12 | t.deepEqual(ss.sample([1,2,3], 1, rng), [1], 'edge case - sample of 1'); 13 | t.deepEqual(ss.sample([1,2,3], 1, rng), [2]); 14 | t.deepEqual(ss.sample([1,2,3], 3, rng), [2,3,1]); 15 | t.deepEqual(ss.sample([1,2,3,4], 2, rng), [3,1]); 16 | t.deepEqual(ss.sample([1,2,3,4,6,7,8], 2, rng), [8,7]); 17 | t.deepEqual(ss.sample(['foo', 'bar'], 1, rng), ['foo'], 'non-number contents'); 18 | t.end(); 19 | }); 20 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/sample_correlation.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(x) { 5 | return Math.round(x * 1000) / 1000; 6 | } 7 | 8 | test('sample correlation', function(t) { 9 | 10 | test('can get the sample correlation of identical arrays', function(t) { 11 | var data = [1, 2, 3, 4, 5, 6]; 12 | t.equal(rnd(ss.sample_correlation(data, data)), 1); 13 | t.end(); 14 | }); 15 | 16 | test('can get the sample correlation of different arrays', function(t) { 17 | var a = [1, 2, 3, 4, 5, 6]; 18 | var b = [2, 2, 3, 4, 5, 60]; 19 | t.equal(rnd(ss.sample_correlation(a, b)), 0.691); 20 | t.end(); 21 | }); 22 | 23 | test('zero-length corner case', function(t) { 24 | t.equal(rnd(ss.sample_correlation([], [])), 0); 25 | t.end(); 26 | }); 27 | 28 | t.end(); 29 | }); 30 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/sample_covariance.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(x) { 5 | return Math.round(x * 1000) / 1000; 6 | } 7 | 8 | test('sample covariance', function(t) { 9 | test('can get perfect negative covariance', function(t) { 10 | var x = [1, 2, 3, 4, 5, 6]; 11 | var y = [6, 5, 4, 3, 2, 1]; 12 | t.equal(rnd(ss.sample_covariance(x, y)), -3.5); 13 | t.end(); 14 | }); 15 | 16 | test('covariance of something with itself is its variance', function(t) { 17 | var x = [1, 2, 3, 4, 5, 6]; 18 | t.equal(rnd(ss.sample_covariance(x, x)), 3.5); 19 | t.end(); 20 | }); 21 | 22 | test('covariance is zero for something with no correlation', function(t) { 23 | var x = [1, 2, 3, 4, 5, 6]; 24 | var y = [1, 1, 2, 2, 1, 1]; 25 | t.equal(rnd(ss.sample_covariance(x, y)), 0); 26 | t.end(); 27 | }); 28 | 29 | test('zero-length corner case', function(t) { 30 | t.equal(rnd(ss.sample_covariance([], [])), 0); 31 | t.end(); 32 | }); 33 | t.end(); 34 | }); 35 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/sample_skewness.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('sample skewness', function(t) { 5 | 6 | test('the skewness of an empty sample is null', function(t) { 7 | var data = []; 8 | t.equal(ss.sample_skewness(data), null); 9 | t.end(); 10 | }); 11 | 12 | test('the skewness of an sample with one number is null', function(t) { 13 | var data = [1]; 14 | t.equal(ss.sample_skewness(data), null); 15 | t.end(); 16 | }); 17 | 18 | test('the skewness of an sample with two numbers is null', function(t) { 19 | var data = [1, 2]; 20 | t.equal(ss.sample_skewness(data), null); 21 | t.end(); 22 | }); 23 | 24 | test('can calculate the skewness of SAS example 1', function(t) { 25 | // Data and answer taken from SKEWNESS function documentation at 26 | // http://support.sas.com/documentation/c../lrdict/64316/HTML/default/viewer.htm#a000245947.htm 27 | var data = [0, 1, 1]; 28 | t.equal(+ss.sample_skewness(data).toPrecision(10), -1.732050808); 29 | t.end(); 30 | }); 31 | 32 | test('can calculate the skewness of SAS example 2', function(t) { 33 | // Data and answer taken from SKEWNESS function documentation at 34 | // http://support.sas.com/documentation/c../lrdict/64316/HTML/default/viewer.htm#a000245947.htm 35 | var data = [2, 4, 6, 3, 1]; 36 | t.equal(+ss.sample_skewness(data).toPrecision(10), 0.5901286564); 37 | t.end(); 38 | }); 39 | 40 | test('can calculate the skewness of SAS example 3', function(t) { 41 | // Data and answer taken from SKEWNESS function documentation at 42 | // http://support.sas.com/documentation/c../lrdict/64316/HTML/default/viewer.htm#a000245947.htm 43 | var data = [2, 0, 0]; 44 | t.equal(+ss.sample_skewness(data).toPrecision(10), 1.732050808); 45 | t.end(); 46 | }); 47 | t.end(); 48 | }); 49 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/sample_standard_deviation.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(x) { 5 | return Math.round(x * 1000) / 1000; 6 | } 7 | 8 | test('sample_standard_deviation', function(t) { 9 | test('can get the standard deviation of an example on wikipedia', function(t) { 10 | t.equal(rnd(ss.sample_standard_deviation([2, 4, 4, 4, 5, 5, 7, 9])), 2.138); 11 | t.end(); 12 | }); 13 | 14 | test('zero-length corner case', function(t) { 15 | t.equal(rnd(ss.sample_standard_deviation([])), 0); 16 | t.end(); 17 | }); 18 | t.end(); 19 | }); 20 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/sample_variance.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(x) { 5 | return Math.round(x * 1000) / 1000; 6 | } 7 | 8 | test('sample variance', function(t) { 9 | test('can get the sample variance of a six-sided die', function(t) { 10 | t.equal(rnd(ss.sample_variance([1, 2, 3, 4, 5, 6])), 3.5); 11 | t.end(); 12 | }); 13 | 14 | // confirmed in R 15 | // 16 | // > var(1:10) 17 | // [1] 9.166667 18 | test('can get the sample variance of numbers 1-10', function(t) { 19 | t.equal(rnd(ss.sample_variance([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])), 9.167); 20 | t.end(); 21 | }); 22 | 23 | test('the sample variance of two numbers that are the same is 0', function(t) { 24 | t.equal(rnd(ss.sample_variance([1, 1])), 0); 25 | t.end(); 26 | }); 27 | 28 | test('the sample variance of one number is null', function(t) { 29 | t.equal(ss.sample_variance([1]), null); 30 | t.end(); 31 | }); 32 | 33 | test('the sample variance of no numbers is null', function(t) { 34 | t.equal(ss.sample_variance([]), null); 35 | t.end(); 36 | }); 37 | t.end(); 38 | }); 39 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/shuffle.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var Random = require('random-js'); 3 | var random = new Random(Random.engines.mt19937().seed(0)); 4 | var ss = require('../'); 5 | 6 | function rng() { return random.real(0, 1); } 7 | 8 | test('shuffle', function(t) { 9 | var input = [1, 2, 3, 4, 5, 6]; 10 | t.deepEqual(ss.shuffle([], rng), []); 11 | t.deepEqual(ss.shuffle(input, rng), [1, 5, 3, 2, 4, 6]); 12 | t.deepEqual(input, [1, 2, 3, 4, 5, 6], 'does not change original array'); 13 | t.deepEqual(ss.shuffle(input, rng), [5, 4, 1, 3, 6, 2]); 14 | t.deepEqual(input, [1, 2, 3, 4, 5, 6], 'does not change original array'); 15 | t.end(); 16 | }); 17 | 18 | test('shuffle_in_place', function(t) { 19 | var input = [1, 2, 3, 4, 5, 6]; 20 | t.deepEqual(ss.shuffle_in_place([], rng), []); 21 | t.deepEqual(ss.shuffle_in_place(input, rng), [6, 1, 5, 2, 4, 3]); 22 | t.deepEqual(input, [6, 1, 5, 2, 4, 3], 'changes original array'); 23 | t.end(); 24 | }); 25 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/standard_deviation.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(x) { 5 | return Math.round(x * 1000) / 1000; 6 | } 7 | 8 | test('standard_deviation', function(t) { 9 | test('can get the standard deviation of an example on wikipedia', function(t) { 10 | t.equal(rnd(ss.standard_deviation([2, 4, 4, 4, 5, 5, 7, 9])), 2); 11 | t.end(); 12 | }); 13 | 14 | // confirmed with numpy 15 | // In [4]: numpy.std([1,2,3]) 16 | // Out[4]: 0.81649658092772603 17 | test('can get the standard deviation of 1-3', function(t) { 18 | t.equal(rnd(ss.standard_deviation([1, 2, 3])), 0.816); 19 | t.end(); 20 | }); 21 | 22 | test('zero-length array corner case', function(t) { 23 | t.equal(rnd(ss.standard_deviation([])), 0); 24 | t.end(); 25 | }); 26 | 27 | // In [6]: numpy.std([0,1,2,3,4,5,6,7,8,9,10]) 28 | // Out[6]: 3.1622776601683795 29 | test('can get the standard deviation of 1-10', function(t) { 30 | t.equal(rnd(ss.standard_deviation([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])), 3.162); 31 | t.end(); 32 | }); 33 | 34 | test('the standard deviation of one number is zero', function(t) { 35 | t.equal(rnd(ss.standard_deviation([1])), 0); 36 | t.end(); 37 | }); 38 | t.end(); 39 | }); 40 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/standard_normal_table.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('standard_normal_table', function(t) { 5 | test('all entries are numeric', function(t) { 6 | for (var i = 0; i < ss.standard_normal_table.length; i++) { 7 | t.equal(typeof ss.standard_normal_table[i], 'number'); 8 | t.ok(ss.standard_normal_table[i] >= 0); 9 | t.ok(ss.standard_normal_table[i] <= 1); 10 | } 11 | t.end(); 12 | }); 13 | t.end(); 14 | }); 15 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/sum.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | test('sum', function(t) { 5 | test('can get the sum of two numbers', function(t) { 6 | t.equal(ss.sum([1, 2]), 3); 7 | t.end(); 8 | }); 9 | 10 | test('the sum of no numbers is zero', function(t) { 11 | t.equal(ss.sum([]), 0); 12 | t.end(); 13 | }); 14 | t.end(); 15 | }); 16 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/t_test.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'), 2 | ss = require('../'); 3 | 4 | test('t test', function(t) { 5 | 6 | test('can compare a known value to the mean of samples', function(t) { 7 | var res = ss.t_test([1, 2, 3, 4, 5, 6], 3.385); 8 | t.equal(res, 0.1649415480881466); 9 | t.end(); 10 | }); 11 | 12 | test('can test independency of two samples', function(t) { 13 | var res = ss.t_test_two_sample([1, 2, 3, 4], [3, 4, 5, 6], 0); 14 | t.equal(res, -2.1908902300206643); 15 | t.end(); 16 | }); 17 | 18 | test('can test independency of two samples (mu == -2)', function(t) { 19 | var res = ss.t_test_two_sample([1, 2, 3, 4], [3, 4, 5, 6], -2); 20 | t.equal(res, 0); 21 | t.end(); 22 | }); 23 | 24 | test('can test independency of two samples of different lengths', function(t) { 25 | var res = ss.t_test_two_sample([1, 2, 3, 4], [3, 4, 5, 6, 1, 2, 0]); 26 | t.equal(res, -0.4165977904505309); 27 | t.end(); 28 | }); 29 | 30 | test('has an edge case for one sample being of size zero', function(t) { 31 | t.equal(ss.t_test_two_sample([1, 2, 3, 4], []), null); 32 | t.equal(ss.t_test_two_sample([], [1, 2, 3, 4]), null); 33 | t.equal(ss.t_test_two_sample([], []), null); 34 | t.end(); 35 | }); 36 | 37 | t.end(); 38 | }); 39 | -------------------------------------------------------------------------------- /perf/simple-statistics/test/variance.test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var ss = require('../'); 3 | 4 | function rnd(x) { 5 | return Math.round(x * 1000) / 1000; 6 | } 7 | 8 | test('variance', function(t) { 9 | test('can get the variance of a six-sided die', function(t) { 10 | t.equal(rnd(ss.variance([1, 2, 3, 4, 5, 6])), 2.917); 11 | t.end(); 12 | }); 13 | 14 | test('the variance of one number is zero', function(t) { 15 | t.equal(rnd(ss.variance([1])), 0); 16 | t.end(); 17 | }); 18 | 19 | test('the variance of no numbers is null', function(t) { 20 | t.equal(ss.variance([]), null); 21 | t.end(); 22 | }); 23 | t.end(); 24 | }); 25 | -------------------------------------------------------------------------------- /shims/fs.js: -------------------------------------------------------------------------------- 1 | const { FileSystem } = require('../src/index'); 2 | 3 | let Provider; 4 | try { 5 | Provider = require('fsProvider'); 6 | } 7 | catch (err) { 8 | Provider = require('./providers/default'); 9 | } 10 | 11 | const provider = new Provider(); 12 | 13 | let onFsReady; 14 | let onFsError; 15 | 16 | let fsReady = new Promise((resolve, reject) => { 17 | onFsReady = resolve; 18 | onFsError = reject; 19 | }); 20 | 21 | var fsInstance = new FileSystem({ provider }, (err) => { 22 | if (err) { 23 | onFsError(err); 24 | } else { 25 | onFsReady(true); 26 | } 27 | }); 28 | 29 | function proxyHasProp(target, prop) { 30 | return prop in target; 31 | } 32 | 33 | const fsPromises = new Proxy(fsInstance.promises, { 34 | get(target, prop) { 35 | if (!proxyHasProp(target, prop)) { 36 | return; 37 | } 38 | 39 | return async (...args) => { 40 | await fsReady; 41 | return await target[prop](...args); 42 | }; 43 | }, 44 | }); 45 | 46 | const fs = new Proxy(fsInstance, { 47 | get(target, prop) { 48 | if (!proxyHasProp(target, prop)) { 49 | return; 50 | } 51 | 52 | if (prop === 'promises') { 53 | return fsPromises; 54 | } 55 | 56 | return (...args) => { 57 | (async () => { 58 | await fsReady; 59 | target[prop](...args); 60 | })(); 61 | }; 62 | }, 63 | }); 64 | 65 | module.exports = fs; 66 | -------------------------------------------------------------------------------- /shims/path.js: -------------------------------------------------------------------------------- 1 | const { path } = require('../src/index'); 2 | 3 | module.exports = path; 4 | -------------------------------------------------------------------------------- /shims/providers/default.js: -------------------------------------------------------------------------------- 1 | const { Default } = require('../../src/providers/index'); 2 | module.exports = Default; -------------------------------------------------------------------------------- /shims/providers/indexeddb.js: -------------------------------------------------------------------------------- 1 | const IndexedDB = require('../../src/providers/indexeddb'); 2 | module.exports = IndexedDB; -------------------------------------------------------------------------------- /shims/providers/memory.js: -------------------------------------------------------------------------------- 1 | const Memory = require('../../src/providers/memory'); 2 | module.exports = Memory; -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | var O_READ = 'READ'; 2 | var O_WRITE = 'WRITE'; 3 | var O_CREATE = 'CREATE'; 4 | var O_EXCLUSIVE = 'EXCLUSIVE'; 5 | var O_TRUNCATE = 'TRUNCATE'; 6 | var O_APPEND = 'APPEND'; 7 | var XATTR_CREATE = 'CREATE'; 8 | var XATTR_REPLACE = 'REPLACE'; 9 | 10 | module.exports = { 11 | FILE_SYSTEM_NAME: 'local', 12 | 13 | FILE_STORE_NAME: 'files', 14 | 15 | IDB_RO: 'readonly', 16 | IDB_RW: 'readwrite', 17 | 18 | WSQL_VERSION: '1', 19 | WSQL_SIZE: 5 * 1024 * 1024, 20 | WSQL_DESC: 'FileSystem Storage', 21 | 22 | NODE_TYPE_FILE: 'FILE', 23 | NODE_TYPE_DIRECTORY: 'DIRECTORY', 24 | NODE_TYPE_SYMBOLIC_LINK: 'SYMLINK', 25 | NODE_TYPE_META: 'META', 26 | 27 | 28 | DEFAULT_DIR_PERMISSIONS: 0x1ED, // 755 29 | DEFAULT_FILE_PERMISSIONS: 0x1A4, // 644 30 | FULL_READ_WRITE_EXEC_PERMISSIONS: 0x1FF, // 777 31 | READ_WRITE_PERMISSIONS: 0x1B6, /// 666 32 | 33 | SYMLOOP_MAX: 10, 34 | 35 | BINARY_MIME_TYPE: 'application/octet-stream', 36 | JSON_MIME_TYPE: 'application/json', 37 | 38 | ROOT_DIRECTORY_NAME: '/', // basename(normalize(path)) 39 | 40 | // FS Mount Flags 41 | FS_FORMAT: 'FORMAT', 42 | FS_NOCTIME: 'NOCTIME', 43 | FS_NOMTIME: 'NOMTIME', 44 | FS_NODUPEIDCHECK: 'FS_NODUPEIDCHECK', 45 | 46 | // FS File Open Flags 47 | O_READ: O_READ, 48 | O_WRITE: O_WRITE, 49 | O_CREATE: O_CREATE, 50 | O_EXCLUSIVE: O_EXCLUSIVE, 51 | O_TRUNCATE: O_TRUNCATE, 52 | O_APPEND: O_APPEND, 53 | 54 | O_FLAGS: { 55 | 'r': [O_READ], 56 | 'r+': [O_READ, O_WRITE], 57 | 'w': [O_WRITE, O_CREATE, O_TRUNCATE], 58 | 'w+': [O_WRITE, O_READ, O_CREATE, O_TRUNCATE], 59 | 'wx': [O_WRITE, O_CREATE, O_EXCLUSIVE, O_TRUNCATE], 60 | 'wx+': [O_WRITE, O_READ, O_CREATE, O_EXCLUSIVE, O_TRUNCATE], 61 | 'a': [O_WRITE, O_CREATE, O_APPEND], 62 | 'a+': [O_WRITE, O_READ, O_CREATE, O_APPEND], 63 | 'ax': [O_WRITE, O_CREATE, O_EXCLUSIVE, O_APPEND], 64 | 'ax+': [O_WRITE, O_READ, O_CREATE, O_EXCLUSIVE, O_APPEND] 65 | }, 66 | 67 | XATTR_CREATE: XATTR_CREATE, 68 | XATTR_REPLACE: XATTR_REPLACE, 69 | 70 | FS_READY: 'READY', 71 | FS_PENDING: 'PENDING', 72 | FS_ERROR: 'ERROR', 73 | 74 | SUPER_NODE_ID: '00000000-0000-0000-0000-000000000000', 75 | 76 | // Reserved File Descriptors for streams 77 | STDIN: 0, 78 | STDOUT: 1, 79 | STDERR: 2, 80 | FIRST_DESCRIPTOR: 3, 81 | 82 | ENVIRONMENT: { 83 | TMP: '/tmp', 84 | PATH: '' 85 | }, 86 | 87 | // Duplicate Node's fs.constants 88 | fsConstants: { 89 | O_RDONLY: 0, 90 | O_WRONLY: 1, 91 | O_RDWR: 2, 92 | S_IFMT: 61440, 93 | S_IFREG: 32768, 94 | S_IFDIR: 16384, 95 | S_IFCHR: 8192, 96 | S_IFBLK: 24576, 97 | S_IFIFO: 4096, 98 | S_IFLNK: 40960, 99 | S_IFSOCK: 49152, 100 | O_CREAT: 512, 101 | O_EXCL: 2048, 102 | O_NOCTTY: 131072, 103 | O_TRUNC: 1024, 104 | O_APPEND: 8, 105 | O_DIRECTORY: 1048576, 106 | O_NOFOLLOW: 256, 107 | O_SYNC: 128, 108 | O_DSYNC: 4194304, 109 | O_SYMLINK: 2097152, 110 | O_NONBLOCK: 4, 111 | S_IRWXU: 448, 112 | S_IRUSR: 256, 113 | S_IWUSR: 128, 114 | S_IXUSR: 64, 115 | S_IRWXG: 56, 116 | S_IRGRP: 32, 117 | S_IWGRP: 16, 118 | S_IXGRP: 8, 119 | S_IRWXO: 7, 120 | S_IROTH: 4, 121 | S_IWOTH: 2, 122 | S_IXOTH: 1, 123 | F_OK: 0, 124 | R_OK: 4, 125 | W_OK: 2, 126 | X_OK: 1, 127 | UV_FS_COPYFILE_EXCL: 1, 128 | COPYFILE_EXCL: 1 129 | } 130 | }; 131 | -------------------------------------------------------------------------------- /src/directory-entry.js: -------------------------------------------------------------------------------- 1 | var NODE_TYPE_FILE = require('./constants.js').NODE_TYPE_FILE; 2 | 3 | module.exports = function DirectoryEntry(id, type) { 4 | this.id = id; 5 | this.type = type || NODE_TYPE_FILE; 6 | }; 7 | -------------------------------------------------------------------------------- /src/dirent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Stats = require('./stats.js'); 4 | 5 | function Dirent(path, fileNode, devName) { 6 | this.constructor = Dirent; 7 | Stats.call(this, path, fileNode, devName); 8 | } 9 | 10 | Dirent.prototype = Stats.prototype; 11 | 12 | module.exports = Dirent; 13 | -------------------------------------------------------------------------------- /src/errors.js: -------------------------------------------------------------------------------- 1 | var errors = {}; 2 | [ 3 | /** 4 | * node.js errors - we only use some of these, add as needed. 5 | */ 6 | //'-1:UNKNOWN:unknown error', 7 | //'0:OK:success', 8 | //'1:EOF:end of file', 9 | //'2:EADDRINFO:getaddrinfo error', 10 | '3:EACCES:permission denied', 11 | //'4:EAGAIN:resource temporarily unavailable', 12 | //'5:EADDRINUSE:address already in use', 13 | //'6:EADDRNOTAVAIL:address not available', 14 | //'7:EAFNOSUPPORT:address family not supported', 15 | //'8:EALREADY:connection already in progress', 16 | '9:EBADF:bad file descriptor', 17 | '10:EBUSY:resource busy or locked', 18 | //'11:ECONNABORTED:software caused connection abort', 19 | //'12:ECONNREFUSED:connection refused', 20 | //'13:ECONNRESET:connection reset by peer', 21 | //'14:EDESTADDRREQ:destination address required', 22 | //'15:EFAULT:bad address in system call argument', 23 | //'16:EHOSTUNREACH:host is unreachable', 24 | //'17:EINTR:interrupted system call', 25 | '18:EINVAL:invalid argument', 26 | //'19:EISCONN:socket is already connected', 27 | //'20:EMFILE:too many open files', 28 | //'21:EMSGSIZE:message too long', 29 | //'22:ENETDOWN:network is down', 30 | //'23:ENETUNREACH:network is unreachable', 31 | //'24:ENFILE:file table overflow', 32 | //'25:ENOBUFS:no buffer space available', 33 | //'26:ENOMEM:not enough memory', 34 | '27:ENOTDIR:not a directory', 35 | '28:EISDIR:illegal operation on a directory', 36 | //'29:ENONET:machine is not on the network', 37 | // errno 30 skipped, as per https://github.com/rvagg/node-errno/blob/master/errno.js 38 | //'31:ENOTCONN:socket is not connected', 39 | //'32:ENOTSOCK:socket operation on non-socket', 40 | //'33:ENOTSUP:operation not supported on socket', 41 | '34:ENOENT:no such file or directory', 42 | //'35:ENOSYS:function not implemented', 43 | //'36:EPIPE:broken pipe', 44 | //'37:EPROTO:protocol error', 45 | //'38:EPROTONOSUPPORT:protocol not supported', 46 | //'39:EPROTOTYPE:protocol wrong type for socket', 47 | //'40:ETIMEDOUT:connection timed out', 48 | //'41:ECHARSET:invalid Unicode character', 49 | //'42:EAIFAMNOSUPPORT:address family for hostname not supported', 50 | // errno 43 skipped, as per https://github.com/rvagg/node-errno/blob/master/errno.js 51 | //'44:EAISERVICE:servname not supported for ai_socktype', 52 | //'45:EAISOCKTYPE:ai_socktype not supported', 53 | //'46:ESHUTDOWN:cannot send after transport endpoint shutdown', 54 | '47:EEXIST:file already exists', 55 | //'48:ESRCH:no such process', 56 | //'49:ENAMETOOLONG:name too long', 57 | '50:EPERM:operation not permitted', 58 | '51:ELOOP:too many symbolic links encountered', 59 | //'52:EXDEV:cross-device link not permitted', 60 | '53:ENOTEMPTY:directory not empty', 61 | //'54:ENOSPC:no space left on device', 62 | '55:EIO:i/o error', 63 | //'56:EROFS:read-only file system', 64 | //'57:ENODEV:no such device', 65 | //'58:ESPIPE:invalid seek', 66 | //'59:ECANCELED:operation canceled', 67 | 68 | /** 69 | * Filer specific errors 70 | */ 71 | '1000:ENOTMOUNTED:not mounted', 72 | '1001:EFILESYSTEMERROR:missing super node, use \'FORMAT\' flag to format filesystem.', 73 | '1002:ENOATTR:attribute does not exist' 74 | 75 | ].forEach(function(e) { 76 | e = e.split(':'); 77 | var errno = +e[0]; 78 | var errName = e[1]; 79 | var defaultMessage = e[2]; 80 | 81 | function FilerError(msg, path) { 82 | Error.call(this); 83 | 84 | this.name = errName; 85 | this.code = errName; 86 | this.errno = errno; 87 | this.message = msg || defaultMessage; 88 | if(path) { 89 | this.path = path; 90 | } 91 | this.stack = (new Error(this.message)).stack; 92 | } 93 | FilerError.prototype = Object.create(Error.prototype); 94 | FilerError.prototype.constructor = FilerError; 95 | FilerError.prototype.toString = function() { 96 | var pathInfo = this.path ? (', \'' + this.path + '\'') : ''; 97 | return this.name + ': ' + this.message + pathInfo; 98 | }; 99 | 100 | // We expose the error as both Errors.EINVAL and Errors[18] 101 | errors[errName] = errors[errno] = FilerError; 102 | }); 103 | 104 | module.exports = errors; 105 | -------------------------------------------------------------------------------- /src/fs-watcher.js: -------------------------------------------------------------------------------- 1 | 'using strict'; 2 | 3 | const EventEmitter = require('../lib/eventemitter.js'); 4 | const Path = require('./path.js'); 5 | const Intercom = require('../lib/intercom.js'); 6 | 7 | /** 8 | * FSWatcher based on node.js' FSWatcher 9 | * see https://github.com/joyent/node/blob/master/lib/fs.js 10 | */ 11 | function FSWatcher() { 12 | EventEmitter.call(this); 13 | const self = this; 14 | let recursive = false; 15 | let recursivePathPrefix; 16 | let filename; 17 | 18 | function onchange(path) { 19 | // Watch for exact filename, or parent path when recursive is true. 20 | if(filename === path || (recursive && path.indexOf(recursivePathPrefix) === 0)) { 21 | self.trigger('change', 'change', path); 22 | } 23 | } 24 | 25 | // We support, but ignore the second arg, which node.js uses. 26 | self.start = function(filename_, persistent_, recursive_) { 27 | // Bail if we've already started (and therefore have a filename); 28 | if(filename) { 29 | return; 30 | } 31 | 32 | if(Path.isNull(filename_)) { 33 | throw new Error('Path must be a string without null bytes.'); 34 | } 35 | 36 | // TODO: get realpath for symlinks on filename... 37 | 38 | // Filer's Path.normalize strips trailing slashes, which we use here. 39 | // See https://github.com/js-platform/filer/issues/105 40 | filename = Path.normalize(filename_); 41 | 42 | // Whether to watch beneath this path or not 43 | recursive = recursive_ === true; 44 | // If recursive, construct a path prefix portion for comparisons later 45 | // (i.e., '/path' becomes '/path/' so we can search within a filename for the 46 | // prefix). We also take care to allow for '/' on its own. 47 | if(recursive) { 48 | recursivePathPrefix = filename === '/' ? '/' : filename + '/'; 49 | } 50 | 51 | const intercom = Intercom.getInstance(); 52 | intercom.on('change', onchange); 53 | }; 54 | 55 | self.close = function() { 56 | const intercom = Intercom.getInstance(); 57 | intercom.off('change', onchange); 58 | self.removeAllListeners('change'); 59 | }; 60 | } 61 | FSWatcher.prototype = new EventEmitter(); 62 | FSWatcher.prototype.constructor = FSWatcher; 63 | 64 | module.exports = FSWatcher; 65 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | let fs = null; 2 | let Filer = null; 3 | 4 | module.exports = Filer = { 5 | FileSystem: require('./filesystem/interface.js'), 6 | Buffer: Buffer, 7 | // We previously called this Path, but node calls it path. Do both 8 | Path: require('./path.js'), 9 | path: require('./path.js'), 10 | Errors: require('./errors.js'), 11 | Shell: require('./shell/shell.js'), 12 | /** 13 | * @deprecated Importing filer from your webpack config is not recommended. 14 | * 15 | * The filer `FilerWebpackPlugin` class is exposed directly. 16 | * 17 | * ``` 18 | * const { FilerWebpackPlugin } = require('filer/webpack'); 19 | * ``` 20 | */ 21 | FilerWebpackPlugin: require('./webpack-plugin'), 22 | }; 23 | 24 | // Add a getter for the `fs` instance, which returns 25 | // a Filer FileSystem instance, using the default provider/flags. 26 | Object.defineProperty(Filer, 'fs', { 27 | enumerable: true, 28 | get() { 29 | if(!fs) { 30 | fs = new Filer.FileSystem(); 31 | } 32 | return fs; 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /src/open-file-description.js: -------------------------------------------------------------------------------- 1 | const Errors = require('./errors.js'); 2 | const Node = require('./node'); 3 | 4 | function OpenFileDescription(path, id, flags, position) { 5 | this.path = path; 6 | this.id = id; 7 | this.flags = flags; 8 | this.position = position; 9 | } 10 | 11 | // Tries to find the node associated with an ofd's `id`. 12 | // If not found, an error is returned on the callback. 13 | OpenFileDescription.prototype.getNode = function(context, callback) { 14 | var id = this.id; 15 | var path = this.path; 16 | 17 | function check_if_node_exists(error, node) { 18 | if(error) { 19 | return callback(error); 20 | } 21 | 22 | if(!node) { 23 | return callback(new Errors.EBADF('file descriptor refers to unknown node', path)); 24 | } 25 | 26 | Node.create(node, callback); 27 | } 28 | 29 | context.getObject(id, check_if_node_exists); 30 | }; 31 | 32 | module.exports = OpenFileDescription; 33 | -------------------------------------------------------------------------------- /src/open-files.js: -------------------------------------------------------------------------------- 1 | const { FIRST_DESCRIPTOR } = require('./constants'); 2 | const openFiles = {}; 3 | 4 | /** 5 | * Start at FIRST_DESCRIPTOR and go until we find 6 | * an empty file descriptor, then return it. 7 | */ 8 | const getEmptyDescriptor = () => { 9 | let fd = FIRST_DESCRIPTOR; 10 | 11 | while(getOpenFileDescription(fd)) { 12 | fd++; 13 | } 14 | 15 | return fd; 16 | }; 17 | 18 | /** 19 | * Look up the open file description object for a given 20 | * file descriptor. 21 | */ 22 | const getOpenFileDescription = ofd => openFiles[ofd]; 23 | 24 | /** 25 | * Allocate a new file descriptor for the given 26 | * open file description. 27 | */ 28 | const allocDescriptor = openFileDescription => { 29 | const ofd = getEmptyDescriptor(); 30 | openFiles[ofd] = openFileDescription; 31 | return ofd; 32 | }; 33 | 34 | /** 35 | * Release the given existing file descriptor created 36 | * with allocDescriptor(). 37 | */ 38 | const releaseDescriptor = ofd => delete openFiles[ofd]; 39 | 40 | module.exports = { 41 | allocDescriptor, 42 | releaseDescriptor, 43 | getOpenFileDescription 44 | }; 45 | -------------------------------------------------------------------------------- /src/path.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Patch process to add process.cwd(), always giving the root dir. 3 | * NOTE: this line needs to happen *before* we require in `path`. 4 | */ 5 | process.cwd = () => '/'; 6 | 7 | /** 8 | * https://github.com/browserify/path-browserify via Parcel. 9 | * We use is as a base for our own Filer.Path, and patch/add 10 | * a few things we need for the browser environment. 11 | */ 12 | const nodePath = require('path'); 13 | const filerPath = Object.assign({}, nodePath); 14 | 15 | /** 16 | * Patch path.basename() to return / vs. '' 17 | */ 18 | filerPath.basename = (path, ext) => { 19 | const basename = nodePath.basename(path, ext); 20 | return basename === '' ? '/' : basename; 21 | }; 22 | 23 | /** 24 | * Patch path.normalize() to not add a trailing / 25 | */ 26 | filerPath.normalize = (path) => { 27 | path = nodePath.normalize(path); 28 | return path === '/' ? path : filerPath.removeTrailing(path); 29 | }; 30 | 31 | /** 32 | * Add new utility method isNull() to path: check for null paths. 33 | */ 34 | filerPath.isNull = path => ('' + path).indexOf('\u0000') !== -1; 35 | 36 | /** 37 | * Add new utility method addTrailing() to add trailing / without doubling to //. 38 | */ 39 | filerPath.addTrailing = path => path.replace(/\/*$/, '/'); 40 | 41 | /** 42 | * Add new utility method removeTrailing() to remove trailing /, dealing with multiple 43 | */ 44 | filerPath.removeTrailing = path => { 45 | path = path.replace(/\/*$/, ''); 46 | return path === '' ? '/' : path; 47 | }; 48 | 49 | module.exports = filerPath; 50 | -------------------------------------------------------------------------------- /src/providers/index.js: -------------------------------------------------------------------------------- 1 | const IndexedDB = require('./indexeddb.js'); 2 | const Memory = require('./memory.js'); 3 | 4 | module.exports = { 5 | IndexedDB: IndexedDB, 6 | Default: IndexedDB, 7 | Memory: Memory 8 | }; 9 | -------------------------------------------------------------------------------- /src/providers/memory.js: -------------------------------------------------------------------------------- 1 | var FILE_SYSTEM_NAME = require('../constants.js').FILE_SYSTEM_NAME; 2 | // NOTE: prefer setImmediate to nextTick for proper recursion yielding. 3 | // see https://github.com/js-platform/filer/pull/24 4 | var asyncCallback = require('../../lib/async.js').setImmediate; 5 | 6 | /** 7 | * Make shared in-memory DBs possible when using the same name. 8 | */ 9 | var createDB = (function() { 10 | var pool = {}; 11 | return function getOrCreate(name) { 12 | if(!Object.prototype.hasOwnProperty.call(pool, name)) { 13 | pool[name] = {}; 14 | } 15 | return pool[name]; 16 | }; 17 | }()); 18 | 19 | function MemoryContext(db, readOnly) { 20 | this.readOnly = readOnly; 21 | this.objectStore = db; 22 | } 23 | 24 | MemoryContext.prototype.clear = function(callback) { 25 | if(this.readOnly) { 26 | asyncCallback(function() { 27 | callback('[MemoryContext] Error: write operation on read only context'); 28 | }); 29 | return; 30 | } 31 | var objectStore = this.objectStore; 32 | Object.keys(objectStore).forEach(function(key){ 33 | delete objectStore[key]; 34 | }); 35 | asyncCallback(callback); 36 | }; 37 | 38 | // Memory context doesn't care about differences between Object and Buffer 39 | MemoryContext.prototype.getObject = 40 | MemoryContext.prototype.getBuffer = 41 | function(key, callback) { 42 | var that = this; 43 | asyncCallback(function() { 44 | callback(null, that.objectStore[key]); 45 | }); 46 | }; 47 | MemoryContext.prototype.putObject = 48 | MemoryContext.prototype.putBuffer = 49 | function(key, value, callback) { 50 | if(this.readOnly) { 51 | asyncCallback(function() { 52 | callback('[MemoryContext] Error: write operation on read only context'); 53 | }); 54 | return; 55 | } 56 | this.objectStore[key] = value; 57 | asyncCallback(callback); 58 | }; 59 | 60 | MemoryContext.prototype.delete = function(key, callback) { 61 | if(this.readOnly) { 62 | asyncCallback(function() { 63 | callback('[MemoryContext] Error: write operation on read only context'); 64 | }); 65 | return; 66 | } 67 | delete this.objectStore[key]; 68 | asyncCallback(callback); 69 | }; 70 | 71 | 72 | function Memory(name) { 73 | this.name = name || FILE_SYSTEM_NAME; 74 | } 75 | Memory.isSupported = function() { 76 | return true; 77 | }; 78 | 79 | Memory.prototype.open = function(callback) { 80 | this.db = createDB(this.name); 81 | asyncCallback(callback); 82 | }; 83 | Memory.prototype.getReadOnlyContext = function() { 84 | return new MemoryContext(this.db, true); 85 | }; 86 | Memory.prototype.getReadWriteContext = function() { 87 | return new MemoryContext(this.db, false); 88 | }; 89 | 90 | module.exports = Memory; 91 | -------------------------------------------------------------------------------- /src/shared.js: -------------------------------------------------------------------------------- 1 | function generateRandom(template) { 2 | return template.replace(/[xy]/g, function(c) { 3 | var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8); 4 | return v.toString(16); 5 | }); 6 | } 7 | 8 | function guid() { 9 | return generateRandom('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx').toUpperCase(); 10 | } 11 | 12 | /** 13 | * Generate a string of n random characters. Defaults to n=6. 14 | */ 15 | function randomChars(n) { 16 | n = n || 6; 17 | var template = 'x'.repeat(n); 18 | return generateRandom(template); 19 | } 20 | 21 | function nop() {} 22 | 23 | module.exports = { 24 | guid: guid, 25 | nop: nop, 26 | randomChars: randomChars 27 | }; 28 | -------------------------------------------------------------------------------- /src/shell/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const defaults = require('../constants.js').ENVIRONMENT; 3 | 4 | module.exports = function Environment(env) { 5 | env = env || {}; 6 | env.TMP = env.TMP || defaults.TMP; 7 | env.PATH = env.PATH || defaults.PATH; 8 | 9 | this.get = function(name) { 10 | return env[name]; 11 | }; 12 | 13 | this.set = function(name, value) { 14 | env[name] = value; 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /src/stats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Constants = require('./constants.js'); 4 | const Path = require('./path.js'); 5 | 6 | function dateFromMs(ms) { 7 | return new Date(Number(ms)); 8 | } 9 | 10 | function Stats(path, fileNode, devName) { 11 | this.dev = devName; 12 | this.node = fileNode.id; 13 | this.type = fileNode.type; 14 | this.size = fileNode.size; 15 | this.nlinks = fileNode.nlinks; 16 | // Date objects 17 | this.atime = dateFromMs(fileNode.atime); 18 | this.mtime = dateFromMs(fileNode.mtime); 19 | this.ctime = dateFromMs(fileNode.ctime); 20 | // Unix timestamp MS Numbers 21 | this.atimeMs = fileNode.atime; 22 | this.mtimeMs = fileNode.mtime; 23 | this.ctimeMs = fileNode.ctime; 24 | this.version = fileNode.version; 25 | this.mode = fileNode.mode; 26 | this.uid = fileNode.uid; 27 | this.gid = fileNode.gid; 28 | this.name = Path.basename(path); 29 | } 30 | 31 | Stats.prototype.isFile = function() { 32 | return this.type === Constants.NODE_TYPE_FILE; 33 | }; 34 | 35 | Stats.prototype.isDirectory = function() { 36 | return this.type === Constants.NODE_TYPE_DIRECTORY; 37 | }; 38 | 39 | Stats.prototype.isSymbolicLink = function() { 40 | return this.type === Constants.NODE_TYPE_SYMBOLIC_LINK; 41 | }; 42 | 43 | // These will always be false in Filer. 44 | Stats.prototype.isSocket = 45 | Stats.prototype.isFIFO = 46 | Stats.prototype.isCharacterDevice = 47 | Stats.prototype.isBlockDevice = 48 | function() { 49 | return false; 50 | }; 51 | 52 | module.exports = Stats; 53 | -------------------------------------------------------------------------------- /src/super-node.js: -------------------------------------------------------------------------------- 1 | var Constants = require('./constants.js'); 2 | 3 | function SuperNode(options) { 4 | var now = Date.now(); 5 | 6 | this.id = Constants.SUPER_NODE_ID; 7 | this.type = Constants.NODE_TYPE_META; 8 | this.atime = options.atime || now; 9 | this.ctime = options.ctime || now; 10 | this.mtime = options.mtime || now; 11 | // root node id (randomly generated) 12 | this.rnode = options.rnode; 13 | } 14 | 15 | SuperNode.create = function(options, callback) { 16 | options.guid(function(err, rnode) { 17 | if(err) { 18 | callback(err); 19 | return; 20 | } 21 | options.rnode = options.rnode || rnode; 22 | callback(null, new SuperNode(options)); 23 | }); 24 | }; 25 | 26 | module.exports = SuperNode; 27 | -------------------------------------------------------------------------------- /src/webpack-plugin/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var utils = require('./utils'); 3 | 4 | const PLUGIN_NAME = 'filer-webpack-plugin'; 5 | 6 | const OPTIONS_SCHEMA = require('./schema'); 7 | const OPTIONS_PROCESSORS = require('./processors'); 8 | 9 | module.exports = class FilerWebpackPlugin { 10 | 11 | constructor(options = {}) { 12 | utils.validateOptions(options, OPTIONS_SCHEMA); 13 | this.options = utils.processOptions(options, OPTIONS_PROCESSORS); 14 | } 15 | 16 | apply(compiler) { 17 | compiler.hooks.normalModuleFactory.tap( 18 | PLUGIN_NAME, 19 | (factory) => { 20 | factory.hooks.resolve.tap( 21 | PLUGIN_NAME, 22 | (resolveData) => { 23 | // Resolve fsProvider if required 24 | if ( 25 | resolveData.request === 'fsProvider' 26 | && resolveData.context === this.options.shimsDir 27 | ) { 28 | return this.resolveFsProvider(resolveData); 29 | } 30 | 31 | // Ignore filer files (these should resolve modules normally) 32 | if (resolveData.context.startsWith(this.options.filerDir)) return; 33 | 34 | // Apply fs, path and buffer shims if required 35 | switch (resolveData.request) { 36 | case 'fs': 37 | if (!this.options.shimFs) return; 38 | return this.applyFsShim(resolveData); 39 | case 'path': 40 | if (!this.options.shimPath) return; 41 | return this.applyPathShim(resolveData); 42 | default: 43 | return; 44 | } 45 | } 46 | ); 47 | }, 48 | ); 49 | } 50 | 51 | resolveFsProvider(resolveData) { 52 | switch (this.options.fsProvider) { 53 | case 'default': 54 | resolveData.request = path.join(this.options.fsProviderDir, 'default.js'); 55 | break; 56 | case 'indexeddb': 57 | resolveData.request = path.join(this.options.fsProviderDir, 'indexeddb.js'); 58 | break; 59 | case 'memory': 60 | resolveData.request = path.join(this.options.fsProviderDir, 'memory.js'); 61 | break; 62 | case 'custom': 63 | resolveData.request = path.join(this.options.fsProviderDir, 'custom.js'); 64 | break; 65 | default: 66 | throw new Error([ 67 | 'Invalid option for fsProvider.', 68 | 'fsProvider must be one of \'default\', \'indexeddb\', \'memory\' or \'custom\'.', 69 | 'If using a custom fsProvider, you must also provide the fsProviderDir option.' 70 | ].join(' ')); 71 | } 72 | } 73 | 74 | applyFsShim(resolveData) { 75 | resolveData.request = path.join(this.options.shimsDir, 'fs.js'); 76 | } 77 | 78 | applyPathShim(resolveData) { 79 | resolveData.request = path.join(this.options.shimsDir, 'path.js'); 80 | } 81 | }; 82 | -------------------------------------------------------------------------------- /src/webpack-plugin/processors.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | const ROOT_DIR_TAG = ''; 4 | const CWD = process.cwd(); 5 | 6 | module.exports = { 7 | filerDir: { 8 | process: function(value) { 9 | if (!value) { 10 | return path.join(CWD, 'node_modules', 'filer'); 11 | } 12 | return path.resolve(value.replace(ROOT_DIR_TAG, CWD)); 13 | }, 14 | }, 15 | shimsDir: { 16 | process: function(value) { 17 | if (!value) { 18 | return path.join(CWD, 'node_modules', 'filer', 'shims'); 19 | } 20 | return path.resolve(value.replace(ROOT_DIR_TAG, CWD)); 21 | } 22 | }, 23 | fsProviderDir: { 24 | process: function(value) { 25 | if (!value) { 26 | return path.join(CWD, 'node_modules', 'filer', 'shims', 'providers'); 27 | } 28 | return path.resolve(value.replace(ROOT_DIR_TAG, CWD)); 29 | }, 30 | }, 31 | shimFs: { default: true }, 32 | shimPath: { default: true}, 33 | fsProvider: { default: 'default'}, 34 | }; 35 | -------------------------------------------------------------------------------- /src/webpack-plugin/schema.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'object', 3 | properties: { 4 | filerDir: { 5 | type: 'string', 6 | }, 7 | shimsDir: { 8 | type: 'string', 9 | }, 10 | shimFs: { 11 | type: 'boolean', 12 | }, 13 | shimPath: { 14 | type: 'boolean', 15 | }, 16 | fsProvider: { 17 | type: 'string', 18 | }, 19 | fsProviderDir: { 20 | type: 'string', 21 | }, 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/webpack-plugin/utils.js: -------------------------------------------------------------------------------- 1 | var { validate } = require('schema-utils'); 2 | 3 | function validateOptions(options, schema) { 4 | validate(schema, options); 5 | } 6 | 7 | function processOptions(options, processors) { 8 | const processedOptions = {}; 9 | 10 | for (const [property, processor] of Object.entries(processors)) { 11 | processedOptions[property] = options[property]; 12 | if (processedOptions[property] === undefined) { 13 | processedOptions[property] = processor.default; 14 | } 15 | if (processor.process) { 16 | processedOptions[property] = processor.process(processedOptions[property]); 17 | } 18 | } 19 | 20 | return processedOptions; 21 | } 22 | 23 | module.exports = { 24 | validateOptions, 25 | processOptions, 26 | }; 27 | -------------------------------------------------------------------------------- /tests/bugs/issue105.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('trailing slashes in path names, issue 105', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should deal with trailing slashes properly, path == path/', function(done) { 9 | var fs = util.fs(); 10 | 11 | fs.mkdir('/tmp', function(err) { 12 | if(err) throw err; 13 | 14 | fs.mkdir('/tmp/foo', function(err) { 15 | if(err) throw err; 16 | 17 | // Without trailing slash 18 | fs.readdir('/tmp', function(err, result1) { 19 | if(err) throw err; 20 | expect(result1).to.exist; 21 | expect(result1.length).to.equal(1); 22 | 23 | // With trailing slash 24 | fs.readdir('/tmp/', function(err, result2) { 25 | if(err) throw err; 26 | expect(result2).to.exist; 27 | expect(result2[0]).to.equal('foo'); 28 | expect(result1).to.deep.equal(result2); 29 | done(); 30 | }); 31 | }); 32 | }); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/bugs/issue106.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('fs.writeFile truncation - issue 106', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should truncate an existing file', function(done) { 9 | var fs = util.fs(); 10 | var filename = '/test'; 11 | 12 | fs.writeFile(filename, '1', function(err) { 13 | if(err) throw err; 14 | 15 | fs.stat(filename, function(err, stats) { 16 | if(err) throw err; 17 | expect(stats.size).to.equal(1); 18 | 19 | fs.writeFile(filename, '', function(err) { 20 | if(err) throw err; 21 | 22 | fs.stat(filename, function(err, stats) { 23 | if(err) throw err; 24 | expect(stats.size).to.equal(0); 25 | done(); 26 | }); 27 | }); 28 | }); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /tests/bugs/issue239.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('fs.writeFile and non-existing directory, issue 239', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should give ENOENT if writing to a dir that does not exist', function(done) { 9 | var fs = util.fs(); 10 | 11 | fs.writeFile('/abc.txt', 'content', function(err) { 12 | expect(err).not.to.exist; 13 | 14 | fs.writeFile('/abc.txt/abc.txt', 'content', function(err) { 15 | expect(err.code).to.equal('ENOENT'); 16 | done(); 17 | }); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/bugs/issue247.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('sh.cd doesn\'t seem to be working from a relative path if I am one or more folders deep, #247', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should properly deal with relative paths missing ./ and ../', function(done) { 9 | var fs = util.fs(); 10 | var sh = new fs.Shell(); 11 | 12 | sh.mkdirp('/home/scott', function(err) { 13 | if(err) throw err; 14 | 15 | sh.cd('/', function(err) { 16 | if(err) throw err; 17 | 18 | expect(sh.pwd()).to.equal('/'); 19 | 20 | sh.cd('home', function(err) { 21 | if(err) throw err; 22 | 23 | expect(sh.pwd()).to.equal('/home'); 24 | 25 | sh.cd('scott', function(err) { 26 | if(err) throw err; 27 | 28 | expect(sh.pwd()).to.equal('/home/scott'); 29 | done(); 30 | }); 31 | }); 32 | }); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/bugs/issue249.js: -------------------------------------------------------------------------------- 1 | var Filer = require('../../src'); 2 | var util = require('../lib/test-utils.js'); 3 | var expect = require('chai').expect; 4 | 5 | describe('Filer.Buffer should accept initialized ArrayBuffers, issue 249', function() { 6 | beforeEach(util.setup); 7 | afterEach(util.cleanup); 8 | 9 | it('should accept an ArrayBuffer with a specified size', function(done) { 10 | var buffer = Buffer.from(new ArrayBuffer(5)); 11 | expect(buffer.length).to.equal(5); 12 | done(); 13 | }); 14 | }); 15 | 16 | describe('Filer.Buffer static methods are in tact, issue 249', function() { 17 | beforeEach(util.setup); 18 | afterEach(util.cleanup); 19 | 20 | it('should proxy Buffer.isBuffer', function(done) { 21 | expect(Filer.Buffer.isBuffer(Buffer.from([]))).to.equal(true); 22 | expect(Filer.Buffer.isBuffer('')).to.equal(false); 23 | done(); 24 | }); 25 | 26 | it('should proxy Buffer.isEncoding', function(done) { 27 | expect(Filer.Buffer.isEncoding('utf8')).to.equal(true); 28 | expect(Filer.Buffer.isEncoding('smoop')).to.equal(false); 29 | done(); 30 | }); 31 | 32 | it('should proxy Buffer.byteLength', function(done) { 33 | expect(Filer.Buffer.byteLength('01100111', 'binary')).to.equal(8); 34 | done(); 35 | }); 36 | 37 | it('should proxy Buffer.concat', function(done) { 38 | expect(Filer.Buffer.concat([Buffer.alloc(1), Buffer.alloc(2)]).length).to.equal(3); 39 | done(); 40 | }); 41 | }); 42 | 43 | -------------------------------------------------------------------------------- /tests/bugs/issue254.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('EISDIR when trying to open a dir path - issue 254', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should fail with EISDIR for root dir', function(done) { 9 | var fs = util.fs(); 10 | 11 | fs.readFile('/', function(err) { 12 | expect(err.code).to.equal('EISDIR'); 13 | done(); 14 | }); 15 | }); 16 | 17 | it('should fail with EISDIR for regular dir', function(done) { 18 | var fs = util.fs(); 19 | 20 | fs.mkdir('/dir', function(err) { 21 | if(err) throw err; 22 | 23 | fs.readFile('/dir', function(err) { 24 | expect(err.code).to.equal('EISDIR'); 25 | done(); 26 | }); 27 | }); 28 | }); 29 | 30 | it('should fail with EISDIR for symlinked dir', function(done) { 31 | var fs = util.fs(); 32 | 33 | fs.mkdir('/dir', function(err) { 34 | if(err) throw err; 35 | 36 | fs.symlink('/dir', '/link', function(err) { 37 | if(err) throw err; 38 | 39 | fs.readFile('/link', function(err) { 40 | expect(err.code).to.equal('EISDIR'); 41 | done(); 42 | }); 43 | }); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /tests/bugs/issue258.js: -------------------------------------------------------------------------------- 1 | var Filer = require('../../src'); 2 | var expect = require('chai').expect; 3 | var setImmediate = require('../../lib/async.js').setImmediate; 4 | 5 | describe('Queued operations should error when fs is in error state, issue 258', function() { 6 | var provider; 7 | 8 | // Provider that does nothing but fail on open. 9 | function FailingProviderContext(){} 10 | FailingProviderContext.prototype.clear = function(callback) { 11 | this.failCallback(callback); 12 | }; 13 | FailingProviderContext.prototype.getObject = 14 | FailingProviderContext.prototype.getBuffer = function(key, callback) { 15 | this.failCallback(callback); 16 | }; 17 | FailingProviderContext.prototype.putObject = 18 | FailingProviderContext.prototype.putBuffer = function(key, value, callback) { 19 | this.failCallback(callback); 20 | }; 21 | FailingProviderContext.prototype.delete = function(key, callback) { 22 | this.failCallback(callback); 23 | }; 24 | 25 | function FailingProvider() { 26 | var self = this; 27 | self.name = 'failure'; 28 | self.open = function(callback) { 29 | // Wait until caller tells us to fail 30 | self.failNow = function() { 31 | self.failCallback(callback); 32 | }; 33 | }; 34 | self.failCallback = function(callback) { 35 | setImmediate(function() { 36 | callback(new Error); 37 | }); 38 | }; 39 | } 40 | FailingProvider.prototype.getReadWriteContext = 41 | FailingProvider.prototype.getReadWriteContext = function() { 42 | return new FailingProviderContext(); 43 | }; 44 | 45 | beforeEach(function() { 46 | provider = new FailingProvider(); 47 | }); 48 | 49 | afterEach(function() { 50 | provider = null; 51 | }); 52 | 53 | it('should get EFILESYSTEMERROR errors on callbacks to queued operations on provider error', function(done) { 54 | var errCount = 0; 55 | var fs = new Filer.FileSystem({provider: provider}, function() { 56 | // Do nothing 57 | }); 58 | 59 | function maybeDone(err) { 60 | expect(err).to.exist; 61 | expect(err.code).to.equal('EFILESYSTEMERROR'); 62 | errCount++; 63 | 64 | if(errCount === 2) { 65 | done(); 66 | } 67 | } 68 | 69 | // Queue some fs operations, and expect them to fail 70 | fs.mkdir('/tmp', maybeDone); 71 | fs.writeFile('/file', 'data', maybeDone); 72 | 73 | // Operations are queued, tell the provider to fail now. 74 | provider.failNow(); 75 | }); 76 | 77 | it('should get EFILESYSTEMERROR errors on callbacks to queued operations after ready callback', function(done) { 78 | var fs = new Filer.FileSystem({provider: provider}, function(err) { 79 | expect(err).to.exist; 80 | 81 | // Queue is drained, but new operations should also fail 82 | fs.mkdir('/tmp', function(err) { 83 | expect(err).to.exist; 84 | expect(err.code).to.equal('EFILESYSTEMERROR'); 85 | done(); 86 | }); 87 | }); 88 | provider.failNow(); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /tests/bugs/issue267.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('fs.readdir on non-dir paths, issue 267', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should fail with ENOTDIR when called on filepath', function(done) { 9 | var fs = util.fs(); 10 | 11 | fs.writeFile('/myfile.txt', 'data', function(err) { 12 | if(err) throw err; 13 | 14 | fs.readdir('/myfile.txt', function(err, contents) { 15 | expect(err).to.exist; 16 | expect(err.code).to.equal('ENOTDIR'); 17 | expect(contents).not.to.exist; 18 | done(); 19 | }); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/bugs/issue270.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('undefined and relative paths, issue270', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should fail with EINVAL when called on an undefined path', function() { 9 | var fs = util.fs(); 10 | var fn = () => fs.writeFile(undefined, 'data'); 11 | expect(fn).to.throw(); 12 | }); 13 | 14 | it('should fail with EINVAL when called on a relative path', function() { 15 | var fs = util.fs(); 16 | var fn = () => fs.writeFile('relpath/file.txt', 'data'); 17 | expect(fn).to.throw(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/bugs/issue357.js: -------------------------------------------------------------------------------- 1 | var Path = require('../../src').Path; 2 | var expect = require('chai').expect; 3 | 4 | describe('Path.resolve does not work, issue357', function() { 5 | it('Path.relative() should not crash', function() { 6 | expect(Path.relative('/mydir', '/mydir/file')).to.equal('file'); 7 | 8 | // https://nodejs.org/api/path.html#path_path_relative_from_to 9 | expect(Path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb')).to.equal('../../impl/bbb'); 10 | }); 11 | 12 | it('Path.resolve() should work as expectedh', function() { 13 | // https://nodejs.org/api/path.html#path_path_resolve_from_to 14 | expect(Path.resolve('/foo/bar', './baz')).to.equal('/foo/bar/baz'); 15 | expect(Path.resolve('/foo/bar', '/tmp/file/')).to.equal('/tmp/file'); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/bugs/issue773.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const util = require('../lib/test-utils.js'); 3 | 4 | describe('unexpected failures when calling fs functions (e.g. writeFile) with empty options object, issue 773', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should call fs.writeFile with an empty options object', function(done) { 9 | const fs = util.fs(); 10 | fs.writeFile('/a', 'trololol', {}, done); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /tests/bugs/issue775.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var util = require('../lib/test-utils.js'); 3 | var expect = require('chai').expect; 4 | 5 | describe('fs.readdir fails when passing options, issue775', function () { 6 | beforeEach(util.setup); 7 | afterEach(util.cleanup); 8 | 9 | function setup(fs, dir, cb) { 10 | fs.mkdir(dir, undefined, (err) => { 11 | if (err) { 12 | cb(err); 13 | } 14 | else { 15 | fs.writeFile(dir + '/file', '', (err) => { 16 | if (err) { 17 | cb(err); 18 | } 19 | else { 20 | fs.mkdir(dir + '/folder', (err) => { 21 | if (err) { 22 | cb(err); 23 | } 24 | else { 25 | fs.symlink(dir + '/file', dir + '/symlink', (err) => { 26 | if (err) { 27 | cb(err); 28 | } 29 | else { 30 | cb(); 31 | } 32 | }); 33 | } 34 | }); 35 | } 36 | }); 37 | } 38 | }); 39 | } 40 | 41 | it('should create a directory, add a file, folder and symbolic link then call fs.readdir with buffer encoding', function (done) { 42 | var fs = util.fs(); 43 | setup(fs, '/test_dir', (err) => { 44 | if (err) { 45 | done(err); 46 | } 47 | fs.readdir('/test_dir', 'buffer', (err, data) => { 48 | if (err) { 49 | done(err); 50 | } 51 | else { 52 | expect(data).to.have.length(3); 53 | 54 | expect(data[0].toString()).to.equal('file'); 55 | expect(data[1].toString()).to.equal('folder'); 56 | expect(data[2].toString()).to.equal('symlink'); 57 | 58 | done(); 59 | } 60 | }); 61 | }); 62 | }); 63 | 64 | it('should create a directory, add a file, folder and symbolic link then call fs.readdir with withFileTypes and encoding options', function (done) { 65 | var fs = util.fs(); 66 | setup(fs, '/test_dir', (err) => { 67 | if (err) { 68 | done(err); 69 | } 70 | fs.readdir('/test_dir', { encoding: 'base64', withFileTypes: true }, (err, data) => { 71 | if (err) { 72 | done(err); 73 | } 74 | else { 75 | expect(data).to.have.length(3); 76 | 77 | expect(Buffer.from(data[0].name, 'base64').toString()).to.equal('file'); 78 | expect(Buffer.from(data[1].name, 'base64').toString()).to.equal('folder'); 79 | expect(Buffer.from(data[2].name, 'base64').toString()).to.equal('symlink'); 80 | 81 | expect(data[0].isFile()).to.be.true; 82 | expect(data[1].isDirectory()).to.be.true; 83 | expect(data[2].isSymbolicLink()).to.be.true; 84 | 85 | done(); 86 | } 87 | }); 88 | }); 89 | }); 90 | 91 | it('should create a directory then call fs.readdir without options', function (done) { 92 | var fs = util.fs(); 93 | setup(fs, '/test_dir', (err) => { 94 | if (err) { 95 | done(err); 96 | } 97 | else { 98 | fs.readdir('/test_dir', (err, data) => { 99 | if (err) { 100 | done(err); 101 | } 102 | else { 103 | expect(data).to.have.length(3); 104 | 105 | expect(data[0]).to.equal('file'); 106 | expect(data[1]).to.equal('folder'); 107 | expect(data[2]).to.equal('symlink'); 108 | 109 | done(); 110 | } 111 | }); 112 | } 113 | }); 114 | }); 115 | }); 116 | -------------------------------------------------------------------------------- /tests/bugs/issue776.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const util = require('../lib/test-utils.js'); 3 | 4 | describe('fs.mkdir does not recursively create parent directories when called with { recursive: true }, issue776', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it.skip('should not throw when calling fs.mkdir with recursive flag set', function(done) { 9 | const fs = util.fs(); 10 | fs.mkdir('/test_dir/a/b', { recursive: true }, done); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /tests/bugs/ls-depth-bug.js: -------------------------------------------------------------------------------- 1 | var Filer = require('../../src'); 2 | var util = require('../lib/test-utils.js'); 3 | var expect = require('chai').expect; 4 | var async = require('../../lib/async.js'); 5 | 6 | describe('sh.ls and deep directory trees', function() { 7 | beforeEach(util.setup); 8 | afterEach(util.cleanup); 9 | 10 | it('should not crash when calling sh.ls() on deep directory layouts', function(done) { 11 | var fs = util.fs(); 12 | var sh = new fs.Shell(); 13 | 14 | var path = ''; 15 | for(var i=0; i<50; i++) { 16 | path += '/' + i; 17 | } 18 | 19 | sh.mkdirp(path, function(err) { 20 | if(err) throw err; 21 | 22 | sh.ls('/', {recursive: true}, function(err, listing) { 23 | expect(err).not.to.exist; 24 | expect(listing).to.exist; 25 | done(); 26 | }); 27 | }); 28 | }).timeout(15000); 29 | 30 | it('should not crash when calling sh.ls() on wide directory layouts', function(done) { 31 | var fs = util.fs(); 32 | var sh = new fs.Shell(); 33 | 34 | var dirName = '/dir'; 35 | 36 | fs.mkdir(dirName, function(err) { 37 | if(err) throw err; 38 | 39 | var paths = []; 40 | for(var i=0; i<100; i++) { 41 | paths.push(Filer.Path.join(dirName, ''+i)); 42 | } 43 | 44 | function writeFile(path, callback) { 45 | fs.writeFile(path, 'data', callback); 46 | } 47 | 48 | async.eachSeries(paths, writeFile, function(err) { 49 | if(err) throw err; 50 | 51 | sh.ls('/', {recursive: true}, function(err, listing) { 52 | expect(err).not.to.exist; 53 | expect(listing).to.exist; 54 | done(); 55 | }); 56 | }); 57 | }); 58 | }).timeout(15000); 59 | }); 60 | -------------------------------------------------------------------------------- /tests/bugs/rename-dir-trailing-slash.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('trailing slashes in path names to work when renaming a dir', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should deal with trailing slashes in rename, dir == dir/', function(done) { 9 | var fs = util.fs(); 10 | 11 | fs.mkdir('/tmp', function(err) { 12 | if(err) throw err; 13 | 14 | fs.rename('/tmp/', '/new-tmp/', function(err) { 15 | if(err) throw err; 16 | 17 | fs.stat('/new-tmp', function(err, stats) { 18 | if(err) throw err; 19 | expect(stats).to.exist; 20 | expect(stats.isDirectory()).to.be.true; 21 | 22 | done(); 23 | }); 24 | }); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /tests/filesystems/images/README.md: -------------------------------------------------------------------------------- 1 | The following images are JSON filesystem images created with 2 | `tools/fs-image.js`, and named with the filesystem dir used 3 | as the source of the image, and the version of Filer used to 4 | generate them: 5 | 6 | 1. `tiny-fs.0.43.json` was created from `tests/filesystem/tiny-fs/` with https://github.com/filerjs/filer/blob/d66114e20c7f0235698d9933dbd90217ba86fa4e/dist/filer.min.js 7 | 8 | If you need to create a new image, use `tools/get-filer-version.js` to 9 | get a specific version of Filer, then `tools/fs-image.js` to generate an image. 10 | -------------------------------------------------------------------------------- /tests/filesystems/images/tiny-fs.0.43.json: -------------------------------------------------------------------------------- 1 | {"00000000-0000-0000-0000-000000000000":{"id":"00000000-0000-0000-0000-000000000000","mode":"META","atime":1545098425757,"ctime":1545098425757,"mtime":1545098425757,"rnode":"A63180BA-9BCE-4245-86E8-8613726587CA"},"A63180BA-9BCE-4245-86E8-8613726587CA":{"id":"A63180BA-9BCE-4245-86E8-8613726587CA","mode":"DIRECTORY","size":0,"atime":1545098425770,"ctime":1545098425770,"mtime":1545098425770,"flags":[],"xattrs":{},"nlinks":1,"version":0,"nblocks":1,"data":"3A41160B-1F39-4162-85BD-473F16ABF35F"},"3A41160B-1F39-4162-85BD-473F16ABF35F":{"dir":{"id":"E1634C88-337D-4D1E-9155-81B099BC7E7C","type":"DIRECTORY"},"file.txt":{"id":"DD338D5C-34FF-4AF7-AFBB-C01E8214125F","type":"FILE"}},"E1634C88-337D-4D1E-9155-81B099BC7E7C":{"id":"E1634C88-337D-4D1E-9155-81B099BC7E7C","mode":"DIRECTORY","size":0,"atime":1545098425778,"ctime":1545098425778,"mtime":1545098425778,"flags":[],"xattrs":{},"nlinks":1,"version":0,"nblocks":1,"data":"C8BB5894-9B0C-4CDC-B014-3944FB40DA77"},"C8BB5894-9B0C-4CDC-B014-3944FB40DA77":{"file2.txt":{"id":"EEB56FEC-DABE-48BF-88A5-B9F37723A0BC","type":"FILE"}},"DD338D5C-34FF-4AF7-AFBB-C01E8214125F":{"id":"DD338D5C-34FF-4AF7-AFBB-C01E8214125F","mode":"FILE","size":16,"atime":1545098327877.7542,"ctime":1545098313562.6824,"mtime":1545098313562.6824,"flags":[],"xattrs":{},"nlinks":1,"version":1,"nblocks":1,"data":"BDEE2339-6992-4D8F-AFAA-81813C30592C"},"BDEE2339-6992-4D8F-AFAA-81813C30592C":{"type":"Buffer","data":[84,104,105,115,32,105,115,32,97,32,102,105,108,101,46,10]},"EEB56FEC-DABE-48BF-88A5-B9F37723A0BC":{"id":"EEB56FEC-DABE-48BF-88A5-B9F37723A0BC","mode":"FILE","size":23,"atime":1545098327877.7542,"ctime":1545098313562.6824,"mtime":1545098313562.6824,"flags":[],"xattrs":{},"nlinks":1,"version":1,"nblocks":1,"data":"62262B53-32BC-43F6-983B-4127302C2483"},"62262B53-32BC-43F6-983B-4127302C2483":{"type":"Buffer","data":[84,104,105,115,32,105,115,32,97,32,115,101,99,111,110,100,32,102,105,108,101,46,10]}} -------------------------------------------------------------------------------- /tests/filesystems/migrations/from-0.43.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const Filer = require('../../../src'); 3 | const SerializableMemoryProvider = require('../../lib/serializable-memory-provider'); 4 | const nodeFs = require('fs'); 5 | const nodePath = require('path'); 6 | 7 | describe('Migration tests from Filer 0.43 to current', () => { 8 | 9 | let filerFs; 10 | 11 | before(done => { 12 | // Let the provider parse the JSON 13 | const imagePath = nodePath.resolve(__dirname, '../images/tiny-fs.0.43.json'); 14 | nodeFs.readFile(imagePath, 'utf8', (err, data) => { 15 | if(err) throw err; 16 | 17 | new Filer.FileSystem({ 18 | provider: new SerializableMemoryProvider('0.43', data) 19 | }, (err, fs) => { 20 | if(err) throw err; 21 | 22 | filerFs = fs; 23 | done(); 24 | }); 25 | }); 26 | }); 27 | 28 | it('should have a root directory', done => { 29 | filerFs.stat('/', (err, stats) => { 30 | if(err) throw err; 31 | 32 | expect(stats).to.be.an('object'); 33 | expect(stats.isDirectory()).to.be.true; 34 | done(); 35 | }); 36 | }); 37 | 38 | it('should have expected entries in root dir', done => { 39 | filerFs.readdir('/', (err, entries) => { 40 | if(err) throw err; 41 | 42 | expect(entries).to.be.an('array'); 43 | expect(entries.length).to.equal(2); 44 | expect(entries).to.contain('file.txt'); 45 | expect(entries).to.contain('dir'); 46 | done(); 47 | }); 48 | }); 49 | 50 | it('should have correct contents for /file.txt (read as String)', done => { 51 | const fileTxtPath = nodePath.resolve(__dirname, '../tiny-fs/file.txt'); 52 | 53 | nodeFs.readFile(fileTxtPath, 'utf8', (err, nodeData) => { 54 | if(err) throw err; 55 | 56 | filerFs.readFile('/file.txt', 'utf8', (err, filerData) => { 57 | if(err) throw err; 58 | 59 | expect(nodeData).to.equal(filerData); 60 | done(); 61 | }); 62 | }); 63 | }); 64 | 65 | it('should have expected entries in /dir', done => { 66 | filerFs.readdir('/dir', (err, entries) => { 67 | if(err) throw err; 68 | 69 | expect(entries).to.be.an('array'); 70 | expect(entries.length).to.equal(1); 71 | expect(entries).to.contain('file2.txt'); 72 | done(); 73 | }); 74 | }); 75 | 76 | it('should have correct contents for /dir/file2.txt (read as Buffer)', done => { 77 | const file2TxtPath = nodePath.resolve(__dirname, '../tiny-fs/dir/file2.txt'); 78 | 79 | nodeFs.readFile(file2TxtPath, null, (err, nodeData) => { 80 | if(err) throw err; 81 | 82 | filerFs.readFile('/dir/file2.txt', null, (err, filerData) => { 83 | if(err) throw err; 84 | 85 | expect(nodeData).to.deep.equal(filerData); 86 | done(); 87 | }); 88 | }); 89 | }); 90 | 91 | }); 92 | -------------------------------------------------------------------------------- /tests/filesystems/tiny-fs/dir/file2.txt: -------------------------------------------------------------------------------- 1 | This is a second file. 2 | -------------------------------------------------------------------------------- /tests/filesystems/tiny-fs/file.txt: -------------------------------------------------------------------------------- 1 | This is a file. 2 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Filer Tests 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Add your test spec files to the list in order to 3 | * get them running by default. 4 | */ 5 | 6 | // Shims 7 | require('./spec/shims/fs.spec'); 8 | require('./spec/shims/path.spec'); 9 | 10 | // Webpack Plugin 11 | require('./spec/webpack-plugin/webpack-plugin.spec'); 12 | 13 | // Filer 14 | require('./spec/filer.spec'); 15 | require('./spec/filer.buffer.spec.js'); 16 | 17 | // Filer.FileSystem.* 18 | require('./spec/filer.filesystem.spec'); 19 | require('./spec/fs.spec'); 20 | require('./spec/fs.access.spec'); 21 | require('./spec/fs.stat.spec'); 22 | require('./spec/fs.lstat.spec'); 23 | require('./spec/fs.exists.spec'); 24 | require('./spec/fs.mknod.spec'); 25 | require('./spec/fs.mkdir.spec'); 26 | require('./spec/fs.mkdtemp.spec'); 27 | require('./spec/fs.readdir.spec'); 28 | require('./spec/fs.rmdir.spec'); 29 | require('./spec/fs.open.spec'); 30 | require('./spec/fs.write.spec'); 31 | require('./spec/fs.writeFile-readFile.spec'); 32 | require('./spec/fs.appendFile.spec'); 33 | require('./spec/fs.read.spec'); 34 | require('./spec/fs.close.spec'); 35 | require('./spec/fs.fsync.spec'); 36 | require('./spec/fs.link.spec'); 37 | require('./spec/fs.unlink.spec'); 38 | require('./spec/fs.rename.spec'); 39 | require('./spec/fs.lseek.spec'); 40 | require('./spec/fs.symlink.spec'); 41 | require('./spec/fs.readlink.spec'); 42 | require('./spec/fs.truncate.spec'); 43 | require('./spec/fs.ftruncate.spec'); 44 | require('./spec/fs.utimes.spec'); 45 | require('./spec/fs.xattr.spec'); 46 | require('./spec/path-resolution.spec'); 47 | require('./spec/trailing-slashes.spec'); 48 | require('./spec/times.spec'); 49 | require('./spec/time-flags.spec'); 50 | require('./spec/fs.watch.spec'); 51 | require('./spec/fs.unwatchFile.spec'); 52 | require('./spec/errors.spec'); 53 | require('./spec/fs.shell.spec'); 54 | require('./spec/fs.chmod.spec'); 55 | require('./spec/fs.chown.spec'); 56 | require('./spec/fs.copyFile.spec'); 57 | 58 | // Filer.FileSystem.providers.* 59 | require('./spec/providers/providers.spec'); 60 | require('./spec/providers/providers.indexeddb.spec'); 61 | require('./spec/providers/providers.memory.spec'); 62 | require('./spec/providers/serializable-memory-provider.spec'); 63 | 64 | // Filer.FileSystemShell.* 65 | require('./spec/shell/cd.spec'); 66 | require('./spec/shell/touch.spec'); 67 | require('./spec/shell/exec.spec'); 68 | require('./spec/shell/cat.spec'); 69 | require('./spec/shell/ls.spec'); 70 | require('./spec/shell/rm.spec'); 71 | require('./spec/shell/env.spec'); 72 | require('./spec/shell/mkdirp.spec'); 73 | require('./spec/shell/find.spec'); 74 | 75 | // Ported node.js tests (filenames match names in https://github.com/joyent/node/tree/master/test) 76 | require('./spec/node-js/simple/test-fs-mkdir'); 77 | require('./spec/node-js/simple/test-fs-null-bytes'); 78 | require('./spec/node-js/simple/test-fs-watch'); 79 | require('./spec/node-js/simple/test-fs-watch-recursive'); 80 | 81 | // Regressions, Bugs 82 | require('./bugs/issue105'); 83 | require('./bugs/issue106'); 84 | require('./bugs/issue239'); 85 | require('./bugs/issue249'); 86 | require('./bugs/ls-depth-bug'); 87 | require('./bugs/issue247.js'); 88 | require('./bugs/issue254.js'); 89 | require('./bugs/issue258.js'); 90 | require('./bugs/issue267.js'); 91 | require('./bugs/issue270.js'); 92 | require('./bugs/rename-dir-trailing-slash.js'); 93 | require('./bugs/issue357.js'); 94 | require('./bugs/issue773.js'); 95 | require('./bugs/issue775.js'); 96 | require('./bugs/issue776.js'); 97 | 98 | // Sample code from README 99 | require('./spec/readme.example.spec'); 100 | -------------------------------------------------------------------------------- /tests/lib/indexeddb.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Filer = require('../../src'); 4 | 5 | let needsCleanup = []; 6 | if(global.addEventListener) { 7 | global.addEventListener('beforeunload', function() { 8 | needsCleanup.forEach(function(f) { f(); }); 9 | }); 10 | } 11 | 12 | function IndexedDBTestProvider(name) { 13 | let _done = false; 14 | let that = this; 15 | 16 | function cleanup(callback) { 17 | callback = callback || function(){}; 18 | 19 | if(!that.provider || _done) { 20 | return callback(); 21 | } 22 | 23 | function finished() { 24 | that.provider = null; 25 | _done = true; 26 | callback(); 27 | } 28 | 29 | try { 30 | // We have to force any other connections to close 31 | // before we can delete a db. 32 | if(that.provider.db) { 33 | that.provider.db.close(); 34 | } 35 | 36 | const indexedDB = global.indexedDB || 37 | global.mozIndexedDB || 38 | global.webkitIndexedDB || 39 | global.msIndexedDB; 40 | 41 | let request = indexedDB.deleteDatabase(name); 42 | request.onsuccess = finished; 43 | request.onerror = finished; 44 | } catch(e) { 45 | /* eslint no-console:0 */ 46 | console.log('Failed to delete test database', e); 47 | finished(); 48 | } 49 | } 50 | 51 | function init() { 52 | if(that.provider) { 53 | return; 54 | } 55 | that.provider = new Filer.FileSystem.providers.IndexedDB(name); 56 | needsCleanup.push(cleanup); 57 | } 58 | 59 | this.init = init; 60 | this.cleanup = cleanup; 61 | } 62 | IndexedDBTestProvider.isSupported = function() { 63 | return Filer.FileSystem.providers.IndexedDB.isSupported(); 64 | }; 65 | 66 | module.exports = IndexedDBTestProvider; 67 | -------------------------------------------------------------------------------- /tests/lib/memory.js: -------------------------------------------------------------------------------- 1 | var Filer = require('../../src'); 2 | 3 | function MemoryTestProvider(name) { 4 | var that = this; 5 | 6 | function cleanup(callback) { 7 | callback = callback || function(){}; 8 | 9 | that.provider = null; 10 | callback(); 11 | } 12 | 13 | function init() { 14 | if(that.provider) { 15 | return; 16 | } 17 | that.provider = new Filer.FileSystem.providers.Memory(name); 18 | } 19 | 20 | this.init = init; 21 | this.cleanup = cleanup; 22 | } 23 | MemoryTestProvider.isSupported = function() { 24 | return Filer.FileSystem.providers.Memory.isSupported(); 25 | }; 26 | 27 | module.exports = MemoryTestProvider; 28 | -------------------------------------------------------------------------------- /tests/lib/serializable-memory-provider.js: -------------------------------------------------------------------------------- 1 | const MemoryProvider = require('../../src/providers/memory'); 2 | const { parseBJSON } = require('../lib/test-utils'); 3 | 4 | class SerializableMemoryProvider extends MemoryProvider { 5 | 6 | constructor(name, jsonImage) { 7 | super(name); 8 | this.unparsedJSONImage = jsonImage; 9 | } 10 | 11 | /** 12 | * In addition to the usual setup of a Memory provider, 13 | * also parse and overwrite the internal database. 14 | */ 15 | open(callback) { 16 | super.open(err => { 17 | if(err) { 18 | return callback(err); 19 | } 20 | 21 | // If we don't have an image to import, leave db as is 22 | if(!this.unparsedJSONImage) { 23 | return callback(); 24 | } 25 | 26 | // Try to import the fs image from JSON 27 | try { 28 | this.db = parseBJSON(this.unparsedJSONImage); 29 | this.unparsedJSONImage = null; 30 | callback(); 31 | } catch(e) { 32 | callback(new Error(`unable to parse JSON filesystem image: ${e.message}`)); 33 | } 34 | }); 35 | } 36 | 37 | export() { 38 | return JSON.stringify(this.db); 39 | } 40 | } 41 | 42 | module.exports = SerializableMemoryProvider; 43 | -------------------------------------------------------------------------------- /tests/spec/filer.buffer.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Filer = require('../../src'); 4 | const expect = require('chai').expect; 5 | 6 | describe('Filer.Buffer', function() { 7 | 8 | it('should support .from()', function() { 9 | expect(Filer.Buffer.from).to.be.a('function'); 10 | }); 11 | 12 | it('should support .alloc()', function() { 13 | expect(Filer.Buffer.alloc).to.be.a('function'); 14 | }); 15 | 16 | it('should support .isBuffer()', function() { 17 | const buf = Buffer.alloc(0); 18 | expect(Buffer.isBuffer(buf)).to.be.true; 19 | }); 20 | 21 | describe('Deprecation checks - constructor vs. class method init', function() { 22 | 23 | it('should allow new Buffer(array)', function() { 24 | const arr = [1, 2, 3]; 25 | const buf1 = new Buffer(arr); 26 | const buf2 = new Buffer.from(arr); 27 | expect(buf1).to.deep.equal(buf2); 28 | }); 29 | 30 | it('should allow new Buffer(ArrayBuffer)', function() { 31 | const arrayBuffer = (new Uint8Array([1, 2, 3])).buffer; 32 | const buf1 = new Buffer(arrayBuffer); 33 | const buf2 = Buffer.from(arrayBuffer); 34 | expect(buf1).to.deep.equal(buf2); 35 | }); 36 | 37 | it('should allow new Buffer(ArrayBuffer)', function() { 38 | const buffer = new Buffer.from([1, 2, 3]); 39 | const buf1 = new Buffer(buffer); 40 | const buf2 = Buffer.from(buffer); 41 | expect(buf1).to.deep.equal(buf2); 42 | }); 43 | 44 | it('should allow new Buffer(string)', function() { 45 | const s = 'Hello World'; 46 | const buf1 = new Buffer(s); 47 | const buf2 = Buffer.from(s); 48 | expect(buf1).to.deep.equal(buf2); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /tests/spec/filer.filesystem.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Filer = require('../../src'); 3 | const util = require('../lib/test-utils.js'); 4 | const expect = require('chai').expect; 5 | 6 | describe('Filer.FileSystem', function() { 7 | beforeEach(util.setup); 8 | afterEach(util.cleanup); 9 | 10 | it('should properly mount new or existing filesystem', function(done) { 11 | const provider = util.provider().provider; 12 | 13 | // 1) Should be able to open a new filesystem, and get empty root 14 | const fs1 = new Filer.FileSystem({provider: provider}, function() { 15 | fs1.readdir('/', function(err, entries) { 16 | expect(err).not.to.exist; 17 | expect(entries).to.be.an('array'); 18 | expect(entries.length).to.equal(0); 19 | 20 | fs1.writeFile('/file', 'data', function(err) { 21 | if(err) throw err; 22 | 23 | // 2) Should be able to open an existing filesystem 24 | const fs2 = new Filer.FileSystem({provider: provider}, function() { 25 | fs2.readdir('/', function(err, entries) { 26 | expect(err).not.to.exist; 27 | expect(entries).to.be.an('array'); 28 | expect(entries.length).to.equal(1); 29 | expect(entries[0]).to.equal('file'); 30 | 31 | 32 | // 3) FORMAT flag should wipe an existing filesystem 33 | const fs3 = new Filer.FileSystem({provider: provider, flags: ['FORMAT']}, function() { 34 | fs3.readdir('/', function(err, entries) { 35 | expect(err).not.to.exist; 36 | expect(entries).to.be.an('array'); 37 | expect(entries.length).to.equal(0); 38 | done(); 39 | }); 40 | }); 41 | }); 42 | }); 43 | }); 44 | }); 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /tests/spec/filer.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Filer = require('../../src'); 3 | const util = require('../lib/test-utils'); 4 | const expect = require('chai').expect; 5 | 6 | describe('Filer', function() { 7 | it('is defined', function() { 8 | expect(typeof Filer).not.to.equal(undefined); 9 | }); 10 | 11 | it('has FileSystem constructor', function() { 12 | expect(typeof Filer.FileSystem).to.equal('function'); 13 | }); 14 | 15 | it('has Buffer constructor', function() { 16 | expect(typeof Filer.Buffer).to.equal('function'); 17 | }); 18 | 19 | it('has Path and path objects', function() { 20 | expect(typeof Filer.Path).to.equal('object'); 21 | expect(typeof Filer.path).to.equal('object'); 22 | expect(Filer.Path).to.equal(Filer.path); 23 | }); 24 | 25 | it('has Errors object', function() { 26 | expect(typeof Filer.Errors).to.equal('object'); 27 | }); 28 | 29 | it('has an fs object that returns a Filer.FileSystem', function() { 30 | // Depends on IndexedDB being available, since we can't 31 | // configure our own test provider. Shim for coverage. 32 | util.shimIndexedDB(function() { 33 | expect(typeof Filer.fs).to.equal('object'); 34 | 35 | const fs1 = Filer.fs; 36 | const fs2 = Filer.fs; 37 | 38 | expect(fs1).to.be.an.instanceof(Filer.FileSystem); 39 | expect(fs2).to.be.an.instanceof(Filer.FileSystem); 40 | expect(fs1).to.equal(fs2); 41 | }); 42 | }); 43 | 44 | it('has Shell constructor', function() { 45 | expect(typeof Filer.Shell).to.equal('function'); 46 | }); 47 | 48 | it('must honor the \'FORMAT\' flag', function(done) { 49 | const name = 'local-test'; 50 | // Because we need to use a bunch of Filer filesystems 51 | // in this test, we can't use the usual test infrastructure 52 | // to create/manage the fs instance. Pick the best one 53 | // based on the testing environment (browser vs. node) 54 | const providers = Filer.FileSystem.providers; 55 | let Provider; 56 | if(providers.IndexedDB.isSupported()) { 57 | Provider = providers.IndexedDB; 58 | } else { 59 | Provider = providers.Memory; 60 | } 61 | 62 | let fs = new Filer.FileSystem({name, provider: new Provider(name)}); 63 | let fs2 = new Filer.FileSystem({name, provider: new Provider(name)}); 64 | 65 | fs.mkdir('/test', function(err){ 66 | if(err) throw err; 67 | 68 | fs2.readdir('/', function(err, list) { 69 | if(err) throw err; 70 | 71 | expect(list).to.exist; 72 | expect(list).to.have.length(1); 73 | 74 | fs2 = new Filer.FileSystem({name, provider: new Provider(name), flags:['FORMAT']}); 75 | fs2.readdir('/', function(err, list2) { 76 | expect(err).to.not.exist; 77 | expect(list2).to.exist; 78 | expect(list2).to.have.length(0); 79 | done(); 80 | }); 81 | }); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /tests/spec/fs.chown.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('fs.chown, fs.fchown', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should be functions', function() { 9 | var fs = util.fs(); 10 | expect(typeof fs.chown).to.equal('function'); 11 | expect(typeof fs.fchown).to.equal('function'); 12 | }); 13 | 14 | it('should automatically set a file\'s uid and gid to 0 (i.e., root)', function(done) { 15 | var fs = util.fs(); 16 | 17 | fs.open('/file', 'w', function(err, fd) { 18 | if(err) throw err; 19 | 20 | fs.fstat(fd, function(err, stats) { 21 | if(err) throw err; 22 | 23 | expect(stats.uid).to.equal(0); 24 | expect(stats.gid).to.equal(0); 25 | fs.close(fd, done); 26 | }); 27 | }); 28 | }); 29 | 30 | it('fchown should expect an interger value for uid', function(done) { 31 | var fs = util.fs(); 32 | 33 | fs.open('/file', 'w', function(err, fd) { 34 | if(err) throw err; 35 | 36 | fs.fchown(fd, '1001', 1001, function(err) { 37 | expect(err).to.exist; 38 | expect(err.code).to.equal('EINVAL'); 39 | fs.close(fd, done); 40 | }); 41 | }); 42 | }); 43 | 44 | it('chown should expect an interger value for uid', function(done) { 45 | var fs = util.fs(); 46 | 47 | fs.writeFile('/file', 'w', function(err) { 48 | if(err) throw err; 49 | 50 | fs.chown('/file', '1001', 1001, function(err) { 51 | expect(err).to.exist; 52 | expect(err.code).to.equal('EINVAL'); 53 | done(); 54 | }); 55 | }); 56 | }); 57 | 58 | it('fchown should expect an interger value for gid', function(done) { 59 | var fs = util.fs(); 60 | 61 | fs.open('/file', 'w', function(err, fd) { 62 | if(err) throw err; 63 | 64 | fs.fchown(fd, 1001, '1001', function(err) { 65 | expect(err).to.exist; 66 | expect(err.code).to.equal('EINVAL'); 67 | fs.close(fd, done); 68 | }); 69 | }); 70 | }); 71 | 72 | it('chown should expect an interger value for gid', function(done) { 73 | var fs = util.fs(); 74 | 75 | fs.writeFile('/file', 'w', function(err) { 76 | if(err) throw err; 77 | 78 | fs.chown('/file', 1001, '1001', function(err) { 79 | expect(err).to.exist; 80 | expect(err.code).to.equal('EINVAL'); 81 | done(); 82 | }); 83 | }); 84 | }); 85 | 86 | it('should allow updating gid and uid for a file', function(done) { 87 | var fs = util.fs(); 88 | 89 | fs.open('/file', 'w', function(err, fd) { 90 | if(err) throw err; 91 | 92 | fs.fchown(fd, 1001, 1001, function(err) { 93 | if(err) throw err; 94 | 95 | fs.fstat(fd, function(err, stats) { 96 | if(err) throw err; 97 | 98 | expect(stats.uid).to.equal(1001); 99 | expect(stats.gid).to.equal(1001); 100 | 101 | fs.close(fd, function(err) { 102 | if(err) throw err; 103 | 104 | fs.chown('/file', 500, 500, function(err) { 105 | if(err) throw err; 106 | 107 | fs.stat('/file', function(err, stats) { 108 | if(err) throw err; 109 | 110 | expect(stats.uid).to.equal(500); 111 | expect(stats.gid).to.equal(500); 112 | done(); 113 | }); 114 | }); 115 | }); 116 | }); 117 | }); 118 | }); 119 | }); 120 | }); 121 | 122 | 123 | describe('fs.promises.chown', function(){ 124 | beforeEach(util.setup); 125 | afterEach(util.setup); 126 | 127 | it('should be a function', function(){ 128 | var fsPromises = util.fs().promises; 129 | expect(fsPromises.chown).to.be.a('function'); 130 | }); 131 | 132 | it('should allow updating uid and gid for a file', function() { 133 | var fsPromises = util.fs().promises; 134 | 135 | return fsPromises 136 | .writeFile('/file', 'data') 137 | .then(() => fsPromises.chown('/file', 500, 500)) 138 | .then(() => fsPromises.stat('/file')) 139 | .then((stats) => { 140 | expect(stats.uid).to.equal(500); 141 | expect(stats.gid).to.equal(500); 142 | }); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /tests/spec/fs.close.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('../lib/test-utils.js'); 4 | const expect = require('chai').expect; 5 | 6 | describe('fs.close', function() { 7 | beforeEach(util.setup); 8 | afterEach(util.cleanup); 9 | 10 | it('should be a function', function() { 11 | const fs = util.fs(); 12 | expect(typeof fs.close).to.equal('function'); 13 | }); 14 | 15 | it('should release the file descriptor', function(done) { 16 | const buffer = Buffer.alloc(0); 17 | const fs = util.fs(); 18 | 19 | fs.open('/myfile', 'w+', function(error, result) { 20 | if(error) throw error; 21 | 22 | const fd = result; 23 | fs.close(fd, function(error) { 24 | if(error) throw error; 25 | 26 | fs.read(fd, buffer, 0, buffer.length, undefined, function(error, result) { 27 | expect(error).to.exist; 28 | expect(error.code).to.equal('EBADF'); 29 | expect(result).not.to.exist; 30 | done(); 31 | }); 32 | }); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/spec/fs.copyFile.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | const { COPYFILE_EXCL } = require('../../src/constants').fsConstants; 4 | 5 | // Waiting on implementation to land https://github.com/filerjs/filer/issues/436 6 | describe.skip('fs.copyFile', function() { 7 | const file = { 8 | path: '/srcfile', 9 | contents: 'This is a src file.' 10 | }; 11 | 12 | beforeEach(function(done){ 13 | util.setup(function() { 14 | var fs = util.fs(); 15 | fs.writeFile(file.path, file.contents, done); 16 | }); 17 | }); 18 | afterEach(util.cleanup); 19 | 20 | it('should be a function', function() { 21 | var fs = util.fs(); 22 | expect(fs.copyFile).to.be.a('function'); 23 | }); 24 | 25 | it('should return an error if the src path does not exist', function(done){ 26 | var fs = util.fs(); 27 | 28 | fs.copyFile(null, '/dest.txt', function(error) { 29 | expect(error).to.exist; 30 | expect(error.code).to.equal('ENOENT'); 31 | done(); 32 | }); 33 | }); 34 | 35 | it('should copy file successfully', function(done) { 36 | var fs = util.fs(); 37 | const destPath = '/destfile'; 38 | 39 | fs.copyFile(file.path, destPath, function(error) { 40 | if(error) throw error; 41 | 42 | fs.readFile(destPath, function(error, data) { 43 | expect(error).not.to.exist; 44 | expect(data).to.equal(file.contents); 45 | done(); 46 | }); 47 | }); 48 | }); 49 | 50 | it('should return an error if flag=COPYFILE_EXCL and the destination file exists', function (done) { 51 | var fs = util.fs(); 52 | const destPath = '/destfile'; 53 | 54 | fs.writeFile(destPath, 'data', function(error) { 55 | if(error) throw error; 56 | 57 | fs.copyFile(file.path, destPath, COPYFILE_EXCL, function(error) { 58 | expect(error).to.exist; 59 | expect(error.code).to.equal('ENOENT'); 60 | done(); 61 | }); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /tests/spec/fs.exists.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const util = require('../lib/test-utils.js'); 3 | const expect = require('chai').expect; 4 | 5 | describe('fs.exists', function() { 6 | beforeEach(util.setup); 7 | afterEach(util.cleanup); 8 | 9 | it('should be a function', function() { 10 | const fs = util.fs(); 11 | expect(typeof fs.exists).to.equal('function'); 12 | }); 13 | 14 | it('should return false if path does not exist', function(done) { 15 | const fs = util.fs(); 16 | 17 | fs.exists('/tmp', function(result) { 18 | expect(result).to.be.false; 19 | done(); 20 | }); 21 | }); 22 | 23 | it('should return true if path exists', function(done) { 24 | const fs = util.fs(); 25 | 26 | fs.open('/myfile', 'w', function(err, fd) { 27 | if(err) throw err; 28 | 29 | fs.close(fd, function(err) { 30 | if(err) throw err; 31 | 32 | fs.exists('/myfile', function(result) { 33 | expect(result).to.be.true; 34 | done(); 35 | }); 36 | }); 37 | }); 38 | }); 39 | 40 | it('should follow symbolic links and return true for the resulting path', function(done) { 41 | const fs = util.fs(); 42 | 43 | fs.open('/myfile', 'w', function(error, fd) { 44 | if(error) throw error; 45 | 46 | fs.close(fd, function(error) { 47 | if(error) throw error; 48 | 49 | fs.symlink('/myfile', '/myfilelink', function(error) { 50 | if(error) throw error; 51 | 52 | fs.exists('/myfilelink', function(result) { 53 | expect(result).to.be.true; 54 | done(); 55 | }); 56 | }); 57 | }); 58 | }); 59 | }); 60 | 61 | it('should follow symbolic links and return false if for the resulting path does not exist', function(done) { 62 | const fs = util.fs(); 63 | 64 | fs.open('/myfile', 'w', function(error, fd) { 65 | if(error) throw error; 66 | 67 | fs.close(fd, function(error) { 68 | if(error) throw error; 69 | 70 | fs.symlink('/myfile', '/myfilelink', function(error) { 71 | if(error) throw error; 72 | 73 | fs.unlink('/myfile', function(err) { 74 | if(err) throw err; 75 | 76 | fs.exists('/myfilelink', function(result) { 77 | expect(result).to.be.false; 78 | done(); 79 | }); 80 | }); 81 | }); 82 | }); 83 | }); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /tests/spec/fs.fsync.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('fs.fsync', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should be a function', function() { 9 | var fs = util.fs(); 10 | expect(fs.fsync).to.be.a('function'); 11 | }); 12 | 13 | it('should return error when fd is not a number', function(done) { 14 | var fs = util.fs(); 15 | fs.fsync('1', function(error) { 16 | expect(error).to.exist; 17 | expect(error.code).to.equal('EINVAL'); 18 | done(); 19 | }); 20 | }); 21 | 22 | it('should return error when fd is invalid', function(done) { 23 | var fs = util.fs(); 24 | fs.fsync(1, function(error) { 25 | expect(error).to.exist; 26 | expect(error.code).to.equal('EBADF'); 27 | done(); 28 | }); 29 | }); 30 | 31 | it('should not error if the fd is valid', function(done) { 32 | var fs = util.fs(); 33 | 34 | fs.writeFile('/myfile', 'contents', function(error) { 35 | if(error) throw error; 36 | 37 | fs.open('/myfile', 'r', function(error, fd) { 38 | if(error) throw error; 39 | 40 | fs.fsync(fd, function(error) { 41 | expect(error).not.to.exist; 42 | fs.close(fd, done); 43 | }); 44 | }); 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /tests/spec/fs.lstat.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const util = require('../lib/test-utils.js'); 3 | const expect = require('chai').expect; 4 | 5 | describe('fs.lstat', function() { 6 | beforeEach(util.setup); 7 | afterEach(util.cleanup); 8 | 9 | it('should be a function', function() { 10 | const fs = util.fs(); 11 | expect(typeof fs.lstat).to.equal('function'); 12 | }); 13 | 14 | it('should return an error if path does not exist', function(done) { 15 | const fs = util.fs(); 16 | 17 | fs.lstat('/tmp', function(error, result) { 18 | expect(error).to.exist; 19 | expect(error.code).to.equal('ENOENT'); 20 | expect(result).not.to.exist; 21 | done(); 22 | }); 23 | }); 24 | 25 | it('should return a stat object if path is not a symbolic link', function(done) { 26 | const fs = util.fs(); 27 | 28 | fs.lstat('/', function(error, result) { 29 | expect(error).not.to.exist; 30 | expect(result).to.exist; 31 | expect(result.isDirectory()).to.be.true; 32 | done(); 33 | }); 34 | }); 35 | 36 | it('should return a stat object if path is a symbolic link', function(done) { 37 | const fs = util.fs(); 38 | 39 | fs.symlink('/', '/mylink', function(error) { 40 | if(error) throw error; 41 | 42 | fs.lstat('/mylink', function(error, result) { 43 | expect(error).not.to.exist; 44 | expect(result).to.exist; 45 | expect(result.isSymbolicLink()).to.be.true; 46 | done(); 47 | }); 48 | }); 49 | }); 50 | 51 | it('should have a mode (full node) when stat\'d with lstat', function(done) { 52 | var fs = util.fs(); 53 | 54 | fs.writeFile('/file', 'data', function(error) { 55 | if(error) throw error; 56 | 57 | fs.symlink('/file', '/symlink', function(error) { 58 | if(error) throw error; 59 | 60 | fs.lstat('/symlink', function(error, stats) { 61 | if(error) throw error; 62 | 63 | // We should build and return a full node object, complete with 64 | // calculated mode, which should be a SYMLINK and the default file permissions. 65 | expect(stats.mode).to.equal(fs.constants.S_IFLNK | 0o644); 66 | done(); 67 | }); 68 | }); 69 | }); 70 | }); 71 | }); 72 | 73 | describe('fs.promises.lstat', () => { 74 | beforeEach(util.setup); 75 | afterEach(util.cleanup); 76 | 77 | it('should return an error if path does not exist', () => { 78 | const fsPromises = util.fs().promises; 79 | 80 | return fsPromises.lstat('/tmp') 81 | .catch( error => { 82 | expect(error).to.exist; 83 | expect(error.code).to.equal('ENOENT'); 84 | }); 85 | }); 86 | 87 | it('should return a stat object if path is not a symbolic link', () => { 88 | const fsPromises = util.fs().promises; 89 | 90 | return fsPromises.lstat('/') 91 | .then(result => { 92 | expect(result).to.exist; 93 | expect(result.isDirectory()).to.be.true; 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /tests/spec/fs.mkdir.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const util = require('../lib/test-utils.js'); 3 | const expect = require('chai').expect; 4 | 5 | describe('fs.mkdir', function () { 6 | beforeEach(util.setup); 7 | afterEach(util.cleanup); 8 | 9 | it('should be a function', function () { 10 | const fs = util.fs(); 11 | expect(fs.mkdir).to.be.a('function'); 12 | }); 13 | 14 | it('should return an error if part of the parent path does not exist', function (done) { 15 | const fs = util.fs(); 16 | 17 | fs.mkdir('/tmp/mydir', function (error) { 18 | expect(error).to.exist; 19 | expect(error.code).to.equal('ENOENT'); 20 | done(); 21 | }); 22 | }); 23 | 24 | it('should return an error if the path already exists', function (done) { 25 | const fs = util.fs(); 26 | 27 | fs.mkdir('/', function (error) { 28 | expect(error).to.exist; 29 | expect(error.code).to.equal('EEXIST'); 30 | done(); 31 | }); 32 | }); 33 | 34 | it('should make a new directory', function (done) { 35 | const fs = util.fs(); 36 | 37 | fs.mkdir('/tmp', function (error) { 38 | expect(error).not.to.exist; 39 | if (error) throw error; 40 | 41 | fs.stat('/tmp', function (error, stats) { 42 | expect(error).not.to.exist; 43 | expect(stats).to.exist; 44 | expect(stats.isDirectory()).to.be.true; 45 | done(); 46 | }); 47 | }); 48 | }); 49 | }); 50 | 51 | describe('fs.promises.mkdir', function () { 52 | beforeEach(util.setup); 53 | afterEach(util.cleanup); 54 | 55 | it('should be a function', function () { 56 | const fs = util.fs(); 57 | expect(fs.promises.mkdir).to.be.a('function'); 58 | }); 59 | 60 | it('should return an error if part of the parent path does not exist', function () { 61 | const fs = util.fs(); 62 | 63 | return fs.promises.mkdir('/tmp/mydir') 64 | .catch(error => { 65 | expect(error).to.exist; 66 | expect(error.code).to.equal('ENOENT'); 67 | }); 68 | }); 69 | 70 | it('should return an error if the path already exists', function () { 71 | const fs = util.fs(); 72 | 73 | return fs.promises.mkdir('/') 74 | .catch(error =>{ 75 | expect(error).to.exist; 76 | expect(error.code).to.equal('EEXIST'); 77 | }); 78 | }); 79 | 80 | it('should make a new directory', function () { 81 | const fs = util.fs(); 82 | 83 | return fs.promises.mkdir('/tmp') 84 | .then(() => fs.promises.stat('/tmp')) 85 | .then(stats => { 86 | expect(stats).to.exist; 87 | expect(stats.isDirectory()).to.be.true; 88 | }); 89 | }); 90 | 91 | it('should return a promise', function () { 92 | const fs = util.fs(); 93 | expect(fs.promises.mkdir('/tmp')).to.be.a('promise'); 94 | }); 95 | }); -------------------------------------------------------------------------------- /tests/spec/fs.mkdtemp.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('../lib/test-utils.js'); 4 | const expect = require('chai').expect; 5 | 6 | describe('fs.mkdtemp', function() { 7 | beforeEach(util.setup); 8 | afterEach(util.cleanup); 9 | 10 | it('should be a function', function() { 11 | const fs = util.fs(); 12 | expect(fs.mkdtemp).to.be.a('function'); 13 | }); 14 | 15 | it('should craete temp dir with specified prefix', function(done) { 16 | const fs = util.fs(); 17 | fs.mkdtemp('/foo', function(error, path) { 18 | expect(error).not.to.exist; 19 | expect(path).to.match(/foo-\w{6}/); 20 | done(); 21 | }); 22 | }); 23 | 24 | it('should craete temp dir inside existing directory', function(done) { 25 | const fs = util.fs(); 26 | fs.mkdir('/myDir', (error) => { 27 | expect(error).not.to.exist; 28 | fs.mkdtemp('/myDir/foo', function(error, path) { 29 | expect(error).not.to.exist; 30 | expect(path).to.match(/foo.{6}/); 31 | done(); 32 | }); 33 | }); 34 | }); 35 | 36 | it('should not create temp dir without prefix', function(done) { 37 | const fs = util.fs(); 38 | fs.mkdtemp('', function(error, path) { 39 | expect(error).to.exist; 40 | expect(path).not.to.exist; 41 | done(); 42 | }); 43 | }); 44 | 45 | it('should not create temp dir inside non existing dir', function(done) { 46 | const fs = util.fs(); 47 | fs.mkdtemp('/doesNotExists/foo', function(error, path) { 48 | expect(error).to.exist; 49 | expect(error.code).to.be.equal('ENOENT'); 50 | expect(path).to.exist; 51 | done(); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /tests/spec/fs.mknod.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('fs.mknod', function(){ 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should be a function', function(done){ 9 | var fs = util.fs(); 10 | expect(fs.mknod).to.be.a('function'); 11 | done(); 12 | }); 13 | 14 | it('should return an error if part of the parent path does not exist', function(done){ 15 | var fs = util.fs(); 16 | 17 | fs.mknod('/dir/mydir', 'DIRECTORY', function(error){ 18 | expect(error.code).to.equal('ENOENT'); 19 | done(); 20 | }); 21 | }); 22 | 23 | it('should return an error if path already exists', function(done){ 24 | var fs = util.fs(); 25 | 26 | fs.mknod('/', 'DIRECTORY', function(error){ 27 | expect(error.code).to.equal('EEXIST'); 28 | done(); 29 | }); 30 | }); 31 | 32 | it('should return an error if the parent node is not a directory', function(done){ 33 | var fs = util.fs(); 34 | 35 | fs.mknod('/file', 'FILE', function(error){ 36 | if (error) throw error; 37 | 38 | fs.mknod('/file/myfile', 'FILE', function(error){ 39 | expect(error.code).to.equal('ENOTDIR'); 40 | done(); 41 | }); 42 | }); 43 | }); 44 | 45 | it('should return an error if the mode provided is not DIRECTORY or FILE', function(done){ 46 | var fs = util.fs(); 47 | 48 | fs.mknod('/symlink', 'SYMLINK', function(error){ 49 | expect(error).to.exist; 50 | expect(error.code).to.equal('EINVAL'); 51 | done(); 52 | }); 53 | }); 54 | 55 | it('should make a new directory', function(done){ 56 | var fs = util.fs(); 57 | 58 | fs.mknod('/dir', 'DIRECTORY', function(error){ 59 | if (error) throw error; 60 | 61 | fs.stat('/dir', function(error, stats){ 62 | expect(error).not.to.exist; 63 | expect(stats.isDirectory()).to.be.true; 64 | done(); 65 | }); 66 | }); 67 | }); 68 | 69 | it('should make a new file', function(done){ 70 | var fs = util.fs(); 71 | 72 | fs.mknod('/file', 'FILE', function(error){ 73 | if (error) throw error; 74 | 75 | fs.stat('/file', function(error, result){ 76 | expect(error).not.to.exist; 77 | expect(result.isFile()).to.be.true; 78 | done(); 79 | }); 80 | }); 81 | }); 82 | describe('Promise test mknod', function(){ 83 | 84 | it('should return an error if part of the parent path does not exist', function(){ 85 | var fs = util.fs().promises; 86 | 87 | return fs.mknod('/dir/mydir', 'DIRECTORY') 88 | .catch(error => { 89 | expect(error.code).to.equal('ENOENT'); 90 | }); 91 | }); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /tests/spec/fs.read.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const util = require('../lib/test-utils.js'); 3 | const expect = require('chai').expect; 4 | 5 | describe('fs.read', function() { 6 | beforeEach(util.setup); 7 | afterEach(util.cleanup); 8 | 9 | it('should be a function', function() { 10 | const fs = util.fs(); 11 | expect(fs.read).to.be.a('function'); 12 | }); 13 | 14 | it('should read data from a file', function(done) { 15 | const fs = util.fs(); 16 | const wbuffer = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); 17 | const rbuffer = Buffer.alloc(wbuffer.length); 18 | 19 | fs.open('/myfile', 'w+', function(error, fd) { 20 | if(error) throw error; 21 | 22 | fs.write(fd, wbuffer, 0, wbuffer.length, 0, function(error, result) { 23 | if(error) throw error; 24 | expect(result).to.equal(wbuffer.length); 25 | 26 | fs.read(fd, rbuffer, 0, rbuffer.length, 0, function(error, result) { 27 | expect(error).not.to.exist; 28 | expect(result).to.equal(rbuffer.length); 29 | expect(wbuffer).to.deep.equal(rbuffer); 30 | fs.close(fd, done); 31 | }); 32 | }); 33 | }); 34 | }); 35 | 36 | it('should update the current file position', function(done) { 37 | const fs = util.fs(); 38 | const wbuffer = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); 39 | const rbuffer = Buffer.alloc(wbuffer.length); 40 | let _result = 0; 41 | 42 | fs.open('/myfile', 'w+', function(error, fd) { 43 | if(error) throw error; 44 | 45 | fs.write(fd, wbuffer, 0, wbuffer.length, 0, function(error, result) { 46 | if(error) throw error; 47 | expect(result).to.equal(wbuffer.length); 48 | 49 | fs.read(fd, rbuffer, 0, rbuffer.length / 2, undefined, function(error, result) { 50 | if(error) throw error; 51 | 52 | _result += result; 53 | fs.read(fd, rbuffer, rbuffer.length / 2, rbuffer.length, undefined, function(error, result) { 54 | if(error) throw error; 55 | _result += result; 56 | expect(error).not.to.exist; 57 | expect(_result).to.equal(rbuffer.length); 58 | expect(wbuffer).to.deep.equal(rbuffer); 59 | fs.close(fd, done); 60 | }); 61 | }); 62 | }); 63 | }); 64 | }); 65 | 66 | it('should fail to read a directory', function(done) { 67 | const fs = util.fs(); 68 | const buf = Buffer.alloc(20); 69 | const buf2 = Buffer.alloc(20); 70 | 71 | fs.mkdir('/mydir', function(error) { 72 | if(error) throw error; 73 | 74 | fs.open('/mydir', 'r', function(error, fd) { 75 | if(error) throw error; 76 | 77 | fs.read(fd, buf, 0, buf.length, 0, function(error, result) { 78 | expect(error).to.exist; 79 | expect(error.code).to.equal('EISDIR'); 80 | expect(result).to.equal(0); 81 | expect(buf).to.deep.equal(buf2); 82 | fs.close(fd, done); 83 | }); 84 | }); 85 | }); 86 | }); 87 | 88 | it('should fail to read a file that does not exist', function(done) { 89 | const fs = util.fs(); 90 | 91 | const fd = 0; 92 | const rbuffer = Buffer.alloc(8); 93 | 94 | fs.read(fd, rbuffer, 0, rbuffer.length, 0, function(error, result) { 95 | expect(error).to.exist; 96 | expect(result).not.to.exist; 97 | expect(error.code).to.equal('EBADF'); 98 | done(); 99 | }); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /tests/spec/fs.readdir.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('../lib/test-utils.js'); 4 | const expect = require('chai').expect; 5 | 6 | describe('fs.readdir', function() { 7 | beforeEach(util.setup); 8 | afterEach(util.cleanup); 9 | 10 | it('should be a function', function() { 11 | const fs = util.fs(); 12 | expect(fs.readdir).to.be.a('function'); 13 | }); 14 | 15 | it('should return an error if the path does not exist', function(done) { 16 | const fs = util.fs(); 17 | 18 | fs.readdir('/tmp/mydir', function(error, files) { 19 | expect(error).to.exist; 20 | expect(error.code).to.equal('ENOENT'); 21 | expect(files).not.to.exist; 22 | done(); 23 | }); 24 | }); 25 | 26 | it('should return a list of files from an existing directory', function(done) { 27 | const fs = util.fs(); 28 | 29 | fs.mkdir('/tmp', function(error) { 30 | if(error) throw error; 31 | 32 | fs.readdir('/', function(error, files) { 33 | expect(error).not.to.exist; 34 | expect(files).to.exist; 35 | expect(files.length).to.equal(1); 36 | expect(files[0]).to.equal('tmp'); 37 | done(); 38 | }); 39 | }); 40 | }); 41 | 42 | it('should follow symbolic links', function(done) { 43 | const fs = util.fs(); 44 | 45 | fs.mkdir('/tmp', function(error) { 46 | if(error) throw error; 47 | fs.symlink('/', '/tmp/dirLink', function(error) { 48 | if(error) throw error; 49 | fs.readdir('/tmp/dirLink', function(error, files) { 50 | expect(error).not.to.exist; 51 | expect(files).to.exist; 52 | expect(files.length).to.equal(1); 53 | expect(files[0]).to.equal('tmp'); 54 | done(); 55 | }); 56 | }); 57 | }); 58 | }); 59 | 60 | it('(promise) should be a function', function() { 61 | const fsPromises = util.fs().promises; 62 | expect(fsPromises.readdir).to.be.a('function'); 63 | }); 64 | 65 | it('should return an error if the path is a file', function() { 66 | const fsPromises = util.fs().promises; 67 | 68 | return fsPromises.writeFile('/myfile', 'contents') 69 | .then(() => fsPromises.readdir('/myfile')) 70 | .catch(error => { 71 | expect(error).to.exist; 72 | expect(error.code).to.equal('ENOTDIR'); 73 | }); 74 | }); 75 | 76 | it('(promise) should return a list of files from an existing directory', function() { 77 | const fsPromises = util.fs().promises; 78 | 79 | return fsPromises.mkdir('/tmp') 80 | .then(() => { 81 | return fsPromises.stat('/'); 82 | }) 83 | .then(stats => { 84 | expect(stats).to.exist; 85 | expect(stats.isDirectory()).to.be.true; 86 | }) 87 | .then(() => { 88 | return fsPromises.readdir('/'); 89 | }) 90 | .then(files => { 91 | expect(files).to.exist; 92 | expect(files.length).to.equal(1); 93 | expect(files[0]).to.equal('tmp'); 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /tests/spec/fs.readlink.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('../lib/test-utils.js'); 4 | const expect = require('chai').expect; 5 | 6 | describe('fs.readlink', function () { 7 | beforeEach(util.setup); 8 | afterEach(util.cleanup); 9 | 10 | it('should be a function', function () { 11 | const fs = util.fs(); 12 | expect(fs.readlink).to.be.a('function'); 13 | }); 14 | 15 | it('should return an error if part of the parent destination path does not exist', function (done) { 16 | const fs = util.fs(); 17 | 18 | fs.readlink('/tmp/mydir', function (error) { 19 | expect(error).to.exist; 20 | expect(error.code).to.equal('ENOENT'); 21 | done(); 22 | }); 23 | }); 24 | 25 | it('should return an error if the path is not a symbolic link', function (done) { 26 | const fs = util.fs(); 27 | 28 | fs.readlink('/', function (error) { 29 | expect(error).to.exist; 30 | expect(error.code).to.equal('ENOENT'); 31 | done(); 32 | }); 33 | }); 34 | 35 | it('should return an error if the path is not a symbolic link', function (done) { 36 | const fs = util.fs(); 37 | 38 | fs.mkdir('/tmp', function (error) { 39 | if (error) throw error; 40 | 41 | fs.readlink('/tmp', function (error) { 42 | expect(error).to.exist; 43 | expect(error.code).to.equal('EINVAL'); 44 | done(); 45 | }); 46 | 47 | }); 48 | }); 49 | 50 | it('should return the contents of a symbolic link', function (done) { 51 | const fs = util.fs(); 52 | 53 | fs.symlink('/', '/myfile', function (error) { 54 | if (error) throw error; 55 | 56 | fs.readlink('/myfile', function (error, result) { 57 | expect(error).not.to.exist; 58 | expect(result).to.equal('/'); 59 | done(); 60 | }); 61 | }); 62 | }); 63 | 64 | it('should allow relative paths, but resolve to the dstpath', function (done) { 65 | const fs = util.fs(); 66 | const contents = 'contents'; 67 | 68 | fs.mkdir('/dir', function (error) { 69 | if (error) throw error; 70 | 71 | fs.writeFile('/file', contents, function (error) { 72 | if (error) throw error; 73 | 74 | fs.symlink('../file', '/dir/symlink', function (error) { 75 | if (error) throw error; 76 | 77 | fs.readlink('/dir/symlink', function (error, result) { 78 | expect(error).not.to.exist; 79 | expect(result).to.equal('../file'); 80 | 81 | fs.readFile('/dir/symlink', 'utf8', function (error, data) { 82 | expect(error).not.to.exist; 83 | expect(data).to.equal(contents); 84 | done(); 85 | }); 86 | }); 87 | }); 88 | }); 89 | }); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /tests/spec/fs.shell.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Filer = require('../../src'); 4 | const util = require('../lib/test-utils.js'); 5 | const expect = require('chai').expect; 6 | 7 | describe('fs.Shell', function() { 8 | beforeEach(util.setup); 9 | afterEach(util.cleanup); 10 | 11 | it('is a function', function() { 12 | const fs = util.fs(); 13 | expect(typeof fs.Shell).to.equal('function'); 14 | }); 15 | 16 | it('should return a FileSystemShell instance', function() { 17 | const fs = util.fs(); 18 | const sh = new fs.Shell(); 19 | 20 | expect(sh.prototype).to.deep.equal((new Filer.Shell(fs)).prototype); 21 | }); 22 | 23 | it('should reflect changes to the prototype', function(){ 24 | const fs = util.fs(); 25 | const sh = new fs.Shell(); 26 | 27 | Filer.Shell.prototype.test = 'foo'; 28 | 29 | expect(sh.test).to.equal('foo'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /tests/spec/fs.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Filer = require('../../src'); 4 | const util = require('../lib/test-utils.js'); 5 | const expect = require('chai').expect; 6 | 7 | describe('fs', function() { 8 | beforeEach(util.setup); 9 | afterEach(util.cleanup); 10 | 11 | it('is an object', function() { 12 | const fs = util.fs(); 13 | expect(typeof fs).to.equal('object'); 14 | expect(fs).to.be.an.instanceof(Filer.FileSystem); 15 | }); 16 | 17 | it('should have a root directory', function(done) { 18 | const fs = util.fs(); 19 | fs.stat('/', function(error, result) { 20 | expect(error).not.to.exist; 21 | expect(result).to.exist; 22 | expect(result.isDirectory()).to.be.true; 23 | done(); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /tests/spec/fs.unwatchFile.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | // Waiting on https://github.com/filerjs/filer/pull/553 to land 5 | describe.skip('fs.unwatchFile', function() { 6 | beforeEach(util.setup); 7 | afterEach(util.cleanup); 8 | 9 | it('should be a function', function() { 10 | var fs = util.fs(); 11 | expect(typeof fs.unwatchFile).to.equal('function'); 12 | }); 13 | 14 | it('should not throw an error when using a file not being watched', function() { 15 | var fs = util.fs(); 16 | 17 | try { 18 | fs.unwatchFile('/myfile'); 19 | } catch(e) { 20 | expect.fail('calling fs.unwatchFile() on a file should not throw'); 21 | } 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/spec/fs.write.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const util = require('../lib/test-utils.js'); 3 | const expect = require('chai').expect; 4 | 5 | describe('fs.write', function() { 6 | beforeEach(util.setup); 7 | afterEach(util.cleanup); 8 | 9 | it('should be a function', function() { 10 | const fs = util.fs(); 11 | expect(fs.write).to.be.a('function'); 12 | }); 13 | 14 | it('should error if file path is undefined', function() { 15 | const fs = util.fs(); 16 | const fn = () => fs.writeFile(undefined, 'data'); 17 | expect(fn).to.throw(); 18 | }); 19 | 20 | it('should write data to a file', function(done) { 21 | const fs = util.fs(); 22 | const buffer = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); 23 | 24 | fs.open('/myfile', 'w', function(error, fd) { 25 | if(error) throw error; 26 | 27 | fs.write(fd, buffer, 0, buffer.length, 0, function(error, result) { 28 | expect(error).not.to.exist; 29 | expect(result).to.equal(buffer.length); 30 | 31 | fs.stat('/myfile', function(error, result) { 32 | expect(error).not.to.exist; 33 | expect(result.isFile()).to.be.true; 34 | expect(result.size).to.equal(buffer.length); 35 | fs.close(fd, done); 36 | }); 37 | }); 38 | }); 39 | }); 40 | 41 | it('should update the current file position', function(done) { 42 | const fs = util.fs(); 43 | const buffer = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); 44 | let _result = 0; 45 | 46 | fs.open('/myfile', 'w', function(error, fd) { 47 | if(error) throw error; 48 | 49 | fs.write(fd, buffer, 0, buffer.length, undefined, function(error, result) { 50 | if(error) throw error; 51 | _result += result; 52 | 53 | fs.write(fd, buffer, 0, buffer.length, undefined, function(error, result) { 54 | if(error) throw error; 55 | _result += result; 56 | 57 | fs.stat('/myfile', function(error, result) { 58 | if(error) throw error; 59 | expect(error).not.to.exist; 60 | expect(_result).to.equal(2 * buffer.length); 61 | expect(result.size).to.equal(_result); 62 | fs.close(fd, done); 63 | }); 64 | }); 65 | }); 66 | }); 67 | }); 68 | 69 | it('should fail if given a file path vs. an fd', function(done) { 70 | const fs = util.fs(); 71 | const buffer = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); 72 | 73 | fs.write('/myfile', buffer, 0, buffer.length, 0, function(error) { 74 | expect(error).to.exist; 75 | expect(error.code).to.equal('EBADF'); 76 | done(); 77 | }); 78 | }); 79 | 80 | it('should fail when trying to write more bytes than are available in the buffer (length too long)', function(done) { 81 | const fs = util.fs(); 82 | const buffer = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); 83 | 84 | fs.open('/myfile', 'w', function(error, fd) { 85 | if(error) throw error; 86 | 87 | fs.write(fd, buffer, 0, (buffer.length + 10), 0, function(error) { 88 | expect(error).to.exist; 89 | expect(error.code).to.equal('EIO'); 90 | fs.close(fd, done); 91 | }); 92 | }); 93 | }); 94 | 95 | it('should fail to write data to a file opened without the O_WRITE flag', function(done) { 96 | const fs = util.fs(); 97 | const buffer = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); 98 | 99 | fs.mknod('/myfile', 'FILE', function(err) { 100 | if (err) throw err; 101 | 102 | fs.open('/myfile', 'r', function(error, fd) { 103 | if(error) throw error; 104 | 105 | fs.write(fd, buffer, 0, buffer.length, 0, function(error) { 106 | expect(error).to.exist; 107 | expect(error.code).to.equal('EBADF'); 108 | done(); 109 | }); 110 | }); 111 | }); 112 | }); 113 | }); 114 | -------------------------------------------------------------------------------- /tests/spec/node-js/simple/test-fs-mkdir.js: -------------------------------------------------------------------------------- 1 | var util = require('../../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('node.js tests: https://github.com/joyent/node/blob/master/test/simple/test-fs-mkdir.js', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | // Based on test1 from https://github.com/joyent/node/blob/master/test/simple/test-fs-mkdir.js 9 | it('should create a dir without a mode arg', function(done) { 10 | var pathname = '/test1'; 11 | var fs = util.fs(); 12 | 13 | fs.mkdir(pathname, function(error) { 14 | if(error) throw error; 15 | fs.stat(pathname, function(error, result) { 16 | expect(error).not.to.exist; 17 | expect(result).to.exist; 18 | expect(result.isDirectory()).to.be.true; 19 | done(); 20 | }); 21 | }); 22 | }); 23 | 24 | // Based on test2 https://github.com/joyent/node/blob/master/test/simple/test-fs-mkdir.js 25 | it('should create a dir with a mode arg', function(done) { 26 | var pathname = '/test2'; 27 | var fs = util.fs(); 28 | 29 | fs.mkdir(pathname, 511 /*=0777*/, function(error) { 30 | if(error) throw error; 31 | fs.stat(pathname, function(error, result) { 32 | expect(error).not.to.exist; 33 | expect(result).to.exist; 34 | expect(result.isDirectory()).to.be.true; 35 | done(); 36 | }); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /tests/spec/node-js/simple/test-fs-null-bytes.js: -------------------------------------------------------------------------------- 1 | var util = require('../../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('node.js tests: https://github.com/joyent/node/blob/master/test/simple/test-fs-null-bytes.js', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should reject paths with null bytes in them', function(done) { 9 | var checks = []; 10 | var fnCount = 0; 11 | var fnTotal = 16; 12 | var fs = util.fs(); 13 | 14 | // Make sure function fails with null path error in callback. 15 | function check(fn) { 16 | var args = Array.prototype.slice.call(arguments, 1); 17 | fn = () => fn.apply(fs, args); 18 | expect(fn).to.throw(); 19 | 20 | fnCount++; 21 | if(fnCount === fnTotal) { 22 | done(); 23 | } 24 | } 25 | 26 | check(fs.link, '/foo\u0000bar', 'foobar'); 27 | check(fs.link, '/foobar', 'foo\u0000bar'); 28 | check(fs.lstat, '/foo\u0000bar'); 29 | check(fs.mkdir, '/foo\u0000bar', '0755'); 30 | check(fs.open, '/foo\u0000bar', 'r'); 31 | check(fs.readFile, '/foo\u0000bar'); 32 | check(fs.readdir, '/foo\u0000bar'); 33 | check(fs.readlink, '/foo\u0000bar'); 34 | check(fs.rename, '/foo\u0000bar', 'foobar'); 35 | check(fs.rename, '/foobar', 'foo\u0000bar'); 36 | check(fs.rmdir, '/foo\u0000bar'); 37 | check(fs.stat, '/foo\u0000bar'); 38 | check(fs.symlink, '/foo\u0000bar', 'foobar'); 39 | check(fs.symlink, '/foobar', 'foo\u0000bar'); 40 | check(fs.unlink, '/foo\u0000bar'); 41 | check(fs.writeFile, '/foo\u0000bar'); 42 | check(fs.appendFile, '/foo\u0000bar'); 43 | check(fs.truncate, '/foo\u0000bar'); 44 | check(fs.utimes, '/foo\u0000bar', 0, 0); 45 | // Not implemented 46 | // check(fs.realpath, '/foo\u0000bar'); 47 | check(fs.chmod, '/foo\u0000bar', '0644'); 48 | check(fs.chown, '/foo\u0000bar', 12, 34); 49 | 50 | checks.forEach(function(fn){ 51 | fn(); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /tests/spec/node-js/simple/test-fs-watch-recursive.js: -------------------------------------------------------------------------------- 1 | var util = require('../../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | /** 5 | * NOTE: unlike node.js, which either doesn't give filenames (e.g., in case of 6 | * fd vs. path) for events, or gives only a portion thereof (e.g., basname), 7 | * we give full, abs paths always. 8 | */ 9 | describe('node.js tests: https://github.com/joyent/node/blob/master/test/simple/test-fs-watch-recursive.js', function() { 10 | beforeEach(util.setup); 11 | afterEach(util.cleanup); 12 | 13 | it('should get change event for writeFile() under a recursive watched dir', function(done) { 14 | var fs = util.fs(); 15 | 16 | fs.mkdir('/test', function(error) { 17 | if(error) throw error; 18 | 19 | fs.mkdir('/test/subdir', function(error) { 20 | if(error) throw error; 21 | 22 | var watcher = fs.watch('/test', {recursive: true}); 23 | watcher.on('change', function(event, filename) { 24 | expect(event).to.equal('change'); 25 | // Expect to see that a new file was created in /test/subdir 26 | expect(filename).to.equal('/test/subdir'); 27 | watcher.close(); 28 | done(); 29 | }); 30 | 31 | fs.writeFile('/test/subdir/watch.txt', 'world'); 32 | }); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/spec/node-js/simple/test-fs-watch.js: -------------------------------------------------------------------------------- 1 | var util = require('../../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | /** 5 | * NOTE: unlike node.js, which either doesn't give filenames (e.g., in case of 6 | * fd vs. path) for events, or gives only a portion thereof (e.g., basname), 7 | * we give full, abs paths always. 8 | */ 9 | var filenameOne = '/watch.txt'; 10 | var filenameTwo = '/hasOwnProperty'; 11 | 12 | describe('node.js tests: https://github.com/joyent/node/blob/master/test/simple/test-fs-watch.js', function() { 13 | beforeEach(util.setup); 14 | afterEach(util.cleanup); 15 | 16 | it('should get change event for writeFile() using FSWatcher object', function(done) { 17 | var fs = util.fs(); 18 | var changes = 0; 19 | 20 | var watcher = fs.watch(filenameOne); 21 | watcher.on('change', function(event, filename) { 22 | expect(event).to.equal('change'); 23 | expect(filename).to.equal(filenameOne); 24 | 25 | // Make sure only one change event comes in (i.e., close() works) 26 | changes++; 27 | watcher.close(); 28 | 29 | fs.writeFile(filenameOne, 'hello again', function(error) { 30 | if(error) throw error; 31 | expect(changes).to.equal(1); 32 | done(); 33 | }); 34 | }); 35 | 36 | fs.writeFile(filenameOne, 'hello'); 37 | }); 38 | 39 | it('should get change event for writeFile() using fs.watch() only', function(done) { 40 | var fs = util.fs(); 41 | 42 | var watcher = fs.watch(filenameTwo, function(event, filename) { 43 | expect(event).to.equal('change'); 44 | expect(filename).to.equal(filenameTwo); 45 | 46 | watcher.close(); 47 | done(); 48 | }); 49 | 50 | fs.writeFile(filenameTwo, 'pardner'); 51 | }); 52 | 53 | it('should allow watches on dirs', function(done) { 54 | var fs = util.fs(); 55 | 56 | fs.mkdir('/tmp', function(error) { 57 | if(error) throw error; 58 | var steps = 0; 59 | 60 | function cleanup() { 61 | steps++; 62 | 63 | if(steps === 2) { 64 | done(); 65 | } 66 | } 67 | 68 | var watcher = fs.watch('/tmp', function(event, filename) { 69 | // TODO: node thinks this should be 'rename', need to add rename along with change. 70 | expect(event).to.equal('change'); 71 | expect(filename).to.equal('/tmp'); 72 | watcher.close(); 73 | cleanup(); 74 | }); 75 | 76 | fs.open('/tmp/newfile.txt', 'w', function(error, fd) { 77 | if(error) throw error; 78 | fs.close(fd, cleanup); 79 | }); 80 | }); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /tests/spec/providers/providers.indexeddb.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/test-utils.js'); 2 | var providerBase = require('./providers.base.js'); 3 | 4 | providerBase('IndexedDB', util.providers.IndexedDB); 5 | -------------------------------------------------------------------------------- /tests/spec/providers/providers.memory.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/test-utils.js'); 2 | var providerBase = require('./providers.base.js'); 3 | 4 | providerBase('Memory', util.providers.Memory); 5 | -------------------------------------------------------------------------------- /tests/spec/providers/providers.spec.js: -------------------------------------------------------------------------------- 1 | var Filer = require('../../../src'); 2 | var expect = require('chai').expect; 3 | 4 | describe('Filer.FileSystem.providers', function() { 5 | it('is defined', function() { 6 | expect(Filer.FileSystem.providers).to.exist; 7 | }); 8 | 9 | it('has IndexedDB constructor', function() { 10 | expect(Filer.FileSystem.providers.IndexedDB).to.be.a('function'); 11 | }); 12 | 13 | it('has Memory constructor', function() { 14 | expect(Filer.FileSystem.providers.Memory).to.be.a('function'); 15 | }); 16 | 17 | it('has a Default constructor', function() { 18 | expect(Filer.FileSystem.providers.Default).to.be.a('function'); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/spec/readme.example.spec.js: -------------------------------------------------------------------------------- 1 | const { path } = require('../../src'); 2 | var util = require('../lib/test-utils.js'); 3 | var expect = require('chai').expect; 4 | 5 | describe('README example code', function() { 6 | beforeEach(util.setup); 7 | afterEach(util.cleanup); 8 | 9 | it('should run the code in the README overview example', function(done) { 10 | // Slightly modified version of the first example code in the README 11 | // See 12 | const fs = util.fs(); 13 | 14 | fs.mkdir('/docs', (err) => { 15 | if (err) throw err; 16 | 17 | const filename = path.join('/docs', 'first.txt'); 18 | const data = 'Hello World!\n'; 19 | 20 | fs.writeFile(filename, data, (err) => { 21 | if (err) throw err; 22 | 23 | fs.stat(filename, (err, stats) => { 24 | if (err) throw err; 25 | expect(stats.size).to.equal(data.length); 26 | done(); 27 | }); 28 | }); 29 | }); 30 | }); 31 | 32 | it('should run the fsPromises example code', function() { 33 | const fs = util.fs().promises; 34 | const filename = '/myfile'; 35 | const data = 'some data'; 36 | 37 | return fs.writeFile(filename, data) 38 | .then(() => fs.stat(filename)) 39 | .then(stats => { 40 | expect(stats.size).to.equal(data.length); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /tests/spec/shell/cat.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('FileSystemShell.cat', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should be a function', function() { 9 | var shell = util.shell(); 10 | expect(shell.cat).to.be.a('function'); 11 | }); 12 | 13 | it('should fail when files argument is absent', function(done) { 14 | var fs = util.fs(); 15 | var shell = new fs.Shell(); 16 | 17 | shell.cat(null, function(error, data) { 18 | expect(error).to.exist; 19 | expect(error.code).to.equal('EINVAL'); 20 | expect(data).not.to.exist; 21 | done(); 22 | }); 23 | }); 24 | 25 | it('should return the contents of a single file', function(done) { 26 | var fs = util.fs(); 27 | var shell = new fs.Shell(); 28 | var contents = 'file contents'; 29 | 30 | fs.writeFile('/file', contents, function(err) { 31 | if(err) throw err; 32 | 33 | shell.cat('/file', function(err, data) { 34 | expect(err).not.to.exist; 35 | expect(data).to.equal(contents); 36 | done(); 37 | }); 38 | }); 39 | }); 40 | 41 | it('should return the contents of multiple files', function(done) { 42 | var fs = util.fs(); 43 | var shell = new fs.Shell(); 44 | var contents = 'file contents'; 45 | var contents2 = contents + '\n' + contents; 46 | 47 | fs.writeFile('/file', contents, function(err) { 48 | if(err) throw err; 49 | 50 | fs.writeFile('/file2', contents2, function(err) { 51 | if(err) throw err; 52 | 53 | shell.cat(['/file', '/file2'], function(err, data) { 54 | expect(err).not.to.exist; 55 | expect(data).to.equal(contents + '\n' + contents2); 56 | done(); 57 | }); 58 | }); 59 | }); 60 | }); 61 | 62 | it('should fail if any of multiple file paths is invalid', function(done) { 63 | var fs = util.fs(); 64 | var shell = new fs.Shell(); 65 | var contents = 'file contents'; 66 | var contents2 = contents + '\n' + contents; 67 | 68 | fs.writeFile('/file', contents, function(err) { 69 | if(err) throw err; 70 | 71 | fs.writeFile('/file2', contents2, function(err) { 72 | if(err) throw err; 73 | 74 | shell.cat(['/file', '/nofile'], function(err, data) { 75 | expect(err).to.exist; 76 | expect(err.code).to.equal('ENOENT'); 77 | expect(data).not.to.exist; 78 | done(); 79 | }); 80 | }); 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /tests/spec/shell/cd.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('FileSystemShell.cd', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should be a function', function() { 9 | var shell = util.shell(); 10 | expect(shell.cd).to.be.a('function'); 11 | }); 12 | 13 | it('should default to a cwd of /', function() { 14 | var shell = util.shell(); 15 | expect(shell.pwd()).to.equal('/'); 16 | }); 17 | 18 | it('should allow changing the path to a valid dir', function(done) { 19 | var fs = util.fs(); 20 | var shell = new fs.Shell(); 21 | 22 | fs.mkdir('/dir', function(err) { 23 | if(err) throw err; 24 | 25 | expect(shell.pwd()).to.equal('/'); 26 | shell.cd('/dir', function(err) { 27 | expect(err).not.to.exist; 28 | expect(shell.pwd()).to.equal('/dir'); 29 | done(); 30 | }); 31 | }); 32 | }); 33 | 34 | it('should fail when changing the path to an invalid dir', function(done) { 35 | var fs = util.fs(); 36 | var shell = new fs.Shell(); 37 | 38 | fs.mkdir('/dir', function(err) { 39 | if(err) throw err; 40 | 41 | expect(shell.pwd()).to.equal('/'); 42 | shell.cd('/nodir', function(err) { 43 | expect(err).to.exist; 44 | expect(err.code).to.equal('ENOTDIR'); 45 | expect(shell.pwd()).to.equal('/'); 46 | done(); 47 | }); 48 | }); 49 | }); 50 | 51 | it('should fail when changing the path to a file', function(done) { 52 | var fs = util.fs(); 53 | var shell = new fs.Shell(); 54 | 55 | fs.writeFile('/file', 'file', function(err) { 56 | if(err) throw err; 57 | 58 | expect(shell.pwd()).to.equal('/'); 59 | shell.cd('/file', function(err) { 60 | expect(err).to.exist; 61 | expect(err.code).to.equal('ENOTDIR'); 62 | expect(shell.pwd()).to.equal('/'); 63 | done(); 64 | }); 65 | }); 66 | }); 67 | 68 | it('should allow relative paths for a valid dir', function(done) { 69 | var fs = util.fs(); 70 | var shell = new fs.Shell(); 71 | 72 | fs.mkdir('/dir', function(err) { 73 | if(err) throw err; 74 | 75 | expect(shell.pwd()).to.equal('/'); 76 | shell.cd('./dir', function(err) { 77 | expect(err).not.to.exist; 78 | expect(shell.pwd()).to.equal('/dir'); 79 | done(); 80 | }); 81 | }); 82 | }); 83 | 84 | it('should allow .. in paths for a valid dir', function(done) { 85 | var fs = util.fs(); 86 | var shell = new fs.Shell(); 87 | 88 | fs.mkdir('/dir', function(err) { 89 | if(err) throw err; 90 | 91 | expect(shell.pwd()).to.equal('/'); 92 | shell.cd('./dir', function(err) { 93 | expect(err).not.to.exist; 94 | expect(shell.pwd()).to.equal('/dir'); 95 | shell.cd('..', function(err) { 96 | expect(err).not.to.exist; 97 | expect(shell.pwd()).to.equal('/'); 98 | done(); 99 | }); 100 | }); 101 | }); 102 | }); 103 | 104 | it('should follow symlinks to dirs', function(done) { 105 | var fs = util.fs(); 106 | 107 | fs.mkdir('/dir', function(error) { 108 | if(error) throw error; 109 | 110 | fs.symlink('/dir', '/link', function(error) { 111 | if(error) throw error; 112 | 113 | var shell = new fs.Shell(); 114 | shell.cd('link', function(error) { 115 | expect(error).not.to.exist; 116 | expect(shell.pwd()).to.equal('/link'); 117 | done(); 118 | }); 119 | }); 120 | }); 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /tests/spec/shell/env.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('FileSystemShell.env', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should get default env options', function() { 9 | var shell = util.shell(); 10 | expect(shell.env).to.exist; 11 | expect(shell.env.get('TMP')).to.equal('/tmp'); 12 | expect(shell.env.get('PATH')).to.equal(''); 13 | expect(shell.pwd()).to.equal('/'); 14 | }); 15 | 16 | it('should be able to specify env options', function() { 17 | var options = { 18 | env: { 19 | TMP: '/tempdir', 20 | PATH: '/dir' 21 | } 22 | }; 23 | var shell = util.shell(options); 24 | expect(shell.env).to.exist; 25 | expect(shell.env.get('TMP')).to.equal('/tempdir'); 26 | expect(shell.env.get('PATH')).to.equal('/dir'); 27 | expect(shell.pwd()).to.equal('/'); 28 | 29 | expect(shell.env.get('FOO')).not.to.exist; 30 | shell.env.set('FOO', 1); 31 | expect(shell.env.get('FOO')).to.equal(1); 32 | }); 33 | 34 | it('should fail when dirs argument is absent', function(done) { 35 | var fs = util.fs(); 36 | var shell = new fs.Shell(); 37 | 38 | shell.ls(null, function(error, list) { 39 | expect(error).to.exist; 40 | expect(error.code).to.equal('EINVAL'); 41 | expect(list).not.to.exist; 42 | done(); 43 | }); 44 | }); 45 | 46 | it('should give new value for shell.pwd() when cwd changes', function(done) { 47 | var fs = util.fs(); 48 | var shell = new fs.Shell(); 49 | 50 | fs.mkdir('/dir', function(err) { 51 | if(err) throw err; 52 | 53 | expect(shell.pwd()).to.equal('/'); 54 | shell.cd('/dir', function(err) { 55 | expect(err).not.to.exist; 56 | expect(shell.pwd()).to.equal('/dir'); 57 | done(); 58 | }); 59 | }); 60 | }); 61 | 62 | it('should create/return the default tmp dir', function(done) { 63 | var fs = util.fs(); 64 | var shell = new fs.Shell(); 65 | 66 | expect(shell.env.get('TMP')).to.equal('/tmp'); 67 | shell.tempDir(function(err, tmp) { 68 | expect(err).not.to.exist; 69 | shell.cd(tmp, function(err) { 70 | expect(err).not.to.exist; 71 | expect(shell.pwd()).to.equal('/tmp'); 72 | done(); 73 | }); 74 | }); 75 | }); 76 | 77 | it('should create/return the tmp dir specified in env.TMP', function(done) { 78 | var fs = util.fs(); 79 | var shell = new fs.Shell({ 80 | env: { 81 | TMP: '/tempdir' 82 | } 83 | }); 84 | 85 | expect(shell.env.get('TMP')).to.equal('/tempdir'); 86 | shell.tempDir(function(err, tmp) { 87 | expect(err).not.to.exist; 88 | shell.cd(tmp, function(err) { 89 | expect(err).not.to.exist; 90 | expect(shell.pwd()).to.equal('/tempdir'); 91 | done(); 92 | }); 93 | }); 94 | }); 95 | 96 | it('should allow repeated calls to tempDir()', function(done) { 97 | var fs = util.fs(); 98 | var shell = new fs.Shell(); 99 | 100 | expect(shell.env.get('TMP')).to.equal('/tmp'); 101 | shell.tempDir(function(err, tmp) { 102 | expect(err).not.to.exist; 103 | expect(tmp).to.equal('/tmp'); 104 | 105 | shell.tempDir(function(err, tmp) { 106 | expect(err).not.to.exist; 107 | expect(tmp).to.equal('/tmp'); 108 | done(); 109 | }); 110 | }); 111 | }); 112 | }); 113 | -------------------------------------------------------------------------------- /tests/spec/shell/exec.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('FileSystemShell.exec', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should be a function', function() { 9 | var shell = util.shell(); 10 | expect(shell.exec).to.be.a('function'); 11 | }); 12 | 13 | it('should be able to execute a command .js file from the filesystem', function(done) { 14 | var fs = util.fs(); 15 | var shell = new fs.Shell(); 16 | var cmdString = 'fs.writeFile(args[0], args[1], callback);'; 17 | 18 | fs.writeFile('/cmd.js', cmdString, function(error) { 19 | if(error) throw error; 20 | 21 | shell.exec('/cmd.js', ['/test', 'hello world'], function(error) { 22 | if(error) throw error; 23 | 24 | fs.readFile('/test', 'utf8', function(error, data) { 25 | if(error) throw error; 26 | expect(data).to.equal('hello world'); 27 | done(); 28 | }); 29 | }); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/spec/shell/mkdirp.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('FileSystemShell.mkdirp', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should be a function', function() { 9 | var shell = util.shell(); 10 | expect(shell.mkdirp).to.be.a('function'); 11 | }); 12 | 13 | it('should fail without a path provided', function(done) { 14 | var fs = util.fs(); 15 | var shell = new fs.Shell(); 16 | 17 | shell.mkdirp(null, function(err) { 18 | expect(err).to.exist; 19 | expect(err.code).to.equal('EINVAL'); 20 | done(); 21 | }); 22 | }); 23 | 24 | it('should succeed if provided path is root', function(done) { 25 | var fs = util.fs(); 26 | var shell = new fs.Shell(); 27 | shell.mkdirp('/', function(err) { 28 | expect(err).to.not.exist; 29 | done(); 30 | }); 31 | }); 32 | 33 | it('should succeed if provided path is root, given as a relative path (\'.\' in \'/\')', function(done) { 34 | var fs = util.fs(); 35 | var shell = new fs.Shell(); 36 | shell.cd('/', function(err) { 37 | expect(err).to.not.exist; 38 | shell.mkdirp('.', function(err) { 39 | expect(err).to.not.exist; 40 | done(); 41 | }); 42 | }); 43 | }); 44 | 45 | it('should succeed if the directory exists', function(done) { 46 | var fs = util.fs(); 47 | var shell = new fs.Shell(); 48 | fs.mkdir('/test', function(err){ 49 | expect(err).to.not.exist; 50 | shell.mkdirp('/test',function(err) { 51 | expect(err).to.not.exist; 52 | done(); 53 | }); 54 | }); 55 | }); 56 | 57 | it('fail if a file name is provided', function(done) { 58 | var fs = util.fs(); 59 | var shell = new fs.Shell(); 60 | fs.writeFile('/test.txt', 'test', function(err){ 61 | expect(err).to.not.exist; 62 | shell.mkdirp('/test.txt', function(err) { 63 | expect(err).to.exist; 64 | expect(err.code).to.equal('ENOTDIR'); 65 | done(); 66 | }); 67 | }); 68 | }); 69 | 70 | it('should succeed on a folder on root (\'/test\')', function(done) { 71 | var fs = util.fs(); 72 | var shell = new fs.Shell(); 73 | shell.mkdirp('/test', function(err) { 74 | expect(err).to.not.exist; 75 | fs.exists('/test', function(dir){ 76 | expect(dir).to.be.true; 77 | done(); 78 | }); 79 | }); 80 | }); 81 | 82 | it('should succeed on a folder given as a relative path (\'test\' in \'/\')', function(done) { 83 | var fs = util.fs(); 84 | var shell = new fs.Shell(); 85 | shell.cd('/', function(err) { 86 | expect(err).to.not.exist; 87 | shell.mkdirp('test', function(err) { 88 | expect(err).to.not.exist; 89 | fs.exists('/test', function(dir){ 90 | expect(dir).to.be.true; 91 | done(); 92 | }); 93 | }); 94 | }); 95 | }); 96 | 97 | it('should succeed on a folder with a nonexistant parent (\'/test/test\')', function(done) { 98 | var fs = util.fs(); 99 | var shell = new fs.Shell(); 100 | shell.mkdirp('/test/test', function(err) { 101 | expect(err).to.not.exist; 102 | fs.exists('/test', function(dir1){ 103 | expect(dir1).to.be.true; 104 | fs.exists('/test/test', function(dir2){ 105 | expect(dir2).to.be.true; 106 | done(); 107 | }); 108 | }); 109 | }); 110 | }); 111 | 112 | it('should fail on a folder with a file for its parent (\'/test.txt/test\')', function(done) { 113 | var fs = util.fs(); 114 | var shell = new fs.Shell(); 115 | fs.writeFile('/test.txt', 'test', function(err){ 116 | expect(err).to.not.exist; 117 | shell.mkdirp('/test.txt/test', function(err) { 118 | expect(err).to.exist; 119 | expect(err.code).to.equal('ENOTDIR'); 120 | done(); 121 | }); 122 | }); 123 | }); 124 | }); 125 | -------------------------------------------------------------------------------- /tests/spec/shell/rm.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | describe('FileSystemShell.rm', function() { 5 | beforeEach(util.setup); 6 | afterEach(util.cleanup); 7 | 8 | it('should be a function', function() { 9 | var shell = util.shell(); 10 | expect(shell.rm).to.be.a('function'); 11 | }); 12 | 13 | it('should fail when path argument is absent', function(done) { 14 | var fs = util.fs(); 15 | var shell = new fs.Shell(); 16 | 17 | shell.rm(null, function(error, list) { 18 | expect(error).to.exist; 19 | expect(error.code).to.equal('EINVAL'); 20 | expect(list).not.to.exist; 21 | done(); 22 | }); 23 | }); 24 | 25 | it('should remove a single file', function(done) { 26 | var fs = util.fs(); 27 | var shell = new fs.Shell(); 28 | var contents = 'a'; 29 | 30 | fs.writeFile('/file', contents, function(err) { 31 | if(err) throw err; 32 | 33 | shell.rm('/file', function(err) { 34 | expect(err).not.to.exist; 35 | 36 | fs.stat('/file', function(err, stats) { 37 | expect(err).to.exist; 38 | expect(stats).not.to.exist; 39 | done(); 40 | }); 41 | }); 42 | }); 43 | }); 44 | 45 | it('should remove an empty dir', function(done) { 46 | var fs = util.fs(); 47 | var shell = new fs.Shell(); 48 | 49 | fs.mkdir('/dir', function(err) { 50 | if(err) throw err; 51 | 52 | shell.rm('/dir', function(err) { 53 | expect(err).not.to.exist; 54 | 55 | fs.stat('/dir', function(err, stats) { 56 | expect(err).to.exist; 57 | expect(stats).not.to.exist; 58 | done(); 59 | }); 60 | }); 61 | }); 62 | }); 63 | 64 | it('should fail to remove a non-empty dir', function(done) { 65 | var fs = util.fs(); 66 | var shell = new fs.Shell(); 67 | 68 | fs.mkdir('/dir', function(err) { 69 | if(err) throw err; 70 | 71 | shell.touch('/dir/file', function(err) { 72 | if(err) throw err; 73 | 74 | shell.rm('/dir', function(err) { 75 | expect(err).to.exist; 76 | expect(err.code).to.equal('ENOTEMPTY'); 77 | done(); 78 | }); 79 | }); 80 | }); 81 | }); 82 | 83 | it('should remove a non-empty dir with option.recursive set', function(done) { 84 | var fs = util.fs(); 85 | var shell = new fs.Shell(); 86 | 87 | fs.mkdir('/dir', function(err) { 88 | if(err) throw err; 89 | 90 | shell.touch('/dir/file', function(err) { 91 | if(err) throw err; 92 | 93 | shell.rm('/dir', { recursive: true }, function(err) { 94 | expect(err).not.to.exist; 95 | 96 | fs.stat('/dir', function(err, stats) { 97 | expect(err).to.exist; 98 | expect(stats).not.to.exist; 99 | done(); 100 | }); 101 | }); 102 | }); 103 | }); 104 | }); 105 | 106 | it('should work on a complex dir structure', function(done) { 107 | var fs = util.fs(); 108 | var shell = new fs.Shell(); 109 | var contents = 'a'; 110 | 111 | fs.mkdir('/dir', function(err) { 112 | if(err) throw err; 113 | 114 | fs.mkdir('/dir/dir2', function(err) { 115 | if(err) throw err; 116 | 117 | fs.writeFile('/dir/file', contents, function(err) { 118 | if(err) throw err; 119 | 120 | fs.writeFile('/dir/file2', contents, function(err) { 121 | if(err) throw err; 122 | 123 | shell.rm('/dir', { recursive: true }, function(err) { 124 | expect(err).not.to.exist; 125 | 126 | fs.stat('/dir', function(err, stats) { 127 | expect(err).to.exist; 128 | expect(stats).not.to.exist; 129 | done(); 130 | }); 131 | }); 132 | }); 133 | }); 134 | }); 135 | }); 136 | }); 137 | }); 138 | -------------------------------------------------------------------------------- /tests/spec/shell/touch.spec.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/test-utils.js'); 2 | var expect = require('chai').expect; 3 | 4 | function getTimes(fs, path, callback) { 5 | fs.stat(path, function(error, stats) { 6 | if(error) throw error; 7 | callback({mtime: stats.mtimeMs, atime: stats.atimeMs}); 8 | }); 9 | } 10 | 11 | describe('FileSystemShell.touch', function() { 12 | beforeEach(util.setup); 13 | afterEach(util.cleanup); 14 | 15 | it('should be a function', function() { 16 | var shell = util.shell(); 17 | expect(shell.touch).to.be.a('function'); 18 | }); 19 | 20 | it('should create a new file if path does not exist', function(done) { 21 | var fs = util.fs(); 22 | var shell = new fs.Shell(); 23 | 24 | shell.touch('/newfile', function(error) { 25 | if(error) throw error; 26 | 27 | fs.stat('/newfile', function(error, stats) { 28 | expect(error).not.to.exist; 29 | expect(stats.isFile()).to.be.true; 30 | done(); 31 | }); 32 | }); 33 | }); 34 | 35 | it('should skip creating a new file if options.updateOnly is true', function(done) { 36 | var fs = util.fs(); 37 | var shell = new fs.Shell(); 38 | 39 | shell.touch('/newfile', { updateOnly: true }, function(error) { 40 | if(error) throw error; 41 | 42 | fs.stat('/newfile', function(error, stats) { 43 | expect(error).to.exist; 44 | expect(stats).not.to.exist; 45 | done(); 46 | }); 47 | }); 48 | }); 49 | 50 | it('should update times if path does exist', function(done) { 51 | var fs = util.fs(); 52 | var shell = new fs.Shell(); 53 | var atime = Date.parse('1 Oct 2000 15:33:22'); 54 | var mtime = Date.parse('30 Sep 2000 06:43:54'); 55 | 56 | fs.open('/newfile', 'w', function (error, fd) { 57 | if (error) throw error; 58 | 59 | fs.futimes(fd, atime, mtime, function (error) { 60 | if(error) throw error; 61 | 62 | fs.close(fd, function(error) { 63 | if(error) throw error; 64 | 65 | getTimes(fs, '/newfile', function(times1) { 66 | shell.touch('/newfile', function(error) { 67 | expect(error).not.to.exist; 68 | 69 | getTimes(fs, '/newfile', function(times2) { 70 | expect(times2.mtime).to.be.above(times1.mtime); 71 | expect(times2.atime).to.be.above(times1.atime); 72 | done(); 73 | }); 74 | }); 75 | }); 76 | }); 77 | }); 78 | }); 79 | }); 80 | 81 | it('should update times to specified date if path does exist', function(done) { 82 | var fs = util.fs(); 83 | var shell = new fs.Shell(); 84 | var date = Date.parse('1 Oct 2001 15:33:22'); 85 | 86 | fs.open('/newfile', 'w', function (error, fd) { 87 | if (error) throw error; 88 | 89 | fs.close(fd, function(error) { 90 | if(error) throw error; 91 | 92 | shell.touch('/newfile', { date: date }, function(error) { 93 | expect(error).not.to.exist; 94 | 95 | getTimes(fs, '/newfile', function(times) { 96 | expect(times.mtime).to.equal(date); 97 | done(); 98 | }); 99 | }); 100 | }); 101 | }); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /tests/spec/shims/fs.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const expect = require('chai').expect; 3 | const utils = require('../../lib/test-utils'); 4 | const fs = utils.shimIndexedDB(() => require('../../../shims/fs')); 5 | 6 | describe('fs shim', () => { 7 | it('should be defined', () => { 8 | expect(fs).to.not.be.undefined; 9 | }); 10 | 11 | it('should be an object', () => { 12 | expect(typeof fs).to.equal('object'); 13 | }); 14 | 15 | it('should return a function when accessing fs.writeFile', () => { 16 | expect(typeof fs.writeFile).to.equal('function'); 17 | }); 18 | 19 | it('should call callback when calling fs.writeFile', (done) => { 20 | fs.writeFile('/test.txt', 'test', function(err) { 21 | if(err) throw err; 22 | 23 | done(); 24 | }); 25 | }); 26 | 27 | it('should return an object when accessing fs.promises', () => { 28 | expect(typeof fs.promises).to.equal('object'); 29 | }); 30 | 31 | it('should return a function when accessing fs.promises.writeFile', () => { 32 | expect(typeof fs.promises.writeFile).to.equal('function'); 33 | }); 34 | 35 | it('should return a promise which resolves when calling fs.promises.writeFile', (done) => { 36 | const writeFilePromise = fs.promises.writeFile('/test2.txt', ''); 37 | expect(writeFilePromise instanceof Promise).to.equal(true); 38 | writeFilePromise.then(() => { 39 | done(); 40 | }).catch((err) => { 41 | done(err); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /tests/spec/shims/path.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const expect = require('chai').expect; 3 | const path = require('../../../shims/path'); 4 | const pathActual = require('../../../src/index').path; 5 | 6 | describe('path shim', () => { 7 | it('should be defined', () => { 8 | expect(path).to.not.be.undefined; 9 | }); 10 | 11 | it('should be re-exposing path', () => { 12 | expect(path).to.equal(pathActual); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/spec/time-flags.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Filer = require('../../src'); 4 | const util = require('../lib/test-utils.js'); 5 | const expect = require('chai').expect; 6 | 7 | describe('node times (atime, mtime, ctime) with mount flags', function() { 8 | 9 | const dirname = '/dir'; 10 | const filename = '/dir/file'; 11 | 12 | function memoryFS(flags, callback) { 13 | const name = util.uniqueName(); 14 | return new Filer.FileSystem({ 15 | name: name, 16 | flags: flags || [], 17 | provider: new Filer.FileSystem.providers.Memory(name) 18 | }, callback); 19 | } 20 | 21 | function createTree(fs, callback) { 22 | fs.mkdir(dirname, function(error) { 23 | if(error) throw error; 24 | 25 | fs.open(filename, 'w', function(error, fd) { 26 | if(error) throw error; 27 | 28 | fs.close(fd, callback); 29 | }); 30 | }); 31 | } 32 | 33 | function stat(fs, path, callback) { 34 | fs.stat(path, function(error, stats) { 35 | if(error) throw error; 36 | 37 | callback(stats); 38 | }); 39 | } 40 | 41 | /** 42 | * We test the actual time updates in times.spec.js, whereas these just test 43 | * the overrides with the mount flags. The particular fs methods called 44 | * are unimportant, but are known to affect the particular times being suppressed. 45 | */ 46 | 47 | it('should not update ctime when calling fs.rename() with NOCTIME', function(done) { 48 | memoryFS(['NOCTIME'], function(error, fs) { 49 | const newfilename = filename + '1'; 50 | 51 | createTree(fs, function() { 52 | stat(fs, filename, function(stats1) { 53 | 54 | fs.rename(filename, newfilename, function(error) { 55 | if(error) throw error; 56 | 57 | stat(fs, newfilename, function(stats2) { 58 | expect(stats2.ctimeMs).to.equal(stats1.ctimeMs); 59 | expect(stats2.mtimeMs).to.equal(stats1.mtimeMs); 60 | expect(stats2.atimeMs).to.equal(stats1.atimeMs); 61 | done(); 62 | }); 63 | }); 64 | }); 65 | }); 66 | }); 67 | }); 68 | 69 | it('should not update ctime, mtime, atime when calling fs.truncate() with NOCTIME, NOMTIME', function(done) { 70 | memoryFS(['NOCTIME', 'NOMTIME'], function(error, fs) { 71 | createTree(fs, function() { 72 | stat(fs, filename, function(stats1) { 73 | 74 | fs.truncate(filename, 5, function(error) { 75 | if(error) throw error; 76 | 77 | stat(fs, filename, function(stats2) { 78 | expect(stats2.ctimeMs).to.equal(stats1.ctimeMs); 79 | expect(stats2.mtimeMs).to.equal(stats1.mtimeMs); 80 | expect(stats2.atimeMs).to.equal(stats1.atimeMs); 81 | done(); 82 | }); 83 | }); 84 | }); 85 | }); 86 | }); 87 | }); 88 | 89 | it('should not update mtime when calling fs.truncate() with NOMTIME', function(done) { 90 | memoryFS(['NOMTIME'], function(error, fs) { 91 | createTree(fs, function() { 92 | stat(fs, filename, function(stats1) { 93 | 94 | fs.truncate(filename, 5, function(error) { 95 | if(error) throw error; 96 | 97 | stat(fs, filename, function(stats2) { 98 | expect(stats2.ctimeMs).to.be.at.least(stats1.ctimeMs); 99 | expect(stats2.mtimeMs).to.equal(stats1.mtimeMs); 100 | expect(stats2.atimeMs).to.be.at.least(stats1.atimeMs); 101 | done(); 102 | }); 103 | }); 104 | }); 105 | }); 106 | }); 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /tests/spec/trailing-slashes.spec.js: -------------------------------------------------------------------------------- 1 | var Path = require('../../src').Path; 2 | var expect = require('chai').expect; 3 | 4 | describe('Path.normalize and trailing slashes', function() { 5 | 6 | it('should remove trailing slashes as expected', function() { 7 | var strip = Path.normalize; 8 | 9 | expect(strip('/')).to.equal('/'); 10 | expect(strip('/foo/')).to.equal('/foo'); 11 | expect(strip('/foo//')).to.equal('/foo'); 12 | expect(strip('/foo/bar/baz/')).to.equal('/foo/bar/baz'); 13 | }); 14 | 15 | }); 16 | -------------------------------------------------------------------------------- /tests/test-dir.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filerjs/filer/4f4015a4f7e47ba808c7defd4b2293fce2408b73/tests/test-dir.zip -------------------------------------------------------------------------------- /tests/test-file.txt: -------------------------------------------------------------------------------- 1 | This is a test file used in some of the tests. 2 | -------------------------------------------------------------------------------- /tests/test-file.txt.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filerjs/filer/4f4015a4f7e47ba808c7defd4b2293fce2408b73/tests/test-file.txt.zip -------------------------------------------------------------------------------- /tools/get-filer-version.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint-disable no-console */ 3 | 'use strict'; 4 | 5 | const {spawn} = require('child_process'); 6 | const meow = require('meow'); 7 | 8 | const cli = meow(` 9 | Usage 10 | $ get-filer-version [--out path/to/filer.js] 11 | 12 | Options 13 | --out, -o Specify a Filer module path to use for output 14 | 15 | Examples 16 | $ get-filer-version v0.0.44 17 | $ get-filer-version v0.0.44 --out filer.js 18 | `, { 19 | description: 'Try to get an old version of Filer based on git SHA, branch, or tag', 20 | flags: { 21 | out: { 22 | type: 'string', 23 | alias: 'o' 24 | } 25 | } 26 | }); 27 | 28 | // Get arg list, make sure we get a SHA argument 29 | cli.flags.app = cli.input.slice(1); 30 | if(!(cli.input && cli.input.length === 1)) { 31 | console.error('Specify a git SHA, branch or tag to use'); 32 | process.exit(1); 33 | } 34 | 35 | const sha = cli.input[0]; 36 | const out = cli.flags.out || `filer-${sha}.js`; 37 | // https://stackoverflow.com/questions/888414/git-checkout-older-revision-of-a-file-under-a-new-name?answertab=active#tab-top 38 | const cmd = `git show ${sha}:dist/filer.js > ${out}`; 39 | spawn (cmd, [], {stdio: 'inherit', shell: true}); 40 | -------------------------------------------------------------------------------- /webpack/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | FilerWebpackPlugin: require('../src/webpack-plugin'), 3 | }; 4 | --------------------------------------------------------------------------------