process.env.BLUEBIRD_WARNINGS = 0; var assert = require("assert"); assert.equal(require.main, module); var Promise = require("bluebird"); var build = require("./build.js"); var utils = require("./utils.js"); var tableLogger = utils.tableLogger; var argv = require("optimist").argv; var glob = Promise.promisify(require("glob")); var path = require("path"); var mkdirp = Promise.promisify(require("mkdirp")); var rimraf = Promise.promisify(require("rimraf")); var jobRunner = require("./job-runner/job-runner.js"); var mochaRunner = require("./mocha_runner.js"); var fs = Promise.promisifyAll(require("fs")); var testUtils = require("../test/mocha/helpers/util"); jobRunner.setVerbose(0); // Random slowness after tests complete function getTests(options) { var g; if (options.testName === "all") { g = "./test/mocha/*.js"; } else if (options.testName === "aplus") { g = "./test/mocha/[0-9].[0-9].[0-9].js"; } else if (options.testName.indexOf("*") >= 0) { g = "./test/mocha/" + options.testName; } else { var testName = options.testName.replace(/^(\d)(\d)(\d)$/, "$1.$2.$3"); g = "./test/mocha/" + testName + ".js"; } return glob(g).then(function(matches) { return matches.filter(function(match) { if (match.indexOf("generator") >= 0) { return options.generators; } return true; }) }).tap(function(m) { if (m.length === 0) { throw new Error("No test file matches: '" + options.testName + "'"); } }).map(function(filePath, i) { var name = path.basename(filePath); return { name: name, path: filePath, index: i, nameMatcher: "\\b" + name.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + "\\b" }; }); } function getColorForCoverage(coveragePct) { var colorThresholds = { 95: "brightgreen", 85: "green", 80: "yellowgreen", 70: "yellow", 60: "red" }; var values = Object.keys(colorThresholds).map(Number).sort(function(a, b) { return b - a; }); for (var i = 0; i < values.length; ++i) { if (coveragePct >= values[i]) return colorThresholds[values[i].toString()]; } return colorThresholds[values[values.length - 1].toString()]; } function getCoverage() { return utils.run("npm", ["run", "istanbul", "--", "report", "text-summary"]).then(function(result) { var stdout = result.stdout; var pctPattern = /(\d+\.\d+)%/g; var matches = stdout.match(pctPattern); var sum = matches.map(function(pct) { return parseFloat(pct.replace(/[^0-9.]/g, "")) }).reduce(function(a, b) { return a + b; }, 0); var average = Math.round(sum / matches.length); return average; }); } function generateCoverageBadge(coverage) { var text = "coverage-" + coverage + "%"; var color = getColorForCoverage(coverage); var imgSrc = "http://img.shields.io/badge/" + text + "-" + color + ".svg?style=flat"; var link = "http://petkaantonov.github.io/bluebird/coverage/debug/index.html"; var markdown = "[!["+text+"]("+imgSrc+")]("+link+")"; return markdown; } function writeCoverageFile(coverage, groupNumber) { var dir = build.dirs.coverage; var fileName = path.join(dir, "coverage-group" + groupNumber + ".json"); var json = JSON.stringify(coverage); return fs.writeFileAsync(fileName, json, "utf8").thenReturn(fileName); } function needsFreshProcess(testName) { return /regress|using|domain|multiple-copies|unhandled_rejections|nodeify/.test(testName) || testUtils.isOldNode && /api_exceptions|promisify/.test(testName); } function runTestGroup(testGroup, options, progress) { return jobRunner.run(mochaRunner, { isolated: !options.singleTest, log: options.singleTest, progress: progress, context: { testGroup: testGroup, singleTest: options.singleTest, fakeTimers: options.fakeTimers, cover: options.cover } }); } function combineTests(tests) { var arrays = new Array(jobRunner.CHILD_PROCESSES); for (var i = 0; i < arrays.length; ++i) { arrays[i] = []; } var initialLength = arrays.length; for (var i = 0; i < tests.length; ++i) { var test = tests[i]; if (needsFreshProcess(test.name)) { arrays.push([test]); } else { arrays[i % initialLength].push(tests[i]); } } return arrays; } var testName = "all"; if ("run" in argv) { testName = (argv.run + ""); if (testName.indexOf("*") === -1) { testName = testName.toLowerCase() .replace( /\.js$/, "" ) .replace( /[^a-zA-Z0-9_\-.]/g, "" ); } } var options = { generators: (function() { try { new Function("(function*(){})"); return true; } catch (e) { return false; } })() || !!argv.nw, cover: !!argv["cover"], testName: testName, singleTest: false, saucelabs: !!argv.saucelabs, testBrowser: !!argv.saucelabs || !!argv.browser || !!argv.nw, executeBrowserTests: !!argv.saucelabs || !!argv.nw || (typeof argv["execute-browser-tests"] === "boolean" ? argv["execute-browser-tests"] : !!argv.browser), openBrowser: typeof argv["open-browser"] === "boolean" ? argv["open-browser"] : true, port: argv.port || 9999, fakeTimers: typeof argv["fake-timers"] === "boolean" ? argv["fake-timers"] : true, jsHint: typeof argv["js-hint"] === "boolean" ? argv["js-hint"] : true, nw: !!argv.nw, nwPath: argv["nw-path"] }; if (options.cover && typeof argv["cover"] === "string") { options.coverFormat = argv["cover"]; } else { options.coverFormat = "html"; } var jsHint = options.jsHint ? require("./jshint.js")() : Promise.resolve(); var tests = getTests(options); var buildOpts = { debug: true }; if (options.testBrowser) { buildOpts.browser = true; buildOpts.minify = true; } var buildResult = build(buildOpts); if (options.cover) { var exclusions = ["assert.js", "captured_trace.js"]; var coverageInstrumentedRoot = build.ensureDirectory(build.dirs.instrumented,options.cover, true); var coverageReportsRoot = mkdirp(build.dirs.coverage, true).then(function() { return fs.readdirAsync(build.dirs.coverage); }).map(function(fileName) { var filePath = path.join(build.dirs.coverage, fileName); if (path.extname(fileName).indexOf("json") === -1) { return rimraf(filePath); } }); buildResult = Promise.join(coverageInstrumentedRoot, buildResult, coverageReportsRoot, function() { return utils.run("npm", ["-v"]).then(function(result) { var version = result.stdout.split(".").map(Number); if (version[0] < 2) { throw new Error("Npm version 2.x.x required, current version is " + result.stdout); } }); }).tap(function() { var copyExclusions = Promise.map(exclusions, function(exclusion) { var fromPath = path.join(build.dirs.debug, exclusion); var toPath = path.join(build.dirs.instrumented, exclusion); return fs.readFileAsync(fromPath, "utf8").then(function(contents) { return fs.writeFileAsync(toPath, contents, "utf8"); }); }); var args = [ "run", "istanbul", "--", "instrument", "--output", build.dirs.instrumented, "--no-compact", "--preserve-comments", "--embed-source" ]; exclusions.forEach(function(x) { args.push("-x", x); }); args.push(build.dirs.debug); var istanbul = utils.run("npm", args, null, true); return Promise.all([istanbul, copyExclusions]); }); } var testResults = Promise.join(tests, buildResult, function(tests) { var singleTest = tests.length === 1; options.singleTest = singleTest; process.stdout.write("\u001b[m"); if (options.testBrowser) { return require("./browser_test_generator.js")(tests, options); } else if (singleTest) { return runTestGroup(tests, options); } else { utils.cursorTo(0, 0); utils.clearScreenDown(); tableLogger.addTests(tests); return Promise.map(combineTests(tests), function(testGroup, index) { return runTestGroup(testGroup, options, function(test) { if (test.failed) { tableLogger.testFail(test); } else { tableLogger.testSuccess(test); } }).then(function(maybeCoverage) { if (options.cover) { return writeCoverageFile(maybeCoverage.result, index + 1); } }) }).then(function() { var p = Promise.resolve(); if (options.cover) { var coverage = getCoverage(); if (process.execPath.indexOf("iojs") >= 0 && testName === "all") { p = p.return(coverage).then(generateCoverageBadge).then(console.log); } p = p.then(function() { return utils.run("npm", ["run", "istanbul", "--", "report", options.coverFormat], null, true); }).return(coverage).then(function(coverage) { console.log("Total coverage " + coverage + "%"); }); } console.log("All tests passed"); return p; }); } }); Promise.all([testResults, jsHint]).spread(function(_, jsHintResponse) { if (jsHintResponse) { console.log("JSHint:"); console.log(jsHintResponse.stdout); console.log(jsHintResponse.stderr); } }).catch(function(e) { if (e && e.stdout || e.stderr) { console.log(e.stdout); console.error(e.stderr); } if (!e || !e.stack) { console.error(e + ""); } else { console.error(e.noStackPrint ? e.message : e.stack); } process.exit(2); });