@stdlib/bench-harness
Version:
Benchmark harness.
190 lines (172 loc) • 5.31 kB
JavaScript
/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
;
// MODULES //
var isFunction = require( '@stdlib/assert-is-function' );
var isBoolean = require( '@stdlib/assert-is-boolean' ).isPrimitive;
var isObject = require( '@stdlib/assert-is-plain-object' );
var isNodeWritableStreamLike = require( '@stdlib/assert-is-node-writable-stream-like' );
var hasOwnProp = require( '@stdlib/assert-has-own-property' );
var format = require( '@stdlib/string-format' );
var pick = require( '@stdlib/utils-pick' );
var omit = require( '@stdlib/utils-omit' );
var noop = require( '@stdlib/utils-noop' );
var createHarness = require( './harness' );
var logStream = require( './log' );
var canEmitExit = require( './utils/can_emit_exit.js' );
var proc = require( './utils/process.js' );
// MAIN //
/**
* Creates a benchmark harness which supports closing when a process exits.
*
* @private
* @param {Options} [options] - function options
* @param {boolean} [options.autoclose] - boolean indicating whether to automatically close a harness after a harness finishes running all benchmarks
* @param {Stream} [options.stream] - output writable stream
* @param {Callback} [clbk] - callback to invoke when a harness finishes running all benchmarks
* @throws {TypeError} options argument must be an object
* @throws {TypeError} must provide valid options
* @throws {TypeError} callback argument must be a function
* @returns {Function} benchmark harness
*
* @example
* var proc = require( 'process' );
* var bench = createExitHarness( onFinish );
*
* function onFinish() {
* bench.close();
* }
*
* bench( 'beep', function benchmark( b ) {
* var x;
* var i;
* b.tic();
* for ( i = 0; i < b.iterations; i++ ) {
* x = Math.sin( Math.random() );
* if ( x !== x ) {
* b.ok( false, 'should not return NaN' );
* }
* }
* b.toc();
* if ( x !== x ) {
* b.ok( false, 'should not return NaN' );
* }
* b.end();
* });
*
* @example
* var stdout = require( '@stdlib/streams-node-stdout' );
*
* var stream = createExitHarness().createStream();
* stream.pipe( stdout );
*/
function createExitHarness() {
var exitCode;
var pipeline;
var harness;
var options;
var stream;
var topts;
var opts;
var clbk;
if ( arguments.length === 0 ) {
options = {};
clbk = noop;
} else if ( arguments.length === 1 ) {
if ( isFunction( arguments[ 0 ] ) ) {
options = {};
clbk = arguments[ 0 ];
} else if ( isObject( arguments[ 0 ] ) ) {
options = arguments[ 0 ];
clbk = noop;
} else {
throw new TypeError( format( 'invalid argument. Must provide either an options object or a function. Value: `%s`.', arguments[ 0 ] ) );
}
} else {
options = arguments[ 0 ];
if ( !isObject( options ) ) {
throw new TypeError( format( 'invalid argument. First argument must be an object. Value: `%s`.', options ) );
}
clbk = arguments[ 1 ];
if ( !isFunction( clbk ) ) {
throw new TypeError( format( 'invalid argument. Second argument must be a function. Value: `%s`.', clbk ) );
}
}
opts = {};
if ( hasOwnProp( options, 'autoclose' ) ) {
opts.autoclose = options.autoclose;
if ( !isBoolean( opts.autoclose ) ) {
throw new TypeError( format( 'invalid option. `%s` option must be a boolean. Option: `%s`.', 'autoclose', opts.autoclose ) );
}
}
if ( hasOwnProp( options, 'stream' ) ) {
opts.stream = options.stream;
if ( !isNodeWritableStreamLike( opts.stream ) ) {
throw new TypeError( format( 'invalid option. `%s` option must be a writable stream. Option: `%s`.', 'stream', opts.stream ) );
}
}
exitCode = 0;
// Create a new harness:
topts = pick( opts, [ 'autoclose' ] );
harness = createHarness( topts, done );
// Create a results stream:
topts = omit( options, [ 'autoclose', 'stream' ] );
stream = harness.createStream( topts );
// Pipe results to an output stream:
pipeline = stream.pipe( opts.stream || logStream() );
// If a process can emit an 'exit' event, capture errors in order to set the exit code...
if ( canEmitExit ) {
pipeline.on( 'error', onError );
proc.on( 'exit', onExit );
}
return harness;
/**
* Callback invoked when a harness finishes.
*
* @private
* @returns {void}
*/
function done() {
return clbk();
}
/**
* Callback invoked upon a stream `error` event.
*
* @private
* @param {Error} error - error object
*/
function onError() {
exitCode = 1;
}
/**
* Callback invoked upon an `exit` event.
*
* @private
* @param {integer} code - exit code
*/
function onExit( code ) {
if ( code !== 0 ) {
// Allow the process to exit...
return;
}
harness.close();
proc.exit( exitCode || harness.exitCode );
}
}
// EXPORTS //
module.exports = createExitHarness;