Files
mongo/jstests/libs/parallelTester.js
2015-12-09 11:06:06 -05:00

295 lines
12 KiB
JavaScript

/**
* The ParallelTester class is used to test more than one test concurrently
*/
if (typeof _threadInject != "undefined") {
Thread = function(){
this.init.apply( this, arguments );
}
_threadInject( Thread.prototype );
ScopedThread = function() {
this.init.apply( this, arguments );
}
ScopedThread.prototype = new Thread( function() {} );
_scopedThreadInject( ScopedThread.prototype );
fork = function() {
var t = new Thread( function() {} );
Thread.apply( t, arguments );
return t;
}
// Helper class to generate a list of events which may be executed by a ParallelTester
EventGenerator = function( me, collectionName, mean, host ) {
this.mean = mean;
if (host == undefined) host = db.getMongo().host;
this.events = new Array( me, collectionName, host );
}
EventGenerator.prototype._add = function( action ) {
this.events.push( [ Random.genExp( this.mean ), action ] );
}
EventGenerator.prototype.addInsert = function( obj ) {
this._add( "t.insert( " + tojson( obj ) + " )" );
}
EventGenerator.prototype.addRemove = function( obj ) {
this._add( "t.remove( " + tojson( obj ) + " )" );
}
EventGenerator.prototype.addUpdate = function( objOld, objNew ) {
this._add( "t.update( " + tojson( objOld ) + ", " + tojson( objNew ) + " )" );
}
EventGenerator.prototype.addCheckCount = function( count, query, shouldPrint, checkQuery ) {
query = query || {};
shouldPrint = shouldPrint || false;
checkQuery = checkQuery || false;
var action = "assert.eq( " + count + ", t.count( " + tojson( query ) + " ) );"
if ( checkQuery ) {
action += " assert.eq( " + count + ", t.find( " + tojson( query ) + " ).toArray().length );"
}
if ( shouldPrint ) {
action += " print( me + ' ' + " + count + " );";
}
this._add( action );
}
EventGenerator.prototype.getEvents = function() {
return this.events;
}
EventGenerator.dispatch = function() {
var args = argumentsToArray( arguments );
var me = args.shift();
var collectionName = args.shift();
var host = args.shift();
var m = new Mongo( host );
var t = m.getDB( "test" )[ collectionName ];
for( var i in args ) {
sleep( args[ i ][ 0 ] );
eval( args[ i ][ 1 ] );
}
}
// Helper class for running tests in parallel. It assembles a set of tests
// and then calls assert.parallelests to run them.
ParallelTester = function() {
assert.neq(db.getMongo().writeMode(), "legacy", "wrong shell write mode")
this.params = new Array();
}
ParallelTester.prototype.add = function( fun, args ) {
args = args || [];
args.unshift( fun );
this.params.push( args );
}
ParallelTester.prototype.run = function( msg, newScopes ) {
newScopes = newScopes || false;
assert.parallelTests( this.params, msg, newScopes );
}
// creates lists of tests from jstests dir in a format suitable for use by
// ParallelTester.fileTester. The lists will be in random order.
// n: number of lists to split these tests into
ParallelTester.createJstestsLists = function( n ) {
var params = new Array();
for( var i = 0; i < n; ++i ) {
params.push( [] );
}
var makeKeys = function( a ) {
var ret = {};
for( var i in a ) {
ret[ a[ i ] ] = 1;
}
return ret;
}
// some tests can't run in parallel with most others
var skipTests = makeKeys([ "repair.js",
"cursor8.js",
"recstore.js",
"extent.js",
"indexb.js",
// sets a failpoint that causes the server to ignore long keys,
// which makes index_bigkeys.js fail
"index_bigkeys_nofail.js",
// tests turn on profiling
"profile1.js",
"profile3.js",
"profile4.js",
"profile5.js",
"geo_s2cursorlimitskip.js",
"mr_drop.js",
"mr3.js",
"indexh.js",
"apitest_db.js",
"evalb.js",
"evald.js",
"evalf.js",
"killop.js",
"run_program1.js",
"notablescan.js",
"drop2.js",
"dropdb_race.js",
"fsync2.js", // May be placed in serialTestsArr once SERVER-4243 is fixed.
"bench_test1.js",
"padding.js",
"queryoptimizera.js",
"loglong.js",// log might overflow before
// this has a chance to see the message
"connections_opened.js", // counts connections, globally
"opcounters_write_cmd.js",
"currentop.js", // SERVER-8673, plus rwlock yielding issues
"set_param1.js", // changes global state
"geo_update_btree2.js", // SERVER-11132 test disables table scans
"update_setOnInsert.js", // SERVER-9982
"max_time_ms.js", // Sensitive to query execution time, by design
"collection_info_cache_race.js", // Requires collection exists
// This overwrites MinKey/MaxKey's singleton which breaks
// any other test that uses MinKey/MaxKey
"type6.js",
// Assumes that other tests are not creating cursors.
"kill_cursors.js",
] );
var parallelFilesDir = "jstests/core";
// some tests can't be run in parallel with each other
var serialTestsArr = [ parallelFilesDir + "/fsync.js",
parallelFilesDir + "/auth1.js",
// These tests expect the profiler to be on or off at specific points
// during the test run.
parallelFilesDir + "/cursor6.js",
parallelFilesDir + "/profile2.js",
parallelFilesDir + "/updatee.js"
];
var serialTests = makeKeys( serialTestsArr );
// prefix the first thread with the serialTests
// (which we will exclude from the rest of the threads below)
params[ 0 ] = serialTestsArr;
var files = listFiles( parallelFilesDir );
files = Array.shuffle( files );
var i = 0;
files.forEach(
function(x) {
if ( ( /[\/\\]_/.test(x.name) ) ||
( ! /\.js$/.test(x.name) ) ||
( x.name.match(parallelFilesDir + "/(.*\.js)")[1] in skipTests ) || //
( x.name in serialTests )) {
print(" >>>>>>>>>>>>>>> skipping " + x.name);
return;
}
// add the test to run in one of the threads.
params[ i % n ].push( x.name );
++i;
}
);
// randomize ordering of the serialTests
params[ 0 ] = Array.shuffle( params[ 0 ] );
for( var i in params ) {
params[ i ].unshift( i );
}
return params;
}
// runs a set of test files
// first argument is an identifier for this tester, remaining arguments are file names
ParallelTester.fileTester = function() {
var args = argumentsToArray( arguments );
var suite = args.shift();
args.forEach(
function( x ) {
print(" S" + suite + " Test : " + x + " ...");
var time = Date.timeFunc( function() { load(x); }, 1);
print(" S" + suite + " Test : " + x + " " + time + "ms" );
}
);
}
// params: array of arrays, each element of which consists of a function followed
// by zero or more arguments to that function. Each function and its arguments will
// be called in a separate thread.
// msg: failure message
// newScopes: if true, each thread starts in a fresh scope
assert.parallelTests = function( params, msg, newScopes ) {
newScopes = newScopes || false;
var wrapper = function( fun, argv ) {
eval (
"var z = function() {" +
"TestData = " + tojson(TestData) + ";" +
"var __parallelTests__fun = " + fun.toString() + ";" +
"var __parallelTests__argv = " + tojson( argv ) + ";" +
"var __parallelTests__passed = false;" +
"try {" +
"__parallelTests__fun.apply( 0, __parallelTests__argv );" +
"__parallelTests__passed = true;" +
"} catch ( e ) {" +
"print('');" +
"print( '********** Parallel Test FAILED: ' + tojson(e) );" +
"print('');" +
"}" +
"return __parallelTests__passed;" +
"}"
);
return z;
}
var runners = new Array();
for( var i in params ) {
var param = params[ i ];
var test = param.shift();
var t;
if ( newScopes )
t = new ScopedThread( wrapper( test, param ) );
else
t = new Thread( wrapper( test, param ) );
runners.push( t );
}
runners.forEach( function( x ) { x.start(); } );
var nFailed = 0;
// SpiderMonkey doesn't like it if we exit before all threads are joined
// (see SERVER-19615 for a similar issue).
runners.forEach( function( x ) { if( !x.returnData() ) { ++nFailed; } } );
assert.eq( 0, nFailed, msg );
}
}
if ( typeof CountDownLatch !== 'undefined' ) {
CountDownLatch = Object.extend(function(count) {
if (! (this instanceof CountDownLatch)) {
return new CountDownLatch(count);
}
this._descriptor = CountDownLatch._new.apply(null, arguments);
// NOTE: The following methods have to be defined on the instance itself,
// and not on its prototype. This is because properties on the
// prototype are lost during the serialization to BSON that occurs
// when passing data to a child thread.
this.await = function() {
CountDownLatch._await(this._descriptor);
};
this.countDown = function() {
CountDownLatch._countDown(this._descriptor);
};
this.getCount = function() {
return CountDownLatch._getCount(this._descriptor);
};
}, CountDownLatch);
}