profile.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. 'use strict';
  2. require('./lib/worker/load-chalk'); // eslint-disable-line import/no-unassigned-import
  3. // Iron-node does not work with forked processes
  4. // This cli command will run a single file in the current process.
  5. // Intended to be used with iron-node for profiling purposes.
  6. const path = require('path');
  7. const meow = require('meow');
  8. const Promise = require('bluebird');
  9. const uniqueTempDir = require('unique-temp-dir');
  10. const arrify = require('arrify');
  11. const resolveCwd = require('resolve-cwd');
  12. const babelPipeline = require('./lib/babel-pipeline');
  13. const RunStatus = require('./lib/run-status');
  14. const VerboseReporter = require('./lib/reporters/verbose');
  15. const loadConfig = require('./lib/load-config');
  16. function resolveModules(modules) {
  17. return arrify(modules).map(name => {
  18. const modulePath = resolveCwd.silent(name);
  19. if (modulePath === null) {
  20. throw new Error(`Could not resolve required module '${name}'`);
  21. }
  22. return modulePath;
  23. });
  24. }
  25. Promise.longStackTraces();
  26. const conf = loadConfig({
  27. babel: {
  28. testOptions: {}
  29. },
  30. compileEnhancements: true
  31. });
  32. const {projectDir} = conf;
  33. // Define a minimal set of options from the main CLI
  34. const cli = meow(`
  35. Usage
  36. $ iron-node node_modules/ava/profile.js <test-file>
  37. Options
  38. --fail-fast Stop after first test failure
  39. --serial, -s Run tests serially
  40. `, {
  41. string: [
  42. '_'
  43. ],
  44. boolean: [
  45. 'fail-fast',
  46. 'verbose',
  47. 'serial',
  48. 'tap'
  49. ],
  50. default: conf,
  51. alias: {
  52. s: 'serial'
  53. }
  54. });
  55. if (cli.input.length === 0) {
  56. throw new Error('Specify a test file');
  57. }
  58. const file = path.resolve(cli.input[0]);
  59. const cacheDir = conf.cacheEnabled === false ? uniqueTempDir() : path.join(projectDir, 'node_modules', '.cache', 'ava');
  60. const precompileFile = babelPipeline.build(process.cwd(), cacheDir, babelPipeline.validate(conf.babel), conf.compileEnhancements === true);
  61. const precompiled = {};
  62. if (precompileFile) {
  63. precompiled[file] = precompileFile(file);
  64. }
  65. const opts = {
  66. file,
  67. failFast: cli.flags.failFast,
  68. serial: cli.flags.serial,
  69. tty: false,
  70. cacheDir,
  71. precompiled,
  72. require: resolveModules(conf.require)
  73. };
  74. // Mock the behavior of a parent process
  75. process.connected = true;
  76. process.channel = {ref() {}, unref() {}};
  77. const reporter = new VerboseReporter({
  78. reportStream: process.stdout,
  79. stdStream: process.stderr,
  80. watching: false
  81. });
  82. const runStatus = new RunStatus([file]);
  83. runStatus.observeWorker({
  84. file,
  85. onStateChange(listener) {
  86. const emit = evt => listener(Object.assign(evt, {testFile: file}));
  87. process.send = data => {
  88. if (data && data.ava) {
  89. const evt = data.ava;
  90. if (evt.type === 'ping') {
  91. if (console.profileEnd) {
  92. console.profileEnd();
  93. }
  94. if (process.exitCode) {
  95. emit({type: 'worker-failed', nonZeroExitCode: process.exitCode});
  96. } else {
  97. emit({type: 'worker-finished', forcedExit: false});
  98. process.exitCode = runStatus.suggestExitCode({matching: false});
  99. }
  100. setImmediate(() => {
  101. reporter.endRun();
  102. process.emit('message', {ava: {type: 'pong'}});
  103. });
  104. } else {
  105. emit(data.ava);
  106. }
  107. }
  108. };
  109. }
  110. }, file);
  111. reporter.startRun({
  112. failFastEnabled: false,
  113. files: [file],
  114. matching: false,
  115. previousFailures: 0,
  116. status: runStatus
  117. });
  118. process.on('beforeExit', () => {
  119. process.exitCode = process.exitCode || runStatus.suggestExitCode({matching: false});
  120. });
  121. // The "subprocess" will read process.argv[2] for options
  122. process.argv[2] = JSON.stringify(opts);
  123. process.argv.length = 3;
  124. if (console.profile) {
  125. console.profile('AVA test-worker process');
  126. }
  127. setImmediate(() => {
  128. require('./lib/worker/subprocess'); // eslint-disable-line import/no-unassigned-import
  129. });