diff --git a/.travis.yml b/.travis.yml index 07349bf2..f0e44373 100755 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ addons: before_install: 'if [[ `npm -v` != 4* ]]; then npm i -g npm@4; fi' install: - npm install -script: 'rm -rf node_modules && npm cache clean && npm install istanbul --silent && npm link && npm link suman && suman test/src/dev/node/injection.test.js' +script: 'rm -rf node_modules && npm cache clean && npm install istanbul --silent > /dev/null 2>&1 && npm link -f --silent > /dev/null 2>&1 && npm link suman -f --silent > /dev/null 2>&1 && suman test/src/dev/node/injection.test.js' after_script: 'npm install -g coveralls && cat coverage/lcov.info | coveralls' notifications: email: false diff --git a/config/suman-constants.d.ts b/config/suman-constants.d.ts index 9f40749f..637f26d1 100755 --- a/config/suman-constants.d.ts +++ b/config/suman-constants.d.ts @@ -13,6 +13,7 @@ export declare const constants: Readonly<{ }; SUMAN_SERVER_MESSAGE: string; GIT_IGNORE: string[]; + UNKNOWN_INJECT_HOOK_NAME: string; SUMAN_HOOK_FATAL_WARNING_MESSAGE: string; SUMAN_HOOK_FATAL_MESSAGE: string; SUMAN_HARD_LIST: string[]; diff --git a/config/suman-constants.js b/config/suman-constants.js index 996c1318..80233deb 100755 --- a/config/suman-constants.js +++ b/config/suman-constants.js @@ -18,6 +18,7 @@ exports.constants = Object.freeze({ GIT_IGNORE: [ '**suman/logs/**' ], + UNKNOWN_INJECT_HOOK_NAME: '(unknown inject-hook name)', SUMAN_HOOK_FATAL_WARNING_MESSAGE: 'Suman non-fatal error => Error in hook and "fatal" option for the hook is set to false =>\n', SUMAN_HOOK_FATAL_MESSAGE: ' => fatal error in hook => (to continue even in the event of an error in a hook, use option {fatal:false}) =>\n', SUMAN_HARD_LIST: Object.keys({ diff --git a/config/suman-constants.ts b/config/suman-constants.ts index 524c30b1..3303d0ad 100755 --- a/config/suman-constants.ts +++ b/config/suman-constants.ts @@ -27,6 +27,8 @@ export const constants = Object.freeze({ '**suman/logs/**' ], + UNKNOWN_INJECT_HOOK_NAME: '(unknown inject-hook name)', + SUMAN_HOOK_FATAL_WARNING_MESSAGE: 'Suman non-fatal error => Error in hook and "fatal" option for the hook is set to false =>\n', diff --git a/lib/cli-commands/init-opt/index.js b/lib/cli-commands/init-opt/index.js index 334b63fc..630ec9f3 100644 --- a/lib/cli-commands/init-opt/index.js +++ b/lib/cli-commands/init-opt/index.js @@ -17,7 +17,6 @@ var writeSumanFiles = require('./install-suman-files').writeSumanFiles; var determineIfReadlinkAvail = require('./determine-if-readlink-avail').determineIfReadlinkAvail; var makeAppendToBashProfile = require('./append-to-bash-profile').makeAppendToBashProfile; var constants = require('../../../config/suman-constants').constants; -var su = require('suman-utils'); var helpers = require('./init-helpers'); var debug = require('suman-debug')('s:init'); var logPermissonsAdvice = helpers.logPermissonsAdvice; @@ -124,8 +123,8 @@ exports.run = function (opts, projectRoot, cwd) { if (sumanHelperDirFound) { appendToSumanHelpersDir = '-' + timestamp; } - var newSumanHelperDir = '/suman' + appendToSumanHelpersDir; - var newSumanHelperDirAbsPath = path.resolve(projectRoot + '/suman' + appendToSumanHelpersDir); + var newSumanHelperDir = '/.suman' + appendToSumanHelpersDir; + var newSumanHelperDirAbsPath = path.resolve(projectRoot + '/.suman' + appendToSumanHelpersDir); async.series([ function installFiles(cb) { async.parallel([ diff --git a/lib/cli-commands/init-opt/index.ts b/lib/cli-commands/init-opt/index.ts index 46490204..1f953804 100755 --- a/lib/cli-commands/init-opt/index.ts +++ b/lib/cli-commands/init-opt/index.ts @@ -16,7 +16,7 @@ const async = require('async'); const flattenDeep = require('lodash.flattendeep'); import * as chalk from 'chalk'; import {IGlobalSumanObj, ISumanOpts} from "suman-types/dts/global"; - +import su = require('suman-utils'); const chmodr = require('chmodr'); const semver = require('semver'); @@ -28,7 +28,6 @@ const {writeSumanFiles} = require('./install-suman-files'); const {determineIfReadlinkAvail} = require('./determine-if-readlink-avail'); const {makeAppendToBashProfile} = require('./append-to-bash-profile'); const {constants} = require('../../../config/suman-constants'); -const su = require('suman-utils'); const helpers = require('./init-helpers'); const debug = require('suman-debug')('s:init'); @@ -167,8 +166,8 @@ export const run = (opts: ISumanOpts, projectRoot: string, cwd: string) => { appendToSumanHelpersDir = '-' + timestamp; } - const newSumanHelperDir = '/suman' + appendToSumanHelpersDir; - const newSumanHelperDirAbsPath = path.resolve(projectRoot + '/suman' + appendToSumanHelpersDir); + const newSumanHelperDir = '/.suman' + appendToSumanHelpersDir; + const newSumanHelperDirAbsPath = path.resolve(projectRoot + '/.suman' + appendToSumanHelpersDir); async.series([ diff --git a/lib/cli-commands/init-opt/install-suman-files.ts b/lib/cli-commands/init-opt/install-suman-files.ts index 4cc56924..4e7743bf 100755 --- a/lib/cli-commands/init-opt/install-suman-files.ts +++ b/lib/cli-commands/init-opt/install-suman-files.ts @@ -33,117 +33,115 @@ interface ICopyableItem { ////////////////////////////////////////////////////////////////////////////// -export const writeSumanFiles = - - function (newSumanHelperDirAbsPath: string, prependToSumanConf: string, - newSumanHelperDir: string, projectRoot: string) { - - return function installSumanFiles(cb: Function) { - - async.autoInject({ - - createSumanDir: function (cb: Function) { - //if dir exists an error will be thrown - fs.mkdir(newSumanHelperDirAbsPath, 0o777, cb); - }, - - copyDefaultFiles: function (createSumanDir: any, cb: Function) { - async.each([ - { - src: 'default-conf-files/suman.default.conf.js', - dest: prependToSumanConf + 'suman.conf.js' - }, - { - src: 'default-conf-files/suman.default.ioc.static.js', - dest: newSumanHelperDir + '/suman.ioc.static.js' - }, - { - src: 'default-conf-files/suman.default.ioc.js', - dest: newSumanHelperDir + '/suman.ioc.js' - }, - { - //TODO: suman.order.js should be suman.constaints.js ? - src: 'default-conf-files/suman.default.order.js', - dest: newSumanHelperDir + '/suman.order.js' - }, - { - src: 'default-conf-files/suman.default.once.pre.js', - dest: newSumanHelperDir + '/suman.once.pre.js' - }, - { - src: 'default-conf-files/suman.default.once.post.js', - dest: newSumanHelperDir + '/suman.once.post.js' - }, - { - src: 'default-conf-files/suman.default.globals.js', - dest: newSumanHelperDir + '/suman.globals.js' - }, - { - src: 'default-conf-files/suman.default.hooks.js', - dest: newSumanHelperDir + '/suman.hooks.js' - }, - { - src: 'default-conf-files/suman.default.readme', - dest: newSumanHelperDir + '/README.md' - } +export const writeSumanFiles = function (newSumanHelperDirAbsPath: string, prependToSumanConf: string, + newSumanHelperDir: string, projectRoot: string) { - ], function (item: ICopyableItem, cb: Function) { + return function installSumanFiles(cb: Function) { - fs.createReadStream(path.resolve(__dirname + '/../../' + item.src)) - .pipe(fs.createWriteStream(path.resolve(projectRoot + '/' + item.dest))) - .once('error', cb).once('finish', cb); + async.autoInject({ - }, cb); - }, + createSumanDir: function (cb: Function) { + //if dir exists an error will be thrown + fs.mkdir(newSumanHelperDirAbsPath, 0o777, cb); + }, - appendToGitignore: function (cb: Function) { + copyDefaultFiles: function (createSumanDir: any, cb: Function) { + async.each([ + { + src: 'default-conf-files/suman.default.conf.js', + dest: prependToSumanConf + 'suman.conf.js' + }, + { + src: 'default-conf-files/suman.default.ioc.static.js', + dest: newSumanHelperDir + '/suman.ioc.static.js' + }, + { + src: 'default-conf-files/suman.default.ioc.js', + dest: newSumanHelperDir + '/suman.ioc.js' + }, + { + //TODO: suman.order.js should be suman.constaints.js ? + src: 'default-conf-files/suman.default.order.js', + dest: newSumanHelperDir + '/suman.order.js' + }, + { + src: 'default-conf-files/suman.default.once.pre.js', + dest: newSumanHelperDir + '/suman.once.pre.js' + }, + { + src: 'default-conf-files/suman.default.once.post.js', + dest: newSumanHelperDir + '/suman.once.post.js' + }, + { + src: 'default-conf-files/suman.default.globals.js', + dest: newSumanHelperDir + '/suman.globals.js' + }, + { + src: 'default-conf-files/suman.default.hooks.js', + dest: newSumanHelperDir + '/suman.hooks.js' + }, + { + src: 'default-conf-files/suman.default.readme', + dest: newSumanHelperDir + '/README.md' + } + + ], function (item: ICopyableItem, cb: Function) { + + fs.createReadStream(path.resolve(__dirname + '/../../' + item.src)) + .pipe(fs.createWriteStream(path.resolve(projectRoot + '/' + item.dest))) + .once('error', cb).once('finish', cb); + + }, cb); + }, - const gitignore = path.resolve(projectRoot + '/.gitignore'); - fs.readFile(gitignore, function (err: Error, data: Buffer) { - if (err && !String(err.stack || err).match(/ENOENT/i)) { - return cb(err); - } + appendToGitignore: function (cb: Function) { - const filtered = constants.GIT_IGNORE.filter(function (item: string) { - return String(data).indexOf(item) < 0; - }); + const gitignore = path.resolve(projectRoot + '/.gitignore'); + fs.readFile(gitignore, function (err: Error, data: Buffer) { + if (err && !String(err.stack || err).match(/ENOENT/i)) { + return cb(err); + } - const appendable = filtered.join('\n') + '\n'; - fs.appendFile(gitignore, '\n' + String(appendable), cb); + const filtered = constants.GIT_IGNORE.filter(function (item: string) { + return String(data).indexOf(item) < 0; }); - }, - createLogsDir: function (createSumanDir: any, cb: Function) { - fs.mkdir(path.resolve(newSumanHelperDirAbsPath + '/logs'), 0o777, function (err: Error) { - if (err) { - if (!String(err).match(/EEXIST/)) { - return cb(err); - } - } - //we also just overwrite stdio logs - const msg1 = 'Readme file here primarily for version control stability\n'; - const msg2 = 'Suman recommends that you tail the files in this directory when you\'re developing tests => most useful thing to do is to tail the runner-debug.log when running tests with the Suman runner,' + - 'this is because accessing the individual test errors is less transparent due to the nature of child-processes/subprocesses)'; - const msg3 = msg1 + '\n' + msg2; - - async.forEachOf([ - 'README.md', - 'watcher-output.log', - 'test-debug.log', - 'server.log', - 'runner-debug.log' - ], function (item: string, index: number, cb: Function) { - let p = path.resolve(newSumanHelperDirAbsPath + '/logs/' + item); - fs.writeFile(p, index === 0 ? msg3 : msg2, cb); - }, cb); - }); - }, + const appendable = filtered.join('\n') + '\n'; + fs.appendFile(gitignore, '\n' + String(appendable), cb); + }); - chownDirs: function (createLogsDir: any, createSumanDir: any, cb: Function) { - chmodr(newSumanHelperDirAbsPath, 0o777, cb); - } }, - cb); - } + createLogsDir: function (createSumanDir: any, cb: Function) { + fs.mkdir(path.resolve(newSumanHelperDirAbsPath + '/logs'), 0o777, function (err: Error) { + if (err) { + if (!String(err).match(/EEXIST/)) { + return cb(err); + } + } + //we also just overwrite stdio logs + const msg1 = 'Readme file here primarily for version control stability\n'; + const msg2 = 'Suman recommends that you tail the files in this directory when you\'re developing tests => most useful thing to do is to tail the runner-debug.log when running tests with the Suman runner,' + + 'this is because accessing the individual test errors is less transparent due to the nature of child-processes/subprocesses)'; + const msg3 = msg1 + '\n' + msg2; + + async.forEachOf([ + 'README.md', + 'watcher-output.log', + 'test-debug.log', + 'server.log', + 'runner-debug.log' + ], function (item: string, index: number, cb: Function) { + let p = path.resolve(newSumanHelperDirAbsPath + '/logs/' + item); + fs.writeFile(p, index === 0 ? msg3 : msg2, cb); + }, cb); + }); + }, + + chownDirs: function (createLogsDir: any, createSumanDir: any, cb: Function) { + chmodr(newSumanHelperDirAbsPath, 0o777, cb); + } + }, + cb); + } - }; +}; diff --git a/lib/default-conf-files/suman.default.conf.js b/lib/default-conf-files/suman.default.conf.js index d671e936..31ed2e53 100755 --- a/lib/default-conf-files/suman.default.conf.js +++ b/lib/default-conf-files/suman.default.conf.js @@ -15,7 +15,7 @@ module.exports = { //string testDir: 'test', testSrcDir: 'test', - sumanHelpersDir: 'suman', + sumanHelpersDir: '.suman', uniqueAppName: '', browser: 'Firefox', // browser to open test results with logsDir: process.env['SUMAN_LOGS_DIR'], diff --git a/lib/index.js b/lib/index.js index 1ae74eb6..397a4d68 100755 --- a/lib/index.js +++ b/lib/index.js @@ -114,9 +114,7 @@ _suman.writeTestError = function (data, ignore) { fs.appendFileSync(testDebugLogPath, data); } }; - var initMap = new Map(); - exports.init = function ($module, $opts, confOverride) { debugger; if (this instanceof exports.init) { diff --git a/lib/injection/make-block-injector.js b/lib/injection/make-block-injector.js index aed755c7..de6a64f4 100755 --- a/lib/injection/make-block-injector.js +++ b/lib/injection/make-block-injector.js @@ -41,7 +41,6 @@ exports.makeBlockInjector = function (suman, container) { case 'getresumevalue': case 'getresumeval': case 'writable': - case 'inject': return suite[key]; case 'describe': case 'context': @@ -49,6 +48,7 @@ exports.makeBlockInjector = function (suman, container) { case 'afterallparenthooks': case 'before': case 'after': + case 'inject': case 'beforeall': case 'afterall': case 'beforeeach': diff --git a/lib/injection/make-block-injector.ts b/lib/injection/make-block-injector.ts index 84e8cc4e..525d7a6f 100755 --- a/lib/injection/make-block-injector.ts +++ b/lib/injection/make-block-injector.ts @@ -89,7 +89,6 @@ export const makeBlockInjector = function (suman: ISuman, container: Object) { case 'getresumevalue': case 'getresumeval': case 'writable': - case 'inject': return suite[key]; case 'describe': @@ -98,6 +97,7 @@ export const makeBlockInjector = function (suman: ISuman, container: Object) { case 'afterallparenthooks': case 'before': case 'after': + case 'inject': case 'beforeall': case 'afterall': case 'beforeeach': diff --git a/lib/runner-helpers/containerize/.gitkeep b/lib/runner-helpers/containerize/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/lib/runner-helpers/handle-multiple-processes.d.ts b/lib/runner-helpers/handle-multiple-processes.d.ts index 500a0e4f..f20d62f4 100755 --- a/lib/runner-helpers/handle-multiple-processes.d.ts +++ b/lib/runner-helpers/handle-multiple-processes.d.ts @@ -3,4 +3,4 @@ export interface ISumanCPMessages { code: number; signal: any; } -export declare const makeHandleMultipleProcesses: (runnerObj: IRunnerObj, tableRows: ITableRows, messages: ISumanCPMessages[], forkedCPs: ISumanChildProcess[], handleMessage: Function, beforeExitRunOncePost: Function, makeExit: Function) => Function; +export declare const makeHandleMultipleProcesses: (runnerObj: IRunnerObj, tableRows: ITableRows, messages: ISumanCPMessages[], forkedCPs: ISumanChildProcess[], beforeExitRunOncePost: Function, makeExit: Function) => Function; diff --git a/lib/runner-helpers/handle-multiple-processes.js b/lib/runner-helpers/handle-multiple-processes.js index cc9b45ca..f907cd3b 100755 --- a/lib/runner-helpers/handle-multiple-processes.js +++ b/lib/runner-helpers/handle-multiple-processes.js @@ -2,63 +2,42 @@ Object.defineProperty(exports, "__esModule", { value: true }); var process = require('suman-browser-polyfills/modules/process'); var global = require('suman-browser-polyfills/modules/global'); -var cp = require("child_process"); -var fs = require("fs"); var path = require("path"); -var util = require("util"); var EE = require("events"); -var semver = require("semver"); -var merge = require('lodash.merge'); var shuffle = require('lodash.shuffle'); var suman_events_1 = require("suman-events"); var su = require("suman-utils"); -var async = require("async"); var noFilesFoundError = require('../helpers/no-files-found-error'); var chalk = require("chalk"); var _suman = global.__suman = (global.__suman || {}); var resultBroadcaster = _suman.resultBroadcaster = (_suman.resultBroadcaster || new EE()); -var runnerUtils = require('./runner-utils'); var socket_cp_hash_1 = require("./socket-cp-hash"); -var getTapParser = require('./handle-tap').getTapParser; -var getTapJSONParser = require('./handle-tap-json').getTapJSONParser; var constants = require('../../config/suman-constants').constants; -var debug = require('suman-debug')('s:runner'); +var transpile_queue_1 = require("./multi-process/transpile-queue"); +var add_to_transpile_queue_1 = require("./multi-process/add-to-transpile-queue"); var multiple_process_each_on_exit_1 = require("./multiple-process-each-on-exit"); -var prepend_transform_1 = require("prepend-transform"); -var runChildPath = require.resolve(__dirname + '/run-child.js'); -var uuidV4 = require("uuid/v4"); -exports.makeHandleMultipleProcesses = function (runnerObj, tableRows, messages, forkedCPs, handleMessage, beforeExitRunOncePost, makeExit) { +var add_to_run_queue_1 = require("./multi-process/add-to-run-queue"); +var run_queue_1 = require("./multi-process/run-queue"); +exports.makeHandleMultipleProcesses = function (runnerObj, tableRows, messages, forkedCPs, beforeExitRunOncePost, makeExit) { return function (runObj) { + var sumanOpts = _suman.sumanOpts, sumanConfig = _suman.sumanConfig, projectRoot = _suman.projectRoot; + _suman.startDateMillis = Date.now(); process.stderr.setMaxListeners(runObj.files.length + 11); process.stdout.setMaxListeners(runObj.files.length + 11); var logsDir = _suman.sumanConfig.logsDir || _suman.sumanHelperDirRoot + '/logs'; var sumanCPLogs = path.resolve(logsDir + '/runs/'); var f = path.resolve(sumanCPLogs + '/' + _suman.timestamp + '-' + _suman.runId); - _suman.startDateMillis = Date.now(); - var sumanOpts = _suman.sumanOpts, sumanConfig = _suman.sumanConfig, maxProcs = _suman.maxProcs, projectRoot = _suman.projectRoot; - var waitForAllTranformsToFinish = sumanOpts.wait_for_all_transforms; - _suman.log('waitForAllTranformsToFinish => ', chalk.magenta(waitForAllTranformsToFinish)); var args = ['--user-args', sumanOpts.user_args]; + var runQueue = run_queue_1.makeRunQueue(); + var onExitFn = multiple_process_each_on_exit_1.makeOnExitFn(runnerObj, tableRows, messages, forkedCPs, beforeExitRunOncePost, makeExit, runQueue); + var runFile = add_to_run_queue_1.makeAddToRunQueue(runnerObj, args, runQueue, projectRoot, socket_cp_hash_1.cpHash, forkedCPs, onExitFn); + var waitForAllTranformsToFinish = sumanOpts.wait_for_all_transforms; + if (waitForAllTranformsToFinish) { + _suman.log('waitForAllTranformsToFinish => ', chalk.magenta(waitForAllTranformsToFinish)); + } var queuedTestFns = []; var failedTransformObjects = []; - var transpileQueue = async.queue(function (task, cb) { - task(function (err, file, shortFile, stdout, stderr, gd) { - setImmediate(cb); - if (err) { - _suman.logError('tranpile error => ', err.stack || err); - failedTransformObjects.push({ err: err, file: file, shortFile: shortFile, stdout: stdout, stderr: stderr }); - return; - } - if (waitForAllTranformsToFinish) { - queuedTestFns.push(function () { - outer(file, shortFile, stdout, gd); - }); - } - else { - outer(file, shortFile, stdout, gd); - } - }); - }, 3); + var transpileQueue = transpile_queue_1.makeTranspileQueue(failedTransformObjects, runFile, queuedTestFns); if (waitForAllTranformsToFinish) { transpileQueue.drain = function () { _suman.log('all transforms complete, beginning to run first set of tests.'); @@ -73,10 +52,6 @@ exports.makeHandleMultipleProcesses = function (runnerObj, tableRows, messages, 'and will not be listening for websocket messages.')); } } - var handleBlocking = runnerObj.handleBlocking; - if (_suman.usingLiveSumanServer) { - args.push('--live_suman_server'); - } var files = runObj.files; resultBroadcaster.emit(String(suman_events_1.events.RUNNER_STARTED), files.length); if (_suman.sumanOpts.rand) { @@ -84,355 +59,6 @@ exports.makeHandleMultipleProcesses = function (runnerObj, tableRows, messages, } runnerObj.startTime = Date.now(); var fileObjArray = su.removeSharedRootPath(files); - var sumanEnv = Object.assign({}, process.env, { - SUMAN_RUN_CHILD_STATIC_PATH: runChildPath, - SUMAN_CONFIG: JSON.stringify(sumanConfig), - SUMAN_OPTS: JSON.stringify(sumanOpts), - SUMAN_RUNNER: 'yes', - SUMAN_PROJECT_ROOT: projectRoot, - SUMAN_RUN_ID: _suman.runId, - SUMAN_RUNNER_TIMESTAMP: _suman.timestamp, - NPM_COLORS: process.env.NPM_COLORS || (sumanOpts.no_color ? 'no' : 'yes') - }); - if (_suman.socketServerPort > 0) { - sumanEnv['SUMAN_SOCKETIO_SERVER_PORT'] = _suman.socketServerPort; - } - var execFile = path.resolve(__dirname + '/run-child.js'); - var istanbulExecPath = _suman.istanbulExecPath || 'istanbul'; - var isStdoutSilent = sumanOpts.stdout_silent || sumanOpts.silent; - var isStderrSilent = sumanOpts.stderr_silent || sumanOpts.silent; - var inheritTransformStdio = sumanOpts.inherit_all_stdio || sumanOpts.inherit_transform_stdio || process.env.SUMAN_INHERIT_STDIO; - fileObjArray.forEach(function (fileShortAndFull) { - var uuid = String(uuidV4()); - var file = fileShortAndFull[0]; - var shortFile = fileShortAndFull[1]; - var filePathFromProjectRoot = fileShortAndFull[2]; - var basename = file.length > 28 ? ' ' + String(file).substring(Math.max(0, file.length - 28)) + ' ' : file; - var m = String(basename).match(/\//g); - if (m && m.length > 1) { - var arr = String(basename).split(''); - var i = 0; - while (arr[i] !== '/') { - arr.shift(); - } - basename = arr.join(''); - } - tableRows[shortFile] = { - actualExitCode: null, - shortFilePath: shortFile, - tableData: null, - defaultTableData: { - SUITES_DESIGNATOR: basename - } - }; - var gd = socket_cp_hash_1.ganttHash[uuid] = { - uuid: uuid, - fullFilePath: String(file), - shortFilePath: String(shortFile), - filePathFromProjectRoot: String(filePathFromProjectRoot), - }; - var tr = (sumanOpts.no_transpile !== true) && runnerUtils.findPathOfTransformDotSh(file); - if (tr) { - _suman.log(chalk.bgWhite.underline('Suman has found a @transform.sh file => '), chalk.bold(tr)); - transpileQueue.push(function (cb) { - su.makePathExecutable(tr, function (err) { - if (err) { - return cb(err); - } - gd.transformStartDate = Date.now(); - var k = cp.spawn(tr, [], { - cwd: projectRoot, - env: Object.assign({}, process.env, { - SUMAN_TEST_PATHS: JSON.stringify([file]), - SUMAN_CHILD_TEST_PATH: file - }) - }); - k.once('error', cb); - k.stderr.setEncoding('utf8'); - k.stdout.setEncoding('utf8'); - var ln = String(_suman.projectRoot).length; - if (false) { - var onError = function (e) { - _suman.logError('\n', su.getCleanErrorString(e), '\n'); - }; - var temp = su.removePath(file, _suman.projectRoot); - var onlyFile = String(temp).replace(/\//g, '.'); - var logfile = path.resolve(f + '/' + onlyFile + '.log'); - var fileStrm = fs.createWriteStream(logfile); - k.stderr.pipe(fileStrm).once('error', onError); - k.stdout.pipe(fileStrm).once('error', onError); - } - if (inheritTransformStdio) { - var onError = function (e) { - _suman.logError('\n', su.getCleanErrorString(e), '\n'); - }; - var stderrPrepend = " [" + chalk.red('transform process stderr:') + " " + chalk.red.bold(String(file.slice(ln))) + "] "; - k.stderr.pipe(prepend_transform_1.default(stderrPrepend, { omitWhitespace: true })).once('error', onError).pipe(process.stderr); - var stdoutPrepend = " [" + chalk.yellow('transform process stdout:') + " " + chalk.gray.bold(String(file.slice(ln))) + "] "; - k.stdout.pipe(prepend_transform_1.default(stdoutPrepend)).once('error', onError).pipe(process.stdout); - } - var stdout = ''; - k.stdout.on('data', function (data) { - stdout += data; - }); - var stderr = ''; - k.stderr.on('data', function (data) { - stderr += data; - }); - k.once('close', function (code) { - gd.transformEndDate = Date.now(); - if (code > 0) { - cb(new Error("the @transform.sh process, for file " + file + ",\nexitted with non-zero exit code. :( \n \n To see the stderr, use --inherit-stdio.")); - } - else { - cb(null, file, shortFile, stdout, stderr, gd); - } - }); - }); - }); - } - else { - gd.transformStartDate = gd.transformEndDate = null; - gd.wasTransformed = false; - transpileQueue.unshift(function (cb) { - setImmediate(function () { - cb(null, file, shortFile, '', '', gd); - }); - }); - } - }); - var childId = 1; - var debugChildren = sumanOpts.debug_child || sumanOpts.inspect_child; - var inheritRunStdio = debugChildren || sumanOpts.inherit_stdio || - sumanOpts.inherit_all_stdio || process.env.SUMAN_INHERIT_STDIO === 'yes'; - var outer = function (file, shortFile, stdout, gd) { - var run = function () { - if (runnerObj.bailed) { - if (sumanOpts.verbosity > 4) { - _suman.log('"--bailed" option was passed and was tripped, ' + - 'no more child processes will be forked.'); - } - return; - } - var argz = JSON.parse(JSON.stringify(args)); - var execArgz = ['--expose-gc']; - if (sumanOpts.debug_child) { - execArgz.push('--debug=' + (5303 + runnerObj.processId++)); - execArgz.push('--debug-brk'); - } - if (sumanOpts.inspect_child) { - if (semver.gt(process.version, '7.8.0')) { - execArgz.push('--inspect-brk=' + (5303 + runnerObj.processId++)); - } - else { - execArgz.push('--inspect=' + (5303 + runnerObj.processId++)); - execArgz.push('--debug-brk'); - } - } - var execArgs; - if (execArgs = sumanOpts.exec_arg) { - execArgs.forEach(function (n) { - if (n) { - execArgz.push(String(n).trim()); - } - }); - String(execArgs).split(/S+/).forEach(function (n) { - if (n) { - execArgz.push('--' + String(n).trim()); - } - }); - } - var $execArgz = execArgz.filter(function (e, i) { - if (execArgz.indexOf(e) !== i) { - console.error('\n', chalk.yellow(' => Warning you have duplicate items in your exec args => '), '\n' + util.inspect(execArgz), '\n'); - } - return true; - }); - var n, hashbang = false; - var extname = path.extname(shortFile); - var $childId = childId++; - var childUuid = uuidV4(); - var inherit = _suman.$forceInheritStdio ? 'inherit' : ''; - if (inherit) { - _suman.log('we are inheriting stdio of child, because of sumanception.'); - } - var cpOptions = { - detached: false, - cwd: projectRoot, - stdio: [ - 'ignore', - inherit || (isStdoutSilent ? 'ignore' : 'pipe'), - inherit || (isStderrSilent ? 'ignore' : 'pipe'), - ], - env: Object.assign({}, sumanEnv, { - SUMAN_CHILD_TEST_PATH: file, - SUMAN_CHILD_TEST_PATH_TARGET: file, - SUMAN_TRANSFORM_STDOUT: stdout, - SUMAN_CHILD_ID: String($childId), - SUMAN_CHILD_UUID: String($childId) - }) - }; - var sh = runnerUtils.findPathOfRunDotSh(file); - if (sh) { - _suman.log(chalk.bgWhite.underline('Suman has found a @run.sh file => '), chalk.bold(sh)); - cpOptions.cwd = projectRoot; - try { - fs.chmodSync(sh, 511); - } - catch (err) { - } - if (sumanOpts.coverage) { - _suman.logWarning(chalk.magenta('coverage option was set to true, but we are running your tests via @run.sh.')); - _suman.logWarning(chalk.magenta('so in this case, you will need to run your coverage call via @run.sh.')); - } - n = cp.spawn(sh, argz, cpOptions); - } - else { - if ('.js' === extname) { - if (sumanOpts.coverage) { - var coverageDir = path.resolve(_suman.projectRoot + '/coverage/' + String(shortFile).replace(/\//g, '-')); - n = cp.spawn(istanbulExecPath, ['cover', execFile, '--dir', coverageDir, '--'].concat(args), cpOptions); - } - else { - argz.unshift(execFile); - var argzz = $execArgz.concat(argz); - n = cp.spawn('node', argzz, cpOptions); - } - } - else { - _suman.log("perl bash python or ruby file? '" + chalk.magenta(file) + "'"); - hashbang = true; - n = cp.spawn(file, argz, cpOptions); - } - } - socket_cp_hash_1.cpHash[$childId] = n; - if (!_suman.weAreDebugging) { - n.to = setTimeout(function () { - _suman.logError("Suman killed a child process because it timed out: '" + (n.fileName || n.filename) + "'."); - n.kill('SIGINT'); - setTimeout(function () { - n.kill('SIGKILL'); - }, 8000); - }, constants.DEFAULT_CHILD_PROCESS_TIMEOUT); - } - n.testPath = file; - n.shortTestPath = shortFile; - forkedCPs.push(n); - n.on('message', function (msg) { - _suman.logError('Suman runner does not handle standard Node.js IPC messages.'); - }); - n.on('error', function (err) { - _suman.logError('error spawning child process => ', err.stack || err); - if (hashbang) { - console.error('\n'); - console.error(' => The supposed test script file with the following path may not have a hashbang => '); - console.error(chalk.magenta.bold(file)); - console.error(' => A hashbang is necessary for non-.js files and when there is no accompanying @run.sh file.'); - console.error(' => Without a hashbang, Suman (and your OS) will not know how to run the file.'); - console.error(' => See sumanjs.org for more information.'); - } - }); - if (n.stdio && n.stdout && n.stderr) { - if (inherit) { - _suman.logError('n.stdio is defined even though we are in sumanception territory.'); - } - n.stdout.setEncoding('utf8'); - n.stderr.setEncoding('utf8'); - if (false && (sumanOpts.log_stdio_to_files || sumanOpts.log_stdout_to_files || sumanOpts.log_stderr_to_files)) { - var onError = function (e) { - _suman.logError('\n', su.getCleanErrorString(e), '\n'); - }; - var temp = su.removePath(file, _suman.projectRoot); - var onlyFile = String(temp).replace(/\//g, '.'); - var logfile = path.resolve(f + '/' + onlyFile + '.log'); - var fileStrm = fs.createWriteStream(logfile); - console.log('logFile => ', logfile); - if (sumanOpts.log_stdio_to_files || sumanOpts.log_stderr_to_files) { - n.stderr.pipe(fileStrm).once('error', onError); - } - if (sumanOpts.log_stdio_to_files || sumanOpts.log_stdout_to_files) { - n.stdout.pipe(fileStrm).once('error', onError); - } - } - if (inheritRunStdio) { - var onError = function (e) { - _suman.logError('\n', su.getCleanErrorString(e), '\n'); - }; - n.stdout.pipe(prepend_transform_1.default(chalk.cyan(' [suman child stdout] '))) - .once('error', onError).pipe(process.stdout); - n.stderr.pipe(prepend_transform_1.default(chalk.red.bold(' [suman child stderr] '), { omitWhitespace: true })) - .once('error', onError).pipe(process.stderr); - } - if (true || sumanOpts.$useTAPOutput) { - n.tapOutputIsComplete = false; - n.stdout.pipe(getTapParser()) - .on('error', function (e) { - _suman.logError('error parsing TAP output =>', su.getCleanErrorString(e)); - }) - .once('finish', function () { - n.tapOutputIsComplete = true; - process.nextTick(function () { - n.emit('tap-output-is-complete', true); - }); - }); - n.stdout.pipe(getTapJSONParser()) - .on('error', function (e) { - _suman.logError('error parsing TAP JSON output =>', su.getCleanErrorString(e)); - }); - } - n.stdio[2].setEncoding('utf-8'); - n.stdio[2].on('data', function (data) { - var d = String(data).split('\n').filter(function (line) { - return String(line).length; - }) - .map(function (line) { - return '[' + n.shortTestPath + '] ' + line; - }) - .join('\n'); - _suman.sumanStderrStream.write('\n' + d); - if (_suman.weAreDebugging) { - console.log('pid => ', n.pid, 'stderr => ', d); - } - }); - } - else { - if (su.vgt(2)) { - _suman.logWarning('Stdio object not available for child process.'); - } - } - n.dateStartedMillis = gd.startDate = Date.now(); - n.once('exit', multiple_process_each_on_exit_1.default(n, runnerObj, tableRows, messages, forkedCPs, beforeExitRunOncePost, makeExit, gd)); - }; - run.testPath = file; - run.shortTestPath = shortFile; - if (handleBlocking.runNext(run)) { - if (su.vgt(3)) { - _suman.log(chalk.black('File has just started running =>'), chalk.grey.bold("'" + file + "'.")); - } - } - else { - runnerObj.queuedCPs.push(run); - if (su.vgt(3)) { - _suman.log(chalk.yellow('File must wait, and will run later =>'), chalk.grey.bold("'" + file + "'.")); - } - } - if (waitForAllTranformsToFinish) { - if (forkedCPs.length < 1 && runnerObj.queuedCPs.length > 0) { - throw new Error('Suman internal error => fatal start order algorithm error, ' + - 'please file an issue on Github, thanks.'); - } - if (forkedCPs.length < 1) { - noFilesFoundError(files); - } - else { - var totalCount = forkedCPs.length + runnerObj.queuedCPs.length; - var suites = totalCount === 1 ? 'suite' : 'suites'; - var processes = totalCount === 1 ? 'process' : 'processes'; - resultBroadcaster.emit(String(suman_events_1.events.RUNNER_INITIAL_SET), forkedCPs, processes, suites); - var addendum = maxProcs < totalCount ? ' with no more than ' + maxProcs + ' running at a time.' : ''; - resultBroadcaster.emit(String(suman_events_1.events.RUNNER_OVERALL_SET), totalCount, processes, suites, addendum); - } - } - }; + fileObjArray.forEach(add_to_transpile_queue_1.makeAddToTranspileQueue(f, transpileQueue, tableRows, socket_cp_hash_1.ganttHash, projectRoot)); }; }; diff --git a/lib/runner-helpers/handle-multiple-processes.ts b/lib/runner-helpers/handle-multiple-processes.ts index 19a55e1e..a7e63ff0 100755 --- a/lib/runner-helpers/handle-multiple-processes.ts +++ b/lib/runner-helpers/handle-multiple-processes.ts @@ -17,8 +17,6 @@ import assert = require('assert'); import EE = require('events'); //npm -import semver = require('semver'); -const merge = require('lodash.merge'); const shuffle = require('lodash.shuffle'); import {events} from 'suman-events'; import * as su from 'suman-utils'; @@ -29,16 +27,13 @@ import * as chalk from 'chalk'; //project const _suman: IGlobalSumanObj = global.__suman = (global.__suman || {}); const resultBroadcaster = _suman.resultBroadcaster = (_suman.resultBroadcaster || new EE()); -const runnerUtils = require('./runner-utils'); import {cpHash, socketHash, ganttHash, IGanttHash, IGanttData} from './socket-cp-hash'; -const {getTapParser} = require('./handle-tap'); -const {getTapJSONParser} = require('./handle-tap-json'); const {constants} = require('../../config/suman-constants'); -const debug = require('suman-debug')('s:runner'); -import onExitFn from './multiple-process-each-on-exit'; -import pt from 'prepend-transform'; -const runChildPath = require.resolve(__dirname + '/run-child.js'); -import uuidV4 = require('uuid/v4'); +import {makeTranspileQueue} from './multi-process/transpile-queue'; +import {makeAddToTranspileQueue} from './multi-process/add-to-transpile-queue'; +import {makeOnExitFn} from './multiple-process-each-on-exit'; +import {makeAddToRunQueue} from "./multi-process/add-to-run-queue"; +import {makeRunQueue} from "./multi-process/run-queue"; ///////////////////////////////////////////////////////////////////////////////////////////////////// @@ -49,577 +44,69 @@ export interface ISumanCPMessages { ///////////////////////////////////////////////////////////////////////////////////////////////////// -export const makeHandleMultipleProcesses = +export const makeHandleMultipleProcesses = function (runnerObj: IRunnerObj, tableRows: ITableRows, + messages: Array, + forkedCPs: Array, + beforeExitRunOncePost: Function, makeExit: Function): Function { - function (runnerObj: IRunnerObj, tableRows: ITableRows, messages: Array, - forkedCPs: Array, handleMessage: Function, - beforeExitRunOncePost: Function, makeExit: Function): Function { + return function (runObj: IRunObj) { - return function (runObj: IRunObj) { + const {sumanOpts, sumanConfig, projectRoot} = _suman; + _suman.startDateMillis = Date.now(); + process.stderr.setMaxListeners(runObj.files.length + 11); + process.stdout.setMaxListeners(runObj.files.length + 11); - process.stderr.setMaxListeners(runObj.files.length + 11); - process.stdout.setMaxListeners(runObj.files.length + 11); + const logsDir = _suman.sumanConfig.logsDir || _suman.sumanHelperDirRoot + '/logs'; + const sumanCPLogs = path.resolve(logsDir + '/runs/'); + const f = path.resolve(sumanCPLogs + '/' + _suman.timestamp + '-' + _suman.runId); - const logsDir = _suman.sumanConfig.logsDir || _suman.sumanHelperDirRoot + '/logs'; - const sumanCPLogs = path.resolve(logsDir + '/runs/'); - const f = path.resolve(sumanCPLogs + '/' + _suman.timestamp + '-' + _suman.runId); - - _suman.startDateMillis = Date.now(); - - const {sumanOpts, sumanConfig, maxProcs, projectRoot} = _suman; - const waitForAllTranformsToFinish = sumanOpts.wait_for_all_transforms; + const args: Array = ['--user-args', sumanOpts.user_args]; + const runQueue = makeRunQueue(); + const onExitFn = makeOnExitFn(runnerObj, tableRows, messages, forkedCPs, beforeExitRunOncePost, makeExit, runQueue); + const runFile = makeAddToRunQueue(runnerObj, args, runQueue, projectRoot, cpHash, forkedCPs, onExitFn); + const waitForAllTranformsToFinish = sumanOpts.wait_for_all_transforms; + if (waitForAllTranformsToFinish) { _suman.log('waitForAllTranformsToFinish => ', chalk.magenta(waitForAllTranformsToFinish)); + } - const args: Array = ['--user-args', sumanOpts.user_args]; - let queuedTestFns: Array = []; - let failedTransformObjects: Array = []; - - const transpileQueue = async.queue(function (task: Function, cb: Function) { - - task(function (err: Error, file: string, shortFile: string, stdout: string, stderr: string, gd: IGanttData) { - - setImmediate(cb); + let queuedTestFns: Array = []; + let failedTransformObjects: Array = []; - if (err) { - _suman.logError('tranpile error => ', err.stack || err); - failedTransformObjects.push({err, file, shortFile, stdout, stderr}); - return; - } + const transpileQueue = makeTranspileQueue(failedTransformObjects, runFile, queuedTestFns); - if (waitForAllTranformsToFinish) { - queuedTestFns.push(function () { - outer(file, shortFile, stdout, gd); - }); - } - else { - outer(file, shortFile, stdout, gd); - } + if (waitForAllTranformsToFinish) { + transpileQueue.drain = function () { + // => execute all queued tests + _suman.log('all transforms complete, beginning to run first set of tests.'); + queuedTestFns.forEach(function (fn) { + fn(); }); - - }, 3); - - if (waitForAllTranformsToFinish) { - transpileQueue.drain = function () { - // => execute all queued tests - _suman.log('all transforms complete, beginning to run first set of tests.'); - queuedTestFns.forEach(function (fn) { - fn(); - }); - } } + } - if (sumanOpts.$useTAPOutput) { - if (sumanOpts.verbosity > 4) { - _suman.log(chalk.gray.bold('Suman runner is expecting TAP output from Node.js child processes ' + - 'and will not be listening for websocket messages.')); - } - } - - // handleBlocking gets initialized weirdly in runner.js, but we will deal for now - const handleBlocking = runnerObj.handleBlocking; - - if (_suman.usingLiveSumanServer) { - args.push('--live_suman_server'); - } - - let files = runObj.files; - - //TODO: need to remove duplicate files before calling resultBroadcaster - resultBroadcaster.emit(String(events.RUNNER_STARTED), files.length); - - if (_suman.sumanOpts.rand) { - files = shuffle(files); - } - - runnerObj.startTime = Date.now(); - - const fileObjArray = su.removeSharedRootPath(files); - - const sumanEnv = Object.assign({}, process.env, { - SUMAN_RUN_CHILD_STATIC_PATH: runChildPath, - SUMAN_CONFIG: JSON.stringify(sumanConfig), - SUMAN_OPTS: JSON.stringify(sumanOpts), - SUMAN_RUNNER: 'yes', - SUMAN_PROJECT_ROOT: projectRoot, - SUMAN_RUN_ID: _suman.runId, - SUMAN_RUNNER_TIMESTAMP: _suman.timestamp, - NPM_COLORS: process.env.NPM_COLORS || (sumanOpts.no_color ? 'no' : 'yes') - }); - - if (_suman.socketServerPort > 0) { - sumanEnv['SUMAN_SOCKETIO_SERVER_PORT'] = _suman.socketServerPort; + if (sumanOpts.$useTAPOutput) { + if (sumanOpts.verbosity > 4) { + _suman.log(chalk.gray.bold('Suman runner is expecting TAP output from Node.js child processes ' + + 'and will not be listening for websocket messages.')); } + } - const execFile = path.resolve(__dirname + '/run-child.js'); - const istanbulExecPath = _suman.istanbulExecPath || 'istanbul'; - const isStdoutSilent = sumanOpts.stdout_silent || sumanOpts.silent; - const isStderrSilent = sumanOpts.stderr_silent || sumanOpts.silent; - const inheritTransformStdio = - sumanOpts.inherit_all_stdio || sumanOpts.inherit_transform_stdio || process.env.SUMAN_INHERIT_STDIO - - fileObjArray.forEach(function (fileShortAndFull: Array>) { - - const uuid = String(uuidV4()); - const file = fileShortAndFull[0]; - const shortFile = fileShortAndFull[1]; - const filePathFromProjectRoot = fileShortAndFull[2]; - - let basename = file.length > 28 ? ' ' + String(file).substring(Math.max(0, file.length - 28)) + ' ' : file; - - const m = String(basename).match(/\//g); - - if (m && m.length > 1) { - const arr = String(basename).split(''); - let i = 0; - while (arr[i] !== '/') { - arr.shift(); - } - basename = arr.join(''); - } - - //TODO: we should used uuid here instead - tableRows[shortFile] = { - actualExitCode: null, - shortFilePath: shortFile, - tableData: null, - defaultTableData: { - SUITES_DESIGNATOR: basename - } - }; - - const gd = ganttHash[uuid] = { - uuid: uuid, - fullFilePath: String(file), - shortFilePath: String(shortFile), - filePathFromProjectRoot: String(filePathFromProjectRoot), - // transformStartDate: null, - // transformEndDate: null, - // startDate: null, - // endDate: null - } as any; - - const tr = (sumanOpts.no_transpile !== true) && runnerUtils.findPathOfTransformDotSh(file); - - if (tr) { - - _suman.log(chalk.bgWhite.underline('Suman has found a @transform.sh file => '), chalk.bold(tr)); - - transpileQueue.push(function (cb: Function) { - - su.makePathExecutable(tr, function (err: Error) { - - if (err) { - return cb(err); - } - - gd.transformStartDate = Date.now(); - - let k = cp.spawn(tr, [], { - cwd: projectRoot, - env: Object.assign({}, process.env, { - SUMAN_TEST_PATHS: JSON.stringify([file]), - SUMAN_CHILD_TEST_PATH: file - }) - }); - - k.once('error', cb); - - k.stderr.setEncoding('utf8'); - k.stdout.setEncoding('utf8'); - - const ln = String(_suman.projectRoot).length; - - if (false) { - - let onError = function (e: Error) { - _suman.logError('\n', su.getCleanErrorString(e), '\n'); - }; - - const temp = su.removePath(file, _suman.projectRoot); - const onlyFile = String(temp).replace(/\//g, '.'); - const logfile = path.resolve(f + '/' + onlyFile + '.log'); - let fileStrm = fs.createWriteStream(logfile); - k.stderr.pipe(fileStrm).once('error', onError); - k.stdout.pipe(fileStrm).once('error', onError); - } - - if (inheritTransformStdio) { - - let onError = function (e: Error) { - _suman.logError('\n', su.getCleanErrorString(e), '\n'); - }; - - let stderrPrepend = ` [${chalk.red('transform process stderr:')} ${chalk.red.bold(String(file.slice(ln)))}] `; - k.stderr.pipe(pt(stderrPrepend, {omitWhitespace: true})).once('error', onError).pipe(process.stderr); - - let stdoutPrepend = ` [${chalk.yellow('transform process stdout:')} ${chalk.gray.bold(String(file.slice(ln)))}] `; - k.stdout.pipe(pt(stdoutPrepend)).once('error', onError).pipe(process.stdout); - } - - // let strm = fs.createWriteStream(path.resolve(tr + '.log')); - // - // k.stderr.pipe(strm).on('error', function (e: Error) { - // throw e; - // }); - // - // k.stdout.pipe(strm).on('error', function (e: Error) { - // throw e; - // }); - - let stdout = ''; - k.stdout.on('data', function (data: string) { - stdout += data; - }); - - let stderr = ''; - k.stderr.on('data', function (data: string) { - stderr += data; - }); - - k.once('close', function (code: number) { - - gd.transformEndDate = Date.now(); - - if (code > 0) { - cb(new Error(`the @transform.sh process, for file ${file},\nexitted with non-zero exit code. :( - \n To see the stderr, use --inherit-stdio.`)); - } - else { - cb(null, file, shortFile, stdout, stderr, gd); - } - - }); - - }); - - }); - - } - else { - // we don't need to run any transform, so we run right away - gd.transformStartDate = gd.transformEndDate = null; - gd.wasTransformed = false; - transpileQueue.unshift(function (cb: Function) { - setImmediate(function () { - // there is no applicable stdout/stderr, so we pass empty string - cb(null, file, shortFile, '', '', gd); - }); - }); - } - }); - - let childId = 1; - - const debugChildren = sumanOpts.debug_child || sumanOpts.inspect_child; - const inheritRunStdio = debugChildren || sumanOpts.inherit_stdio || - sumanOpts.inherit_all_stdio || process.env.SUMAN_INHERIT_STDIO === 'yes'; - - const outer = function (file: string, shortFile: string, stdout: string, gd: IGanttData) { - - const run = function () { - - if (runnerObj.bailed) { - // should not fork any more child processes if we have bailed - if (sumanOpts.verbosity > 4) { - _suman.log('"--bailed" option was passed and was tripped, ' + - 'no more child processes will be forked.'); - } - return; - } - - const argz = JSON.parse(JSON.stringify(args)); - - const execArgz = ['--expose-gc']; - - if (sumanOpts.debug_child) { - execArgz.push('--debug=' + (5303 + runnerObj.processId++)); - execArgz.push('--debug-brk'); - } - - if (sumanOpts.inspect_child) { - if (semver.gt(process.version, '7.8.0')) { - execArgz.push('--inspect-brk=' + (5303 + runnerObj.processId++)); - } - else { - execArgz.push('--inspect=' + (5303 + runnerObj.processId++)); - execArgz.push('--debug-brk'); - } - } - - let execArgs; - - if (execArgs = sumanOpts.exec_arg) { - execArgs.forEach(function (n: string) { - if (n) { - execArgz.push(String(n).trim()); - } - }); - - String(execArgs).split(/S+/).forEach(function (n) { - if (n) { - execArgz.push('--' + String(n).trim()); - } - }); - } - - const $execArgz = execArgz.filter(function (e, i) { - // filter out duplicate command line args - if (execArgz.indexOf(e) !== i) { - console.error('\n', chalk.yellow(' => Warning you have duplicate items in your exec args => '), - '\n' + util.inspect(execArgz), '\n'); - } - return true; - }); - - let n: ISumanChildProcess, hashbang = false; - - const extname = path.extname(shortFile); - - let $childId = childId++; - let childUuid = uuidV4(); - const inherit = _suman.$forceInheritStdio ? 'inherit' : ''; - - if (inherit) { - _suman.log('we are inheriting stdio of child, because of sumanception.'); - } - - let cpOptions = { - detached: false, - cwd: projectRoot, - // cwd: sumanOpts.force_cwd_to_be_project_root ? projectRoot : path.dirname(file), - stdio: [ - 'ignore', - inherit || (isStdoutSilent ? 'ignore' : 'pipe'), - inherit || (isStderrSilent ? 'ignore' : 'pipe'), - // 'ipc' => we don't need IPC anymore, but also can we assume 'ipc' is ignored if not a .js file? - ], - env: Object.assign({}, sumanEnv, { - SUMAN_CHILD_TEST_PATH: file, - SUMAN_CHILD_TEST_PATH_TARGET: file, - SUMAN_TRANSFORM_STDOUT: stdout, - SUMAN_CHILD_ID: String($childId), - SUMAN_CHILD_UUID: String($childId) - }) - }; - - // we run the file directly, hopefully it has a hashbang - let sh = runnerUtils.findPathOfRunDotSh(file); - - if (sh) { - - _suman.log(chalk.bgWhite.underline('Suman has found a @run.sh file => '), chalk.bold(sh)); - - //force to project root - cpOptions.cwd = projectRoot; - - try { - fs.chmodSync(sh, 0o777); - } - catch (err) { - - } - - if (sumanOpts.coverage) { - _suman.logWarning(chalk.magenta('coverage option was set to true, but we are running your tests via @run.sh.')); - _suman.logWarning(chalk.magenta('so in this case, you will need to run your coverage call via @run.sh.')); - } - - n = cp.spawn(sh, argz, cpOptions) as ISumanChildProcess; - } - else { - - if ('.js' === extname) { - - if (sumanOpts.coverage) { - let coverageDir = path.resolve(_suman.projectRoot + '/coverage/' + String(shortFile).replace(/\//g, '-')); - n = cp.spawn(istanbulExecPath, - //'--include-all-sources' - ['cover', execFile, '--dir', coverageDir, '--'].concat(args), cpOptions) as ISumanChildProcess; - } - else { - argz.unshift(execFile); - let argzz = $execArgz.concat(argz); // append exec args to beginning - n = cp.spawn('node', argzz, cpOptions) as ISumanChildProcess; - } - - } - else { - // .sh .bash .py, perl, ruby, etc - _suman.log(`perl bash python or ruby file? '${chalk.magenta(file)}'`); - hashbang = true; - n = cp.spawn(file, argz, cpOptions) as ISumanChildProcess; - } - } - - cpHash[$childId] = n; - - if (!_suman.weAreDebugging) { - n.to = setTimeout(function () { - _suman.logError(`Suman killed a child process because it timed out: '${n.fileName || n.filename}'.`); - n.kill('SIGINT'); - setTimeout(function () { - // note that we wait 8 seconds for the child process to clean up before sending it a SIGKILL signal - n.kill('SIGKILL'); - }, 8000); - }, constants.DEFAULT_CHILD_PROCESS_TIMEOUT); - } - - n.testPath = file; - n.shortTestPath = shortFile; - - forkedCPs.push(n); - - n.on('message', function (msg) { - _suman.logError('Suman runner does not handle standard Node.js IPC messages.'); - }); - - n.on('error', function (err) { - _suman.logError('error spawning child process => ', err.stack || err); - if (hashbang) { - console.error('\n'); - console.error(' => The supposed test script file with the following path may not have a hashbang => '); - console.error(chalk.magenta.bold(file)); - console.error(' => A hashbang is necessary for non-.js files and when there is no accompanying @run.sh file.'); - console.error(' => Without a hashbang, Suman (and your OS) will not know how to run the file.'); - console.error(' => See sumanjs.org for more information.'); - } - }); - - if (n.stdio && n.stdout && n.stderr) { - - if (inherit) { - _suman.logError('n.stdio is defined even though we are in sumanception territory.'); - } - - n.stdout.setEncoding('utf8'); - n.stderr.setEncoding('utf8'); - - if (false && (sumanOpts.log_stdio_to_files || sumanOpts.log_stdout_to_files || sumanOpts.log_stderr_to_files)) { - - let onError = function (e: Error) { - _suman.logError('\n', su.getCleanErrorString(e), '\n'); - }; - - let temp = su.removePath(file, _suman.projectRoot); - let onlyFile = String(temp).replace(/\//g, '.'); - let logfile = path.resolve(f + '/' + onlyFile + '.log'); - let fileStrm = fs.createWriteStream(logfile); - - console.log('logFile => ', logfile); - - if (sumanOpts.log_stdio_to_files || sumanOpts.log_stderr_to_files) { - n.stderr.pipe(fileStrm).once('error', onError); - } - - if (sumanOpts.log_stdio_to_files || sumanOpts.log_stdout_to_files) { - n.stdout.pipe(fileStrm).once('error', onError); - } - } - - if (inheritRunStdio) { - - let onError = function (e: Error) { - _suman.logError('\n', su.getCleanErrorString(e), '\n'); - }; - - n.stdout.pipe(pt(chalk.cyan(' [suman child stdout] '))) - .once('error', onError).pipe(process.stdout); - n.stderr.pipe(pt(chalk.red.bold(' [suman child stderr] '), {omitWhitespace: true})) - .once('error', onError).pipe(process.stderr); - } - - if (true || sumanOpts.$useTAPOutput) { - - n.tapOutputIsComplete = false; - - n.stdout.pipe(getTapParser()) - .on('error', function (e: Error) { - _suman.logError('error parsing TAP output =>', su.getCleanErrorString(e)); - }) - .once('finish', function () { - n.tapOutputIsComplete = true; - process.nextTick(function () { - n.emit('tap-output-is-complete', true); - }); - }); - - n.stdout.pipe(getTapJSONParser()) - .on('error', function (e: Error) { - _suman.logError('error parsing TAP JSON output =>', su.getCleanErrorString(e)); - }) - - } - - n.stdio[2].setEncoding('utf-8'); - n.stdio[2].on('data', function (data) { - - const d = String(data).split('\n').filter(function (line) { - return String(line).length; - }) - .map(function (line) { - return '[' + n.shortTestPath + '] ' + line; - }) - .join('\n'); - - _suman.sumanStderrStream.write('\n' + d); - - if (_suman.weAreDebugging) { //TODO: add check for NODE_ENV=dev_local_debug - //TODO: go through code and make sure that no console.log statements should in fact be console.error - console.log('pid => ', n.pid, 'stderr => ', d); - } - }); - - } - else { - if (su.vgt(2)) { - _suman.logWarning('Stdio object not available for child process.'); - } - } - - n.dateStartedMillis = gd.startDate = Date.now(); - n.once('exit', onExitFn(n, runnerObj, tableRows, messages, forkedCPs, beforeExitRunOncePost, makeExit, gd)); - - }; - - run.testPath = file; - run.shortTestPath = shortFile; - - if (handleBlocking.runNext(run)) { - if (su.vgt(3)) { - _suman.log(chalk.black('File has just started running =>'), chalk.grey.bold(`'${file}'.`)); - } - } - else { - runnerObj.queuedCPs.push(run); - if(su.vgt(3)){ - _suman.log(chalk.yellow('File must wait, and will run later =>'), chalk.grey.bold(`'${file}'.`)); - } - } - - if (waitForAllTranformsToFinish) { + let files = runObj.files; - if (forkedCPs.length < 1 && runnerObj.queuedCPs.length > 0) { - throw new Error('Suman internal error => fatal start order algorithm error, ' + - 'please file an issue on Github, thanks.'); - } + //TODO: need to remove duplicate files before calling resultBroadcaster + resultBroadcaster.emit(String(events.RUNNER_STARTED), files.length); - if (forkedCPs.length < 1) { - noFilesFoundError(files); - } - else { - const totalCount = forkedCPs.length + runnerObj.queuedCPs.length; - const suites = totalCount === 1 ? 'suite' : 'suites'; - const processes = totalCount === 1 ? 'process' : 'processes'; - resultBroadcaster.emit(String(events.RUNNER_INITIAL_SET), forkedCPs, processes, suites); - const addendum = maxProcs < totalCount ? ' with no more than ' + maxProcs + ' running at a time.' : ''; - resultBroadcaster.emit(String(events.RUNNER_OVERALL_SET), totalCount, processes, suites, addendum); - } + if (_suman.sumanOpts.rand) { + files = shuffle(files); + } - } + runnerObj.startTime = Date.now(); + const fileObjArray = su.removeSharedRootPath(files); - }; + // add all files to transpile queuq + fileObjArray.forEach(makeAddToTranspileQueue(f, transpileQueue, tableRows, ganttHash, projectRoot)); - } + } - }; +}; diff --git a/lib/runner-helpers/make-handle-blocking.js b/lib/runner-helpers/make-handle-blocking.js index 30c1e7f0..c80716b7 100755 --- a/lib/runner-helpers/make-handle-blocking.js +++ b/lib/runner-helpers/make-handle-blocking.js @@ -27,7 +27,8 @@ function default_1(order) { return ended.every(function (item) { return (String(item.testPath) !== String($item.testPath)); }); - }).map(function (item) { + }) + .map(function (item) { return '\n ' + item.testPath; }); if (startedButNotEnded.length > 0) { @@ -39,11 +40,11 @@ function default_1(order) { }, initialTimeout += increaseEachTime); }, interval); } - function findQueuedCPsToStart(queuedCPsObj) { + var findQueuedCPsToStart = function (queuedCPsObj) { if (started.length - ended.length < maxProcs) { return queuedCPsObj.queuedCPs.pop(); } - } + }; return { runNext: function (fn) { if (started.length - ended.length < maxProcs) { diff --git a/lib/runner-helpers/make-handle-blocking.ts b/lib/runner-helpers/make-handle-blocking.ts index 7a147047..e07502d0 100755 --- a/lib/runner-helpers/make-handle-blocking.ts +++ b/lib/runner-helpers/make-handle-blocking.ts @@ -1,4 +1,6 @@ 'use strict'; + +//dts import {IHandleBlocking, IRunnerObj, IRunnerRunFn, ISumanChildProcess} from "suman-types/dts/runner"; //polyfills @@ -49,7 +51,8 @@ export default function (order: Object): IHandleBlocking { return ended.every(function (item) { return (String(item.testPath) !== String($item.testPath)); }); - }).map(function (item) { + }) + .map(function (item) { return '\n ' + item.testPath; }); @@ -64,11 +67,11 @@ export default function (order: Object): IHandleBlocking { }, interval); } - function findQueuedCPsToStart(queuedCPsObj: IRunnerObj): IRunnerRunFn { + const findQueuedCPsToStart = function (queuedCPsObj: IRunnerObj): IRunnerRunFn { if (started.length - ended.length < maxProcs) { return queuedCPsObj.queuedCPs.pop(); } - } + }; return { diff --git a/lib/runner-helpers/multi-process/.gitkeep b/lib/runner-helpers/multi-process/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/lib/runner-helpers/multi-process/add-to-run-queue.d.ts b/lib/runner-helpers/multi-process/add-to-run-queue.d.ts new file mode 100644 index 00000000..0c33bbeb --- /dev/null +++ b/lib/runner-helpers/multi-process/add-to-run-queue.d.ts @@ -0,0 +1,3 @@ +import { IGanttData } from "../socket-cp-hash"; +import { AsyncQueue } from "async"; +export declare const makeAddToRunQueue: (runnerObj: Object, args: string[], runQueue: AsyncQueue, projectRoot: string, cpHash: Object, forkedCPs: any[], onExitFn: Function) => (file: string, shortFile: string, stdout: string, gd: IGanttData) => void; diff --git a/lib/runner-helpers/multi-process/add-to-run-queue.js b/lib/runner-helpers/multi-process/add-to-run-queue.js new file mode 100644 index 00000000..f766c5b6 --- /dev/null +++ b/lib/runner-helpers/multi-process/add-to-run-queue.js @@ -0,0 +1,236 @@ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var process = require('suman-browser-polyfills/modules/process'); +var global = require('suman-browser-polyfills/modules/global'); +var util = require("util"); +var path = require("path"); +var cp = require("child_process"); +var chalk = require("chalk"); +var semver = require("semver"); +var su = require("suman-utils"); +var _suman = global.__suman = (global.__suman || {}); +var handle_tap_1 = require("../handle-tap"); +var handle_tap_json_1 = require("../handle-tap-json"); +var prepend_transform_1 = require("prepend-transform"); +var uuidV4 = require("uuid/v4"); +var runner_utils_1 = require("../runner-utils"); +var suman_constants_1 = require("../../../config/suman-constants"); +var runChildPath = require.resolve(__dirname + '/../run-child.js'); +exports.makeAddToRunQueue = function (runnerObj, args, runQueue, projectRoot, cpHash, forkedCPs, onExitFn) { + var sumanOpts = _suman.sumanOpts, sumanConfig = _suman.sumanConfig, maxProcs = _suman.maxProcs; + var execFile = path.resolve(__dirname + '/../run-child.js'); + var istanbulExecPath = _suman.istanbulExecPath || 'istanbul'; + var isStdoutSilent = sumanOpts.stdout_silent || sumanOpts.silent; + var isStderrSilent = sumanOpts.stderr_silent || sumanOpts.silent; + var debugChildren = sumanOpts.debug_child || sumanOpts.inspect_child; + var inheritRunStdio = debugChildren || sumanOpts.inherit_stdio || + sumanOpts.inherit_all_stdio || process.env.SUMAN_INHERIT_STDIO === 'yes'; + var childId = 1; + var sumanEnv = Object.assign({}, process.env, { + SUMAN_RUN_CHILD_STATIC_PATH: runChildPath, + SUMAN_CONFIG: JSON.stringify(sumanConfig), + SUMAN_OPTS: JSON.stringify(sumanOpts), + SUMAN_RUNNER: 'yes', + SUMAN_PROJECT_ROOT: projectRoot, + SUMAN_RUN_ID: _suman.runId, + SUMAN_RUNNER_TIMESTAMP: _suman.timestamp, + NPM_COLORS: process.env.NPM_COLORS || (sumanOpts.no_color ? 'no' : 'yes'), + SUMAN_SOCKETIO_SERVER_PORT: _suman.socketServerPort > 0 ? _suman.socketServerPort : undefined + }); + return function (file, shortFile, stdout, gd) { + runQueue.push(function (cb) { + if (runnerObj.bailed) { + if (sumanOpts.verbosity > 4) { + _suman.log('"--bailed" option was passed and was tripped, ' + + 'no more child processes will be forked.'); + } + return; + } + var argz = JSON.parse(JSON.stringify(args)); + var execArgz = ['--expose-gc']; + if (sumanOpts.debug_child) { + execArgz.push('--debug=' + (5303 + runnerObj.processId++)); + execArgz.push('--debug-brk'); + } + if (sumanOpts.inspect_child) { + if (semver.gt(process.version, '7.8.0')) { + execArgz.push('--inspect-brk=' + (5303 + runnerObj.processId++)); + } + else { + execArgz.push('--inspect=' + (5303 + runnerObj.processId++)); + execArgz.push('--debug-brk'); + } + } + var execArgs; + if (execArgs = sumanOpts.exec_arg) { + execArgs.forEach(function (n) { + n && execArgz.push(String(n).trim()); + }); + String(execArgs).split(/S+/).forEach(function (n) { + n && execArgz.push('--' + String(n).trim()); + }); + } + var $execArgz = execArgz.filter(function (e, i) { + if (execArgz.indexOf(e) !== i) { + console.error('\n', chalk.yellow(' => Warning you have duplicate items in your exec args => '), '\n' + util.inspect(execArgz), '\n'); + } + return true; + }); + var n, hashbang = false; + var extname = path.extname(shortFile); + var $childId = childId++; + var childUuid = uuidV4(); + var inherit = _suman.$forceInheritStdio ? 'inherit' : ''; + if (inherit) { + _suman.log('we are inheriting stdio of child, because of sumanception.'); + } + var cpOptions = { + detached: false, + cwd: projectRoot, + stdio: [ + 'ignore', + inherit || (isStdoutSilent ? 'ignore' : 'pipe'), + inherit || (isStderrSilent ? 'ignore' : 'pipe'), + ], + env: Object.assign({}, sumanEnv, { + SUMAN_CHILD_TEST_PATH: file, + SUMAN_CHILD_TEST_PATH_TARGET: file, + SUMAN_TRANSFORM_STDOUT: stdout, + SUMAN_CHILD_ID: String($childId), + SUMAN_CHILD_UUID: String($childId) + }) + }; + var sh = runner_utils_1.findPathOfRunDotSh(file); + if (sh) { + _suman.log(chalk.bgWhite.underline('Suman has found a @run.sh file => '), chalk.bold(sh)); + cpOptions.cwd = projectRoot; + try { + fs.chmodSync(sh, 511); + } + catch (err) { + } + if (sumanOpts.coverage) { + _suman.logWarning(chalk.magenta('coverage option was set to true, but we are running your tests via @run.sh.')); + _suman.logWarning(chalk.magenta('so in this case, you will need to run your coverage call via @run.sh.')); + } + n = cp.spawn(sh, argz, cpOptions); + } + else { + if ('.js' === extname) { + if (sumanOpts.coverage) { + var coverageDir = path.resolve(_suman.projectRoot + '/coverage/' + String(shortFile).replace(/\//g, '-')); + n = cp.spawn(istanbulExecPath, ['cover', execFile, '--dir', coverageDir, '--'].concat(args), cpOptions); + } + else { + argz.unshift(execFile); + var argzz = $execArgz.concat(argz); + n = cp.spawn('node', argzz, cpOptions); + } + } + else { + _suman.log("perl bash python or ruby file? '" + chalk.magenta(file) + "'"); + hashbang = true; + n = cp.spawn(file, argz, cpOptions); + } + } + cpHash[$childId] = n; + if (!_suman.weAreDebugging) { + n.to = setTimeout(function () { + _suman.logError("Suman killed a child process because it timed out: '" + (n.fileName || n.filename) + "'."); + n.kill('SIGINT'); + setTimeout(function () { + n.kill('SIGKILL'); + }, 8000); + }, suman_constants_1.constants.DEFAULT_CHILD_PROCESS_TIMEOUT); + } + n.testPath = file; + n.shortTestPath = shortFile; + forkedCPs.push(n); + n.on('message', function (msg) { + _suman.logError('Warning - Suman runner does not handle standard Node.js IPC messages.'); + }); + n.on('error', function (err) { + _suman.logError('error spawning child process => ', err.stack || err); + if (hashbang) { + console.error('\n'); + console.error(' => The supposed test script file with the following path may not have a hashbang => '); + console.error(chalk.magenta.bold(file)); + console.error(' => A hashbang is necessary for non-.js files and when there is no accompanying @run.sh file.'); + console.error(' => Without a hashbang, Suman (and your OS) will not know how to run the file.'); + console.error(' => See sumanjs.org for more information.'); + } + }); + if (n.stdio && n.stdout && n.stderr) { + if (inherit) { + _suman.logError('n.stdio is defined even though we are in sumanception territory.'); + } + n.stdout.setEncoding('utf8'); + n.stderr.setEncoding('utf8'); + if (false && (sumanOpts.log_stdio_to_files || sumanOpts.log_stdout_to_files || sumanOpts.log_stderr_to_files)) { + var onError = function (e) { + _suman.logError('\n', su.getCleanErrorString(e), '\n'); + }; + var temp = su.removePath(file, _suman.projectRoot); + var onlyFile = String(temp).replace(/\//g, '.'); + var logfile = path.resolve(f + '/' + onlyFile + '.log'); + var fileStrm = fs.createWriteStream(logfile); + console.log('logFile => ', logfile); + if (sumanOpts.log_stdio_to_files || sumanOpts.log_stderr_to_files) { + n.stderr.pipe(fileStrm).once('error', onError); + } + if (sumanOpts.log_stdio_to_files || sumanOpts.log_stdout_to_files) { + n.stdout.pipe(fileStrm).once('error', onError); + } + } + if (inheritRunStdio) { + var onError = function (e) { + _suman.logError('\n', su.getCleanErrorString(e), '\n'); + }; + n.stdout.pipe(prepend_transform_1.default(chalk.cyan(' [suman child stdout] '))) + .once('error', onError).pipe(process.stdout); + n.stderr.pipe(prepend_transform_1.default(chalk.red.bold(' [suman child stderr] '), { omitWhitespace: true })) + .once('error', onError).pipe(process.stderr); + } + if (true || sumanOpts.$useTAPOutput) { + n.tapOutputIsComplete = false; + n.stdout.pipe(handle_tap_1.getTapParser()) + .on('error', function (e) { + _suman.logError('error parsing TAP output =>', su.getCleanErrorString(e)); + }) + .once('finish', function () { + n.tapOutputIsComplete = true; + process.nextTick(function () { + n.emit('tap-output-is-complete', true); + }); + }); + n.stdout.pipe(handle_tap_json_1.getTapJSONParser()) + .on('error', function (e) { + _suman.logError('error parsing TAP JSON output =>', su.getCleanErrorString(e)); + }); + } + n.stdio[2].setEncoding('utf-8'); + n.stdio[2].on('data', function (data) { + var d = String(data).split('\n').filter(function (line) { + return String(line).length; + }) + .map(function (line) { + return '[' + n.shortTestPath + '] ' + line; + }) + .join('\n'); + _suman.sumanStderrStream.write('\n' + d); + if (_suman.weAreDebugging) { + console.log('pid => ', n.pid, 'stderr => ', d); + } + }); + } + else { + if (su.vgt(2)) { + _suman.logWarning('Stdio object not available for child process.'); + } + } + _suman.log(chalk.black('File has just started running =>'), chalk.grey.bold("'" + file + "'.")); + n.dateStartedMillis = gd.startDate = Date.now(); + n.once('exit', onExitFn(n, gd, cb)); + }); + }; +}; diff --git a/lib/runner-helpers/multi-process/add-to-run-queue.ts b/lib/runner-helpers/multi-process/add-to-run-queue.ts new file mode 100644 index 00000000..3fda5479 --- /dev/null +++ b/lib/runner-helpers/multi-process/add-to-run-queue.ts @@ -0,0 +1,347 @@ +'use strict'; + +//dts +import {IGlobalSumanObj} from "suman-types/dts/global"; +import {IGanttData} from "../socket-cp-hash"; +import {IRunnerRunFn, ISumanChildProcess} from "suman-types/dts/runner"; +import {AsyncQueue} from "async"; + +//polyfills +const process = require('suman-browser-polyfills/modules/process'); +const global = require('suman-browser-polyfills/modules/global'); + +//core +import util = require('util'); +import path = require('path'); +import cp = require('child_process'); + +//npm +import async = require('async'); +import chalk = require('chalk'); +import semver = require('semver'); +import su = require('suman-utils'); + +//project +const _suman: IGlobalSumanObj = global.__suman = (global.__suman || {}); +import {getTapParser} from '../handle-tap'; +import {getTapJSONParser} from '../handle-tap-json'; +import pt from 'prepend-transform'; +import uuidV4 = require('uuid/v4'); +import {findPathOfRunDotSh} from '../runner-utils' +import {constants} from "../../../config/suman-constants"; +const runChildPath = require.resolve(__dirname + '/../run-child.js'); + +////////////////////////////////////////////////////////////////////// + +export const makeAddToRunQueue = function (runnerObj: Object, args: Array, runQueue: AsyncQueue, + projectRoot: string, cpHash: Object, forkedCPs: Array, + onExitFn: Function) { + + const {sumanOpts, sumanConfig, maxProcs} = _suman; + const execFile = path.resolve(__dirname + '/../run-child.js'); + const istanbulExecPath = _suman.istanbulExecPath || 'istanbul'; + const isStdoutSilent = sumanOpts.stdout_silent || sumanOpts.silent; + const isStderrSilent = sumanOpts.stderr_silent || sumanOpts.silent; + const debugChildren = sumanOpts.debug_child || sumanOpts.inspect_child; + const inheritRunStdio = debugChildren || sumanOpts.inherit_stdio || + sumanOpts.inherit_all_stdio || process.env.SUMAN_INHERIT_STDIO === 'yes'; + let childId = 1; + + const sumanEnv = Object.assign({}, process.env, { + SUMAN_RUN_CHILD_STATIC_PATH: runChildPath, + SUMAN_CONFIG: JSON.stringify(sumanConfig), + SUMAN_OPTS: JSON.stringify(sumanOpts), + SUMAN_RUNNER: 'yes', + SUMAN_PROJECT_ROOT: projectRoot, + SUMAN_RUN_ID: _suman.runId, + SUMAN_RUNNER_TIMESTAMP: _suman.timestamp, + NPM_COLORS: process.env.NPM_COLORS || (sumanOpts.no_color ? 'no' : 'yes'), + SUMAN_SOCKETIO_SERVER_PORT: _suman.socketServerPort > 0 ? _suman.socketServerPort : undefined + }); + + return function (file: string, shortFile: string, stdout: string, gd: IGanttData) { + + runQueue.push(function (cb: Function) { + + if (runnerObj.bailed) { + // should not fork any more child processes if we have bailed + if (sumanOpts.verbosity > 4) { + _suman.log('"--bailed" option was passed and was tripped, ' + + 'no more child processes will be forked.'); + } + return; + } + + const argz = JSON.parse(JSON.stringify(args)); + + const execArgz = ['--expose-gc']; + + if (sumanOpts.debug_child) { + execArgz.push('--debug=' + (5303 + runnerObj.processId++)); + execArgz.push('--debug-brk'); + } + + if (sumanOpts.inspect_child) { + if (semver.gt(process.version, '7.8.0')) { + execArgz.push('--inspect-brk=' + (5303 + runnerObj.processId++)); + } + else { + execArgz.push('--inspect=' + (5303 + runnerObj.processId++)); + execArgz.push('--debug-brk'); + } + } + + let execArgs; + + if (execArgs = sumanOpts.exec_arg) { + execArgs.forEach(function (n: string) { + n && execArgz.push(String(n).trim()); + }); + + String(execArgs).split(/S+/).forEach(function (n) { + n && execArgz.push('--' + String(n).trim()); + }); + } + + const $execArgz = execArgz.filter(function (e, i) { + // filter out duplicate command line args + if (execArgz.indexOf(e) !== i) { + console.error('\n', chalk.yellow(' => Warning you have duplicate items in your exec args => '), + '\n' + util.inspect(execArgz), '\n'); + } + return true; + }); + + let n: ISumanChildProcess, hashbang = false; + + const extname = path.extname(shortFile); + + let $childId = childId++; + let childUuid = uuidV4(); + const inherit = _suman.$forceInheritStdio ? 'inherit' : ''; + + if (inherit) { + _suman.log('we are inheriting stdio of child, because of sumanception.'); + } + + let cpOptions = { + detached: false, + cwd: projectRoot, + // cwd: sumanOpts.force_cwd_to_be_project_root ? projectRoot : path.dirname(file), + stdio: [ + 'ignore', + inherit || (isStdoutSilent ? 'ignore' : 'pipe'), + inherit || (isStderrSilent ? 'ignore' : 'pipe'), + // 'ipc' => we don't need IPC anymore, but also can we assume 'ipc' is ignored if not a .js file? + ], + env: Object.assign({}, sumanEnv, { + SUMAN_CHILD_TEST_PATH: file, + SUMAN_CHILD_TEST_PATH_TARGET: file, + SUMAN_TRANSFORM_STDOUT: stdout, + SUMAN_CHILD_ID: String($childId), + SUMAN_CHILD_UUID: String($childId) + }) + }; + + // we run the file directly, hopefully it has a hashbang + let sh = findPathOfRunDotSh(file); + + if (sh) { + + _suman.log(chalk.bgWhite.underline('Suman has found a @run.sh file => '), chalk.bold(sh)); + + //force to project root + cpOptions.cwd = projectRoot; + + try { + fs.chmodSync(sh, 0o777); + } + catch (err) { + + } + + if (sumanOpts.coverage) { + _suman.logWarning(chalk.magenta('coverage option was set to true, but we are running your tests via @run.sh.')); + _suman.logWarning(chalk.magenta('so in this case, you will need to run your coverage call via @run.sh.')); + } + + n = cp.spawn(sh, argz, cpOptions) as ISumanChildProcess; + } + else { + + if ('.js' === extname) { + + if (sumanOpts.coverage) { + let coverageDir = path.resolve(_suman.projectRoot + '/coverage/' + String(shortFile).replace(/\//g, '-')); + n = cp.spawn(istanbulExecPath, + //'--include-all-sources' + ['cover', execFile, '--dir', coverageDir, '--'].concat(args), cpOptions) as ISumanChildProcess; + } + else { + argz.unshift(execFile); + let argzz = $execArgz.concat(argz); // append exec args to beginning + n = cp.spawn('node', argzz, cpOptions) as ISumanChildProcess; + } + + } + else { + // .sh .bash .py, perl, ruby, etc + _suman.log(`perl bash python or ruby file? '${chalk.magenta(file)}'`); + hashbang = true; + n = cp.spawn(file, argz, cpOptions) as ISumanChildProcess; + } + } + + cpHash[$childId] = n; + + if (!_suman.weAreDebugging) { + n.to = setTimeout(function () { + _suman.logError(`Suman killed a child process because it timed out: '${n.fileName || n.filename}'.`); + n.kill('SIGINT'); + setTimeout(function () { + // note that we wait 8 seconds for the child process to clean up before sending it a SIGKILL signal + n.kill('SIGKILL'); + }, 8000); + }, constants.DEFAULT_CHILD_PROCESS_TIMEOUT); + } + + n.testPath = file; + n.shortTestPath = shortFile; + forkedCPs.push(n); + + n.on('message', function (msg) { + _suman.logError('Warning - Suman runner does not handle standard Node.js IPC messages.'); + }); + + n.on('error', function (err) { + _suman.logError('error spawning child process => ', err.stack || err); + if (hashbang) { + console.error('\n'); + console.error(' => The supposed test script file with the following path may not have a hashbang => '); + console.error(chalk.magenta.bold(file)); + console.error(' => A hashbang is necessary for non-.js files and when there is no accompanying @run.sh file.'); + console.error(' => Without a hashbang, Suman (and your OS) will not know how to run the file.'); + console.error(' => See sumanjs.org for more information.'); + } + }); + + if (n.stdio && n.stdout && n.stderr) { + + if (inherit) { + _suman.logError('n.stdio is defined even though we are in sumanception territory.'); + } + + n.stdout.setEncoding('utf8'); + n.stderr.setEncoding('utf8'); + + if (false && (sumanOpts.log_stdio_to_files || sumanOpts.log_stdout_to_files || sumanOpts.log_stderr_to_files)) { + + let onError = function (e: Error) { + _suman.logError('\n', su.getCleanErrorString(e), '\n'); + }; + + let temp = su.removePath(file, _suman.projectRoot); + let onlyFile = String(temp).replace(/\//g, '.'); + let logfile = path.resolve(f + '/' + onlyFile + '.log'); + let fileStrm = fs.createWriteStream(logfile); + + console.log('logFile => ', logfile); + + if (sumanOpts.log_stdio_to_files || sumanOpts.log_stderr_to_files) { + n.stderr.pipe(fileStrm).once('error', onError); + } + + if (sumanOpts.log_stdio_to_files || sumanOpts.log_stdout_to_files) { + n.stdout.pipe(fileStrm).once('error', onError); + } + } + + if (inheritRunStdio) { + + let onError = function (e: Error) { + _suman.logError('\n', su.getCleanErrorString(e), '\n'); + }; + + n.stdout.pipe(pt(chalk.cyan(' [suman child stdout] '))) + .once('error', onError).pipe(process.stdout); + n.stderr.pipe(pt(chalk.red.bold(' [suman child stderr] '), {omitWhitespace: true})) + .once('error', onError).pipe(process.stderr); + } + + if (true || sumanOpts.$useTAPOutput) { + + n.tapOutputIsComplete = false; + + n.stdout.pipe(getTapParser()) + .on('error', function (e: Error) { + _suman.logError('error parsing TAP output =>', su.getCleanErrorString(e)); + }) + .once('finish', function () { + n.tapOutputIsComplete = true; + process.nextTick(function () { + n.emit('tap-output-is-complete', true); + }); + }); + + n.stdout.pipe(getTapJSONParser()) + .on('error', function (e: Error) { + _suman.logError('error parsing TAP JSON output =>', su.getCleanErrorString(e)); + }) + + } + + n.stdio[2].setEncoding('utf-8'); + n.stdio[2].on('data', function (data) { + + const d = String(data).split('\n').filter(function (line) { + return String(line).length; + }) + .map(function (line) { + return '[' + n.shortTestPath + '] ' + line; + }) + .join('\n'); + + _suman.sumanStderrStream.write('\n' + d); + + if (_suman.weAreDebugging) { //TODO: add check for NODE_ENV=dev_local_debug + //TODO: go through code and make sure that no console.log statements should in fact be console.error + console.log('pid => ', n.pid, 'stderr => ', d); + } + }); + + } + else { + if (su.vgt(2)) { + _suman.logWarning('Stdio object not available for child process.'); + } + } + + _suman.log(chalk.black('File has just started running =>'), chalk.grey.bold(`'${file}'.`)); + n.dateStartedMillis = gd.startDate = Date.now(); + n.once('exit', onExitFn(n, gd, cb)); + + // if (waitForAllTranformsToFinish) { + // + // if (forkedCPs.length < 1 && runnerObj.queuedCPs.length > 0) { + // throw new Error('Suman internal error => fatal start order algorithm error, ' + + // 'please file an issue on Github, thanks.'); + // } + // + // if (forkedCPs.length < 1) { + // noFilesFoundError(files); + // } + // else { + // const totalCount = forkedCPs.length + runnerObj.queuedCPs.length; + // const suites = totalCount === 1 ? 'suite' : 'suites'; + // const processes = totalCount === 1 ? 'process' : 'processes'; + // resultBroadcaster.emit(String(events.RUNNER_INITIAL_SET), forkedCPs, processes, suites); + // const addendum = maxProcs < totalCount ? ' with no more than ' + maxProcs + ' running at a time.' : ''; + // resultBroadcaster.emit(String(events.RUNNER_OVERALL_SET), totalCount, processes, suites, addendum); + // } + // + // } + + }); + } + +}; diff --git a/lib/runner-helpers/multi-process/add-to-transpile-queue.d.ts b/lib/runner-helpers/multi-process/add-to-transpile-queue.d.ts new file mode 100644 index 00000000..a0c1ab8a --- /dev/null +++ b/lib/runner-helpers/multi-process/add-to-transpile-queue.d.ts @@ -0,0 +1,4 @@ +import { ITableRows } from "suman-types/dts/runner"; +import { AsyncQueue } from "async"; +import { IGanttHash } from "../socket-cp-hash"; +export declare const makeAddToTranspileQueue: (f: string, transpileQueue: AsyncQueue, tableRows: ITableRows, ganttHash: IGanttHash, projectRoot: string) => (fileShortAndFull: string[][]) => void; diff --git a/lib/runner-helpers/multi-process/add-to-transpile-queue.js b/lib/runner-helpers/multi-process/add-to-transpile-queue.js new file mode 100644 index 00000000..0b581f75 --- /dev/null +++ b/lib/runner-helpers/multi-process/add-to-transpile-queue.js @@ -0,0 +1,117 @@ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var process = require('suman-browser-polyfills/modules/process'); +var global = require('suman-browser-polyfills/modules/global'); +var path = require("path"); +var cp = require("child_process"); +var chalk = require("chalk"); +var su = require("suman-utils"); +var prepend_transform_1 = require("prepend-transform"); +var uuidV4 = require("uuid/v4"); +var _suman = global.__suman = (global.__suman || {}); +var runner_utils_1 = require("../runner-utils"); +exports.makeAddToTranspileQueue = function (f, transpileQueue, tableRows, ganttHash, projectRoot) { + var sumanOpts = _suman.sumanOpts; + var inheritTransformStdio = sumanOpts.inherit_all_stdio || + sumanOpts.inherit_transform_stdio || process.env.SUMAN_INHERIT_STDIO; + return function (fileShortAndFull) { + var uuid = String(uuidV4()); + var file = fileShortAndFull[0]; + var shortFile = fileShortAndFull[1]; + var filePathFromProjectRoot = fileShortAndFull[2]; + var basename = file.length > 28 ? ' ' + String(file).substring(Math.max(0, file.length - 28)) + ' ' : file; + var m = String(basename).match(/\//g); + if (m && m.length > 1) { + var arr = String(basename).split(''); + var i = 0; + while (arr[i] !== '/') { + arr.shift(); + } + basename = arr.join(''); + } + tableRows[String(shortFile)] = { + actualExitCode: null, + shortFilePath: shortFile, + tableData: null, + defaultTableData: { + SUITES_DESIGNATOR: basename + } + }; + var gd = ganttHash[uuid] = { + uuid: uuid, + fullFilePath: String(file), + shortFilePath: String(shortFile), + filePathFromProjectRoot: String(filePathFromProjectRoot), + }; + var tr = (sumanOpts.no_transpile !== true) && runner_utils_1.findPathOfTransformDotSh(file); + if (tr) { + _suman.log(chalk.bgWhite.underline('Suman has found a @transform.sh file => '), chalk.bold(tr)); + transpileQueue.push(function (cb) { + su.makePathExecutable(tr, function (err) { + if (err) { + return cb(err); + } + gd.transformStartDate = Date.now(); + var k = cp.spawn(tr, [], { + cwd: projectRoot, + env: Object.assign({}, process.env, { + SUMAN_TEST_PATHS: JSON.stringify([file]), + SUMAN_CHILD_TEST_PATH: file + }) + }); + k.once('error', cb); + k.stderr.setEncoding('utf8'); + k.stdout.setEncoding('utf8'); + var ln = String(_suman.projectRoot).length; + if (false) { + var onError = function (e) { + _suman.logError('\n', su.getCleanErrorString(e), '\n'); + }; + var temp = su.removePath(file, _suman.projectRoot); + var onlyFile = String(temp).replace(/\//g, '.'); + var logfile = path.resolve(f + '/' + onlyFile + '.log'); + var fileStrm = fs.createWriteStream(logfile); + k.stderr.pipe(fileStrm).once('error', onError); + k.stdout.pipe(fileStrm).once('error', onError); + } + if (inheritTransformStdio) { + var onError = function (e) { + _suman.logError('\n', su.getCleanErrorString(e), '\n'); + }; + var stderrPrepend = " [" + chalk.red('transform process stderr:') + " " + chalk.red.bold(String(file.slice(ln))) + "] "; + k.stderr.pipe(prepend_transform_1.pt(stderrPrepend, { omitWhitespace: true })).once('error', onError).pipe(process.stderr); + var stdoutPrepend = " [" + chalk.yellow('transform process stdout:') + " " + chalk.gray.bold(String(file.slice(ln))) + "] "; + k.stdout.pipe(prepend_transform_1.pt(stdoutPrepend)).once('error', onError).pipe(process.stdout); + } + var stdout = ''; + k.stdout.on('data', function (data) { + stdout += data; + }); + var stderr = ''; + k.stderr.on('data', function (data) { + stderr += data; + }); + k.once('exit', function (code) { + console.log(chalk.red('exxxxited.')); + gd.transformEndDate = Date.now(); + if (code > 0) { + cb(new Error("the @transform.sh process, for file " + file + ",\nexitted with non-zero exit code. :( \n \n To see the stderr, use --inherit-stdio.")); + } + else { + cb(null, file, shortFile, stdout, stderr, gd); + } + }); + }); + }); + } + else { + gd.transformStartDate = gd.transformEndDate = null; + gd.wasTransformed = false; + transpileQueue.unshift(function (cb) { + setImmediate(function () { + cb(null, file, shortFile, '', '', gd); + }); + }); + } + }; +}; diff --git a/lib/runner-helpers/multi-process/add-to-transpile-queue.ts b/lib/runner-helpers/multi-process/add-to-transpile-queue.ts new file mode 100644 index 00000000..d2278e1b --- /dev/null +++ b/lib/runner-helpers/multi-process/add-to-transpile-queue.ts @@ -0,0 +1,179 @@ +'use strict'; + +//dts +import {IGlobalSumanObj} from "suman-types/dts/global"; +import {ITableRows} from "suman-types/dts/runner"; +import {AsyncQueue} from "async"; + +//polyfills +const process = require('suman-browser-polyfills/modules/process'); +const global = require('suman-browser-polyfills/modules/global'); + +//core +import path = require('path'); +import cp = require('child_process'); + +//npm +import async = require('async'); +import chalk = require('chalk'); +import su = require('suman-utils'); +import {pt} from 'prepend-transform'; +import uuidV4 = require('uuid/v4'); + +//project +const _suman: IGlobalSumanObj = global.__suman = (global.__suman || {}); +import {findPathOfTransformDotSh} from '../runner-utils'; +import {IGanttHash} from "../socket-cp-hash"; + +/////////////////////////////////////////////////////////////////////////////////////////////// + +export const makeAddToTranspileQueue = function (f: string, transpileQueue: AsyncQueue, tableRows: ITableRows, + ganttHash: IGanttHash, projectRoot: string) { + + const {sumanOpts} = _suman; + const inheritTransformStdio = sumanOpts.inherit_all_stdio || + sumanOpts.inherit_transform_stdio || process.env.SUMAN_INHERIT_STDIO; + + return function (fileShortAndFull: Array>) { + + const uuid = String(uuidV4()); + const file = fileShortAndFull[0]; + const shortFile = fileShortAndFull[1]; + const filePathFromProjectRoot = fileShortAndFull[2]; + + let basename = file.length > 28 ? ' ' + String(file).substring(Math.max(0, file.length - 28)) + ' ' : file; + + const m = String(basename).match(/\//g); + + if (m && m.length > 1) { + const arr = String(basename).split(''); + let i = 0; + while (arr[i] !== '/') { + arr.shift(); + } + basename = arr.join(''); + } + + //TODO: we should used uuid here instead + tableRows[String(shortFile)] = { + actualExitCode: null, + shortFilePath: shortFile, + tableData: null, + defaultTableData: { + SUITES_DESIGNATOR: basename + } + }; + + const gd = ganttHash[uuid] = { + uuid: uuid, + fullFilePath: String(file), + shortFilePath: String(shortFile), + filePathFromProjectRoot: String(filePathFromProjectRoot), + // transformStartDate: null, + // transformEndDate: null, + // startDate: null, + // endDate: null + } as any; + + const tr = (sumanOpts.no_transpile !== true) && findPathOfTransformDotSh(file); + + if (tr) { + + _suman.log(chalk.bgWhite.underline('Suman has found a @transform.sh file => '), chalk.bold(tr)); + + transpileQueue.push(function (cb: Function) { + + su.makePathExecutable(tr, function (err: Error) { + + if (err) { + return cb(err); + } + + gd.transformStartDate = Date.now(); + + let k = cp.spawn(tr, [], { + cwd: projectRoot, + env: Object.assign({}, process.env, { + SUMAN_TEST_PATHS: JSON.stringify([file]), + SUMAN_CHILD_TEST_PATH: file + }) + }); + + k.once('error', cb); + k.stderr.setEncoding('utf8'); + k.stdout.setEncoding('utf8'); + + const ln = String(_suman.projectRoot).length; + + if (false) { + + let onError = function (e: Error) { + _suman.logError('\n', su.getCleanErrorString(e), '\n'); + }; + + const temp = su.removePath(file, _suman.projectRoot); + const onlyFile = String(temp).replace(/\//g, '.'); + const logfile = path.resolve(f + '/' + onlyFile + '.log'); + let fileStrm = fs.createWriteStream(logfile); + k.stderr.pipe(fileStrm).once('error', onError); + k.stdout.pipe(fileStrm).once('error', onError); + } + + if (inheritTransformStdio) { + + let onError = function (e: Error) { + _suman.logError('\n', su.getCleanErrorString(e), '\n'); + }; + + let stderrPrepend = ` [${chalk.red('transform process stderr:')} ${chalk.red.bold(String(file.slice(ln)))}] `; + k.stderr.pipe(pt(stderrPrepend, {omitWhitespace: true})).once('error', onError).pipe(process.stderr); + + let stdoutPrepend = ` [${chalk.yellow('transform process stdout:')} ${chalk.gray.bold(String(file.slice(ln)))}] `; + k.stdout.pipe(pt(stdoutPrepend)).once('error', onError).pipe(process.stdout); + } + + let stdout = ''; + k.stdout.on('data', function (data: string) { + stdout += data; + }); + + let stderr = ''; + k.stderr.on('data', function (data: string) { + stderr += data; + }); + + k.once('exit', function (code: number) { + + console.log(chalk.red('exxxxited.')); + + gd.transformEndDate = Date.now(); + + if (code > 0) { + cb(new Error(`the @transform.sh process, for file ${file},\nexitted with non-zero exit code. :( + \n To see the stderr, use --inherit-stdio.`)); + } + else { + cb(null, file, shortFile, stdout, stderr, gd); + } + + }); + + }); + + }); + + } + else { + // we don't need to run any transform, so we run right away + gd.transformStartDate = gd.transformEndDate = null; + gd.wasTransformed = false; + transpileQueue.unshift(function (cb: Function) { + setImmediate(function () { + // there is no applicable stdout/stderr, so we pass empty string + cb(null, file, shortFile, '', '', gd); + }); + }); + } + } + +}; diff --git a/lib/runner-helpers/multi-process/run-queue.d.ts b/lib/runner-helpers/multi-process/run-queue.d.ts new file mode 100644 index 00000000..2a33b447 --- /dev/null +++ b/lib/runner-helpers/multi-process/run-queue.d.ts @@ -0,0 +1,2 @@ +import async = require('async'); +export declare const makeRunQueue: () => async.AsyncQueue<{}>; diff --git a/lib/runner-helpers/multi-process/run-queue.js b/lib/runner-helpers/multi-process/run-queue.js new file mode 100644 index 00000000..6baf81aa --- /dev/null +++ b/lib/runner-helpers/multi-process/run-queue.js @@ -0,0 +1,12 @@ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var process = require('suman-browser-polyfills/modules/process'); +var global = require('suman-browser-polyfills/modules/global'); +var async = require("async"); +var _suman = global.__suman = (global.__suman || {}); +exports.makeRunQueue = function () { + var sumanConfig = _suman.sumanConfig, maxProcs = _suman.maxProcs; + return async.queue(function (task, cb) { + task(cb); + }, maxProcs); +}; diff --git a/lib/runner-helpers/multi-process/run-queue.ts b/lib/runner-helpers/multi-process/run-queue.ts new file mode 100644 index 00000000..a89151ca --- /dev/null +++ b/lib/runner-helpers/multi-process/run-queue.ts @@ -0,0 +1,28 @@ +'use strict'; + +//dts +import {IGlobalSumanObj} from "suman-types/dts/global"; + +//polyfills +const process = require('suman-browser-polyfills/modules/process'); +const global = require('suman-browser-polyfills/modules/global'); + +//npm +import async = require('async'); + +//project +const _suman: IGlobalSumanObj = global.__suman = (global.__suman || {}); + +////////////////////////////////////////////////////////////////////// + +export const makeRunQueue = function () { + + const {sumanConfig, maxProcs} = _suman; + + return async.queue(function (task, cb) { + + task(cb); + + }, maxProcs); + +}; diff --git a/lib/runner-helpers/multi-process/transpile-queue.d.ts b/lib/runner-helpers/multi-process/transpile-queue.d.ts new file mode 100644 index 00000000..f20615fe --- /dev/null +++ b/lib/runner-helpers/multi-process/transpile-queue.d.ts @@ -0,0 +1,3 @@ +import async = require('async'); +export declare const getTranspileQueue: () => any; +export declare const makeTranspileQueue: (failedTransformObjects: any, runFile: any, queuedTestFns: any) => async.AsyncQueue; diff --git a/lib/runner-helpers/multi-process/transpile-queue.js b/lib/runner-helpers/multi-process/transpile-queue.js new file mode 100644 index 00000000..ee6e9605 --- /dev/null +++ b/lib/runner-helpers/multi-process/transpile-queue.js @@ -0,0 +1,34 @@ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var process = require('suman-browser-polyfills/modules/process'); +var global = require('suman-browser-polyfills/modules/global'); +var async = require("async"); +var chalk = require("chalk"); +var _suman = global.__suman = (global.__suman || {}); +var q = null; +exports.getTranspileQueue = function () { + return q; +}; +exports.makeTranspileQueue = function (failedTransformObjects, runFile, queuedTestFns) { + var sumanOpts = _suman.sumanOpts, sumanConfig = _suman.sumanConfig, projectRoot = _suman.projectRoot; + var waitForAllTranformsToFinish = sumanOpts.wait_for_all_transforms; + return q = async.queue(function (task, cb) { + task(function (err, file, shortFile, stdout, stderr, gd) { + if (err) { + _suman.logError('tranpile error => ', err.stack || err); + failedTransformObjects.push({ err: err, file: file, shortFile: shortFile, stdout: stdout, stderr: stderr }); + return; + } + setImmediate(cb); + console.log(chalk.red('pushing file '), file); + if (waitForAllTranformsToFinish) { + queuedTestFns.push(function () { + runFile(file, shortFile, stdout, gd); + }); + } + else { + runFile(file, shortFile, stdout, gd); + } + }); + }, 3); +}; diff --git a/lib/runner-helpers/multi-process/transpile-queue.ts b/lib/runner-helpers/multi-process/transpile-queue.ts new file mode 100644 index 00000000..0c0e4447 --- /dev/null +++ b/lib/runner-helpers/multi-process/transpile-queue.ts @@ -0,0 +1,58 @@ +'use strict'; + +//dts +import {IGlobalSumanObj} from "suman-types/dts/global"; +import {IGanttData} from "../socket-cp-hash"; + +//polyfills +const process = require('suman-browser-polyfills/modules/process'); +const global = require('suman-browser-polyfills/modules/global'); + +//npm +import async = require('async'); +import chalk = require('chalk'); + +//project +const _suman: IGlobalSumanObj = global.__suman = (global.__suman || {}); + +////////////////////////////////////////////////////////////////////////// + +let q = null; + +export const getTranspileQueue = function(){ + return q; +}; + +export const makeTranspileQueue = function (failedTransformObjects, runFile, queuedTestFns) { + + const {sumanOpts, sumanConfig, projectRoot} = _suman; + const waitForAllTranformsToFinish = sumanOpts.wait_for_all_transforms; + + return q = async.queue(function (task: Function, cb: Function) { + + task(function (err: Error, file: string, shortFile: string, stdout: string, stderr: string, gd: IGanttData) { + + if (err) { + _suman.logError('tranpile error => ', err.stack || err); + failedTransformObjects.push({err, file, shortFile, stdout, stderr}); + return; + } + + setImmediate(cb); + + console.log(chalk.red('pushing file '), file); + + if (waitForAllTranformsToFinish) { + queuedTestFns.push(function () { + runFile(file, shortFile, stdout, gd); + }); + } + else { + runFile(file, shortFile, stdout, gd); + } + + }); + + }, 3); + +}; diff --git a/lib/runner-helpers/multiple-process-each-on-exit.d.ts b/lib/runner-helpers/multiple-process-each-on-exit.d.ts index 8983a3e3..e8906df7 100755 --- a/lib/runner-helpers/multiple-process-each-on-exit.d.ts +++ b/lib/runner-helpers/multiple-process-each-on-exit.d.ts @@ -1,4 +1,4 @@ import { IRunnerObj, ISumanChildProcess, ITableRows } from "suman-types/dts/runner"; import { ISumanCPMessages } from "./handle-multiple-processes"; import { IGanttData } from "./socket-cp-hash"; -export default function (n: ISumanChildProcess, runnerObj: IRunnerObj, tableRows: ITableRows, messages: Array, forkedCPs: Array, beforeExitRunOncePost: Function, makeExit: Function, gd: IGanttData): (code: number, signal: number) => void; +export declare const makeOnExitFn: (runnerObj: IRunnerObj, tableRows: ITableRows, messages: ISumanCPMessages[], forkedCPs: ISumanChildProcess[], beforeExitRunOncePost: Function, makeExit: Function, runQueue: any) => (n: ISumanChildProcess, gd: IGanttData, cb: Function) => (code: number, signal: number) => void; diff --git a/lib/runner-helpers/multiple-process-each-on-exit.js b/lib/runner-helpers/multiple-process-each-on-exit.js index 30416ea7..26052442 100755 --- a/lib/runner-helpers/multiple-process-each-on-exit.js +++ b/lib/runner-helpers/multiple-process-each-on-exit.js @@ -16,90 +16,95 @@ var _suman = global.__suman = (global.__suman || {}); var runnerUtils = require('./runner-utils'); var coverage_reporting_1 = require("./coverage-reporting"); var constants = require('../../config/suman-constants').constants; -var debug = require('suman-debug')('s:runner'); +var transpile_queue_1 = require("./multi-process/transpile-queue"); var resultBroadcaster = _suman.resultBroadcaster = (_suman.resultBroadcaster || new EE()); -function default_1(n, runnerObj, tableRows, messages, forkedCPs, beforeExitRunOncePost, makeExit, gd) { - return function (code, signal) { - n.dateEndedMillis = gd.endDate = Date.now(); - n.sumanExitCode = gd.sumanExitCode = code; - n.removeAllListeners(); - var sumanOpts = _suman.sumanOpts; - var handleBlocking = runnerObj.handleBlocking; - resultBroadcaster.emit(String(suman_events_1.events.TEST_FILE_CHILD_PROCESS_EXITED), { - testPath: n.testPath, - exitCode: code - }); - if (su.isSumanDebug() || su.vgt(5)) { - _suman.log(chalk.black.bgYellow("process given by => '" + n.shortTestPath + "' exited with code: " + code + " ")); - } - if (su.isSumanDebug()) { - _suman.timeOfMostRecentExit = Date.now(); - } - var originalExitCode = code; - if (n.expectedExitCode !== undefined) { - if (code === n.expectedExitCode) { - code = 0; +exports.makeOnExitFn = function (runnerObj, tableRows, messages, forkedCPs, beforeExitRunOncePost, makeExit, runQueue) { + return function (n, gd, cb) { + var weHaveBailed = function (code) { + if (code > 0 && _suman.sumanOpts.bail) { + return runnerObj.bailed = true; } - } - runnerObj.doneCount++; - messages.push({ code: code, signal: signal }); - tableRows[n.shortTestPath].actualExitCode = n.expectedExitCode !== undefined ? - (n.expectedExitCode + '/' + originalExitCode) : originalExitCode; - if ((runnerObj.bailed = (code > 0 && _suman.sumanOpts.bail)) || - (runnerObj.doneCount >= forkedCPs.length && runnerObj.queuedCPs.length < 1)) { - if (runnerObj.bailed) { - console.log('\n'); - _suman.logError(chalk.magenta('We have ' + chalk.red.bold('bailed') + - ' the test runner because a child process experienced an error and exitted with a non-zero code.')); - _suman.logError(chalk.magenta('Since we have bailed, Suman will send a SIGTERM signal ' + - 'to any outstanding child processes.')); - forkedCPs.forEach(function (n) { - n.kill('SIGTERM'); - setTimeout(function () { - n.kill('SIGKILL'); - }, 3000); - }); + }; + var allDone = function (q) { + return q.length() < 1 && q.running() < 1; + }; + return function (code, signal) { + cb(null); + n.dateEndedMillis = gd.endDate = Date.now(); + n.sumanExitCode = gd.sumanExitCode = code; + n.removeAllListeners(); + var sumanOpts = _suman.sumanOpts; + var transpileQueue = transpile_queue_1.getTranspileQueue(); + resultBroadcaster.emit(String(suman_events_1.events.TEST_FILE_CHILD_PROCESS_EXITED), { + testPath: n.testPath, + exitCode: code + }); + if (su.isSumanDebug() || su.vgt(5)) { + _suman.log(chalk.black.bgYellow("process given by => '" + n.shortTestPath + "' exited with code: " + code + " ")); } - else { - if (sumanOpts.verbosity > 4) { - console.log('\n'); - _suman.log(chalk.gray.bold.underline(' All scheduled child processes have exited.')); - console.log('\n'); + if (su.isSumanDebug()) { + _suman.timeOfMostRecentExit = Date.now(); + } + var originalExitCode = code; + if (n.expectedExitCode !== undefined) { + if (code === n.expectedExitCode) { + code = 0; } } - runnerObj.endTime = Date.now(); - runnerObj.listening = false; - var onTAPOutputComplete = function () { - var tasks = [ - beforeExitRunOncePost, - coverage_reporting_1.handleTestCoverageReporting - ]; - async.parallel(tasks, function (err) { - err && _suman.logError(err.stack || err); - makeExit(messages, { - total: runnerObj.endTime - _suman.startTime, - runner: runnerObj.endTime - runnerObj.startTime + runnerObj.doneCount++; + messages.push({ code: code, signal: signal }); + tableRows[n.shortTestPath].actualExitCode = n.expectedExitCode !== undefined ? + (n.expectedExitCode + '/' + originalExitCode) : originalExitCode; + if (weHaveBailed(code) || (allDone(runQueue) && allDone(transpileQueue))) { + if (runnerObj.bailed) { + runQueue.kill(); + transpileQueue.kill(); + console.log('\n'); + _suman.logError(chalk.magenta('We have ' + chalk.red.bold('bailed') + + ' the test runner because a child process experienced an error and exitted with a non-zero code.')); + _suman.logError(chalk.magenta('Since we have bailed, Suman will send a SIGTERM signal ' + + 'to any outstanding child processes.')); + forkedCPs.forEach(function (n) { + n.kill('SIGTERM'); + setTimeout(function () { + n.kill('SIGKILL'); + }, 3000); }); - }); - }; - if ('tapOutputIsComplete' in n) { - if (n.tapOutputIsComplete === true) { - process.nextTick(onTAPOutputComplete); } else { - n.once('tap-output-is-complete', onTAPOutputComplete); + if (sumanOpts.verbosity > 4) { + console.log('\n'); + _suman.log(chalk.gray.bold.underline(' All scheduled child processes have exited.')); + console.log('\n'); + } + } + runnerObj.endTime = Date.now(); + runnerObj.listening = false; + var onTAPOutputComplete = function () { + var tasks = [ + beforeExitRunOncePost, + coverage_reporting_1.handleTestCoverageReporting + ]; + async.parallel(tasks, function (err) { + err && _suman.logError(err.stack || err); + makeExit(messages, { + total: runnerObj.endTime - _suman.startTime, + runner: runnerObj.endTime - runnerObj.startTime + }); + }); + }; + if ('tapOutputIsComplete' in n) { + if (n.tapOutputIsComplete === true) { + process.nextTick(onTAPOutputComplete); + } + else { + n.once('tap-output-is-complete', onTAPOutputComplete); + } + } + else { + process.nextTick(onTAPOutputComplete); } } - else { - process.nextTick(onTAPOutputComplete); - } - } - else { - handleBlocking.releaseNextTests(n.testPath, runnerObj); - if (su.isSumanDebug()) { - _suman.log("Time required to release next test(s) => " + (Date.now() - _suman.timeOfMostRecentExit) + "ms"); - } - } + }; }; -} -exports.default = default_1; +}; diff --git a/lib/runner-helpers/multiple-process-each-on-exit.ts b/lib/runner-helpers/multiple-process-each-on-exit.ts index 77988003..382b3c7c 100755 --- a/lib/runner-helpers/multiple-process-each-on-exit.ts +++ b/lib/runner-helpers/multiple-process-each-on-exit.ts @@ -1,7 +1,8 @@ 'use strict'; + +//dts import {IRunnerObj, IRunnerRunFn, IRunObj, ISumanChildProcess, ITableRows} from "suman-types/dts/runner"; import {IGlobalSumanObj, IPseudoError} from "suman-types/dts/global"; -import * as fs from "fs"; //polyfills const process = require('suman-browser-polyfills/modules/process'); @@ -9,11 +10,9 @@ const global = require('suman-browser-polyfills/modules/global'); //core import {ChildProcess} from "child_process"; - const cp = require('child_process'); const path = require('path'); import util = require('util'); - const EE = require('events'); //npm @@ -23,7 +22,6 @@ import {events} from 'suman-events'; import {ISumanCPMessages} from "./handle-multiple-processes"; import su = require('suman-utils'); import async = require('async'); - const noFilesFoundError = require('../helpers/no-files-found-error'); import * as chalk from 'chalk'; import {IGanttData} from "./socket-cp-hash"; @@ -32,123 +30,133 @@ import {IGanttData} from "./socket-cp-hash"; const _suman: IGlobalSumanObj = global.__suman = (global.__suman || {}); const runnerUtils = require('./runner-utils'); import {handleTestCoverageReporting} from './coverage-reporting'; - const {constants} = require('../../config/suman-constants'); -const debug = require('suman-debug')('s:runner'); +import {getTranspileQueue} from './multi-process/transpile-queue'; const resultBroadcaster = _suman.resultBroadcaster = (_suman.resultBroadcaster || new EE()); ////////////////////////////////////////////////////////////////////////////////////////////////// -export default function (n: ISumanChildProcess, runnerObj: IRunnerObj, tableRows: ITableRows, - messages: Array, forkedCPs: Array, - beforeExitRunOncePost: Function, makeExit: Function, gd: IGanttData) { +export const makeOnExitFn = function (runnerObj: IRunnerObj, tableRows: ITableRows, + messages: Array, forkedCPs: Array, + beforeExitRunOncePost: Function, makeExit: Function, runQueue: any) { - return function (code: number, signal: number) { + return function (n: ISumanChildProcess, gd: IGanttData, cb: Function) { - n.dateEndedMillis = gd.endDate = Date.now(); - n.sumanExitCode = gd.sumanExitCode = code; - n.removeAllListeners(); + let weHaveBailed = function (code: number) { + if (code > 0 && _suman.sumanOpts.bail) { + return runnerObj.bailed = true; + } + }; - const sumanOpts = _suman.sumanOpts; - // handleBlocking gets initialized weirdly in runner.js, but we will deal for now - const handleBlocking = runnerObj.handleBlocking; + let allDone = function (q: Object) { + return q.length() < 1 && q.running() < 1; + }; - resultBroadcaster.emit(String(events.TEST_FILE_CHILD_PROCESS_EXITED), { - testPath: n.testPath, - exitCode: code - }); + return function (code: number, signal: number) { - if (su.isSumanDebug() || su.vgt(5)) { - _suman.log(chalk.black.bgYellow(`process given by => '${n.shortTestPath}' exited with code: ${code} `)); - } + cb(null); // fire run queue callback - if (su.isSumanDebug()) { - _suman.timeOfMostRecentExit = Date.now(); - } + n.dateEndedMillis = gd.endDate = Date.now(); + n.sumanExitCode = gd.sumanExitCode = code; + n.removeAllListeners(); + + const sumanOpts = _suman.sumanOpts; + const transpileQueue = getTranspileQueue(); - const originalExitCode = code; + resultBroadcaster.emit(String(events.TEST_FILE_CHILD_PROCESS_EXITED), { + testPath: n.testPath, + exitCode: code + }); - if (n.expectedExitCode !== undefined) { - if (code === n.expectedExitCode) { - code = 0; + if (su.isSumanDebug() || su.vgt(5)) { + _suman.log(chalk.black.bgYellow(`process given by => '${n.shortTestPath}' exited with code: ${code} `)); + } + + if (su.isSumanDebug()) { + _suman.timeOfMostRecentExit = Date.now(); } - } - runnerObj.doneCount++; - messages.push({code, signal}); + const originalExitCode = code; - tableRows[n.shortTestPath].actualExitCode = n.expectedExitCode !== undefined ? - (n.expectedExitCode + '/' + originalExitCode) : originalExitCode; + if (n.expectedExitCode !== undefined) { + if (code === n.expectedExitCode) { + code = 0; + } + } - if ((runnerObj.bailed = (code > 0 && _suman.sumanOpts.bail)) || - (runnerObj.doneCount >= forkedCPs.length && runnerObj.queuedCPs.length < 1)) { + runnerObj.doneCount++; + messages.push({code, signal}); - if (runnerObj.bailed) { + tableRows[n.shortTestPath].actualExitCode = n.expectedExitCode !== undefined ? + (n.expectedExitCode + '/' + originalExitCode) : originalExitCode; - console.log('\n'); - _suman.logError(chalk.magenta('We have ' + chalk.red.bold('bailed') + - ' the test runner because a child process experienced an error and exitted with a non-zero code.')); + // console.log('transpileQ:', util.inspect(transpileQueue)); - _suman.logError(chalk.magenta('Since we have bailed, Suman will send a SIGTERM signal ' + - 'to any outstanding child processes.')); + if (weHaveBailed(code) || (allDone(runQueue) && allDone(transpileQueue))) { - forkedCPs.forEach(function (n: ISumanChildProcess) { - n.kill('SIGTERM'); - setTimeout(function () { - n.kill('SIGKILL'); - }, 3000); - }); + if (runnerObj.bailed) { - } - else { + runQueue.kill(); + transpileQueue.kill(); - if (sumanOpts.verbosity > 4) { - console.log('\n'); - _suman.log(chalk.gray.bold.underline(' All scheduled child processes have exited.')); console.log('\n'); + _suman.logError(chalk.magenta('We have ' + chalk.red.bold('bailed') + + ' the test runner because a child process experienced an error and exitted with a non-zero code.')); + + _suman.logError(chalk.magenta('Since we have bailed, Suman will send a SIGTERM signal ' + + 'to any outstanding child processes.')); + + forkedCPs.forEach(function (n: ISumanChildProcess) { + n.kill('SIGTERM'); + setTimeout(function () { + n.kill('SIGKILL'); + }, 3000); + }); + + } + else { + + if (sumanOpts.verbosity > 4) { + console.log('\n'); + _suman.log(chalk.gray.bold.underline(' All scheduled child processes have exited.')); + console.log('\n'); + } } - } - runnerObj.endTime = Date.now(); - runnerObj.listening = false; + runnerObj.endTime = Date.now(); + runnerObj.listening = false; - const onTAPOutputComplete = function () { + const onTAPOutputComplete = function () { - const tasks = [ - beforeExitRunOncePost, - handleTestCoverageReporting - ] as any; + const tasks = [ + beforeExitRunOncePost, + handleTestCoverageReporting + ] as any; - async.parallel(tasks, function (err: IPseudoError) { - err && _suman.logError(err.stack || err); - makeExit(messages, { - total: runnerObj.endTime - _suman.startTime, - runner: runnerObj.endTime - runnerObj.startTime + async.parallel(tasks, function (err: IPseudoError) { + err && _suman.logError(err.stack || err); + makeExit(messages, { + total: runnerObj.endTime - _suman.startTime, + runner: runnerObj.endTime - runnerObj.startTime + }); }); - }); - }; + }; - if ('tapOutputIsComplete' in n) { - if (n.tapOutputIsComplete === true) { - process.nextTick(onTAPOutputComplete); + if ('tapOutputIsComplete' in n) { + if (n.tapOutputIsComplete === true) { + process.nextTick(onTAPOutputComplete); + } + else { + n.once('tap-output-is-complete', onTAPOutputComplete); + } } else { - n.once('tap-output-is-complete', onTAPOutputComplete); + process.nextTick(onTAPOutputComplete); } - } - else { - process.nextTick(onTAPOutputComplete); - } - } - else { - handleBlocking.releaseNextTests(n.testPath, runnerObj); - if (su.isSumanDebug()) { - _suman.log(`Time required to release next test(s) => ${Date.now() - _suman.timeOfMostRecentExit}ms`); } - } + } } - -} +}; diff --git a/lib/runner-helpers/single-process/.gitkeep b/lib/runner-helpers/single-process/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/lib/runner.js b/lib/runner.js index 530a275d..d6959bd8 100755 --- a/lib/runner.js +++ b/lib/runner.js @@ -17,7 +17,6 @@ if (false) { stderr_1.apply(process.stderr, arguments); }; } -var path = require("path"); var util = require("util"); var EE = require("events"); var fnArgs = require('function-arguments'); @@ -25,13 +24,11 @@ var mapValues = require('lodash.mapvalues'); var chalk = require("chalk"); var a8b = require('ansi-256-colors'), fg = a8b.fg, bg = a8b.bg; var suman_events_1 = require("suman-events"); -var suman_utils_1 = require("suman-utils"); -var debug = require('suman-debug')('s:runner'); +var su = require("suman-utils"); var _suman = global.__suman = (global.__suman || {}); var integrant_injector_1 = require("./injection/integrant-injector"); var suman_constants_1 = require("../config/suman-constants"); var ascii = require('./helpers/ascii'); -var make_handle_blocking_1 = require("./runner-helpers/make-handle-blocking"); var resultBroadcaster = _suman.resultBroadcaster = (_suman.resultBroadcaster || new EE()); var handle_fatal_message_1 = require("./runner-helpers/handle-fatal-message"); var log_test_result_1 = require("./runner-helpers/log-test-result"); @@ -74,11 +71,11 @@ var beforeExitRunOncePost = make_before_exit_once_post_1.makeBeforeExit(runnerOb process.once('exit', onExit); } process.on('error', function (e) { - _suman.logError(chalk.magenta('Whoops! "error" event in runner process:') + " \n " + chalk.bold(suman_utils_1.default.getCleanErrorString(e))); + _suman.logError(chalk.magenta('Whoops! "error" event in runner process:') + " \n " + chalk.bold(su.getCleanErrorString(e))); }); process.once('uncaughtException', function (e) { debugger; - _suman.logError(chalk.magenta('Suman runner "uncaughtException" event:') + " \n " + chalk.bold(suman_utils_1.default.getCleanErrorString(e))); + _suman.logError(chalk.magenta('Suman runner "uncaughtException" event:') + " \n " + chalk.bold(su.getCleanErrorString(e))); process.exit(1); }); process.on('message', function (data) { @@ -119,58 +116,9 @@ server.on('connection', function (socket) { log_test_result_1.logTestResult(msg, n, socket); }); }); -function handleMessageForSingleProcess(msg, n) { - switch (msg.type) { - case suman_constants_1.constants.runner_message_type.TABLE_DATA: - break; - case suman_constants_1.constants.runner_message_type.INTEGRANT_INFO: - handleIntegrantInfo(msg, n); - break; - case suman_constants_1.constants.runner_message_type.LOG_RESULT: - log_test_result_1.logTestResult(msg, n); - break; - case suman_constants_1.constants.runner_message_type.FATAL: - n.send({ info: 'fatal-message-received' }); - handle_fatal_message_1.handleFatalMessage(msg.data, n); - break; - case suman_constants_1.constants.runner_message_type.NON_FATAL_ERR: - console.error('\n\n ' + chalk.red('non-fatal suite error: ' + msg.msg + '\n')); - break; - case suman_constants_1.constants.runner_message_type.MAX_MEMORY: - console.log('\nmax memory: ' + util.inspect(msg.msg)); - break; - default: - throw new Error(' => Suman internal error => bad msg.type in runner'); - } -} -function handleMessage(msg, n) { - switch (msg.type) { - case suman_constants_1.constants.runner_message_type.TABLE_DATA: - handleTableData(n, msg.data); - break; - case suman_constants_1.constants.runner_message_type.INTEGRANT_INFO: - handleIntegrantInfo(msg, n); - break; - case suman_constants_1.constants.runner_message_type.LOG_RESULT: - log_test_result_1.logTestResult(msg, n); - break; - case suman_constants_1.constants.runner_message_type.FATAL: - n.send({ info: 'fatal-message-received' }); - handle_fatal_message_1.handleFatalMessage(msg.data, n); - break; - case suman_constants_1.constants.runner_message_type.WARNING: - console.error('\n\n ' + chalk.bgYellow('Suman warning: ' + msg.msg + '\n')); - break; - case suman_constants_1.constants.runner_message_type.NON_FATAL_ERR: - console.error('\n\n ' + chalk.red('non-fatal suite error: ' + msg.msg + '\n')); - break; - default: - throw new Error(' => Suman implementation error => Bad msg.type in runner, perhaps the user sent a message with process.send?'); - } -} -var runSingleOrMultipleDirs = handle_multiple_processes_1.makeHandleMultipleProcesses(runnerObj, tableRows, messages, forkedCPs, handleMessage, beforeExitRunOncePost, exit); -var runAllTestsInSingleProcess = makeSingleProcess(runnerObj, handleMessageForSingleProcess, messages, beforeExitRunOncePost, exit); -var runAllTestsInContainer = makeContainerize(runnerObj, handleMessageForSingleProcess, messages, beforeExitRunOncePost, exit); +var runSingleOrMultipleDirs = handle_multiple_processes_1.makeHandleMultipleProcesses(runnerObj, tableRows, messages, forkedCPs, beforeExitRunOncePost, exit); +var runAllTestsInSingleProcess = makeSingleProcess(runnerObj, messages, beforeExitRunOncePost, exit); +var runAllTestsInContainer = makeContainerize(runnerObj, messages, beforeExitRunOncePost, exit); exports.findTestsAndRunThem = function (runObj, runOnce, $order) { debugger; var sumanOpts = _suman.sumanOpts; @@ -178,16 +126,11 @@ exports.findTestsAndRunThem = function (runObj, runOnce, $order) { resultBroadcaster.emit(String(suman_events_1.events.ERRORS_ONLY_OPTION)); } delete process.env.SUMAN_EXTRANEOUS_EXECUTABLE; - var projectRoot = _suman.projectRoot || suman_utils_1.default.findProjectRoot(process.cwd()); - runnerObj.handleBlocking = make_handle_blocking_1.default(mapValues($order, function (val) { - val.testPath = path.resolve(projectRoot + '/' + val.testPath); - return val; - })); process.nextTick(function () { var args = fnArgs(runOnce); - var ret = runOnce.apply(null, integrant_injector_1.default(args)); + var ret = runOnce.apply(null, integrant_injector_1.default(args, null)); if (ret.dependencies) { - if (suman_utils_1.default.isObject(ret.dependencies)) { + if (su.isObject(ret.dependencies)) { runnerObj.depContainerObj = ret.dependencies; } else { diff --git a/lib/runner.ts b/lib/runner.ts index e5895918..cc21d78a 100755 --- a/lib/runner.ts +++ b/lib/runner.ts @@ -52,8 +52,7 @@ const mapValues = require('lodash.mapvalues'); import * as chalk from 'chalk'; const a8b = require('ansi-256-colors'), fg = a8b.fg, bg = a8b.bg; import {events} from 'suman-events'; -import su from 'suman-utils'; -const debug = require('suman-debug')('s:runner'); +import su = require('suman-utils'); //project const _suman: IGlobalSumanObj = global.__suman = (global.__suman || {}); @@ -129,7 +128,6 @@ process.on('error', function (e: IPseudoError) { process.once('uncaughtException', function (e: IPseudoError) { debugger; // leave debugger statement here please - _suman.logError(`${chalk.magenta('Suman runner "uncaughtException" event:')} \n ${chalk.bold(su.getCleanErrorString(e))}`); process.exit(1); }); @@ -137,7 +135,6 @@ process.once('uncaughtException', function (e: IPseudoError) { process.on('message', function (data: any) { debugger; // leave debugger statement here please - _suman.logError('Weird! => Suman runner received an IPC message:\n', chalk.magenta(typeof data === 'string' ? data : util.inspect(data))); }); @@ -184,71 +181,9 @@ server.on('connection', function (socket: SocketIOClient.Socket) { }); -function handleMessageForSingleProcess(msg: Object, n: ISumanChildProcess) { - - switch (msg.type) { - - case constants.runner_message_type.TABLE_DATA: - // handleTableData(n, msg.data); - break; - - case constants.runner_message_type.INTEGRANT_INFO: - handleIntegrantInfo(msg, n); - break; - case constants.runner_message_type.LOG_RESULT: - logTestResult(msg, n); - break; - case constants.runner_message_type.FATAL: - n.send({info: 'fatal-message-received'}); - //TODO: need to make sure this is only called once per file - handleFatalMessage(msg.data, n); - break; - case constants.runner_message_type.NON_FATAL_ERR: - console.error('\n\n ' + chalk.red('non-fatal suite error: ' + msg.msg + '\n')); - break; - case constants.runner_message_type.MAX_MEMORY: - console.log('\nmax memory: ' + util.inspect(msg.msg)); - break; - default: - throw new Error(' => Suman internal error => bad msg.type in runner'); - } -} - -function handleMessage(msg: Object, n: ISumanChildProcess) { - - switch (msg.type) { - case constants.runner_message_type.TABLE_DATA: - handleTableData(n, msg.data); - break; - case constants.runner_message_type.INTEGRANT_INFO: - handleIntegrantInfo(msg, n); - break; - case constants.runner_message_type.LOG_RESULT: - logTestResult(msg, n); - break; - case constants.runner_message_type.FATAL: - n.send({info: 'fatal-message-received'}); - handleFatalMessage(msg.data, n); - break; - case constants.runner_message_type.WARNING: - console.error('\n\n ' + chalk.bgYellow('Suman warning: ' + msg.msg + '\n')); - break; - case constants.runner_message_type.NON_FATAL_ERR: - console.error('\n\n ' + chalk.red('non-fatal suite error: ' + msg.msg + '\n')); - break; - default: - throw new Error(' => Suman implementation error => Bad msg.type in runner, perhaps the user sent a message with process.send?'); - } -} - -const runSingleOrMultipleDirs = - makeHandleMultipleProcesses(runnerObj, tableRows, messages, forkedCPs, handleMessage, beforeExitRunOncePost, exit); - -const runAllTestsInSingleProcess = - makeSingleProcess(runnerObj, handleMessageForSingleProcess, messages, beforeExitRunOncePost, exit); - -const runAllTestsInContainer = - makeContainerize(runnerObj, handleMessageForSingleProcess, messages, beforeExitRunOncePost, exit); +const runSingleOrMultipleDirs = makeHandleMultipleProcesses(runnerObj, tableRows, messages, forkedCPs, beforeExitRunOncePost, exit); +const runAllTestsInSingleProcess = makeSingleProcess(runnerObj, messages, beforeExitRunOncePost, exit); +const runAllTestsInContainer = makeContainerize(runnerObj, messages, beforeExitRunOncePost, exit); /////////////// @@ -257,7 +192,6 @@ export const findTestsAndRunThem = function (runObj: Object, runOnce: Function, debugger; // leave it here const {sumanOpts} = _suman; - if (sumanOpts.errors_only) { resultBroadcaster.emit(String(events.ERRORS_ONLY_OPTION)); } @@ -265,17 +199,10 @@ export const findTestsAndRunThem = function (runObj: Object, runOnce: Function, //need to get rid of this property so child processes cannot require Suman index file delete process.env.SUMAN_EXTRANEOUS_EXECUTABLE; - const projectRoot = _suman.projectRoot || su.findProjectRoot(process.cwd()); - - runnerObj.handleBlocking = makeHandleBlocking(mapValues($order, function (val) { - val.testPath = path.resolve(projectRoot + '/' + val.testPath); - return val; - })); - process.nextTick(function () { const args: Array = fnArgs(runOnce); - const ret = runOnce.apply(null, integrantInjector(args)); + const ret = runOnce.apply(null, integrantInjector(args, null)); if (ret.dependencies) { if (su.isObject(ret.dependencies)) { diff --git a/lib/test-suite-helpers/handle-injections.d.ts b/lib/test-suite-helpers/handle-injections.d.ts index aed2310c..65918b6d 100755 --- a/lib/test-suite-helpers/handle-injections.d.ts +++ b/lib/test-suite-helpers/handle-injections.d.ts @@ -1,4 +1,2 @@ import { ITestSuite } from "suman-types/dts/test-suite"; export declare const handleInjections: (suite: ITestSuite, cb: Function) => void; -declare let $exports: any; -export default $exports; diff --git a/lib/test-suite-helpers/handle-injections.js b/lib/test-suite-helpers/handle-injections.js index ab24ca07..f6930475 100755 --- a/lib/test-suite-helpers/handle-injections.js +++ b/lib/test-suite-helpers/handle-injections.js @@ -6,22 +6,23 @@ var async = require("async"); var freeze_existing_props_1 = require("freeze-existing-props"); var _suman = global.__suman = (global.__suman || {}); var t_proto_1 = require("./t-proto"); +var suman_constants_1 = require("../../config/suman-constants"); var weAreDebugging = require('../helpers/we-are-debugging'); exports.handleInjections = function (suite, cb) { - function addValuesToSuiteInjections(k, val) { + var addValuesToSuiteInjections = function (k, val) { if (k in suite.injectedValues) { - throw new Error(' => Injection value ' + k + ' was used more than once;' + - ' this value needs to be unique.'); + throw new Error(" => Injection value '" + k + "' was used more than once; this value needs to be unique."); } - else { - Object.defineProperty(suite.injectedValues, k, { - enumerable: true, - writable: false, - configurable: true, - value: val - }); - } - } + Object.defineProperty(suite.injectedValues, k, { + enumerable: true, + writable: false, + configurable: true, + value: val + }); + }; + var isDescValid = function (desc) { + return desc && String(desc) !== String(suman_constants_1.constants.UNKNOWN_INJECT_HOOK_NAME); + }; var injections = suite.getInjections(); async.each(injections, function (inj, cb) { var callable = true; @@ -44,12 +45,12 @@ exports.handleInjections = function (suite, cb) { return first(err); } Promise.resolve(results).then(function (ret) { - if (inj.desc) { - addValuesToSuiteInjections(inj.desc, ret); + if (isDescValid(inj.desc)) { + addValuesToSuiteInjections(String(inj.desc), ret); } else { Object.keys(ret).forEach(function (k) { - addValuesToSuiteInjections(k, freeze_existing_props_1.freezeExistingProps(ret[k])); + addValuesToSuiteInjections(String(k), freeze_existing_props_1.freezeExistingProps(ret[k])); }); } first(undefined); @@ -59,7 +60,7 @@ exports.handleInjections = function (suite, cb) { } else { Promise.resolve(inj.fn.call(suite)).then(function (ret) { - if (inj.desc) { + if (isDescValid(inj.desc)) { addValuesToSuiteInjections(inj.desc, freeze_existing_props_1.freezeExistingProps(ret)); return first(undefined); } @@ -79,7 +80,7 @@ exports.handleInjections = function (suite, cb) { }); Promise.all(potentialPromises).then(function (vals) { keys.forEach(function (k, index) { - addValuesToSuiteInjections(k, freeze_existing_props_1.freezeExistingProps(vals[index])); + addValuesToSuiteInjections(String(k), freeze_existing_props_1.freezeExistingProps(vals[index])); }); first(undefined); }, first); @@ -87,5 +88,3 @@ exports.handleInjections = function (suite, cb) { } }, cb); }; -var $exports = module.exports; -exports.default = $exports; diff --git a/lib/test-suite-helpers/handle-injections.ts b/lib/test-suite-helpers/handle-injections.ts index 2017d981..d3ecbed0 100755 --- a/lib/test-suite-helpers/handle-injections.ts +++ b/lib/test-suite-helpers/handle-injections.ts @@ -17,6 +17,7 @@ import {freezeExistingProps as freeze} from 'freeze-existing-props'; //project const _suman = global.__suman = (global.__suman || {}); import {tProto} from './t-proto'; +import {constants} from "../../config/suman-constants"; const weAreDebugging = require('../helpers/we-are-debugging'); /////////////////////////////////////////////////////////////////// @@ -29,21 +30,23 @@ interface IInjectionRetObj { export const handleInjections = function (suite: ITestSuite, cb: Function) { - function addValuesToSuiteInjections(k: string, val: any) { + const addValuesToSuiteInjections = function (k: string, val: any) : void { if (k in suite.injectedValues) { - throw new Error(' => Injection value ' + k + ' was used more than once;' + - ' this value needs to be unique.'); + throw new Error(` => Injection value '${k}' was used more than once; this value needs to be unique.`); } - else { - // freeze the property so it cannot be modified by user after the fact - Object.defineProperty(suite.injectedValues, k, { - enumerable: true, - writable: false, - configurable: true, - value: val - }); - } - } + + // freeze the property so it cannot be modified by user after the fact + Object.defineProperty(suite.injectedValues, k, { + enumerable: true, + writable: false, + configurable: true, + value: val + }); + }; + + const isDescValid = function(desc: string){ + return desc && String(desc) !== String(constants.UNKNOWN_INJECT_HOOK_NAME) + }; const injections = suite.getInjections(); @@ -76,12 +79,12 @@ export const handleInjections = function (suite: ITestSuite, cb: Function) { Promise.resolve(results).then(ret => { - if (inj.desc) { - addValuesToSuiteInjections(inj.desc, ret); + if (isDescValid(inj.desc)) { + addValuesToSuiteInjections(String(inj.desc), ret); } else { Object.keys(ret).forEach(function (k) { - addValuesToSuiteInjections(k, freeze(ret[k])); + addValuesToSuiteInjections(String(k), freeze(ret[k])); }); } @@ -97,7 +100,7 @@ export const handleInjections = function (suite: ITestSuite, cb: Function) { Promise.resolve(inj.fn.call(suite)).then(ret => { - if (inj.desc) { + if (isDescValid(inj.desc)) { addValuesToSuiteInjections(inj.desc, freeze(ret)); return first(undefined); } @@ -124,7 +127,7 @@ export const handleInjections = function (suite: ITestSuite, cb: Function) { Promise.all(potentialPromises).then(function (vals) { keys.forEach(function (k, index) { - addValuesToSuiteInjections(k, freeze(vals[index])); + addValuesToSuiteInjections(String(k), freeze(vals[index])); }); first(undefined); @@ -140,9 +143,3 @@ export const handleInjections = function (suite: ITestSuite, cb: Function) { }; -///////////// support node style imports ////////////////////////////////////////////// - -let $exports = module.exports; -export default $exports; - -//////////////////////////////////////////////////////////////////////////////////////// diff --git a/lib/test-suite-helpers/make-test-suite.js b/lib/test-suite-helpers/make-test-suite.js index 1d512349..f1601c14 100755 --- a/lib/test-suite-helpers/make-test-suite.js +++ b/lib/test-suite-helpers/make-test-suite.js @@ -118,12 +118,6 @@ exports.makeTestSuite = function (suman, gracefulExit, handleBeforesAndAfters, n TestBlock.prototype.toString = function () { return 'Suman test block: ' + this.desc; }; - TestBlock.prototype.series = function (cb) { - if (typeof cb === 'function') { - cb.apply(this, [(_interface === 'TDD' ? this.test : this.it).bind(this)]); - } - return this; - }; TestBlock.prototype.invokeChildren = function (val, start) { async.eachSeries(this.getChildren(), makeRunChild(val), start); }; diff --git a/lib/test-suite-helpers/make-test-suite.ts b/lib/test-suite-helpers/make-test-suite.ts index 0294c0c7..9ef9d661 100755 --- a/lib/test-suite-helpers/make-test-suite.ts +++ b/lib/test-suite-helpers/make-test-suite.ts @@ -176,13 +176,6 @@ export const makeTestSuite = function (suman: ISuman, gracefulExit: Function, return 'Suman test block: ' + this.desc; } - series(cb: Function) { - if (typeof cb === 'function') { - cb.apply(this, [(_interface === 'TDD' ? this.test : this.it).bind(this)]); - } - return this; - } - invokeChildren(val: any, start: Function) { async.eachSeries(this.getChildren(), makeRunChild(val), start); } diff --git a/lib/test-suite-helpers/suman-methods.ts b/lib/test-suite-helpers/suman-methods.ts index f4419c18..48f026e5 100644 --- a/lib/test-suite-helpers/suman-methods.ts +++ b/lib/test-suite-helpers/suman-methods.ts @@ -59,8 +59,6 @@ export const makeSumanMethods = function (suman: ISuman, TestBlock: TestBlockBas ///////////////////////////////////////////////////////////////////////////////////////// const getProxy = makeProxy(suman); - - // _interface === 'TDD' ? m.setup = before : m.before = before; m.describe = m.context = m.suite = getProxy(describe, rules.blockSignature) as IDescribeFn; m.it = m.test = getProxy(it, rules.testCaseSignature) as ItFn; m.inject = getProxy(inject, rules.hookSignature) as IInjectFn; diff --git a/lib/test-suite-methods/make-inject.js b/lib/test-suite-methods/make-inject.js index 1d3e9ec0..bbd82c42 100644 --- a/lib/test-suite-methods/make-inject.js +++ b/lib/test-suite-methods/make-inject.js @@ -68,7 +68,7 @@ exports.makeInject = function (suman) { else { zuite.getInjections().push({ ctx: zuite, - desc: desc || fn.name || '(unknonw inject-hook name)', + desc: desc || fn.name || constants.UNKNOWN_INJECT_HOOK_NAME, timeout: opts.timeout || 11000, cb: opts.cb || false, throws: opts.throws, diff --git a/lib/test-suite-methods/make-inject.ts b/lib/test-suite-methods/make-inject.ts index fd2de480..c9eb3cbe 100755 --- a/lib/test-suite-methods/make-inject.ts +++ b/lib/test-suite-methods/make-inject.ts @@ -114,7 +114,7 @@ export const makeInject = function (suman: ISuman): IInjectFn { zuite.getInjections().push({ ctx: zuite, - desc: desc || fn.name || '(unknonw inject-hook name)', + desc: desc || fn.name || constants.UNKNOWN_INJECT_HOOK_NAME, timeout: opts.timeout || 11000, cb: opts.cb || false, throws: opts.throws, diff --git a/scripts/get-core-modules.d.ts b/scripts/get-core-modules.d.ts deleted file mode 100755 index 98231bc8..00000000 --- a/scripts/get-core-modules.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare const core: any; diff --git a/scripts/get-core-modules.js b/scripts/get-core-modules.js deleted file mode 100755 index 68a37492..00000000 --- a/scripts/get-core-modules.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; -var core = require('builtin-modules'); -console.log(core); diff --git a/scripts/get-core-modules.ts b/scripts/get-core-modules.ts deleted file mode 100755 index 9ffadd18..00000000 --- a/scripts/get-core-modules.ts +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; -const core = require('builtin-modules'); -console.log(core); diff --git a/suman-todos.txt b/suman-todos.txt index 5f110419..9a0bb587 100755 --- a/suman-todos.txt +++ b/suman-todos.txt @@ -25,6 +25,21 @@ but my question is - how can I set global_npm_modules_path so that it's held in /// +describe('nested1', opts, () => {}); ---> this makes an error (see 1.test.js) + + [suman] ⚑ Suman fatal error => making a graceful exit => + [suman] TypeError: arr.slice is not a function + [suman] at Object.exports.parseArgs (/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/lib/helpers/parse-pragmatik-args.js:20:29) + [suman] at /Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/lib/test-suite-methods/make-describe.js:45:45 + + [suman] at /Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/1.test.js:27:3 + +/// + + give user option to bundle with selenium webdriver + +/// + https://www.jetbrains.com/help/webstorm/keyboard-shortcuts-you-cannot-miss.html /// @@ -159,10 +174,6 @@ cd $(dirname "$0") after always may not be run in the right order async.eachSeries(allDescribeBlocks, function (block: ITestSuite, cb: Function) { -/// - - => test suman itself with suman, then we can make final adjustments before release - /// => chai 4.0.2 works. @@ -268,6 +279,10 @@ node test/src/exp.js | grep -v [[suman]] block injector should be for the other blocks all blocks should at least have one parameter b, or s +/// + + in handle-multiple-processes - if a transpile action takes too long, it won't be put on the run queue, and the whole thing + will exit because all child processes have exitted. /// @@ -293,31 +308,6 @@ node test/src/exp.js | grep -v [[suman]] at /Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/lib/test-suite-helpers/make-handle-each.js:75:15 at Domain.handleError (/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/lib/test-suite-helpers/make-handle-each.js:61:27) -//// - - [watch-worker] (node:5440) Warning: Possible EventEmitter memory leak detected. 11 finish listeners added. Use emitter.setMaxListeners() to increase limit - [watch-worker] => Suman-Utils warning => Warning: Possible EventEmitter memory leak detected. 11 finish listeners added. Use emitter.setMaxListeners() to increase limit - [watch-worker] at _addListener (events.js:259:19) - [watch-worker] at Socket.addListener (events.js:275:10) - [watch-worker] at Socket.Readable.on (_stream_readable.js:687:35) - [watch-worker] at Socket.once (events.js:301:8) - [watch-worker] at Socket.Readable.pipe (_stream_readable.js:596:8) - [watch-worker] at handleStdioAndExit (/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman-watch/lib/make-execute.js:38:22) - [watch-worker] at noDaemon_1 (/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman-watch/lib/make-execute.js:89:21) - [watch-worker] at _combinedTickCallback (internal/process/next_tick.js:73:7) - [watch-worker] at process._tickCallback (internal/process/next_tick.js:104:9) - [watch-worker] => Suman warning => Warning: Possible EventEmitter memory leak detected. 11 finish listeners added. Use emitter.setMaxListeners() to increase limit - [watch-worker] at _addListener (events.js:259:19) - [watch-worker] at Socket.addListener (events.js:275:10) - [watch-worker] at Socket.Readable.on (_stream_readable.js:687:35) - [watch-worker] at Socket.once (events.js:301:8) - [watch-worker] at Socket.Readable.pipe (_stream_readable.js:596:8) - [watch-worker] at handleStdioAndExit (/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman-watch/lib/make-execute.js:38:22) - [watch-worker] at noDaemon_1 (/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman-watch/lib/make-execute.js:89:21) - [watch-worker] at _combinedTickCallback (internal/process/next_tick.js:73:7) - [watch-worker] at process._tickCallback (internal/process/next_tick.js:104:9) - - /// => put Patreon link in all repos except main Suman repo diff --git a/suman.conf.js b/suman.conf.js index 8033d148..f7cdfde2 100755 --- a/suman.conf.js +++ b/suman.conf.js @@ -13,7 +13,7 @@ module.exports = Object.freeze({ //regex matchAny: [/\.js$/, /.sh$/, /\.jar$/, /\.java$/, /\.go$/, /\.ts$/], - matchNone: [/fixture/, /correct-exit-codes/, /@transform.sh/, /@run.sh/, /\/target\//], + matchNone: [/fixture/, /correct-exit-codes/, /@transform.sh/, /@run.sh/, /\/@target\//], matchAll: [], //string diff --git a/test/.suman/gantt-4.html b/test/.suman/gantt-4.html index b1c5278a..be8262ef 100644 --- a/test/.suman/gantt-4.html +++ b/test/.suman/gantt-4.html @@ -220,9 +220,7 @@ "status": "KILLED" }]; - - let tasksRaw = JSON.parse('[{"startDate":1508224074515,"endDate":1508224075275,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/2.test.js","status":"FAILED"},{"startDate":1508224073683,"endDate":1508224074502,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/1.test.js","status":"FAILED"},{"startDate":1508224073680,"endDate":1508224074724,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/3.test.js","status":"SUCCEEDED"},{"startDate":1508224073677,"endDate":1508224074670,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/4.test.js","status":"SUCCEEDED"},{"startDate":1508224073674,"endDate":1508224074521,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/5.test.js","status":"FAILED"},{"startDate":1508224073671,"endDate":1508224074586,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/6.test.js","status":"FAILED"},{"startDate":1508224073667,"endDate":1508224075531,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/events.test.js","status":"SUCCEEDED"},{"startDate":1508224073663,"endDate":1508224074579,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/injection.test.js","status":"SUCCEEDED"},{"startDate":1508224073658,"endDate":1508224074875,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/simple.js","status":"SUCCEEDED"}]'); - + let tasksRaw = JSON.parse('[{"startDate":1508442141614,"endDate":1508442142359,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/2.test.js","status":"FAILED"},{"startDate":1508442140600,"endDate":1508442141642,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/1.test.js","status":"FAILED"},{"startDate":1508442140581,"endDate":1508442141735,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/3.test.js","status":"SUCCEEDED"},{"startDate":1508442140578,"endDate":1508442141776,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/4.test.js","status":"SUCCEEDED"},{"startDate":1508442140575,"endDate":1508442141806,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/5.test.js","status":"SUCCEEDED"},{"startDate":1508442140572,"endDate":1508442141729,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/6.test.js","status":"SUCCEEDED"},{"startDate":1508442140568,"endDate":1508442142487,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/events.test.js","status":"SUCCEEDED"},{"startDate":1508442140565,"endDate":1508442141683,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/injection.test.js","status":"SUCCEEDED"},{"startDate":1508442140560,"endDate":1508442141614,"transformStartDate":null,"transformEndDate":null,"taskName":"/Users/alexamil/WebstormProjects/oresoftware/sumanjs/suman/test/src/dev/node/simple.js","status":"SUCCEEDED"}]'); let tasks = []; diff --git a/test/src/dev/node/3.test.js b/test/src/dev/node/3.test.js index 91606da1..9c3957ee 100644 --- a/test/src/dev/node/3.test.js +++ b/test/src/dev/node/3.test.js @@ -8,9 +8,7 @@ const {Test} = suman.init(module); Test.create({delay: true}, b => { - b.resume(); - }); Test.create(function (assert, describe, before, beforeEach, after, afterEach, it, $core) { diff --git a/test/src/dev/node/5.test.js b/test/src/dev/node/5.test.js index 8247d7b3..7e73f88c 100644 --- a/test/src/dev/node/5.test.js +++ b/test/src/dev/node/5.test.js @@ -3,7 +3,8 @@ const suman = require('suman'); const {Test} = suman.init(module, { - forceParallel: true // parallel, not parallel-max + forceParallel: true, // parallel, not parallel-max + __expectedExitCode: 56 }); /////////////////////////////////////////////////////////////////////// diff --git a/test/src/dev/node/6.test.js b/test/src/dev/node/6.test.js index 51df1760..bb002dbd 100644 --- a/test/src/dev/node/6.test.js +++ b/test/src/dev/node/6.test.js @@ -10,16 +10,11 @@ const {Test} = suman.init(module, { let count = 0; -Test.create(function (test, setup, setupTest, setuptest, teardown, teardownTest, describe) { +Test.create(function (b, test, setup, setupTest, setuptest, teardown, teardownTest, describe) { test('here we go'); - const x = this; - debugger; - // console.log(this.describe); - debugger; - - this.shared.set('users', 888); + b.shared.set('users', 888); setup({}, h => { console.log('this is a setup..'); diff --git a/test/src/dev/node/simple.js b/test/src/dev/node/simple.js index a5f00e4a..f77c8004 100644 --- a/test/src/dev/node/simple.js +++ b/test/src/dev/node/simple.js @@ -10,7 +10,7 @@ let count = 0; Test.create('X', (test, context) => { - 700..times(function () { + 1..times(function () { context('silly', function () { diff --git a/test/src/exp/tap-output/@src/tap-producer1.ts b/test/src/exp/tap-output/@src/tap-producer1.ts index b7c5ce5a..9b4616bb 100755 --- a/test/src/exp/tap-output/@src/tap-producer1.ts +++ b/test/src/exp/tap-output/@src/tap-producer1.ts @@ -21,13 +21,15 @@ Test.create(function (it: ItFn, before: IBeforeFn, describe: IDescribeFn, it('zoom', (t: ITestCaseParam) => { // t.assert.deepEqual(false, true); - throw new Error('whole me 3'); + // throw new Error('whole me 3'); - }) - .it('rudolph', t => { - throw new Error('whole me 4'); - }) - .afterEach('ram', (h: IHookParam) => { + }); + + it('rudolph', t => { + // throw new Error('whole me 4'); + }); + + afterEach('ram', (h: IHookParam) => { }); diff --git a/test/src/exp/tap-output/@src/tap-producer2.ts b/test/src/exp/tap-output/@src/tap-producer2.ts index 74856bfd..1d28a123 100755 --- a/test/src/exp/tap-output/@src/tap-producer2.ts +++ b/test/src/exp/tap-output/@src/tap-producer2.ts @@ -10,11 +10,15 @@ Test.create(function (it: ItFn) { }); - it('fails', (t: ITestCaseParam) => { + it.cb('fails', (t: ITestCaseParam) => { // t.skip(); throw new Error('whole me 2') + setTimeout(function () { + t.done(); + }, 1000); + }); }); diff --git a/test/src/exp/tap-output/@target/tap-producer1.js b/test/src/exp/tap-output/@target/tap-producer1.js index d69bd4fb..45f44fe7 100755 --- a/test/src/exp/tap-output/@target/tap-producer1.js +++ b/test/src/exp/tap-output/@target/tap-producer1.js @@ -11,12 +11,12 @@ Test.create(function (it, before, describe, beforeEach, afterEach, after) { }); it('zoom', function (t) { // t.assert.deepEqual(false, true); - throw new Error('whole me 3'); - }) - .it('rudolph', function (t) { - throw new Error('whole me 4'); - }) - .afterEach('ram', function (h) { + // throw new Error('whole me 3'); + }); + it('rudolph', function (t) { + // throw new Error('whole me 4'); + }); + afterEach('ram', function (h) { }); after('b', function (h) { h.slow(); diff --git a/test/src/exp/tap-output/@target/tap-producer2.js b/test/src/exp/tap-output/@target/tap-producer2.js index ff644c00..0d04eb9d 100755 --- a/test/src/exp/tap-output/@target/tap-producer2.js +++ b/test/src/exp/tap-output/@target/tap-producer2.js @@ -7,8 +7,11 @@ Test.create(function (it) { // t.skip(); throw new Error('whole me 1'); }); - it('fails', function (t) { + it.cb('fails', function (t) { // t.skip(); throw new Error('whole me 2'); + setTimeout(function () { + t.done(); + }, 1000); }); });