Skip to content

Commit

Permalink
Fix error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyBelym committed Jan 21, 2019
1 parent 28de3e1 commit 4874638
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 19 deletions.
34 changes: 25 additions & 9 deletions src/video-recorder/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import debug from 'debug';
import { join, dirname } from 'path';
import fs from 'fs';
import { spawnSync } from 'child_process';
Expand All @@ -8,6 +9,8 @@ import PathPattern from '../utils/path-pattern';
import WARNING_MESSAGES from '../notifications/warning-message';


const DEBUG_LOGGER = debug('testcafe:video-recorder');

const VIDEO_EXTENSION = 'mp4';

const TEMP_DIR_PREFIX = 'video';
Expand Down Expand Up @@ -39,12 +42,25 @@ export default class VideoRecorder {
this._assignEventHandlers(browserJob);
}

_createSafeListener (listener) {
return async (...args) => {
try {
return await listener.apply(this, args);
}
catch (error) {
DEBUG_LOGGER(listener && listener.name, error);

return void 0;
}
};
}

_assignEventHandlers (browserJob) {
browserJob.once('start', () => this._onBrowserJobStart());
browserJob.once('done', () => this._onBrowserJobDone());
browserJob.on('test-run-create', testRunInfo => this._onTestRunCreate(testRunInfo));
browserJob.on('test-run-ready', testRun => this._onTestRunReady(testRun));
browserJob.on('test-run-before-done', testRun => this._onTestRunBeforeDone(testRun));
browserJob.once('start', this._createSafeListener(this._onBrowserJobStart));
browserJob.once('done', this._createSafeListener(this._onBrowserJobDone));
browserJob.on('test-run-create', this._createSafeListener(this._onTestRunCreate));
browserJob.on('test-run-ready', this._createSafeListener(this._onTestRunReady));
browserJob.on('test-run-before-done', this._createSafeListener(this._onTestRunBeforeDone));
}

_getTargetVideoPath (testRunInfo) {
Expand Down Expand Up @@ -113,7 +129,7 @@ export default class VideoRecorder {
return;
}

this.testRunInfo[testRun] = testRunInfo;
this.testRunInfo[testRun.id] = testRunInfo;

testRunInfo.tempFiles = this._generateTempNames(connection.id);

Expand All @@ -124,7 +140,7 @@ export default class VideoRecorder {
}

async _onTestRunReady (testRun) {
const testRunInfo = this.testRunInfo[testRun];
const testRunInfo = this.testRunInfo[testRun.id];

if (!testRunInfo)
return;
Expand All @@ -133,12 +149,12 @@ export default class VideoRecorder {
}

async _onTestRunBeforeDone (testRun) {
const testRunInfo = this.testRunInfo[testRun];
const testRunInfo = this.testRunInfo[testRun.id];

if (!testRunInfo)
return;

delete this.testRunInfo[testRun];
delete this.testRunInfo[testRun.id];

await testRunInfo.videoRecorder.finishCapturing();

Expand Down
66 changes: 56 additions & 10 deletions src/video-recorder/process.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import debug from 'debug';
import { spawn } from 'child_process';
import { flatten } from 'lodash';
import Promise from 'pinkie';
import AsyncEmitter from '../utils/async-event-emitter';
import delay from '../utils/delay';


const DEBUG_LOGGER_PREFIX = 'testcafe:video-recorder:process:';

const DEFAULT_OPTIONS = {
// NOTE: don't ask confirmation for rewriting the output file
'y': true,
Expand Down Expand Up @@ -38,12 +41,17 @@ export default class VideoRecorder extends AsyncEmitter {
constructor (basePath, ffmpegPath, connection, customOptions) {
super();

this.debugLogger = debug(DEBUG_LOGGER_PREFIX + connection.id);

this.customOptions = customOptions;
this.videoPath = basePath;
this.connection = connection;
this.ffmpegPath = ffmpegPath;
this.ffmpegProcess = null;

this.ffmpegStdoutBuf = '';
this.ffmpegStderrBuf = '';

this.ffmpegClosingPromise = null;

this.closed = false;
Expand All @@ -60,6 +68,23 @@ export default class VideoRecorder extends AsyncEmitter {
return ['-' + key, value];
}

_setupFFMPEGBuffers () {
this.ffmpegProcess.stdout.on('data', data => {
this.ffmpegStdoutBuf += String(data);
});

this.ffmpegProcess.stderr.on('data', data => {
this.ffmpegStderrBuf += String(data);
});
}

_getChildProcessPromise () {
return new Promise((resolve, reject) => {
this.ffmpegProcess.on('exit', resolve);
this.ffmpegProcess.on('error', reject);
});
}

_getOptionsList () {
const optionsObject = Object.assign({}, DEFAULT_OPTIONS, this.customOptions);

Expand All @@ -79,22 +104,43 @@ export default class VideoRecorder extends AsyncEmitter {

async _capture () {
while (!this.closed) {
const frame = await this.connection.provider.getVideoFrameData(this.connection.id);
try {
const frame = await this.connection.provider.getVideoFrameData(this.connection.id);

if (frame) {
await this.emit('frame');
await this._addFrame(frame);
if (frame) {
await this.emit('frame');
await this._addFrame(frame);
}
}
catch (error) {
this.debugLogger(error);
}
}
}

async init () {
this.ffmpegProcess = spawn(this.ffmpegPath, this.optionsList, { stdio: ['pipe', 'ignore', 'ignore' ] });

this.ffmpegClosingPromise = new Promise(r => {
this.ffmpegProcess.on('exit', r);
this.ffmpegProcess.on('error', r);
});
this.ffmpegProcess = spawn(this.ffmpegPath, this.optionsList, { stdio: 'pipe' });

this._setupFFMPEGBuffers();

this.ffmpegClosingPromise = this
._getChildProcessPromise()
.then(code => {
this.closed = true;

if (code) {
this.debugLogger(code);
this.debugLogger(this.ffmpegStdoutBuf);
this.debugLogger(this.ffmpegStderrBuf);
}
})
.catch(error => {
this.closed = true;

this.debugLogger(error);
this.debugLogger(this.ffmpegStdoutBuf);
this.debugLogger(this.ffmpegStderrBuf);
});

await delay(FFMPEG_START_DELAY);
}
Expand Down

0 comments on commit 4874638

Please sign in to comment.