From 460d423e5ad6b086f0505ddd759754779711b2b9 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Tue, 18 Apr 2017 21:06:24 +0200 Subject: [PATCH] feat(runner): Buffer stdout and stderr for output when errors occur When it is determined that an error occured during the run of a launch it's useful to have output from the process. We just need to know what was going on in order to debug (if necessary) Related to karma-runner/karma#2663 Complete browser log output when process isn't started --- lib/launchers/process.js | 25 ++++++++++++++++++++++++- test/unit/launchers/process.spec.js | 15 +++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/launchers/process.js b/lib/launchers/process.js index 015af70ab..8d6a2063f 100644 --- a/lib/launchers/process.js +++ b/lib/launchers/process.js @@ -6,6 +6,11 @@ var ProcessLauncher = function (spawn, tempDir, timer, processKillTimeout) { var self = this var onExitCallback var killTimeout = processKillTimeout || 2000 + // Will hold output from the spawned child process + var streamedOutputs = { + stdout: '', + stderr: '' + } this._tempDir = tempDir.getPath('/karma-' + this.id.toString()) @@ -46,6 +51,14 @@ var ProcessLauncher = function (spawn, tempDir, timer, processKillTimeout) { return path.normalize(cmd) } + this._onStdout = function (data) { + streamedOutputs.stdout += data + } + + this._onStderr = function (data) { + streamedOutputs.stderr += data + } + this._execCommand = function (cmd, args) { if (!cmd) { log.error('No binary for %s browser on your platform.\n ' + @@ -61,9 +74,12 @@ var ProcessLauncher = function (spawn, tempDir, timer, processKillTimeout) { log.debug(cmd + ' ' + args.join(' ')) self._process = spawn(cmd, args) - var errorOutput = '' + self._process.stdout.on('data', self._onStdout) + + self._process.stderr.on('data', self._onStderr) + self._process.on('exit', function (code) { self._onProcessExit(code, errorOutput) }) @@ -98,7 +114,14 @@ var ProcessLauncher = function (spawn, tempDir, timer, processKillTimeout) { error = 'crashed' } + if (error) { + log.error('%s stdout: %s', self.name, streamedOutputs.stdout) + log.error('%s stderr: %s', self.name, streamedOutputs.stderr) + } + self._process = null + streamedOutputs.stdout = '' + streamedOutputs.stderr = '' if (self._killTimer) { timer.clearTimeout(self._killTimer) self._killTimer = null diff --git a/test/unit/launchers/process.spec.js b/test/unit/launchers/process.spec.js index 0333f715a..f97c28907 100644 --- a/test/unit/launchers/process.spec.js +++ b/test/unit/launchers/process.spec.js @@ -22,6 +22,7 @@ describe('launchers/process.js', () => { mockSpawn = sinon.spy(function (cmd, args) { var process = new EventEmitter() + process.stdout = new EventEmitter() process.stderr = new EventEmitter() process.kill = sinon.spy() process.exitCode = null @@ -137,6 +138,9 @@ describe('launchers/process.js', () => { // when the browser fails to get captured in default timeout, it should restart it('start -> timeout -> restart', (done) => { + var stdOutSpy = sinon.spy(launcher, '_onStdout') + var stdErrSpy = sinon.spy(launcher, '_onStderr') + // start launcher.start('http://localhost/') @@ -144,9 +148,20 @@ describe('launchers/process.js', () => { expect(mockSpawn).to.have.been.calledWith(BROWSER_PATH, ['http://localhost/?id=fake-id']) var browserProcess = mockSpawn._processes.shift() + var expectedStdoutString = 'starting...' + var expectedStderrString = 'Oops...there was a problem' + browserProcess.stdout.emit('data', expectedStdoutString) + browserProcess.stderr.emit('data', expectedStderrString) + // timeout mockTimer.wind(101) + // We must've caught some output + expect(stdOutSpy).to.have.been.called + expect(stdErrSpy).to.have.been.called + + stdOutSpy.calledWith(expectedStdoutString) + // expect killing browser expect(browserProcess.kill).to.have.been.called browserProcess.emit('exit', 0)