diff options
Diffstat (limited to 'shell/utils.js')
-rw-r--r-- | shell/utils.js | 907 |
1 files changed, 907 insertions, 0 deletions
diff --git a/shell/utils.js b/shell/utils.js new file mode 100644 index 0000000..2d59b80 --- /dev/null +++ b/shell/utils.js @@ -0,0 +1,907 @@ + +__quiet = false; + +chatty = function(s){ + if ( ! __quiet ) + print( s ); +} + +friendlyEqual = function( a , b ){ + if ( a == b ) + return true; + + if ( tojson( a ) == tojson( b ) ) + return true; + + return false; +} + + +doassert = function( msg ){ + print( "assert: " + msg ); + throw msg; +} + +assert = function( b , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + + if ( b ) + return; + + doassert( "assert failed : " + msg ); +} + +assert._debug = false; + +assert.eq = function( a , b , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + + if ( a == b ) + return; + + if ( ( a != null && b != null ) && friendlyEqual( a , b ) ) + return; + + doassert( "[" + tojson( a ) + "] != [" + tojson( b ) + "] are not equal : " + msg ); +} + +assert.neq = function( a , b , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + if ( a != b ) + return; + + doassert( "[" + a + "] != [" + b + "] are equal : " + msg ); +} + +assert.soon = function( f, msg, timeout, interval ) { + if ( assert._debug && msg ) print( "in assert for: " + msg ); + + var start = new Date(); + timeout = timeout || 30000; + interval = interval || 200; + var last; + while( 1 ) { + + if ( typeof( f ) == "string" ){ + if ( eval( f ) ) + return; + } + else { + if ( f() ) + return; + } + + if ( ( new Date() ).getTime() - start.getTime() > timeout ) + doassert( "assert.soon failed: " + f + ", msg:" + msg ); + sleep( interval ); + } +} + +assert.throws = function( func , params , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + try { + func.apply( null , params ); + } + catch ( e ){ + return e; + } + + doassert( "did not throw exception: " + msg ); +} + +assert.commandWorked = function( res , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + + if ( res.ok == 1 ) + return; + + doassert( "command failed: " + tojson( res ) + " : " + msg ); +} + +assert.commandFailed = function( res , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + + if ( res.ok == 0 ) + return; + + doassert( "command worked when it should have failed: " + tojson( res ) + " : " + msg ); +} + +assert.isnull = function( what , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + + if ( what == null ) + return; + + doassert( "supposed to null (" + ( msg || "" ) + ") was: " + tojson( what ) ); +} + +assert.lt = function( a , b , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + + if ( a < b ) + return; + doassert( a + " is not less than " + b + " : " + msg ); +} + +assert.gt = function( a , b , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + + if ( a > b ) + return; + doassert( a + " is not greater than " + b + " : " + msg ); +} + +Object.extend = function( dst , src , deep ){ + for ( var k in src ){ + var v = src[k]; + if ( deep && typeof(v) == "object" ){ + v = Object.extend( typeof ( v.length ) == "number" ? [] : {} , v , true ); + } + dst[k] = v; + } + return dst; +} + +argumentsToArray = function( a ){ + var arr = []; + for ( var i=0; i<a.length; i++ ) + arr[i] = a[i]; + return arr; +} + +isString = function( x ){ + return typeof( x ) == "string"; +} + +isNumber = function(x){ + return typeof( x ) == "number"; +} + +isObject = function( x ){ + return typeof( x ) == "object"; +} + +String.prototype.trim = function() { + return this.replace(/^\s+|\s+$/g,""); +} +String.prototype.ltrim = function() { + return this.replace(/^\s+/,""); +} +String.prototype.rtrim = function() { + return this.replace(/\s+$/,""); +} + +Date.timeFunc = function( theFunc , numTimes ){ + + var start = new Date(); + + numTimes = numTimes || 1; + for ( var i=0; i<numTimes; i++ ){ + theFunc.apply( null , argumentsToArray( arguments ).slice( 2 ) ); + } + + return (new Date()).getTime() - start.getTime(); +} + +Date.prototype.tojson = function(){ + return "\"" + this.toString() + "\""; +} + +RegExp.prototype.tojson = RegExp.prototype.toString; + +Array.contains = function( a , x ){ + for ( var i=0; i<a.length; i++ ){ + if ( a[i] == x ) + return true; + } + return false; +} + +Array.unique = function( a ){ + var u = []; + for ( var i=0; i<a.length; i++){ + var o = a[i]; + if ( ! Array.contains( u , o ) ){ + u.push( o ); + } + } + return u; +} + +Array.shuffle = function( arr ){ + for ( var i=0; i<arr.length-1; i++ ){ + var pos = i+Random.randInt(arr.length-i); + var save = arr[i]; + arr[i] = arr[pos]; + arr[pos] = save; + } + return arr; +} + + +Array.tojson = function( a , indent ){ + if (!indent) + indent = ""; + + if (a.length == 0) { + return "[ ]"; + } + + var s = "[\n"; + indent += "\t"; + for ( var i=0; i<a.length; i++){ + s += indent + tojson( a[i], indent ); + if ( i < a.length - 1 ){ + s += ",\n"; + } + } + if ( a.length == 0 ) { + s += indent; + } + + indent = indent.substring(1); + s += "\n"+indent+"]"; + return s; +} + +Array.fetchRefs = function( arr , coll ){ + var n = []; + for ( var i=0; i<arr.length; i ++){ + var z = arr[i]; + if ( coll && coll != z.getCollection() ) + continue; + n.push( z.fetch() ); + } + + return n; +} + +Array.sum = function( arr ){ + if ( arr.length == 0 ) + return null; + var s = arr[0]; + for ( var i=1; i<arr.length; i++ ) + s += arr[i]; + return s; +} + +Array.avg = function( arr ){ + if ( arr.length == 0 ) + return null; + return Array.sum( arr ) / arr.length; +} + +Array.stdDev = function( arr ){ + var avg = Array.avg( arr ); + var sum = 0; + + for ( var i=0; i<arr.length; i++ ){ + sum += Math.pow( arr[i] - avg , 2 ); + } + + return Math.sqrt( sum / arr.length ); +} + +Object.keySet = function( o ) { + var ret = new Array(); + for( i in o ) { + if ( !( i in o.__proto__ && o[ i ] === o.__proto__[ i ] ) ) { + ret.push( i ); + } + } + return ret; +} + +if ( ! ObjectId.prototype ) + ObjectId.prototype = {} + +ObjectId.prototype.toString = function(){ + return this.str; +} + +ObjectId.prototype.tojson = function(){ + return "ObjectId(\"" + this.str + "\")"; +} + +ObjectId.prototype.isObjectId = true; + +if ( typeof( DBPointer ) != "undefined" ){ + DBPointer.prototype.fetch = function(){ + assert( this.ns , "need a ns" ); + assert( this.id , "need an id" ); + + return db[ this.ns ].findOne( { _id : this.id } ); + } + + DBPointer.prototype.tojson = function(indent){ + return tojson({"ns" : this.ns, "id" : this.id}, indent); + } + + DBPointer.prototype.getCollection = function(){ + return this.ns; + } + + DBPointer.prototype.toString = function(){ + return "DBPointer " + this.ns + ":" + this.id; + } +} +else { + print( "warning: no DBPointer" ); +} + +if ( typeof( DBRef ) != "undefined" ){ + DBRef.prototype.fetch = function(){ + assert( this.$ref , "need a ns" ); + assert( this.$id , "need an id" ); + + return db[ this.$ref ].findOne( { _id : this.$id } ); + } + + DBRef.prototype.tojson = function(indent){ + return tojson({"$ref" : this.$ref, "$id" : this.$id}, indent); + } + + DBRef.prototype.getCollection = function(){ + return this.$ref; + } + + DBRef.prototype.toString = function(){ + return this.tojson(); + } +} +else { + print( "warning: no DBRef" ); +} + +if ( typeof( BinData ) != "undefined" ){ + BinData.prototype.tojson = function(){ + return "BinData type: " + this.type + " len: " + this.len; + } +} +else { + print( "warning: no BinData" ); +} + +if ( typeof _threadInject != "undefined" ){ + print( "fork() available!" ); + + 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 ) { + this.mean = mean; + this.events = new Array( me, collectionName ); + } + + 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 m = new Mongo( db.getMongo().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() { + 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( [ "jstests/dbadmin.js", + "jstests/repair.js", + "jstests/cursor8.js", + "jstests/recstore.js", + "jstests/extent.js", + "jstests/indexb.js", + "jstests/profile1.js", + "jstests/mr3.js"] ); + + // some tests can't be run in parallel with each other + var serialTestsArr = [ "jstests/fsync.js", + "jstests/fsync2.js" ]; + var serialTests = makeKeys( serialTestsArr ); + + params[ 0 ] = serialTestsArr; + + var files = listFiles("jstests"); + files = Array.shuffle( files ); + + var i = 0; + files.forEach( + function(x) { + + if ( /_runner/.test(x.name) || + /_lodeRunner/.test(x.name) || + ( x.name in skipTests ) || + ( x.name in serialTests ) || + ! /\.js$/.test(x.name ) ){ + print(" >>>>>>>>>>>>>>> skipping " + x.name); + return; + } + + 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() {" + + "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( e );" + + "}" + + "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; + // v8 doesn't like it if we exit before all threads are joined (SERVER-529) + runners.forEach( function( x ) { if( !x.returnData() ) { ++nFailed; } } ); + assert.eq( 0, nFailed, msg ); + } +} + +tojson = function( x, indent , nolint ){ + if ( x == null ) + return "null"; + + if ( x == undefined ) + return "undefined"; + + if (!indent) + indent = ""; + + switch ( typeof x ){ + + case "string": { + var s = "\""; + for ( var i=0; i<x.length; i++ ){ + if ( x[i] == '"' ){ + s += "\\\""; + } + else + s += x[i]; + } + return s + "\""; + } + + case "number": + case "boolean": + return "" + x; + + case "object":{ + var s = tojsonObject( x, indent , nolint ); + if ( ( nolint == null || nolint == true ) && s.length < 80 && ( indent == null || indent.length == 0 ) ){ + s = s.replace( /[\s\r\n ]+/gm , " " ); + } + return s; + } + + case "function": + return x.toString(); + + + default: + throw "tojson can't handle type " + ( typeof x ); + } + +} + +tojsonObject = function( x, indent , nolint ){ + var lineEnding = nolint ? " " : "\n"; + var tabSpace = nolint ? "" : "\t"; + + assert.eq( ( typeof x ) , "object" , "tojsonObject needs object, not [" + ( typeof x ) + "]" ); + + if (!indent) + indent = ""; + + if ( typeof( x.tojson ) == "function" && x.tojson != tojson ) { + return x.tojson(indent,nolint); + } + + if ( typeof( x.constructor.tojson ) == "function" && x.constructor.tojson != tojson ) { + return x.constructor.tojson( x, indent , nolint ); + } + + if ( x.toString() == "[object MaxKey]" ) + return "{ $maxKey : 1 }"; + if ( x.toString() == "[object MinKey]" ) + return "{ $minKey : 1 }"; + + var s = "{" + lineEnding; + + // push one level of indent + indent += tabSpace; + + var total = 0; + for ( var k in x ) total++; + if ( total == 0 ) { + s += indent + lineEnding; + } + + var keys = x; + if ( typeof( x._simpleKeys ) == "function" ) + keys = x._simpleKeys(); + var num = 1; + for ( var k in keys ){ + + var val = x[k]; + if ( val == DB.prototype || val == DBCollection.prototype ) + continue; + + s += indent + "\"" + k + "\" : " + tojson( val, indent , nolint ); + if (num != total) { + s += ","; + num++; + } + s += lineEnding; + } + + // pop one level of indent + indent = indent.substring(1); + return s + indent + "}"; +} + +shellPrint = function( x ){ + it = x; + if ( x != undefined ) + shellPrintHelper( x ); + + if ( db ){ + var e = db.getPrevError(); + if ( e.err ) { + if( e.nPrev <= 1 ) + print( "error on last call: " + tojson( e.err ) ); + else + print( "an error " + tojson(e.err) + " occurred " + e.nPrev + " operations back in the command invocation" ); + } + db.resetError(); + } +} + +printjson = function(x){ + print( tojson( x ) ); +} + +shellPrintHelper = function( x ){ + + if ( typeof( x ) == "undefined" ){ + + if ( typeof( db ) != "undefined" && db.getLastError ){ + var e = db.getLastError(); + if ( e != null ) + print( e ); + } + + return; + } + + if ( x == null ){ + print( "null" ); + return; + } + + if ( typeof x != "object" ) + return print( x ); + + var p = x.shellPrint; + if ( typeof p == "function" ) + return x.shellPrint(); + + var p = x.tojson; + if ( typeof p == "function" ) + print( x.tojson() ); + else + print( tojson( x ) ); +} + +shellHelper = function( command , rest , shouldPrint ){ + command = command.trim(); + var args = rest.trim().replace(/;$/,"").split( "\s+" ); + + if ( ! shellHelper[command] ) + throw "no command [" + command + "]"; + + var res = shellHelper[command].apply( null , args ); + if ( shouldPrint ){ + shellPrintHelper( res ); + } + return res; +} + +help = shellHelper.help = function(){ + print( "HELP" ); + print( "\t" + "show dbs show database names"); + print( "\t" + "show collections show collections in current database"); + print( "\t" + "show users show users in current database"); + print( "\t" + "show profile show most recent system.profile entries with time >= 1ms"); + print( "\t" + "use <db name> set curent database to <db name>" ); + print( "\t" + "db.help() help on DB methods"); + print( "\t" + "db.foo.help() help on collection methods"); + print( "\t" + "db.foo.find() list objects in collection foo" ); + print( "\t" + "db.foo.find( { a : 1 } ) list objects in foo where a == 1" ); + print( "\t" + "it result of the last line evaluated; use to further iterate"); +} + +shellHelper.use = function( dbname ){ + db = db.getMongo().getDB( dbname ); + print( "switched to db " + db.getName() ); +} + +shellHelper.it = function(){ + if ( typeof( ___it___ ) == "undefined" || ___it___ == null ){ + print( "no cursor" ); + return; + } + shellPrintHelper( ___it___ ); +} + +shellHelper.show = function( what ){ + assert( typeof what == "string" ); + + if( what == "profile" ) { + if( db.system.profile.count() == 0 ) { + print("db.system.profile is empty"); + print("Use db.setProfilingLevel(2) will enable profiling"); + print("Use db.system.profile.find() to show raw profile entries"); + } + else { + print(); + db.system.profile.find({ millis : { $gt : 0 } }).sort({$natural:-1}).limit(5).forEach( function(x){print(""+x.millis+"ms " + String(x.ts).substring(0,24)); print(x.info); print("\n");} ) + } + return ""; + } + + if ( what == "users" ){ + db.system.users.find().forEach( printjson ); + return ""; + } + + if ( what == "collections" || what == "tables" ) { + db.getCollectionNames().forEach( function(x){print(x)} ); + return ""; + } + + if ( what == "dbs" ) { + db.getMongo().getDBNames().sort().forEach( function(x){print(x)} ); + return ""; + } + + throw "don't know how to show [" + what + "]"; + +} + +if ( typeof( Map ) == "undefined" ){ + Map = function(){ + this._data = {}; + } +} + +Map.hash = function( val ){ + if ( ! val ) + return val; + + switch ( typeof( val ) ){ + case 'string': + case 'number': + case 'date': + return val.toString(); + case 'object': + case 'array': + var s = ""; + for ( var k in val ){ + s += k + val[k]; + } + return s; + } + + throw "can't hash : " + typeof( val ); +} + +Map.prototype.put = function( key , value ){ + var o = this._get( key ); + var old = o.value; + o.value = value; + return old; +} + +Map.prototype.get = function( key ){ + return this._get( key ).value; +} + +Map.prototype._get = function( key ){ + var h = Map.hash( key ); + var a = this._data[h]; + if ( ! a ){ + a = []; + this._data[h] = a; + } + + for ( var i=0; i<a.length; i++ ){ + if ( friendlyEqual( key , a[i].key ) ){ + return a[i]; + } + } + var o = { key : key , value : null }; + a.push( o ); + return o; +} + +Map.prototype.values = function(){ + var all = []; + for ( var k in this._data ){ + this._data[k].forEach( function(z){ all.push( z.value ); } ); + } + return all; +} + +if ( typeof( gc ) == "undefined" ){ + gc = function(){ + } +} + + +Math.sigFig = function( x , N ){ + if ( ! N ){ + N = 3; + } + var p = Math.pow( 10, N - Math.ceil( Math.log( Math.abs(x) ) / Math.log( 10 )) ); + return Math.round(x*p)/p; +} + +Random = function() {} + +// set random seed +Random.srand = function( s ) { _srand( s ); } + +// random number 0 <= r < 1 +Random.rand = function() { return _rand(); } + +// random integer 0 <= r < n +Random.randInt = function( n ) { return Math.floor( Random.rand() * n ); } + +Random.setRandomSeed = function( s ) { + s = s || new Date().getTime(); + print( "setting random seed: " + s ); + Random.srand( s ); +} + +// generate a random value from the exponential distribution with the specified mean +Random.genExp = function( mean ) { + return -Math.log( Random.rand() ) * mean; +} |