Skip to content

Commit

Permalink
Support test suite on Windows
Browse files Browse the repository at this point in the history
Based on work by Tyler Waters <tyler.waters@gmail.com> in mochajs#1814.

This commit contains the following changes:

- Add quotation marks to Makefile variables for programs. The variables
  use POSIX-style paths, which cannot be used on Windows to launch a
  program except when quoted. Using double quotation marks instead of
  single since the latter is not available on Windows
- Use os-tmpdir module to get an acceptable directory for temporary
  usage instead of relying on the POSIX /tmp
- Use process.execPath as an authoritative path for Node.js executable
- Detect whether symbolic links are supported on the platform before
  testing. On Windows, creating symlinks can fail since it needs
  additional user permissions
- Fix hook tests. The tests parse output of the "dot" reporter to
  separate output of individual tests. The "dot" reporter uses "·"
  symbol (U+2024 ONE DOT LEADER) under POSIX environments and "." symbol
  (U+002E FULL STOP) under Windows, which means that having "." in the
  echoed message makes it ambiguous to be parsed in Windows. To fix this
  issue, two separate changes are necessary:
  - Use a dynamically created regular expression to split the tests
    based on the specific dot character used on the platform
  - Replace "." with "-" in echoed messages in fixtures for hook tests
    to avoid ambiguity with the character output by the reporter

Changes from mochajs#1814 include:

- Rebasing
- Use process.execPath as an authoritative path for Node.js executable
- Avoid external dependencies for child_process.spawn()
- Detect whether symbol links are supported on the platform before
  testing. On Windows, creating symlinks can fail since it needs
  additional user permissions

Fixes mochajs#1813.
  • Loading branch information
TimothyGu committed Jun 14, 2016
1 parent 4cfab1c commit 6c2addc
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 144 deletions.
6 changes: 3 additions & 3 deletions Makefile
@@ -1,6 +1,6 @@
BROWSERIFY := node_modules/.bin/browserify
ESLINT := node_modules/.bin/eslint
KARMA := node_modules/.bin/karma
BROWSERIFY := "node_modules/.bin/browserify"
ESLINT := "node_modules/.bin/eslint"
KARMA := "node_modules/.bin/karma"

REPORTER ?= spec
TM_BUNDLE = JavaScript\ mocha.tmbundle
Expand Down
20 changes: 13 additions & 7 deletions mocha.js
Expand Up @@ -2474,7 +2474,7 @@ function coverageClass(coveragePctg) {
return 'terrible';
}

}).call(this,require('_process'),"/lib/reporters")
}).call(this,require('_process'),"/lib\\reporters")
},{"./json-cov":23,"_process":58,"fs":43,"jade":43,"path":43}],21:[function(require,module,exports){
(function (global){
/* eslint-env browser */
Expand Down Expand Up @@ -4814,7 +4814,13 @@ Runner.prototype.hook = function(name, fn) {
}
if (err) {
if (err instanceof Pending) {
suite.pending = true;
if (name === 'beforeEach' || name === 'afterEach') {
self.test.pending = true;
} else {
suite.tests.forEach(function(test) {
test.pending = true;
});
}
} else {
self.failHook(hook, err);

Expand Down Expand Up @@ -5028,7 +5034,7 @@ Runner.prototype.runTests = function(suite, fn) {
// execute test and hook(s)
self.emit('test', self.test = test);
self.hookDown('beforeEach', function(err, errSuite) {
if (suite.isPending()) {
if (test.isPending()) {
self.emit('pending', test);
self.emit('test end', test);
return next();
Expand Down Expand Up @@ -5355,8 +5361,8 @@ function filterLeaks(ok, globals) {
}

// in firefox
// if runner runs in an iframe, this iframe's window.getInterface method not init at first
// it is assigned in some seconds
// if runner runs in an iframe, this iframe's window.getInterface method
// not init at first it is assigned in some seconds
if (global.navigator && (/^getInterface/).test(key)) {
return false;
}
Expand Down Expand Up @@ -6117,8 +6123,8 @@ exports.slug = function(str) {
exports.clean = function(str) {
str = str
.replace(/\r\n?|[\n\u2028\u2029]/g, '\n').replace(/^\uFEFF/, '')
.replace(/^function *\(.*\)\s*\{|\(.*\) *=> *\{?/, '')
.replace(/\s+\}$/, '');
// (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content
.replace(/^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/, '$1$2$3');

var spaces = str.match(/^\n?( *)/)[1].length;
var tabs = str.match(/^\n?(\t*)/)[1].length;
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -324,6 +324,7 @@
"growl": "1.9.2",
"jade": "0.26.3",
"mkdirp": "0.5.1",
"os-tmpdir": "1.0.1",
"supports-color": "1.2.0",
"to-iso-string": "0.0.2"
},
Expand Down
6 changes: 4 additions & 2 deletions test/acceptance/fs.js
@@ -1,16 +1,18 @@
var fs = require('fs');
var path = require('path');
var tmpFile = 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(tmpFile('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(tmpFile('does-not-exist'), done);
done();
})
})
Expand Down
75 changes: 51 additions & 24 deletions test/acceptance/lookup-files.js
@@ -1,56 +1,83 @@
var utils = require('../../lib/utils');

describe('lookupFiles', function() {
var fs = require('fs'), path = require('path'), existsSync = fs.existsSync ||
path.existsSync;
var fs = require('fs'),
path = require('path'),
existsSync = fs.existsSync || path.existsSync,
tmpDir = require('os-tmpdir')(),
tmpFile = path.join.bind(path, tmpDir),
symlinkSupported = false;

fs.writeFileSync(tmpFile('mocha-utils.js'), 'yippy skippy ying yang yow');
try {
fs.symlinkSync(tmpFile('mocha-utils.js'), tmpFile('mocha-utils-link.js'));
symlinkSupported = true;
} catch (ignored) {
}

cleanup();

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(tmpFile('mocha-utils.js'), 'yippy skippy ying yang yow');
if (symlinkSupported) {
fs.symlinkSync(tmpFile('mocha-utils.js'), tmpFile('mocha-utils-link.js'));
}
});

it('should not choke on symlinks', function() {
expect(utils.lookupFiles('/tmp', ['js'], false))
(symlinkSupported ? it : it.skip)('should not choke on symlinks', function() {
expect(utils.lookupFiles(tmpDir, ['js'], false))
.to
.contain('/tmp/mocha-utils-link.js')
.contain(tmpFile('mocha-utils-link.js'))
.and
.contain('/tmp/mocha-utils.js')
.contain(tmpFile('mocha-utils.js'))
.and
.have
.length(2);
expect(existsSync('/tmp/mocha-utils-link.js'))
expect(existsSync(tmpFile('mocha-utils-link.js')))
.to
.be(true);
fs.renameSync('/tmp/mocha-utils.js', '/tmp/bob');
expect(existsSync('/tmp/mocha-utils-link.js'))
fs.renameSync(tmpFile('mocha-utils.js'), tmpFile('bob'));
expect(existsSync(tmpFile('mocha-utils-link.js')))
.to
.be(false);
expect(utils.lookupFiles('/tmp', ['js'], false))
expect(utils.lookupFiles(tmpDir, ['js'], false))
.to
.eql([]);
});

it('should accept a glob "path" value', function() {
expect(utils.lookupFiles('/tmp/mocha-utils*', ['js'], false))
var res = utils.lookupFiles(tmpFile('mocha-utils*'), ['js'], false)
.map(path.normalize.bind(path));

var expectedLength = 0;
var ex = expect(res)
.to
.contain('/tmp/mocha-utils-link.js')
.and
.contain('/tmp/mocha-utils.js')
.and
.contain(tmpFile('mocha-utils.js'));
expectedLength++;

if (symlinkSupported) {
ex = ex.and
.contain(tmpFile('mocha-utils-link.js'));
expectedLength++;
}

ex.and
.have
.length(2);
.length(expectedLength);
});

afterEach(function() {
afterEach(cleanup);

function cleanup() {
[
'/tmp/mocha-utils.js',
'/tmp/mocha-utils-link.js',
'/tmp/bob'
'mocha-utils.js',
'mocha-utils-link.js',
'bob'
].forEach(function(path) {
try {
fs.unlinkSync(path);
fs.unlinkSync(tmpFile(path));
} catch (ignored) {
}
});
});
}
});
5 changes: 3 additions & 2 deletions 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.execPath, command, function(err, stdout, stderr) {
if (err) return cb(err);

assert(stdout.indexOf('[90m') === -1);
Expand Down
78 changes: 39 additions & 39 deletions test/integration/fixtures/hooks/multiple.hook.async.error.js
Expand Up @@ -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');
});
});

Expand All @@ -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');
});
});

Expand Down

0 comments on commit 6c2addc

Please sign in to comment.