diff --git a/external/umdutils/verifier.js b/external/umdutils/verifier.js
index 000872a9b..e61c6afd0 100644
--- a/external/umdutils/verifier.js
+++ b/external/umdutils/verifier.js
@@ -455,13 +455,13 @@ function validateFiles(paths, options) {
   };
 
   // Finds all files.
+  var foundFiles = [];
   for (var name in paths) {
     if (!paths.hasOwnProperty(name)) {
       continue;
     }
     var path = paths[name];
     var stats = fs.statSync(path);
-    var foundFiles = [];
     if (stats.isFile()) {
       foundFiles.push({path: path, name: name});
     } else if (stats.isDirectory()) {
diff --git a/gulpfile.js b/gulpfile.js
index c68fe3c00..6796065ac 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -23,11 +23,13 @@ var gutil = require('gulp-util');
 var rimraf = require('rimraf');
 var stream = require('stream');
 var exec = require('child_process').exec;
+var spawn = require('child_process').spawn;
 var streamqueue = require('streamqueue');
 var zip = require('gulp-zip');
 
 var BUILD_DIR = 'build/';
 var L10N_DIR = 'l10n/';
+var TEST_DIR = 'test/';
 
 var makeFile = require('./make.js');
 var stripCommentHeaders = makeFile.stripCommentHeaders;
@@ -247,6 +249,62 @@ function createWebBundle(defines) {
   return source;
 }
 
+function checkFile(path) {
+  try {
+    var stat = fs.lstatSync(path);
+    return stat.isFile();
+  } catch (e) {
+    return false;
+  }
+}
+
+function createTestSource(testsName) {
+  var source = stream.Readable({ objectMode: true });
+  source._read = function () {
+    console.log();
+    console.log('### Running ' + testsName + ' tests');
+
+    var PDF_TEST = process.env['PDF_TEST'] || 'test_manifest.json';
+    var PDF_BROWSERS = process.env['PDF_BROWSERS'] ||
+      'resources/browser_manifests/browser_manifest.json';
+
+    if (!checkFile('test/' + PDF_BROWSERS)) {
+      console.log('Browser manifest file test/' + PDF_BROWSERS +
+                  ' does not exist.');
+      console.log('Copy and adjust the example in ' +
+                  'test/resources/browser_manifests.');
+      this.emit('error', new Error('Missing manifest file'));
+      return null;
+    }
+
+    var args = ['test.js'];
+    switch (testsName) {
+      case 'browser':
+        args.push('--reftest', '--manifestFile=' + PDF_TEST);
+        break;
+      case 'browser (no reftest)':
+        args.push('--manifestFile=' + PDF_TEST);
+        break;
+      case 'unit':
+        args.push('--unitTest');
+        break;
+      case 'font':
+        args.push('--fontTest');
+        break;
+      default:
+        this.emit('error', new Error('Unknown name: ' + testsName));
+        return null;
+    }
+    args.push('--browserManifestFile=' + PDF_BROWSERS);
+
+    var testProcess = spawn('node', args, {cwd: TEST_DIR, stdio: 'inherit'});
+    testProcess.on('close', function (code) {
+      source.push(null);
+    });
+  };
+  return source;
+}
+
 gulp.task('default', function() {
   console.log('Available tasks:');
   var tasks = Object.keys(gulp.tasks);
@@ -359,6 +417,83 @@ gulp.task('publish', ['generic'], function (done) {
     });
 });
 
+gulp.task('test', function () {
+  return streamqueue({ objectMode: true },
+    createTestSource('unit'), createTestSource('browser'));
+});
+
+gulp.task('bottest', function () {
+  return streamqueue({ objectMode: true },
+    createTestSource('unit'), createTestSource('font'),
+    createTestSource('browser (no reftest)'));
+});
+
+gulp.task('browsertest', function () {
+  return createTestSource('browser');
+});
+
+gulp.task('browsertest-noreftest', function () {
+  return createTestSource('browser (no reftest)');
+});
+
+gulp.task('unittest', function () {
+  return createTestSource('unit');
+});
+
+gulp.task('fonttest', function () {
+  return createTestSource('font');
+});
+
+gulp.task('botmakeref', function (done) {
+  console.log();
+  console.log('### Creating reference images');
+
+  var PDF_BROWSERS = process.env['PDF_BROWSERS'] ||
+    'resources/browser_manifests/browser_manifest.json';
+
+  if (!checkFile('test/' + PDF_BROWSERS)) {
+    console.log('Browser manifest file test/' + PDF_BROWSERS +
+      ' does not exist.');
+    console.log('Copy and adjust the example in ' +
+      'test/resources/browser_manifests.');
+    done(new Error('Missing manifest file'));
+    return;
+  }
+
+  var args = ['test.js', '--masterMode', '--noPrompts',
+              '--browserManifestFile=' + PDF_BROWSERS];
+  var testProcess = spawn('node', args, {cwd: TEST_DIR, stdio: 'inherit'});
+  testProcess.on('close', function (code) {
+    done();
+  });
+});
+
+gulp.task('lint', function (done) {
+  console.log();
+  console.log('### Linting JS files');
+
+  // Lint the Firefox specific *.jsm files.
+  var options = ['node_modules/jshint/bin/jshint', '--extra-ext', '.jsm', '.'];
+  var jshintProcess = spawn('node', options, {stdio: 'inherit'});
+  jshintProcess.on('close', function (code) {
+    if (code !== 0) {
+      done(new Error('jshint failed.'));
+      return;
+    }
+
+    console.log();
+    console.log('### Checking UMD dependencies');
+    var umd = require('./external/umdutils/verifier.js');
+    if (!umd.validateFiles({'pdfjs': './src', 'pdfjs-web': './web'})) {
+      done(new Error('UMD check failed.'));
+      return;
+    }
+
+    console.log('files checked, no errors found');
+    done();
+  });
+});
+
 gulp.task('server', function (done) {
   console.log();
   console.log('### Starting local server');
diff --git a/make.js b/make.js
index 6ed360457..45cc6aee9 100644
--- a/make.js
+++ b/make.js
@@ -1080,9 +1080,7 @@ target.chromium = function() {
 // make test
 //
 target.test = function() {
-  target.unittest({}, function() {
-    target.browsertest();
-  });
+  exec('gulp test');
 };
 
 //
@@ -1090,103 +1088,39 @@ target.test = function() {
 // (Special tests for the Github bot)
 //
 target.bottest = function() {
-  target.unittest({}, function() {
-    target.fonttest({}, function() {
-      target.browsertest({noreftest: true});
-    });
-  });
+  exec('gulp bottest');
 };
 
 //
 // make browsertest
 //
 target.browsertest = function(options) {
-  cd(ROOT_DIR);
-  echo();
-  echo('### Running browser tests');
-
-  var PDF_TEST = env['PDF_TEST'] || 'test_manifest.json',
-      PDF_BROWSERS = env['PDF_BROWSERS'] ||
-                     'resources/browser_manifests/browser_manifest.json';
-
-  if (!test('-f', 'test/' + PDF_BROWSERS)) {
-    echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.');
-    echo('Copy and adjust the example in test/resources/browser_manifests.');
-    exit(1);
+  if (options && options.noreftest) {
+    exec('gulp browsertest-noreftest');
+  } else {
+    exec('gulp browsertest');
   }
-
-  var reftest = (options && options.noreftest) ? '' : '--reftest';
-
-  cd('test');
-  exec('node test.js ' + reftest + ' --browserManifestFile=' +
-       PDF_BROWSERS + ' --manifestFile=' + PDF_TEST, {async: true});
 };
 
 //
 // make unittest
 //
 target.unittest = function(options, callback) {
-  cd(ROOT_DIR);
-  echo();
-  echo('### Running unit tests');
-
-  var PDF_TEST = env['PDF_TEST'] || 'test_manifest.json';
-  var PDF_BROWSERS = env['PDF_BROWSERS'] ||
-                     'resources/browser_manifests/browser_manifest.json';
-
-  if (!test('-f', 'test/' + PDF_BROWSERS)) {
-    echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.');
-    echo('Copy and adjust the example in test/resources/browser_manifests.');
-    exit(1);
-  }
-  callback = callback || function() {};
-  cd('test');
-  exec('node test.js --unitTest --browserManifestFile=' +
-       PDF_BROWSERS + ' --manifestFile=' + PDF_TEST, {async: true}, callback);
+  exec('gulp unittest');
 };
 
 //
 // make fonttest
 //
 target.fonttest = function(options, callback) {
-  cd(ROOT_DIR);
-  echo();
-  echo('### Running font tests');
-
-  var PDF_BROWSERS = env['PDF_BROWSERS'] ||
-                     'resources/browser_manifests/browser_manifest.json';
-
-  if (!test('-f', 'test/' + PDF_BROWSERS)) {
-    echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.');
-    echo('Copy and adjust the example in test/resources/browser_manifests.');
-    exit(1);
-  }
-  callback = callback || function() {};
-  cd('test');
-  exec('node test.js --fontTest --browserManifestFile=' +
-       PDF_BROWSERS, {async: true}, callback);
+  exec('gulp fonttest');
 };
 
 //
 // make botmakeref
 //
 target.botmakeref = function() {
-  cd(ROOT_DIR);
-  echo();
-  echo('### Creating reference images');
-
-  var PDF_BROWSERS = env['PDF_BROWSERS'] ||
-                     'resources/browser_manifests/browser_manifest.json';
-
-  if (!test('-f', 'test/' + PDF_BROWSERS)) {
-    echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.');
-    echo('Copy and adjust the example in test/resources/browser_manifests.');
-    exit(1);
-  }
-
-  cd('test');
-  exec('node test.js --masterMode --noPrompts ' +
-       '--browserManifestFile=' + PDF_BROWSERS, {async: true});
+  exec('gulp botmakeref');
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1367,27 +1301,7 @@ target.server = function () {
 // make lint
 //
 target.lint = function() {
-  cd(ROOT_DIR);
-  echo();
-  echo('### Linting JS files');
-
-  var jshintPath = path.normalize('./node_modules/.bin/jshint');
-  // Lint the Firefox specific *.jsm files.
-  var options = '--extra-ext .jsm';
-
-  var exitCode = exec('"' + jshintPath + '" ' + options + ' .').code;
-  if (exitCode !== 0) {
-    exit(1);
-  }
-
-  echo();
-  echo('### Checking UMD dependencies');
-  var umd = require('./external/umdutils/verifier.js');
-  if (!umd.validateFiles({'pdfjs': './src', 'pdfjs-web': './web'})) {
-    exit(1);
-  }
-
-  echo('files checked, no errors found');
+  exit(exec('gulp lint'));
 };
 
 //