text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  var tslib_1 = require("tslib");
  var os_1 = require("os");
  var test_runner_1 = require("stryker-api/test_runner");
  var TranspilerFacade_1 = require("../transpiler/TranspilerFacade");
  var logging_1 = require("stryker-api/logging");
  var Sandbox_1 = require("../Sandbox");
  var CoverageInstrumenterTranspiler_1 = require("../transpiler/CoverageInstrumenterTranspiler");
  var SourceMapper_1 = require("../transpiler/SourceMapper");
  var coverageHooks_1 = require("../transpiler/coverageHooks");
  // The initial run might take a while.
  // For example: angular-bootstrap takes up to 45 seconds.
  // Lets take 5 minutes just to be sure
  var INITIAL_RUN_TIMEOUT = 60 * 1000 * 5;
  var INITIAL_TEST_RUN_MARKER = 'Initial test run';
  var InitialTestExecutor = /** @class */ (function () {
      function InitialTestExecutor(options, inputFiles, testFramework, timer, loggingContext) {
          this.options = options;
          this.inputFiles = inputFiles;
          this.testFramework = testFramework;
          this.timer = timer;
          this.loggingContext = loggingContext;
          this.log = logging_1.getLogger(InitialTestExecutor.name);
      }
      InitialTestExecutor.prototype.run = function () {
          return tslib_1.__awaiter(this, void 0, void 0, function () {
              var transpiledFiles, sourceMapper, _a, coverageMaps, instrumentedFiles, _b, runResult, grossTimeMS, timing;
              return tslib_1.__generator(this, function (_c) {
                  switch (_c.label) {
                      case 0:
                          this.log.info("Starting initial test run. This may take a while.");
                          return [4 /*yield*/, this.transpileInputFiles()];
                      case 1:
                          transpiledFiles = _c.sent();
                          sourceMapper = SourceMapper_1.default.create(transpiledFiles, this.options);
                          return [4 /*yield*/, this.annotateForCodeCoverage(transpiledFiles, sourceMapper)];
                      case 2:
                          _a = _c.sent(), coverageMaps = _a.coverageMaps, instrumentedFiles = _a.instrumentedFiles;
                          this.logTranspileResult(instrumentedFiles);
                          return [4 /*yield*/, this.runInSandbox(instrumentedFiles)];
                      case 3:
                          _b = _c.sent(), runResult = _b.runResult, grossTimeMS = _b.grossTimeMS;
                          timing = this.calculateTiming(grossTimeMS, runResult.tests);
                          this.validateResult(runResult, timing);
                          return [2 /*return*/, {
                                  overheadTimeMS: timing.overhead,
                                  sourceMapper: sourceMapper,
                                  runResult: runResult,
                                  coverageMaps: coverageMaps
                              }];
                  }
              });
          });
      };
      InitialTestExecutor.prototype.runInSandbox = function (files) {
          return tslib_1.__awaiter(this, void 0, void 0, function () {
              var sandbox, runResult, grossTimeMS;
              return tslib_1.__generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, Sandbox_1.default.create(this.options, 0, files, this.testFramework, 0, this.loggingContext)];
                      case 1:
                          sandbox = _a.sent();
                          this.timer.mark(INITIAL_TEST_RUN_MARKER);
                          return [4 /*yield*/, sandbox.run(INITIAL_RUN_TIMEOUT, this.getCollectCoverageHooksIfNeeded())];
                      case 2:
                          runResult = _a.sent();
                          grossTimeMS = this.timer.elapsedMs(INITIAL_TEST_RUN_MARKER);
                          return [4 /*yield*/, sandbox.dispose()];
                      case 3:
                          _a.sent();
                          return [2 /*return*/, { runResult: runResult, grossTimeMS: grossTimeMS }];
                  }
              });
          });
      };
      InitialTestExecutor.prototype.transpileInputFiles = function () {
          return tslib_1.__awaiter(this, void 0, void 0, function () {
              var transpilerFacade;
              return tslib_1.__generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          transpilerFacade = this.createTranspilerFacade();
                          return [4 /*yield*/, transpilerFacade.transpile(this.inputFiles.files)];
                      case 1: return [2 /*return*/, _a.sent()];
                  }
              });
          });
      };
      InitialTestExecutor.prototype.annotateForCodeCoverage = function (files, sourceMapper) {
          return tslib_1.__awaiter(this, void 0, void 0, function () {
              var filesToInstrument, coverageInstrumenterTranspiler, instrumentedFiles;
              return tslib_1.__generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          filesToInstrument = this.inputFiles.filesToMutate.map(function (mutateFile) { return sourceMapper.transpiledFileNameFor(mutateFile.name); });
                          coverageInstrumenterTranspiler = new CoverageInstrumenterTranspiler_1.default(this.options, filesToInstrument);
                          return [4 /*yield*/, coverageInstrumenterTranspiler.transpile(files)];
                      case 1:
                          instrumentedFiles = _a.sent();
                          return [2 /*return*/, { coverageMaps: coverageInstrumenterTranspiler.fileCoverageMaps, instrumentedFiles: instrumentedFiles }];
                  }
              });
          });
      };
      InitialTestExecutor.prototype.validateResult = function (runResult, timing) {
          switch (runResult.status) {
              case test_runner_1.RunStatus.Complete:
                  var failedTests = this.filterOutFailedTests(runResult);
                  if (failedTests.length) {
                      this.logFailedTestsInInitialRun(failedTests);
                      throw new Error('There were failed tests in the initial test run.');
                  }
                  if (runResult.tests.length === 0) {
                      this.log.warn('No tests were executed. Stryker will exit prematurely. Please check your configuration.');
                      return;
                  }
                  else {
                      this.logInitialTestRunSucceeded(runResult.tests, timing);
                      return;
                  }
              case test_runner_1.RunStatus.Error:
                  this.logErrorsInInitialRun(runResult);
                  break;
              case test_runner_1.RunStatus.Timeout:
                  this.logTimeoutInitialRun(runResult);
                  break;
          }
          throw new Error('Something went wrong in the initial test run');
      };
      /**
       * Calculates the timing variables for the test run.
       * grossTime = NetTime + overheadTime
       *
       * The overhead time is used to calculate exact timeout values during mutation testing.
       * See timeoutMS setting in README for more information on this calculation
       */
      InitialTestExecutor.prototype.calculateTiming = function (grossTimeMS, tests) {
          var netTimeMS = tests.reduce(function (total, test) { return total + test.timeSpentMs; }, 0);
          var overheadTimeMS = grossTimeMS - netTimeMS;
          return {
              overhead: overheadTimeMS < 0 ? 0 : overheadTimeMS,
              net: netTimeMS
          };
      };
      /**
       * Creates a facade for the transpile pipeline.
       * Also includes the coverage instrumenter transpiler,
       * which is used to instrument for code coverage when needed.
       */
      InitialTestExecutor.prototype.createTranspilerFacade = function () {
          // Let the transpiler produce source maps only if coverage analysis is enabled
          var transpilerSettings = {
              config: this.options,
              produceSourceMaps: this.options.coverageAnalysis !== 'off'
          };
          return new TranspilerFacade_1.default(transpilerSettings);
      };
      InitialTestExecutor.prototype.getCollectCoverageHooksIfNeeded = function () {
          if (this.options.coverageAnalysis === 'perTest') {
              if (this.testFramework) {
                  // Add piece of javascript to collect coverage per test results
                  this.log.debug("Adding test hooks for coverageAnalysis \"perTest\".");
                  return coverageHooks_1.coveragePerTestHooks(this.testFramework);
              }
              else {
                  this.log.warn('Cannot measure coverage results per test, there is no testFramework and thus no way of executing code right before and after each test.');
              }
          }
          return undefined;
      };
      InitialTestExecutor.prototype.logTranspileResult = function (transpiledFiles) {
          if (this.options.transpilers.length && this.log.isDebugEnabled()) {
              this.log.debug("Transpiled files: " + JSON.stringify(transpiledFiles.map(function (f) { return "" + f.name; }), null, 2));
          }
      };
      InitialTestExecutor.prototype.filterOutFailedTests = function (runResult) {
          return runResult.tests.filter(function (testResult) { return testResult.status === test_runner_1.TestStatus.Failed; });
      };
      InitialTestExecutor.prototype.logInitialTestRunSucceeded = function (tests, timing) {
          this.log.info('Initial test run succeeded. Ran %s tests in %s (net %s ms, overhead %s ms).', tests.length, this.timer.humanReadableElapsed(), timing.net, timing.overhead);
      };
      InitialTestExecutor.prototype.logFailedTestsInInitialRun = function (failedTests) {
          var message = 'One or more tests failed in the initial test run:';
          failedTests.forEach(function (test) {
              message += os_1.EOL + "\t" + test.name;
              if (test.failureMessages && test.failureMessages.length) {
                  message += os_1.EOL + "\t\t" + test.failureMessages.join(os_1.EOL + "\t\t");
              }
          });
          this.log.error(message);
      };
      InitialTestExecutor.prototype.logErrorsInInitialRun = function (runResult) {
          var message = 'One or more tests resulted in an error:';
          if (runResult.errorMessages && runResult.errorMessages.length) {
              runResult.errorMessages.forEach(function (error) { return message += os_1.EOL + "\t" + error; });
          }
          this.log.error(message);
      };
      InitialTestExecutor.prototype.logTimeoutInitialRun = function (runResult) {
          var message = 'Initial test run timed out! Ran following tests before timeout:';
          runResult.tests.forEach(function (test) { return message += os_1.EOL + "\t" + test.name + " (" + test_runner_1.TestStatus[test.status] + ")"; });
          this.log.error(message);
      };
      return InitialTestExecutor;
  }());
  exports.default = InitialTestExecutor;
  //# sourceMappingURL=InitialTestExecutor.js.map