From a08d768444dd43f72d8ba3d2ae3d6b6bfb64c7a3 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Sat, 21 May 2016 17:36:38 -0700 Subject: [PATCH] Support test suite on Windows Based on work by Tyler Waters in #1814. Changes from #1814 include: - Rebasing - Use process.argv[0] as an authoritative path for Node.js executable - Support having spaces in path of Node.js executable - Avoid external dependencies for child_process.spawn() - Fix symlink tests on Windows. On Windows, creating symlinks can fail since it needs additional user permissions Fixes #1813. --- Makefile | 4 +- package.json | 1 + test/acceptance/fs.js | 6 +- test/acceptance/utils.js | 62 ++++++++++----- test/color.js | 5 +- .../hooks/multiple.hook.async.error.js | 78 +++++++++---------- .../fixtures/hooks/multiple.hook.error.js | 78 +++++++++---------- test/integration/helpers.js | 11 ++- test/integration/hook.err.js | 47 +++++------ test/integration/hooks.js | 3 +- test/integration/retries.js | 4 +- 11 files changed, 168 insertions(+), 131 deletions(-) diff --git a/Makefile b/Makefile index 28e6c87f6f..bb0f162919 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -BROWSERIFY := node_modules/.bin/browserify -ESLINT := node_modules/.bin/eslint +BROWSERIFY := "node_modules/.bin/browserify" +ESLINT := "node_modules/.bin/eslint" REPORTER ?= spec TM_BUNDLE = JavaScript\ mocha.tmbundle diff --git a/package.json b/package.json index 49b780cc18..b96d801c33 100644 --- a/package.json +++ b/package.json @@ -277,6 +277,7 @@ "growl": "1.9.2", "jade": "0.26.3", "mkdirp": "0.5.1", + "os-tmpdir": "1.0.1", "supports-color": "1.2.0" }, "devDependencies": { diff --git a/test/acceptance/fs.js b/test/acceptance/fs.js index cdd32166d5..8306208750 100644 --- a/test/acceptance/fs.js +++ b/test/acceptance/fs.js @@ -1,16 +1,18 @@ var fs = require('fs'); +var path = require('path'); +var t = path.join.bind(path, require('os-tmpdir')()); describe('fs.readFile()', function(){ describe('when the file exists', function(){ it('should succeed', function(done){ - fs.writeFile('/tmp/mocha', 'wahoo', done) + fs.writeFile(t('mocha'), 'wahoo', done) }) }) describe('when the file does not exist', function(){ it('should fail', function(done){ // uncomment - // fs.readFile('/tmp/does-not-exist', done); + // fs.readFile(t('does-not-exist'), done); done(); }) }) diff --git a/test/acceptance/utils.js b/test/acceptance/utils.js index 2028d4870b..f9ab18ac74 100644 --- a/test/acceptance/utils.js +++ b/test/acceptance/utils.js @@ -386,39 +386,63 @@ describe('lib/utils', function () { describe('lookupFiles', function () { var fs = require('fs'), path = require('path'), - existsSync = fs.existsSync || path.existsSync; + tmpdir = require('os-tmpdir')(), + t = path.join.bind(path, tmpdir); + existsSync = fs.existsSync || path.existsSync, + symlinkSupported = (function () { + var res = false; + fs.writeFileSync(t('mocha-utils.js'), 'yippy skippy ying yang yow'); + try { + fs.symlinkSync(t('mocha-utils.js'), t('mocha-utils-link.js')); + res = true; + } + catch (ignored) {} + + ['mocha-utils.js', 'mocha-utils-link.js', 'bob'].forEach(function (path) { + try { + fs.unlinkSync(t(path)); + } + catch (ignored) {} + }); + + return res; + })(); beforeEach(function () { - fs.writeFileSync('/tmp/mocha-utils.js', 'yippy skippy ying yang yow'); - fs.symlinkSync('/tmp/mocha-utils.js', '/tmp/mocha-utils-link.js'); + fs.writeFileSync(t('mocha-utils.js'), 'yippy skippy ying yang yow'); + if (symlinkSupported) { + fs.symlinkSync(t('mocha-utils.js'), t('mocha-utils-link.js')); + } }); - it('should not choke on symlinks', function () { - utils.lookupFiles('/tmp', ['js'], false) - .should.containEql('/tmp/mocha-utils-link.js') - .and.containEql('/tmp/mocha-utils.js') + (symlinkSupported ? it : it.skip)('should not choke on symlinks', function () { + utils.lookupFiles(tmpdir, ['js'], false) + .should.containEql(t('mocha-utils-link.js')) + .and.containEql(t('mocha-utils.js')) .and.have.lengthOf(2); - existsSync('/tmp/mocha-utils-link.js').should.be.true(); - fs.renameSync('/tmp/mocha-utils.js', '/tmp/bob'); - existsSync('/tmp/mocha-utils-link.js').should.be.false(); - utils.lookupFiles('/tmp', ['js'], false).should.eql([]); + existsSync(t('mocha-utils-link.js')).should.be.true(); + fs.renameSync(t('mocha-utils.js'), t('bob')); + existsSync(t('mocha-utils-link.js')).should.be.false(); + utils.lookupFiles(tmpdir, ['js'], false).should.eql([]); }); it('should accept a glob "path" value', function () { - utils.lookupFiles('/tmp/mocha-utils*', ['js'], false) - .should - .containEql('/tmp/mocha-utils-link.js') - .and - .containEql('/tmp/mocha-utils.js') + var res = utils.lookupFiles(t('mocha-utils*'), ['js'], false) + .map(path.normalize.bind(path)) + .should; + if (symlinkSupported) { + res = res.containEql(t('mocha-utils-link.js')); + } + res.containEql(t('mocha-utils.js')) .and .have - .lengthOf(2); + .lengthOf(1 + (+symlinkSupported)); }); afterEach(function () { - ['/tmp/mocha-utils.js', '/tmp/mocha-utils-link.js', '/tmp/bob'].forEach(function (path) { + ['mocha-utils.js', 'mocha-utils-link.js', 'bob'].forEach(function (path) { try { - fs.unlinkSync(path); + fs.unlinkSync(t(path)); } catch (ignored) {} }); diff --git a/test/color.js b/test/color.js index 8c7167b0fa..5d0fa79d23 100644 --- a/test/color.js +++ b/test/color.js @@ -1,12 +1,13 @@ var assert = require('assert'); var child_process = require('child_process'); +var path = require('path'); describe('Mocha', function() { this.timeout(1000); it('should not output colors to pipe', function(cb) { - var command = 'bin/mocha --grep missing-test'; - child_process.exec(command, function(err, stdout, stderr) { + var command = [path.join('bin', 'mocha'), '--grep', 'missing-test']; + child_process.execFile(process.argv[0], command, function(err, stdout, stderr) { if (err) return cb(err); assert(stdout.indexOf('[90m') === -1); diff --git a/test/integration/fixtures/hooks/multiple.hook.async.error.js b/test/integration/fixtures/hooks/multiple.hook.async.error.js index c5474ad99b..93ace7ff0d 100644 --- a/test/integration/fixtures/hooks/multiple.hook.async.error.js +++ b/test/integration/fixtures/hooks/multiple.hook.async.error.js @@ -9,54 +9,54 @@ describe('1', function () { console.log('1 before each'); }); - describe('1.1', function () { + describe('1-1', function () { before(function () { - console.log('1.1 before'); + console.log('1-1 before'); }); beforeEach(function (done) { - console.log('1.1 before each'); + console.log('1-1 before each'); process.nextTick(function () { - throw new Error('1.1 before each hook failed'); + throw new Error('1-1 before each hook failed'); }); }); - it('1.1 test 1', function () { - console.log('1.1 test 1'); + it('1-1 test 1', function () { + console.log('1-1 test 1'); }); - it('1.1 test 2', function () { - console.log('1.1 test 2'); + it('1-1 test 2', function () { + console.log('1-1 test 2'); }); afterEach(function () { - console.log('1.1 after each'); + console.log('1-1 after each'); }); after(function (done) { - console.log('1.1 after'); + console.log('1-1 after'); process.nextTick(function () { - throw new Error('1.1 after hook failed'); + throw new Error('1-1 after hook failed'); }); }); }); - describe('1.2', function () { + describe('1-2', function () { before(function () { - console.log('1.2 before'); + console.log('1-2 before'); }); beforeEach(function () { - console.log('1.2 before each'); + console.log('1-2 before each'); }); - it('1.2 test 1', function () { - console.log('1.2 test 1'); + it('1-2 test 1', function () { + console.log('1-2 test 1'); }); - it('1.2 test 2', function () { - console.log('1.2 test 2'); + it('1-2 test 2', function () { + console.log('1-2 test 2'); }); afterEach(function (done) { - console.log('1.2 after each'); + console.log('1-2 after each'); process.nextTick(function () { - throw new Error('1.2 after each hook failed'); + throw new Error('1-2 after each hook failed'); }); }); after(function () { - console.log('1.2 after'); + console.log('1-2 after'); }); }); @@ -77,45 +77,45 @@ describe('2', function () { }); }); - describe('2.1', function () { + describe('2-1', function () { before(function () { - console.log('2.1 before'); + console.log('2-1 before'); }); beforeEach(function () { - console.log('2.1 before each'); + console.log('2-1 before each'); }); - it('2.1 test 1', function () { - console.log('2.1 test 1'); + it('2-1 test 1', function () { + console.log('2-1 test 1'); }); - it('2.1 test 2', function () { - console.log('2.1 test 2'); + it('2-1 test 2', function () { + console.log('2-1 test 2'); }); afterEach(function () { - console.log('2.1 after each'); + console.log('2-1 after each'); }); after(function () { - console.log('2.1 after'); + console.log('2-1 after'); }); }); - describe('2.2', function () { + describe('2-2', function () { before(function () { - console.log('2.2 before'); + console.log('2-2 before'); }); beforeEach(function () { - console.log('2.2 before each'); + console.log('2-2 before each'); }); - it('2.2 test 1', function () { - console.log('2.2 test 1'); + it('2-2 test 1', function () { + console.log('2-2 test 1'); }); - it('2.2 test 2', function () { - console.log('2.2 test 2'); + it('2-2 test 2', function () { + console.log('2-2 test 2'); }); afterEach(function () { - console.log('2.2 after each'); + console.log('2-2 after each'); }); after(function () { - console.log('2.2 after'); + console.log('2-2 after'); }); }); diff --git a/test/integration/fixtures/hooks/multiple.hook.error.js b/test/integration/fixtures/hooks/multiple.hook.error.js index f0983cf42f..53d167b1cf 100644 --- a/test/integration/fixtures/hooks/multiple.hook.error.js +++ b/test/integration/fixtures/hooks/multiple.hook.error.js @@ -9,48 +9,48 @@ describe('1', function () { console.log('1 before each'); }); - describe('1.1', function () { + describe('1-1', function () { before(function () { - console.log('1.1 before'); + console.log('1-1 before'); }); beforeEach(function () { - console.log('1.1 before each'); - throw new Error('1.1 before each hook failed'); + console.log('1-1 before each'); + throw new Error('1-1 before each hook failed'); }); - it('1.1 test 1', function () { - console.log('1.1 test 1'); + it('1-1 test 1', function () { + console.log('1-1 test 1'); }); - it('1.1 test 2', function () { - console.log('1.1 test 2'); + it('1-1 test 2', function () { + console.log('1-1 test 2'); }); afterEach(function () { - console.log('1.1 after each'); + console.log('1-1 after each'); }); after(function () { - console.log('1.1 after'); - throw new Error('1.1 after hook failed'); + console.log('1-1 after'); + throw new Error('1-1 after hook failed'); }); }); - describe('1.2', function () { + describe('1-2', function () { before(function () { - console.log('1.2 before'); + console.log('1-2 before'); }); beforeEach(function () { - console.log('1.2 before each'); + console.log('1-2 before each'); }); - it('1.2 test 1', function () { - console.log('1.2 test 1'); + it('1-2 test 1', function () { + console.log('1-2 test 1'); }); - it('1.2 test 2', function () { - console.log('1.2 test 2'); + it('1-2 test 2', function () { + console.log('1-2 test 2'); }); afterEach(function () { - console.log('1.2 after each'); - throw new Error('1.2 after each hook failed'); + console.log('1-2 after each'); + throw new Error('1-2 after each hook failed'); }); after(function () { - console.log('1.2 after'); + console.log('1-2 after'); }); }); @@ -69,45 +69,45 @@ describe('2', function () { throw new Error('2 before each hook failed'); }); - describe('2.1', function () { + describe('2-1', function () { before(function () { - console.log('2.1 before'); + console.log('2-1 before'); }); beforeEach(function () { - console.log('2.1 before each'); + console.log('2-1 before each'); }); - it('2.1 test 1', function () { - console.log('2.1 test 1'); + it('2-1 test 1', function () { + console.log('2-1 test 1'); }); - it('2.1 test 2', function () { - console.log('2.1 test 2'); + it('2-1 test 2', function () { + console.log('2-1 test 2'); }); afterEach(function () { - console.log('2.1 after each'); + console.log('2-1 after each'); }); after(function () { - console.log('2.1 after'); + console.log('2-1 after'); }); }); - describe('2.2', function () { + describe('2-2', function () { before(function () { - console.log('2.2 before'); + console.log('2-2 before'); }); beforeEach(function () { - console.log('2.2 before each'); + console.log('2-2 before each'); }); - it('2.2 test 1', function () { - console.log('2.2 test 1'); + it('2-2 test 1', function () { + console.log('2-2 test 1'); }); - it('2.2 test 2', function () { - console.log('2.2 test 2'); + it('2-2 test 2', function () { + console.log('2-2 test 2'); }); afterEach(function () { - console.log('2.2 after each'); + console.log('2-2 after each'); }); after(function () { - console.log('2.2 after'); + console.log('2-2 after'); }); }); diff --git a/test/integration/helpers.js b/test/integration/helpers.js index d63756c0a1..548c72295a 100644 --- a/test/integration/helpers.js +++ b/test/integration/helpers.js @@ -1,6 +1,7 @@ var spawn = require('child_process').spawn; var path = require('path'); var fs = require('fs'); +var baseReporter = require('../../lib/reporters/base'); module.exports = { /** @@ -142,14 +143,20 @@ module.exports = { return diffs.map(function(diff) { return diff.slice(1, -3).join('\n'); }); - } + }, + + /** + * regular expression used for splitting lines based on new line / dot symbol. + */ + splitRegExp: new RegExp('[\\n' + baseReporter.symbols.dot + ']+') }; function invokeMocha(args, fn) { var output, mocha, listener; output = ''; - mocha = spawn('./bin/mocha', args); + args = [path.join('bin', 'mocha')].concat(args); + mocha = spawn(process.argv[0], args); listener = function(data) { output += data; diff --git a/test/integration/hook.err.js b/test/integration/hook.err.js index ca1a8fe1bb..cf3d7c7c7b 100644 --- a/test/integration/hook.err.js +++ b/test/integration/hook.err.js @@ -1,5 +1,6 @@ var assert = require('assert'); var runMocha = require('./helpers').runMocha; +var splitRegExp = require('./helpers').splitRegExp; describe('hook error handling', function() { this.timeout(1000); @@ -63,30 +64,30 @@ describe('hook error handling', function() { lines, [ 'root before', - '1.1 before', + '1-1 before', 'root before each', '1 before each', - '1.1 before each', - '1.1 after each', + '1-1 before each', + '1-1 after each', '1 after each', 'root after each', - '1.1 after', - '1.2 before', + '1-1 after', + '1-2 before', 'root before each', '1 before each', - '1.2 before each', - '1.2 test 1', - '1.2 after each', + '1-2 before each', + '1-2 test 1', + '1-2 after each', '1 after each', 'root after each', - '1.2 after', + '1-2 after', '1 after', - '2.1 before', + '2-1 before', 'root before each', '2 before each', '2 after each', 'root after each', - '2.1 after', + '2-1 after', '2 after', 'root after' ] @@ -151,30 +152,30 @@ describe('hook error handling', function() { lines, [ 'root before', - '1.1 before', + '1-1 before', 'root before each', '1 before each', - '1.1 before each', - '1.1 after each', + '1-1 before each', + '1-1 after each', '1 after each', 'root after each', - '1.1 after', - '1.2 before', + '1-1 after', + '1-2 before', 'root before each', '1 before each', - '1.2 before each', - '1.2 test 1', - '1.2 after each', + '1-2 before each', + '1-2 test 1', + '1-2 after each', '1 after each', 'root after each', - '1.2 after', + '1-2 after', '1 after', - '2.1 before', + '2-1 before', 'root before each', '2 before each', '2 after each', 'root after each', - '2.1 after', + '2-1 after', '2 after', 'root after' ] @@ -188,7 +189,7 @@ describe('hook error handling', function() { assert.ifError(err); lines = res.output - .split(/[\n․]+/) + .split(splitRegExp) .map(function(line) { return line.trim(); }) diff --git a/test/integration/hooks.js b/test/integration/hooks.js index 4236c7f82d..127af18965 100644 --- a/test/integration/hooks.js +++ b/test/integration/hooks.js @@ -1,5 +1,6 @@ var assert = require('assert'); var run = require('./helpers').runMocha; +var splitRegExp = require('./helpers').splitRegExp; var args = []; describe('hooks', function() { @@ -11,7 +12,7 @@ describe('hooks', function() { assert(!err); - lines = res.output.split(/[\n․]+/).map(function(line) { + lines = res.output.split(splitRegExp).map(function(line) { return line.trim(); }).filter(function(line) { return line.length; diff --git a/test/integration/retries.js b/test/integration/retries.js index 68a8bca3e2..756cc9dd9b 100644 --- a/test/integration/retries.js +++ b/test/integration/retries.js @@ -11,7 +11,7 @@ describe('retries', function() { assert(!err); - lines = res.output.split(/[\n․]+/).map(function(line) { + lines = res.output.split(helpers.splitRegExp).map(function(line) { return line.trim(); }).filter(function(line) { return line.length; @@ -76,7 +76,7 @@ describe('retries', function() { assert(!err); - lines = res.output.split(/[\n․]+/).map(function(line) { + lines = res.output.split(helpers.splitRegExp).map(function(line) { return line.trim(); }).filter(function(line) { return line.length;