Repository: mochajs/mocha

Problem statement:
Tests are broken in Windows
Having read the contributing guidelines, I realized that on a windows machine I could not confirm whether any changes I committed failed tests or not because many of the tests are failing in windows.

Summary of issues and potential fixes: 
- I wasn't able to get the windows make to work properly at all.  using cygwin seems to works though.
- many references to '/tmp' inside acceptance fs/utils - these can be updated to use os.tmpdir()
- passing `bin/mocha` to exec fails,  "bin is not recognized as an internal or external command" -- updating it to `node bin/mocha` makes it work.
- using `spawn` in integration/helpers doesn't work at all.  Updating to use `exec`, passing args delimited by a space works. (also need to preface with `node`, similar to above)

The trickiest one relates to the dot symbol used by windows --

Integration/hook tests takes the output of test results and splits by new lines and the dot symbol.  Thing is, windows doesn't use the dot symbol (\u2024) - [it uses a full stop](https://github.com/mochajs/mocha/blob/master/lib/reporters/base.js#L88), so the tests fail because the split doesn't work, and a full stop winds up in some of the titles.  Updating the [regexp](https://github.com/mochajs/mocha/blob/master/test/integration/hook.err.js#L340) to use a dot in addition to \u2024 makes it fail in a different way (similar to how the tests would fail if the test titles included \u2024).

I think the best way to fix this is to first, change the test to use whatever base reporter exports for the dot symbol, and second, change the windows dot to use \u00B7.   \u2024 would work, but using the default cmd font causes a beep every time it is used (which gets exceedingly annoying) - \u00B7 looks pretty close to the dot character, is supported by cmd and likely wouldn't be used in test titles.

I have fixes ready for these and will send a PR...  I'm looking for confirmation that these changes (a) are welcome, (b) wouldn't break the tests when run in a unix environment (unsure if spawn vs. exec produces different results and whether prefacing the command with `node` causes issues)

Tests are broken in Windows
Having read the contributing guidelines, I realized that on a windows machine I could not confirm whether any changes I committed failed tests or not because many of the tests are failing in windows.

Summary of issues and potential fixes: 
- I wasn't able to get the windows make to work properly at all.  using cygwin seems to works though.
- many references to '/tmp' inside acceptance fs/utils - these can be updated to use os.tmpdir()
- passing `bin/mocha` to exec fails,  "bin is not recognized as an internal or external command" -- updating it to `node bin/mocha` makes it work.
- using `spawn` in integration/helpers doesn't work at all.  Updating to use `exec`, passing args delimited by a space works. (also need to preface with `node`, similar to above)

The trickiest one relates to the dot symbol used by windows --

Integration/hook tests takes the output of test results and splits by new lines and the dot symbol.  Thing is, windows doesn't use the dot symbol (\u2024) - [it uses a full stop](https://github.com/mochajs/mocha/blob/master/lib/reporters/base.js#L88), so the tests fail because the split doesn't work, and a full stop winds up in some of the titles.  Updating the [regexp](https://github.com/mochajs/mocha/blob/master/test/integration/hook.err.js#L340) to use a dot in addition to \u2024 makes it fail in a different way (similar to how the tests would fail if the test titles included \u2024).

I think the best way to fix this is to first, change the test to use whatever base reporter exports for the dot symbol, and second, change the windows dot to use \u00B7.   \u2024 would work, but using the default cmd font causes a beep every time it is used (which gets exceedingly annoying) - \u00B7 looks pretty close to the dot character, is supported by cmd and likely wouldn't be used in test titles.

I have fixes ready for these and will send a PR...  I'm looking for confirmation that these changes (a) are welcome, (b) wouldn't break the tests when run in a unix environment (unsure if spawn vs. exec produces different results and whether prefacing the command with `node` causes issues)



Hints:
Hi @tswaters,

> I'm looking for confirmation that these changes (a) are welcome

Fantastic initiative! I must admit I wasn't even aware of the failing tests on windows, but fixing these is more than welcome!

>  (b) wouldn't break the tests when run in a unix environment (unsure if spawn vs. exec produces different results and whether prefacing the command with node causes issues)

The easiest way for you to automatically test in a unix environment would be via Travis-CI. If you create a PR for these fixes it will run automatically, but you can also enable it for your own for `tswaters/mocha` so you can experiment before creating a PR.

Currently at least one test fails when running `npm test`

``` bash
  34 passing (8s)
  1 failing

  1) options --grep runs specs matching a RegExp:

      Uncaught AssertionError: false == true
      + expected - actual

      -false
      +true

      at test/integration/options.js:117:9
      at test/integration/helpers.js:102:16
      at ChildProcess.<anonymous> (test/integration/helpers.js:169:5)
      at maybeClose (child_process.js:1015:16)
      at Socket.<anonymous> (child_process.js:1183:11)
      at Pipe.close (net.js:485:12)



make: *** [test-integration] Error 1
```

Going over your individual points:

> I wasn't able to get the windows make to work properly at all. using cygwin seems to works though.

I'm not familiar with Windows `make` at all. Is there a compatible syntax that both versions of `make` can use? Or would this entail a Windows-specific Makefile? I would like to avoid the latter, if at all possible.

> many references to '/tmp' inside acceptance fs/utils - these can be updated to use os.tmpdir()

Good catch.

> passing bin/mocha to exec fails, "bin is not recognized as an internal or external command" -- updating it to node bin/mocha makes it work.

That should be the same. As you can see `bin/mocha` stats with a [`shebang`](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) ``#!/usr/bin/env node`, which indeed means as much as 'run this file with the `node` version on the user's path'.

> using spawn in integration/helpers doesn't work at all. Updating to use exec, passing args delimited by a space works. (also need to preface with node, similar to above)

What's the issue with `spawn`? As far as I know this should be supported on Windows. The risk with using `exec` is that the max buffer size could potentially be exceeded, as we actually parse the `stdout` of the `child_process`. On the other hand, this probably won't actually be an issue as the output is relatively small.

> The trickiest one relates to the dot symbol used by windows --
> I think the best way to fix this is to first, change the test to use whatever base reporter exports for the dot symbol, 

:+1:

> and second, change the windows dot to use \u00B7.

This sounds like it's a backwards incompatible change. Doesn't mean we can't do it, but would require a release note and a bump in major versions. 

I'm not entirely sure about the problems with make -- the windows make is basically this:  http://gnuwin32.sourceforge.net/packages/make.htm -- it returns some nonsensical errors and ends up dying -- here's the output I see:

```
C:\Users\Tyler\github\mocha>npm test

> mocha@2.2.5 test C:\Users\Tyler\github\mocha
> make test-all

Access denied - LIB
File not found - -NAME
File not found - -TYPE
File not found - F
Access denied - LIB
File not found - -NAME
File not found - -TYPE
File not found - F
'node_modules' is not recognized as an internal or external command,
operable program or batch file.
make: *** [lint] Error 1
npm ERR! Test failed.  See above for more details.
```

## 

So, the problem with Windows and spawn is that it doesn't read shebangs -- like, at all -- and has a tendency to fail if you pass spawn a file that isn't a bat, cmd or exe.  Windows doesn't read the shabang, doesn't recognize the file as an executable and flops. 

exec works a little bit differently than spawn in that you can at least pass it node and a script and it works.  There's a lot of stuff I'm probably glossing over here, but if you're interested here's a node core ticket that's been open for 4.5 years that describes a lot of what is going on https://github.com/joyent/node/issues/2318

I have a feeling the failing test is a symptom of switching to use `exec`, which is what I was afraid of.   My guess would be that this one is failing because of the regexp string passed directly into `exec` ... spawn must work better with the arguments.

There is another module that can be used as a drop-in replacement for spawn called https://github.com/IndigoUnited/node-cross-spawn-async  -- this will work some magic on the commands before passing it to spawn to make it work properly in a windows environment.  

Anyway, I'll give the travisCI a go in my repo and see if I can get a clean patch that has everything working.

Ok, I got it working but I borked the squash... starting over.

So I blew away my repo and reapplied the one commit -- travis ci was buggered, still pointing at the deleted repo -- got that setup again, but I need to push something to get it to go.  I'm going to go ahead and create the PR - last I checked, I had everything working.

Is having the build/tests working on windows desirable then? I did some work on it too and can submit a pull request. 

Most changes require changing some paths to use `path.join`, using `os.tmpDir` instead of /tmp, and running scripts through `child_process.exec(node <script>,..)` instead of assuming a working shebang behavior.

I still have some issues on the integration tests on master (I think mostly caused by EOL differences), but I'll keep working on it if it's on scope.

I suppose it's not an option, but moving from the Makefile to use npm scripts might be a good idea, getting make to run on windows does take some time :).

Oh hey, I forgot about this....  

I've re-based this against master (5b325c8) and verified everything is still working (it appears to be, my travis ci passed anyways).  After running tests again after rebase under cygwin make, I noticed two that were failing -- each was doing a split based on new line and bullet.  I had added a test helper export that has a working regex so I just updated them to use that instead (1cf2f60).

if I recall the only crunchy bit on this was updating the dot to not use full stop under windows and make it \u00B7 which is a breaking change.  Now that I've had 7 months to think about it, it may be easier to instead update the titles of the tests to not include periods so the split works as expected and the tests pass.  I've done that. (06911ab)

Hi @tswaters,

> I'm looking for confirmation that these changes (a) are welcome

Fantastic initiative! I must admit I wasn't even aware of the failing tests on windows, but fixing these is more than welcome!

>  (b) wouldn't break the tests when run in a unix environment (unsure if spawn vs. exec produces different results and whether prefacing the command with node causes issues)

The easiest way for you to automatically test in a unix environment would be via Travis-CI. If you create a PR for these fixes it will run automatically, but you can also enable it for your own for `tswaters/mocha` so you can experiment before creating a PR.

Currently at least one test fails when running `npm test`

``` bash
  34 passing (8s)
  1 failing

  1) options --grep runs specs matching a RegExp:

      Uncaught AssertionError: false == true
      + expected - actual

      -false
      +true

      at test/integration/options.js:117:9
      at test/integration/helpers.js:102:16
      at ChildProcess.<anonymous> (test/integration/helpers.js:169:5)
      at maybeClose (child_process.js:1015:16)
      at Socket.<anonymous> (child_process.js:1183:11)
      at Pipe.close (net.js:485:12)



make: *** [test-integration] Error 1
```

Going over your individual points:

> I wasn't able to get the windows make to work properly at all. using cygwin seems to works though.

I'm not familiar with Windows `make` at all. Is there a compatible syntax that both versions of `make` can use? Or would this entail a Windows-specific Makefile? I would like to avoid the latter, if at all possible.

> many references to '/tmp' inside acceptance fs/utils - these can be updated to use os.tmpdir()

Good catch.

> passing bin/mocha to exec fails, "bin is not recognized as an internal or external command" -- updating it to node bin/mocha makes it work.

That should be the same. As you can see `bin/mocha` stats with a [`shebang`](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) ``#!/usr/bin/env node`, which indeed means as much as 'run this file with the `node` version on the user's path'.

> using spawn in integration/helpers doesn't work at all. Updating to use exec, passing args delimited by a space works. (also need to preface with node, similar to above)

What's the issue with `spawn`? As far as I know this should be supported on Windows. The risk with using `exec` is that the max buffer size could potentially be exceeded, as we actually parse the `stdout` of the `child_process`. On the other hand, this probably won't actually be an issue as the output is relatively small.

> The trickiest one relates to the dot symbol used by windows --
> I think the best way to fix this is to first, change the test to use whatever base reporter exports for the dot symbol, 

:+1:

> and second, change the windows dot to use \u00B7.

This sounds like it's a backwards incompatible change. Doesn't mean we can't do it, but would require a release note and a bump in major versions. 

I'm not entirely sure about the problems with make -- the windows make is basically this:  http://gnuwin32.sourceforge.net/packages/make.htm -- it returns some nonsensical errors and ends up dying -- here's the output I see:

```
C:\Users\Tyler\github\mocha>npm test

> mocha@2.2.5 test C:\Users\Tyler\github\mocha
> make test-all

Access denied - LIB
File not found - -NAME
File not found - -TYPE
File not found - F
Access denied - LIB
File not found - -NAME
File not found - -TYPE
File not found - F
'node_modules' is not recognized as an internal or external command,
operable program or batch file.
make: *** [lint] Error 1
npm ERR! Test failed.  See above for more details.
```

## 

So, the problem with Windows and spawn is that it doesn't read shebangs -- like, at all -- and has a tendency to fail if you pass spawn a file that isn't a bat, cmd or exe.  Windows doesn't read the shabang, doesn't recognize the file as an executable and flops. 

exec works a little bit differently than spawn in that you can at least pass it node and a script and it works.  There's a lot of stuff I'm probably glossing over here, but if you're interested here's a node core ticket that's been open for 4.5 years that describes a lot of what is going on https://github.com/joyent/node/issues/2318

I have a feeling the failing test is a symptom of switching to use `exec`, which is what I was afraid of.   My guess would be that this one is failing because of the regexp string passed directly into `exec` ... spawn must work better with the arguments.

There is another module that can be used as a drop-in replacement for spawn called https://github.com/IndigoUnited/node-cross-spawn-async  -- this will work some magic on the commands before passing it to spawn to make it work properly in a windows environment.  

Anyway, I'll give the travisCI a go in my repo and see if I can get a clean patch that has everything working.

Ok, I got it working but I borked the squash... starting over.

So I blew away my repo and reapplied the one commit -- travis ci was buggered, still pointing at the deleted repo -- got that setup again, but I need to push something to get it to go.  I'm going to go ahead and create the PR - last I checked, I had everything working.

Is having the build/tests working on windows desirable then? I did some work on it too and can submit a pull request. 

Most changes require changing some paths to use `path.join`, using `os.tmpDir` instead of /tmp, and running scripts through `child_process.exec(node <script>,..)` instead of assuming a working shebang behavior.

I still have some issues on the integration tests on master (I think mostly caused by EOL differences), but I'll keep working on it if it's on scope.

I suppose it's not an option, but moving from the Makefile to use npm scripts might be a good idea, getting make to run on windows does take some time :).

Oh hey, I forgot about this....  

I've re-based this against master (5b325c8) and verified everything is still working (it appears to be, my travis ci passed anyways).  After running tests again after rebase under cygwin make, I noticed two that were failing -- each was doing a split based on new line and bullet.  I had added a test helper export that has a working regex so I just updated them to use that instead (1cf2f60).

if I recall the only crunchy bit on this was updating the dot to not use full stop under windows and make it \u00B7 which is a breaking change.  Now that I've had 7 months to think about it, it may be easier to instead update the titles of the tests to not include periods so the split works as expected and the tests pass.  I've done that. (06911ab)


Patch:
diff --git a/karma.conf.js b/karma.conf.js
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -41,7 +41,7 @@ module.exports = function(config) {
   // TO RUN LOCALLY:
   // Execute `CI=1 make test-browser`, once you've set the SAUCE_USERNAME and
   // SAUCE_ACCESS_KEY env vars.
-  if (process.env.CI) {
+  if (process.env.CI && !process.env.APPVEYOR) {
     // we can't run SauceLabs tests on PRs from forks on Travis cuz security.
     if (process.env.TRAVIS) {
       if (process.env.TRAVIS_REPO_SLUG === 'mochajs/mocha'
diff --git a/mocha.js b/mocha.js
--- a/mocha.js
+++ b/mocha.js
@@ -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 */
@@ -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);
 
@@ -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();
@@ -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;
     }
@@ -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;


Test patch:
diff --git a/test/acceptance/fs.js b/test/acceptance/fs.js
--- a/test/acceptance/fs.js
+++ b/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();
     })
   })
diff --git a/test/acceptance/lookup-files.js b/test/acceptance/lookup-files.js
--- a/test/acceptance/lookup-files.js
+++ b/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) {
       }
     });
-  });
+  }
 });
diff --git a/test/color.js b/test/color.js
--- 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.execPath, command, function(err, stdout, stderr) {
       if (err) return cb(err);
 
       assert(stdout.indexOf('[90m') === -1);
diff --git a/test/integration/diffs.js b/test/integration/diffs.js
--- a/test/integration/diffs.js
+++ b/test/integration/diffs.js
@@ -15,7 +15,7 @@ function getExpectedOutput() {
 
 describe('diffs', function() {
   var diffs, expected;
-  this.timeout(1000);
+  this.timeout(2000);
 
   before(function(done) {
     run('diffs/diffs.js', ['-C'], function(err, res) {
diff --git a/test/integration/fixtures/hooks/multiple.hook.async.error.js b/test/integration/fixtures/hooks/multiple.hook.async.error.js
--- 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
--- 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/fixtures/regression/issue-1991.js b/test/integration/fixtures/regression/issue-1991.js
--- a/test/integration/fixtures/regression/issue-1991.js
+++ b/test/integration/fixtures/regression/issue-1991.js
@@ -39,7 +39,7 @@ for (var i = 0; i < numOfTests; i += 1) {
 
     it('access a variable via a closure', function () {
       // slow performance on older node.js versions
-      this.timeout(1000);
+      this.timeout(2000);
       closureVar = new MemoryLeak();
     });
 
diff --git a/test/integration/helpers.js b/test/integration/helpers.js
--- 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.execPath, args);
 
   listener = function(data) {
     output += data;
diff --git a/test/integration/hook.err.js b/test/integration/hook.err.js
--- a/test/integration/hook.err.js
+++ b/test/integration/hook.err.js
@@ -1,8 +1,9 @@
 var assert = require('assert');
 var runMocha = require('./helpers').runMocha;
+var splitRegExp = require('./helpers').splitRegExp;
 
 describe('hook error handling', function() {
-  this.timeout(1000);
+  this.timeout(2000);
 
   var lines;
 
@@ -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
--- a/test/integration/hooks.js
+++ b/test/integration/hooks.js
@@ -1,9 +1,10 @@
 var assert = require('assert');
 var run    = require('./helpers').runMocha;
+var splitRegExp = require('./helpers').splitRegExp;
 var args   = [];
 
 describe('hooks', function() {
-  this.timeout(1000);
+  this.timeout(2000);
 
   it('are ran in correct order', function(done) {
     run('cascade.js', args, function(err, res) {
@@ -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/multiple.done.js b/test/integration/multiple.done.js
--- a/test/integration/multiple.done.js
+++ b/test/integration/multiple.done.js
@@ -4,7 +4,7 @@ var args   = [];
 
 describe('multiple calls to done()', function() {
   var res;
-  this.timeout(1000);
+  this.timeout(2000);
 
   describe('from a spec', function() {
     before(function(done) {
diff --git a/test/integration/pending.js b/test/integration/pending.js
--- a/test/integration/pending.js
+++ b/test/integration/pending.js
@@ -4,7 +4,7 @@ var args   = [];
 
 describe('pending', function() {
   describe('pending specs', function() {
-    this.timeout(1000);
+    this.timeout(2000);
 
     it('should be created by omitting a function', function(done) {
       run('pending/spec.js', args, function(err, res) {
@@ -19,7 +19,7 @@ describe('pending', function() {
   });
 
   describe('synchronous skip()', function() {
-    this.timeout(1000);
+    this.timeout(2000);
 
     describe('in spec', function() {
       it('should immediately skip the spec and run all others', function(done) {
@@ -62,7 +62,7 @@ describe('pending', function() {
   });
 
   describe('asynchronous skip()', function() {
-    this.timeout(1000);
+    this.timeout(2000);
 
     describe('in spec', function() {
       it('should immediately skip the spec and run all others', function(done) {
diff --git a/test/integration/regression.js b/test/integration/regression.js
--- a/test/integration/regression.js
+++ b/test/integration/regression.js
@@ -4,7 +4,7 @@ var path   = require('path');
 var run    = require('./helpers').runMocha;
 
 describe('regressions', function() {
-  this.timeout(1000);
+  this.timeout(2000);
 
   it('issue-1327: should run all 3 specs exactly once', function(done) {
     var args = [];
diff --git a/test/integration/retries.js b/test/integration/retries.js
--- 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;
diff --git a/test/integration/timeout.js b/test/integration/timeout.js
--- a/test/integration/timeout.js
+++ b/test/integration/timeout.js
@@ -3,7 +3,7 @@ var run    = require('./helpers').runMochaJSON;
 var args   = [];
 
 describe('this.timeout()', function() {
-  this.timeout(1000);
+  this.timeout(2000);
 
   it('is respected by sync and async suites', function(done) {
     run('timeout.js', args, function(err, res) {
diff --git a/test/integration/uncaught.js b/test/integration/uncaught.js
--- a/test/integration/uncaught.js
+++ b/test/integration/uncaught.js
@@ -3,7 +3,7 @@ var run    = require('./helpers').runMochaJSON;
 var args   = [];
 
 describe('uncaught exceptions', function() {
-  this.timeout(1000);
+  this.timeout(2000);
 
   it('handles uncaught exceptions from hooks', function(done) {
     run('uncaught.hook.js', args, function(err, res) {