diff options
Diffstat (limited to 'src/mongo/dbtests/jstests.cpp')
-rw-r--r-- | src/mongo/dbtests/jstests.cpp | 4041 |
1 files changed, 2059 insertions, 1982 deletions
diff --git a/src/mongo/dbtests/jstests.cpp b/src/mongo/dbtests/jstests.cpp index 67299420841..2842daffea3 100644 --- a/src/mongo/dbtests/jstests.cpp +++ b/src/mongo/dbtests/jstests.cpp @@ -57,2119 +57,2196 @@ using std::vector; namespace JSTests { - class BuiltinTests { +class BuiltinTests { +public: + void run() { + // Run any tests included with the scripting engine + globalScriptEngine->runTest(); + } +}; + +class BasicScope { +public: + void run() { + scoped_ptr<Scope> s; + s.reset(globalScriptEngine->newScope()); + + s->setNumber("x", 5); + ASSERT(5 == s->getNumber("x")); + + s->setNumber("x", 1.67); + ASSERT(1.67 == s->getNumber("x")); + + s->setString("s", "eliot was here"); + ASSERT("eliot was here" == s->getString("s")); + + s->setBoolean("b", true); + ASSERT(s->getBoolean("b")); + + s->setBoolean("b", false); + ASSERT(!s->getBoolean("b")); + } +}; + +class ResetScope { +public: + void run() { + /* Currently reset does not clear data in v8 or spidermonkey scopes. See SECURITY-10 + auto_ptr<Scope> s; + s.reset( globalScriptEngine->newScope() ); + + s->setBoolean( "x" , true ); + ASSERT( s->getBoolean( "x" ) ); + + s->reset(); + ASSERT( !s->getBoolean( "x" ) ); + */ + } +}; + +class FalseTests { +public: + void run() { + // Test falsy javascript values + scoped_ptr<Scope> s; + s.reset(globalScriptEngine->newScope()); + + ASSERT(!s->getBoolean("notSet")); + + s->setString("emptyString", ""); + ASSERT(!s->getBoolean("emptyString")); + + s->setNumber("notANumberVal", std::numeric_limits<double>::quiet_NaN()); + ASSERT(!s->getBoolean("notANumberVal")); + + s->setElement("nullVal", BSONObjBuilder().appendNull("null").obj().getField("null")); + ASSERT(!s->getBoolean("nullVal")); + + s->setNumber("zeroVal", 0); + ASSERT(!s->getBoolean("zeroVal")); + } +}; + +class SimpleFunctions { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); + + s->invoke("x=5;", 0, 0); + ASSERT(5 == s->getNumber("x")); + + s->invoke("return 17;", 0, 0); + ASSERT(17 == s->getNumber("__returnValue")); + + s->invoke("function(){ return 17; }", 0, 0); + ASSERT(17 == s->getNumber("__returnValue")); + + s->setNumber("x", 1.76); + s->invoke("return x == 1.76; ", 0, 0); + ASSERT(s->getBoolean("__returnValue")); + + s->setNumber("x", 1.76); + s->invoke("return x == 1.79; ", 0, 0); + ASSERT(!s->getBoolean("__returnValue")); + + BSONObj obj = BSON("" << 11.0); + s->invoke("function( z ){ return 5 + z; }", &obj, 0); + ASSERT_EQUALS(16, s->getNumber("__returnValue")); + + delete s; + } +}; + +/** Installs a tee for auditing log messages in the same thread. */ +class LogRecordingScope { +public: + LogRecordingScope() + : _logged(false), + _threadName(mongo::getThreadName()), + _handle(mongo::logger::globalLogDomain()->attachAppender( + mongo::logger::MessageLogDomain::AppenderAutoPtr(new Tee(this)))) {} + ~LogRecordingScope() { + mongo::logger::globalLogDomain()->detachAppender(_handle); + } + /** @return most recent log entry. */ + bool logged() const { + return _logged; + } + +private: + class Tee : public mongo::logger::MessageLogDomain::EventAppender { public: - void run() { - // Run any tests included with the scripting engine - globalScriptEngine->runTest(); + Tee(LogRecordingScope* scope) : _scope(scope) {} + virtual ~Tee() {} + virtual Status append(const logger::MessageEventEphemeral& event) { + // Don't want to consider logging by background threads. + if (mongo::getThreadName() == _scope->_threadName) { + _scope->_logged = true; + } + return Status::OK(); } - }; - - class BasicScope { - public: - void run() { - scoped_ptr<Scope> s; - s.reset( globalScriptEngine->newScope() ); - - s->setNumber( "x" , 5 ); - ASSERT( 5 == s->getNumber( "x" ) ); - - s->setNumber( "x" , 1.67 ); - ASSERT( 1.67 == s->getNumber( "x" ) ); - - s->setString( "s" , "eliot was here" ); - ASSERT( "eliot was here" == s->getString( "s" ) ); - - s->setBoolean( "b" , true ); - ASSERT( s->getBoolean( "b" ) ); - - s->setBoolean( "b" , false ); - ASSERT( ! s->getBoolean( "b" ) ); - } - }; - - class ResetScope { - public: - void run() { - /* Currently reset does not clear data in v8 or spidermonkey scopes. See SECURITY-10 - auto_ptr<Scope> s; - s.reset( globalScriptEngine->newScope() ); - - s->setBoolean( "x" , true ); - ASSERT( s->getBoolean( "x" ) ); - - s->reset(); - ASSERT( !s->getBoolean( "x" ) ); - */ - } - }; - - class FalseTests { - public: - void run() { - // Test falsy javascript values - scoped_ptr<Scope> s; - s.reset( globalScriptEngine->newScope() ); - - ASSERT( ! s->getBoolean( "notSet" ) ); - - s->setString( "emptyString" , "" ); - ASSERT( ! s->getBoolean( "emptyString" ) ); - - s->setNumber( "notANumberVal" , std::numeric_limits<double>::quiet_NaN()); - ASSERT( ! s->getBoolean( "notANumberVal" ) ); - - s->setElement( "nullVal" , BSONObjBuilder().appendNull("null").obj().getField("null") ); - ASSERT( ! s->getBoolean( "nullVal" ) ); - - s->setNumber( "zeroVal" , 0 ); - ASSERT( ! s->getBoolean( "zeroVal" ) ); - } - }; - - class SimpleFunctions { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); - - s->invoke( "x=5;" , 0, 0 ); - ASSERT( 5 == s->getNumber( "x" ) ); - - s->invoke( "return 17;" , 0, 0 ); - ASSERT( 17 == s->getNumber( "__returnValue" ) ); - - s->invoke( "function(){ return 17; }" , 0, 0 ); - ASSERT( 17 == s->getNumber( "__returnValue" ) ); - - s->setNumber( "x" , 1.76 ); - s->invoke( "return x == 1.76; " , 0, 0 ); - ASSERT( s->getBoolean( "__returnValue" ) ); - - s->setNumber( "x" , 1.76 ); - s->invoke( "return x == 1.79; " , 0, 0 ); - ASSERT( ! s->getBoolean( "__returnValue" ) ); - BSONObj obj = BSON( "" << 11.0 ); - s->invoke( "function( z ){ return 5 + z; }" , &obj, 0 ); - ASSERT_EQUALS( 16 , s->getNumber( "__returnValue" ) ); - - delete s; - } - }; - - /** Installs a tee for auditing log messages in the same thread. */ - class LogRecordingScope { - public: - LogRecordingScope() : - _logged(false), - _threadName(mongo::getThreadName()), - _handle(mongo::logger::globalLogDomain()->attachAppender( - mongo::logger::MessageLogDomain::AppenderAutoPtr(new Tee(this)))) { - } - ~LogRecordingScope() { - mongo::logger::globalLogDomain()->detachAppender(_handle); - } - /** @return most recent log entry. */ - bool logged() const { return _logged; } private: - class Tee : public mongo::logger::MessageLogDomain::EventAppender { - public: - Tee(LogRecordingScope* scope) : _scope(scope) {} - virtual ~Tee() {} - virtual Status append(const logger::MessageEventEphemeral& event) { - // Don't want to consider logging by background threads. - if (mongo::getThreadName() == _scope->_threadName) { - _scope->_logged = true; - } - return Status::OK(); - } - private: - LogRecordingScope* _scope; - }; - bool _logged; - const string _threadName; - mongo::logger::MessageLogDomain::AppenderHandle _handle; + LogRecordingScope* _scope; }; - - /** Error logging in Scope::exec(). */ - class ExecLogError { - public: - void run() { - Scope *scope = globalScriptEngine->newScope(); - - // No error is logged when reportError == false. - ASSERT( !scope->exec( "notAFunction()", "foo", false, false, false ) ); - ASSERT( !_logger.logged() ); - - // No error is logged for a valid statement. - ASSERT( scope->exec( "validStatement = true", "foo", false, true, false ) ); - ASSERT( !_logger.logged() ); - - // An error is logged for an invalid statement when reportError == true. - ASSERT( !scope->exec( "notAFunction()", "foo", false, true, false ) ); - ASSERT( _logger.logged() ); + bool _logged; + const string _threadName; + mongo::logger::MessageLogDomain::AppenderHandle _handle; +}; + +/** Error logging in Scope::exec(). */ +class ExecLogError { +public: + void run() { + Scope* scope = globalScriptEngine->newScope(); + + // No error is logged when reportError == false. + ASSERT(!scope->exec("notAFunction()", "foo", false, false, false)); + ASSERT(!_logger.logged()); + + // No error is logged for a valid statement. + ASSERT(scope->exec("validStatement = true", "foo", false, true, false)); + ASSERT(!_logger.logged()); + + // An error is logged for an invalid statement when reportError == true. + ASSERT(!scope->exec("notAFunction()", "foo", false, true, false)); + ASSERT(_logger.logged()); + } + +private: + LogRecordingScope _logger; +}; + +/** Error logging in Scope::invoke(). */ +class InvokeLogError { +public: + void run() { + Scope* scope = globalScriptEngine->newScope(); + + // No error is logged for a valid statement. + ASSERT_EQUALS(0, scope->invoke("validStatement = true", 0, 0)); + ASSERT(!_logger.logged()); + + // An error is logged for an invalid statement. + try { + scope->invoke("notAFunction()", 0, 0); + } catch (const DBException&) { + // ignore the exception; just test that we logged something } - private: - LogRecordingScope _logger; - }; - - /** Error logging in Scope::invoke(). */ - class InvokeLogError { - public: - void run() { - Scope *scope = globalScriptEngine->newScope(); - - // No error is logged for a valid statement. - ASSERT_EQUALS( 0, scope->invoke( "validStatement = true", 0, 0 ) ); - ASSERT( !_logger.logged() ); + ASSERT(_logger.logged()); + } - // An error is logged for an invalid statement. - try { - scope->invoke( "notAFunction()", 0, 0 ); - } - catch(const DBException&) { - // ignore the exception; just test that we logged something - } - ASSERT( _logger.logged() ); - } - private: - LogRecordingScope _logger; - }; +private: + LogRecordingScope _logger; +}; - class ObjectMapping { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); +class ObjectMapping { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); - BSONObj o = BSON( "x" << 17.0 << "y" << "eliot" << "z" << "sara" ); - s->setObject( "blah" , o ); + BSONObj o = BSON("x" << 17.0 << "y" + << "eliot" + << "z" + << "sara"); + s->setObject("blah", o); - s->invoke( "return blah.x;" , 0, 0 ); - ASSERT_EQUALS( 17 , s->getNumber( "__returnValue" ) ); - s->invoke( "return blah.y;" , 0, 0 ); - ASSERT_EQUALS( "eliot" , s->getString( "__returnValue" ) ); + s->invoke("return blah.x;", 0, 0); + ASSERT_EQUALS(17, s->getNumber("__returnValue")); + s->invoke("return blah.y;", 0, 0); + ASSERT_EQUALS("eliot", s->getString("__returnValue")); - s->invoke( "return this.z;" , 0, &o ); - ASSERT_EQUALS( "sara" , s->getString( "__returnValue" ) ); + s->invoke("return this.z;", 0, &o); + ASSERT_EQUALS("sara", s->getString("__returnValue")); - s->invoke( "return this.z == 'sara';" , 0, &o ); - ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) ); + s->invoke("return this.z == 'sara';", 0, &o); + ASSERT_EQUALS(true, s->getBoolean("__returnValue")); - s->invoke( "this.z == 'sara';" , 0, &o ); - ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) ); + s->invoke("this.z == 'sara';", 0, &o); + ASSERT_EQUALS(true, s->getBoolean("__returnValue")); - s->invoke( "this.z == 'asara';" , 0, &o ); - ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) ); + s->invoke("this.z == 'asara';", 0, &o); + ASSERT_EQUALS(false, s->getBoolean("__returnValue")); - s->invoke( "return this.x == 17;" , 0, &o ); - ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) ); + s->invoke("return this.x == 17;", 0, &o); + ASSERT_EQUALS(true, s->getBoolean("__returnValue")); - s->invoke( "return this.x == 18;" , 0, &o ); - ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) ); + s->invoke("return this.x == 18;", 0, &o); + ASSERT_EQUALS(false, s->getBoolean("__returnValue")); - s->invoke( "function(){ return this.x == 17; }" , 0, &o ); - ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) ); + s->invoke("function(){ return this.x == 17; }", 0, &o); + ASSERT_EQUALS(true, s->getBoolean("__returnValue")); - s->invoke( "function(){ return this.x == 18; }" , 0, &o ); - ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) ); + s->invoke("function(){ return this.x == 18; }", 0, &o); + ASSERT_EQUALS(false, s->getBoolean("__returnValue")); - s->invoke( "function (){ return this.x == 17; }" , 0, &o ); - ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) ); + s->invoke("function (){ return this.x == 17; }", 0, &o); + ASSERT_EQUALS(true, s->getBoolean("__returnValue")); - s->invoke( "function z(){ return this.x == 18; }" , 0, &o ); - ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) ); + s->invoke("function z(){ return this.x == 18; }", 0, &o); + ASSERT_EQUALS(false, s->getBoolean("__returnValue")); - s->invoke( "function (){ this.x == 17; }" , 0, &o ); - ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) ); + s->invoke("function (){ this.x == 17; }", 0, &o); + ASSERT_EQUALS(false, s->getBoolean("__returnValue")); - s->invoke( "function z(){ this.x == 18; }" , 0, &o ); - ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) ); + s->invoke("function z(){ this.x == 18; }", 0, &o); + ASSERT_EQUALS(false, s->getBoolean("__returnValue")); - s->invoke( "x = 5; for( ; x <10; x++){ a = 1; }" , 0, &o ); - ASSERT_EQUALS( 10 , s->getNumber( "x" ) ); + s->invoke("x = 5; for( ; x <10; x++){ a = 1; }", 0, &o); + ASSERT_EQUALS(10, s->getNumber("x")); - delete s; - } - }; + delete s; + } +}; - class ObjectDecoding { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); +class ObjectDecoding { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); - s->invoke( "z = { num : 1 };" , 0, 0 ); - BSONObj out = s->getObject( "z" ); - ASSERT_EQUALS( 1 , out["num"].number() ); - ASSERT_EQUALS( 1 , out.nFields() ); + s->invoke("z = { num : 1 };", 0, 0); + BSONObj out = s->getObject("z"); + ASSERT_EQUALS(1, out["num"].number()); + ASSERT_EQUALS(1, out.nFields()); - s->invoke( "z = { x : 'eliot' };" , 0, 0 ); - out = s->getObject( "z" ); - ASSERT_EQUALS( (string)"eliot" , out["x"].valuestr() ); - ASSERT_EQUALS( 1 , out.nFields() ); + s->invoke("z = { x : 'eliot' };", 0, 0); + out = s->getObject("z"); + ASSERT_EQUALS((string) "eliot", out["x"].valuestr()); + ASSERT_EQUALS(1, out.nFields()); - BSONObj o = BSON( "x" << 17 ); - s->setObject( "blah" , o ); - out = s->getObject( "blah" ); - ASSERT_EQUALS( 17 , out["x"].number() ); + BSONObj o = BSON("x" << 17); + s->setObject("blah", o); + out = s->getObject("blah"); + ASSERT_EQUALS(17, out["x"].number()); - delete s; - } - }; + delete s; + } +}; - class JSOIDTests { - public: - void run() { +class JSOIDTests { +public: + void run() { #ifdef MOZJS - Scope * s = globalScriptEngine->newScope(); + Scope* s = globalScriptEngine->newScope(); - s->localConnect( "blah" ); + s->localConnect("blah"); - s->invoke( "z = { _id : new ObjectId() , a : 123 };" , 0, 0 ); - BSONObj out = s->getObject( "z" ); - ASSERT_EQUALS( 123 , out["a"].number() ); - ASSERT_EQUALS( jstOID , out["_id"].type() ); + s->invoke("z = { _id : new ObjectId() , a : 123 };", 0, 0); + BSONObj out = s->getObject("z"); + ASSERT_EQUALS(123, out["a"].number()); + ASSERT_EQUALS(jstOID, out["_id"].type()); - OID save = out["_id"].__oid(); + OID save = out["_id"].__oid(); - s->setObject( "a" , out ); + s->setObject("a", out); - s->invoke( "y = { _id : a._id , a : 124 };" , 0, 0 ); - out = s->getObject( "y" ); - ASSERT_EQUALS( 124 , out["a"].number() ); - ASSERT_EQUALS( jstOID , out["_id"].type() ); - ASSERT_EQUALS( out["_id"].__oid().str() , save.str() ); + s->invoke("y = { _id : a._id , a : 124 };", 0, 0); + out = s->getObject("y"); + ASSERT_EQUALS(124, out["a"].number()); + ASSERT_EQUALS(jstOID, out["_id"].type()); + ASSERT_EQUALS(out["_id"].__oid().str(), save.str()); - s->invoke( "y = { _id : new ObjectId( a._id ) , a : 125 };" , 0, 0 ); - out = s->getObject( "y" ); - ASSERT_EQUALS( 125 , out["a"].number() ); - ASSERT_EQUALS( jstOID , out["_id"].type() ); - ASSERT_EQUALS( out["_id"].__oid().str() , save.str() ); + s->invoke("y = { _id : new ObjectId( a._id ) , a : 125 };", 0, 0); + out = s->getObject("y"); + ASSERT_EQUALS(125, out["a"].number()); + ASSERT_EQUALS(jstOID, out["_id"].type()); + ASSERT_EQUALS(out["_id"].__oid().str(), save.str()); - delete s; + delete s; #endif - } - }; - - class SetImplicit { - public: - void run() { - Scope *s = globalScriptEngine->newScope(); - - BSONObj o = BSON( "foo" << "bar" ); - s->setObject( "a.b", o ); - ASSERT( s->getObject( "a" ).isEmpty() ); - - BSONObj o2 = BSONObj(); - s->setObject( "a", o2 ); - s->setObject( "a.b", o ); - ASSERT( s->getObject( "a" ).isEmpty() ); - - o2 = fromjson( "{b:{}}" ); - s->setObject( "a", o2 ); - s->setObject( "a.b", o ); - ASSERT( !s->getObject( "a" ).isEmpty() ); - } - }; - - class ObjectModReadonlyTests { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); - - BSONObj o = BSON( "x" << 17 << "y" << "eliot" << "z" << "sara" << "zz" << BSONObj() ); - s->setObject( "blah" , o , true ); - - s->invoke( "blah.y = 'e'", 0, 0 ); - BSONObj out = s->getObject( "blah" ); - ASSERT( strlen( out["y"].valuestr() ) > 1 ); - - s->invoke( "blah.a = 19;" , 0, 0 ); - out = s->getObject( "blah" ); - ASSERT( out["a"].eoo() ); - - s->invoke( "blah.zz.a = 19;" , 0, 0 ); - out = s->getObject( "blah" ); - ASSERT( out["zz"].embeddedObject()["a"].eoo() ); - - s->setObject( "blah.zz", BSON( "a" << 19 ) ); - out = s->getObject( "blah" ); - ASSERT( out["zz"].embeddedObject()["a"].eoo() ); - - s->invoke( "delete blah['x']" , 0, 0 ); - out = s->getObject( "blah" ); - ASSERT( !out["x"].eoo() ); - - // read-only object itself can be overwritten - s->invoke( "blah = {}", 0, 0 ); - out = s->getObject( "blah" ); - ASSERT( out.isEmpty() ); - - // test array - can't implement this in v8 -// o = fromjson( "{a:[1,2,3]}" ); -// s->setObject( "blah", o, true ); -// out = s->getObject( "blah" ); -// s->invoke( "blah.a[ 0 ] = 4;", BSONObj() ); -// s->invoke( "delete blah['a'][ 2 ];", BSONObj() ); -// out = s->getObject( "blah" ); -// ASSERT_EQUALS( 1.0, out[ "a" ].embeddedObject()[ 0 ].number() ); -// ASSERT_EQUALS( 3.0, out[ "a" ].embeddedObject()[ 2 ].number() ); - - delete s; - } - }; - - class OtherJSTypes { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); - - { - // date - BSONObj o; - { - BSONObjBuilder b; - b.appendDate( "d" , 123456789 ); - o = b.obj(); - } - s->setObject( "x" , o ); - - s->invoke( "return x.d.getTime() != 12;" , 0, 0 ); - ASSERT_EQUALS( true, s->getBoolean( "__returnValue" ) ); - - s->invoke( "z = x.d.getTime();" , 0, 0 ); - ASSERT_EQUALS( 123456789 , s->getNumber( "z" ) ); - - s->invoke( "z = { z : x.d }" , 0, 0 ); - BSONObj out = s->getObject( "z" ); - ASSERT( out["z"].type() == Date ); - } - - { - // regex - BSONObj o; - { - BSONObjBuilder b; - b.appendRegex( "r" , "^a" , "i" ); - o = b.obj(); - } - s->setObject( "x" , o ); - - s->invoke( "z = x.r.test( 'b' );" , 0, 0 ); - ASSERT_EQUALS( false , s->getBoolean( "z" ) ); - - s->invoke( "z = x.r.test( 'a' );" , 0, 0 ); - ASSERT_EQUALS( true , s->getBoolean( "z" ) ); - - s->invoke( "z = x.r.test( 'ba' );" , 0, 0 ); - ASSERT_EQUALS( false , s->getBoolean( "z" ) ); - - s->invoke( "z = { a : x.r };" , 0, 0 ); - - BSONObj out = s->getObject("z"); - ASSERT_EQUALS( (string)"^a" , out["a"].regex() ); - ASSERT_EQUALS( (string)"i" , out["a"].regexFlags() ); - - // This regex used to cause a segfault because x isn't a valid flag for a js RegExp. - // Now it throws a JS exception. - BSONObj invalidRegex = BSON_ARRAY(BSON("regex" << BSONRegEx("asdf", "x"))); - const char* code = "function (obj) {" - " var threw = false;" - " try {" - " obj.regex;" // should throw - " } catch(e) {" - " threw = true;" - " }" - " assert(threw);" - "}"; - ASSERT_EQUALS(s->invoke(code, &invalidRegex, NULL), 0); - } - - // array - { - BSONObj o = fromjson( "{r:[1,2,3]}" ); - s->setObject( "x", o, false ); - BSONObj out = s->getObject( "x" ); - ASSERT_EQUALS( Array, out.firstElement().type() ); - - s->setObject( "x", o, true ); - out = s->getObject( "x" ); - ASSERT_EQUALS( Array, out.firstElement().type() ); - } - - // symbol - { - // test mutable object with symbol type - BSONObjBuilder builder; - builder.appendSymbol("sym", "value"); - BSONObj in = builder.done(); - s->setObject( "x", in, false ); - BSONObj out = s->getObject( "x" ); - ASSERT_EQUALS( Symbol, out.firstElement().type() ); - - // readonly - s->setObject( "x", in, true ); - out = s->getObject( "x" ); - ASSERT_EQUALS( Symbol, out.firstElement().type() ); - } - - delete s; - } - }; - - class SpecialDBTypes { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); - - BSONObjBuilder b; - b.appendTimestamp( "a" , 123456789 ); - b.appendMinKey( "b" ); - b.appendMaxKey( "c" ); - b.appendTimestamp( "d" , 1234000 , 9876 ); - - - { - BSONObj t = b.done(); - ASSERT_EQUALS( 1234000U , t["d"].timestampTime() ); - ASSERT_EQUALS( 9876U , t["d"].timestampInc() ); - } - - s->setObject( "z" , b.obj() ); - - ASSERT( s->invoke( "y = { a : z.a , b : z.b , c : z.c , d: z.d }" , 0, 0 ) == 0 ); - - BSONObj out = s->getObject( "y" ); - ASSERT_EQUALS( Timestamp , out["a"].type() ); - ASSERT_EQUALS( MinKey , out["b"].type() ); - ASSERT_EQUALS( MaxKey , out["c"].type() ); - ASSERT_EQUALS( Timestamp , out["d"].type() ); - - ASSERT_EQUALS( 9876U , out["d"].timestampInc() ); - ASSERT_EQUALS( 1234000U , out["d"].timestampTime() ); - ASSERT_EQUALS( 123456789U , out["a"].date() ); - - delete s; - } - }; - - class TypeConservation { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); - - // -- A -- - + } +}; + +class SetImplicit { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); + + BSONObj o = BSON("foo" + << "bar"); + s->setObject("a.b", o); + ASSERT(s->getObject("a").isEmpty()); + + BSONObj o2 = BSONObj(); + s->setObject("a", o2); + s->setObject("a.b", o); + ASSERT(s->getObject("a").isEmpty()); + + o2 = fromjson("{b:{}}"); + s->setObject("a", o2); + s->setObject("a.b", o); + ASSERT(!s->getObject("a").isEmpty()); + } +}; + +class ObjectModReadonlyTests { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); + + BSONObj o = BSON("x" << 17 << "y" + << "eliot" + << "z" + << "sara" + << "zz" << BSONObj()); + s->setObject("blah", o, true); + + s->invoke("blah.y = 'e'", 0, 0); + BSONObj out = s->getObject("blah"); + ASSERT(strlen(out["y"].valuestr()) > 1); + + s->invoke("blah.a = 19;", 0, 0); + out = s->getObject("blah"); + ASSERT(out["a"].eoo()); + + s->invoke("blah.zz.a = 19;", 0, 0); + out = s->getObject("blah"); + ASSERT(out["zz"].embeddedObject()["a"].eoo()); + + s->setObject("blah.zz", BSON("a" << 19)); + out = s->getObject("blah"); + ASSERT(out["zz"].embeddedObject()["a"].eoo()); + + s->invoke("delete blah['x']", 0, 0); + out = s->getObject("blah"); + ASSERT(!out["x"].eoo()); + + // read-only object itself can be overwritten + s->invoke("blah = {}", 0, 0); + out = s->getObject("blah"); + ASSERT(out.isEmpty()); + + // test array - can't implement this in v8 + // o = fromjson( "{a:[1,2,3]}" ); + // s->setObject( "blah", o, true ); + // out = s->getObject( "blah" ); + // s->invoke( "blah.a[ 0 ] = 4;", BSONObj() ); + // s->invoke( "delete blah['a'][ 2 ];", BSONObj() ); + // out = s->getObject( "blah" ); + // ASSERT_EQUALS( 1.0, out[ "a" ].embeddedObject()[ 0 ].number() ); + // ASSERT_EQUALS( 3.0, out[ "a" ].embeddedObject()[ 2 ].number() ); + + delete s; + } +}; + +class OtherJSTypes { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); + + { + // date BSONObj o; { - BSONObjBuilder b ; - b.append( "a" , (int)5 ); - b.append( "b" , 5.6 ); + BSONObjBuilder b; + b.appendDate("d", 123456789); o = b.obj(); } - ASSERT_EQUALS( NumberInt , o["a"].type() ); - ASSERT_EQUALS( NumberDouble , o["b"].type() ); + s->setObject("x", o); - s->setObject( "z" , o ); - s->invoke( "return z" , 0, 0 ); - BSONObj out = s->getObject( "__returnValue" ); - ASSERT_EQUALS( 5 , out["a"].number() ); - ASSERT_EQUALS( 5.6 , out["b"].number() ); + s->invoke("return x.d.getTime() != 12;", 0, 0); + ASSERT_EQUALS(true, s->getBoolean("__returnValue")); - ASSERT_EQUALS( NumberDouble , out["b"].type() ); - ASSERT_EQUALS( NumberInt , out["a"].type() ); + s->invoke("z = x.d.getTime();", 0, 0); + ASSERT_EQUALS(123456789, s->getNumber("z")); - // -- B -- + s->invoke("z = { z : x.d }", 0, 0); + BSONObj out = s->getObject("z"); + ASSERT(out["z"].type() == Date); + } + { + // regex + BSONObj o; { - BSONObjBuilder b ; - b.append( "a" , (int)5 ); - b.append( "b" , 5.6 ); + BSONObjBuilder b; + b.appendRegex("r", "^a", "i"); o = b.obj(); } + s->setObject("x", o); + + s->invoke("z = x.r.test( 'b' );", 0, 0); + ASSERT_EQUALS(false, s->getBoolean("z")); + + s->invoke("z = x.r.test( 'a' );", 0, 0); + ASSERT_EQUALS(true, s->getBoolean("z")); + + s->invoke("z = x.r.test( 'ba' );", 0, 0); + ASSERT_EQUALS(false, s->getBoolean("z")); + + s->invoke("z = { a : x.r };", 0, 0); + + BSONObj out = s->getObject("z"); + ASSERT_EQUALS((string) "^a", out["a"].regex()); + ASSERT_EQUALS((string) "i", out["a"].regexFlags()); + + // This regex used to cause a segfault because x isn't a valid flag for a js RegExp. + // Now it throws a JS exception. + BSONObj invalidRegex = BSON_ARRAY(BSON("regex" << BSONRegEx("asdf", "x"))); + const char* code = + "function (obj) {" + " var threw = false;" + " try {" + " obj.regex;" // should throw + " } catch(e) {" + " threw = true;" + " }" + " assert(threw);" + "}"; + ASSERT_EQUALS(s->invoke(code, &invalidRegex, NULL), 0); + } - s->setObject( "z" , o , false ); - s->invoke( "return z" , 0, 0 ); - out = s->getObject( "__returnValue" ); - ASSERT_EQUALS( 5 , out["a"].number() ); - ASSERT_EQUALS( 5.6 , out["b"].number() ); + // array + { + BSONObj o = fromjson("{r:[1,2,3]}"); + s->setObject("x", o, false); + BSONObj out = s->getObject("x"); + ASSERT_EQUALS(Array, out.firstElement().type()); - ASSERT_EQUALS( NumberDouble , out["b"].type() ); - ASSERT_EQUALS( NumberInt , out["a"].type() ); + s->setObject("x", o, true); + out = s->getObject("x"); + ASSERT_EQUALS(Array, out.firstElement().type()); + } + // symbol + { + // test mutable object with symbol type + BSONObjBuilder builder; + builder.appendSymbol("sym", "value"); + BSONObj in = builder.done(); + s->setObject("x", in, false); + BSONObj out = s->getObject("x"); + ASSERT_EQUALS(Symbol, out.firstElement().type()); + + // readonly + s->setObject("x", in, true); + out = s->getObject("x"); + ASSERT_EQUALS(Symbol, out.firstElement().type()); + } - // -- C -- + delete s; + } +}; - { - BSONObjBuilder b ; +class SpecialDBTypes { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); - { - BSONObjBuilder c; - c.append( "0" , 5.5 ); - c.append( "1" , 6 ); - b.appendArray( "a" , c.obj() ); - } + BSONObjBuilder b; + b.appendTimestamp("a", 123456789); + b.appendMinKey("b"); + b.appendMaxKey("c"); + b.appendTimestamp("d", 1234000, 9876); - o = b.obj(); - } - ASSERT_EQUALS( NumberDouble , o["a"].embeddedObjectUserCheck()["0"].type() ); - ASSERT_EQUALS( NumberInt , o["a"].embeddedObjectUserCheck()["1"].type() ); + { + BSONObj t = b.done(); + ASSERT_EQUALS(1234000U, t["d"].timestampTime()); + ASSERT_EQUALS(9876U, t["d"].timestampInc()); + } - s->setObject( "z" , o , false ); - out = s->getObject( "z" ); + s->setObject("z", b.obj()); - ASSERT_EQUALS( NumberDouble , out["a"].embeddedObjectUserCheck()["0"].type() ); - ASSERT_EQUALS( NumberInt , out["a"].embeddedObjectUserCheck()["1"].type() ); + ASSERT(s->invoke("y = { a : z.a , b : z.b , c : z.c , d: z.d }", 0, 0) == 0); - s->invokeSafe( "z.z = 5;" , 0, 0 ); - out = s->getObject( "z" ); - ASSERT_EQUALS( 5 , out["z"].number() ); - ASSERT_EQUALS( NumberDouble , out["a"].embeddedObjectUserCheck()["0"].type() ); - // Commenting so that v8 tests will work -// ASSERT_EQUALS( NumberDouble , out["a"].embeddedObjectUserCheck()["1"].type() ); // TODO: this is technically bad, but here to make sure that i understand the behavior + BSONObj out = s->getObject("y"); + ASSERT_EQUALS(Timestamp, out["a"].type()); + ASSERT_EQUALS(MinKey, out["b"].type()); + ASSERT_EQUALS(MaxKey, out["c"].type()); + ASSERT_EQUALS(Timestamp, out["d"].type()); + ASSERT_EQUALS(9876U, out["d"].timestampInc()); + ASSERT_EQUALS(1234000U, out["d"].timestampTime()); + ASSERT_EQUALS(123456789U, out["a"].date()); - // Eliot says I don't have to worry about this case + delete s; + } +}; -// // -- D -- -// -// o = fromjson( "{a:3.0,b:4.5}" ); -// ASSERT_EQUALS( NumberDouble , o["a"].type() ); -// ASSERT_EQUALS( NumberDouble , o["b"].type() ); -// -// s->setObject( "z" , o , false ); -// s->invoke( "return z" , BSONObj() ); -// out = s->getObject( "__returnValue" ); -// ASSERT_EQUALS( 3 , out["a"].number() ); -// ASSERT_EQUALS( 4.5 , out["b"].number() ); -// -// ASSERT_EQUALS( NumberDouble , out["b"].type() ); -// ASSERT_EQUALS( NumberDouble , out["a"].type() ); -// +class TypeConservation { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); - delete s; - } + // -- A -- - }; - - class NumberLong { - public: - void run() { - auto_ptr<Scope> s( globalScriptEngine->newScope() ); + BSONObj o; + { BSONObjBuilder b; - long long val = (long long)( 0xbabadeadbeefbaddULL ); - b.append( "a", val ); - BSONObj in = b.obj(); - s->setObject( "a", in ); - BSONObj out = s->getObject( "a" ); - ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() ); - - ASSERT( s->exec( "b = {b:a.a}", "foo", false, true, false ) ); - out = s->getObject( "b" ); - ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() ); - if( val != out.firstElement().numberLong() ) { - cout << val << endl; - cout << out.firstElement().numberLong() << endl; - cout << out.toString() << endl; - ASSERT_EQUALS( val, out.firstElement().numberLong() ); - } - - ASSERT( s->exec( "c = {c:a.a.toString()}", "foo", false, true, false ) ); - out = s->getObject( "c" ); - stringstream ss; - ss << "NumberLong(\"" << val << "\")"; - ASSERT_EQUALS( ss.str(), out.firstElement().valuestr() ); - - ASSERT( s->exec( "d = {d:a.a.toNumber()}", "foo", false, true, false ) ); - out = s->getObject( "d" ); - ASSERT_EQUALS( NumberDouble, out.firstElement().type() ); - ASSERT_EQUALS( double( val ), out.firstElement().number() ); - - ASSERT( s->exec( "e = {e:a.a.floatApprox}", "foo", false, true, false ) ); - out = s->getObject( "e" ); - ASSERT_EQUALS( NumberDouble, out.firstElement().type() ); - ASSERT_EQUALS( double( val ), out.firstElement().number() ); - - ASSERT( s->exec( "f = {f:a.a.top}", "foo", false, true, false ) ); - out = s->getObject( "f" ); - ASSERT( NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type() ); - - s->setObject( "z", BSON( "z" << (long long)( 4 ) ) ); - ASSERT( s->exec( "y = {y:z.z.top}", "foo", false, true, false ) ); - out = s->getObject( "y" ); - ASSERT_EQUALS( Undefined, out.firstElement().type() ); - - ASSERT( s->exec( "x = {x:z.z.floatApprox}", "foo", false, true, false ) ); - out = s->getObject( "x" ); - ASSERT( NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type() ); - ASSERT_EQUALS( double( 4 ), out.firstElement().number() ); - - ASSERT( s->exec( "w = {w:z.z}", "foo", false, true, false ) ); - out = s->getObject( "w" ); - ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() ); - ASSERT_EQUALS( 4, out.firstElement().numberLong() ); - + b.append("a", (int)5); + b.append("b", 5.6); + o = b.obj(); } - }; + ASSERT_EQUALS(NumberInt, o["a"].type()); + ASSERT_EQUALS(NumberDouble, o["b"].type()); - class NumberLong2 { - public: - void run() { - auto_ptr<Scope> s( globalScriptEngine->newScope() ); - - BSONObj in; - { - BSONObjBuilder b; - b.append( "a" , 5 ); - b.append( "b" , (long long)5 ); - b.append( "c" , (long long)pow( 2.0, 29 ) ); - b.append( "d" , (long long)pow( 2.0, 30 ) ); - b.append( "e" , (long long)pow( 2.0, 31 ) ); - b.append( "f" , (long long)pow( 2.0, 45 ) ); - in = b.obj(); - } - s->setObject( "a" , in ); - - ASSERT( s->exec( "x = tojson( a ); " ,"foo" , false , true , false ) ); - string outString = s->getString( "x" ); + s->setObject("z", o); + s->invoke("return z", 0, 0); + BSONObj out = s->getObject("__returnValue"); + ASSERT_EQUALS(5, out["a"].number()); + ASSERT_EQUALS(5.6, out["b"].number()); - ASSERT( s->exec( (string)"y = " + outString , "foo2" , false , true , false ) ); - BSONObj out = s->getObject( "y" ); - ASSERT_EQUALS( in , out ); - } - }; + ASSERT_EQUALS(NumberDouble, out["b"].type()); + ASSERT_EQUALS(NumberInt, out["a"].type()); - class NumberLongUnderLimit { - public: - void run() { - auto_ptr<Scope> s( globalScriptEngine->newScope() ); + // -- B -- + { BSONObjBuilder b; - // limit is 2^53 - long long val = (long long)( 9007199254740991ULL ); - b.append( "a", val ); - BSONObj in = b.obj(); - s->setObject( "a", in ); - BSONObj out = s->getObject( "a" ); - ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() ); - - ASSERT( s->exec( "b = {b:a.a}", "foo", false, true, false ) ); - out = s->getObject( "b" ); - ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() ); - if( val != out.firstElement().numberLong() ) { - cout << val << endl; - cout << out.firstElement().numberLong() << endl; - cout << out.toString() << endl; - ASSERT_EQUALS( val, out.firstElement().numberLong() ); - } - - ASSERT( s->exec( "c = {c:a.a.toString()}", "foo", false, true, false ) ); - out = s->getObject( "c" ); - stringstream ss; - ss << "NumberLong(\"" << val << "\")"; - ASSERT_EQUALS( ss.str(), out.firstElement().valuestr() ); - - ASSERT( s->exec( "d = {d:a.a.toNumber()}", "foo", false, true, false ) ); - out = s->getObject( "d" ); - ASSERT_EQUALS( NumberDouble, out.firstElement().type() ); - ASSERT_EQUALS( double( val ), out.firstElement().number() ); - - ASSERT( s->exec( "e = {e:a.a.floatApprox}", "foo", false, true, false ) ); - out = s->getObject( "e" ); - ASSERT_EQUALS( NumberDouble, out.firstElement().type() ); - ASSERT_EQUALS( double( val ), out.firstElement().number() ); - - ASSERT( s->exec( "f = {f:a.a.top}", "foo", false, true, false ) ); - out = s->getObject( "f" ); - ASSERT( Undefined == out.firstElement().type() ); + b.append("a", (int)5); + b.append("b", 5.6); + o = b.obj(); } - }; - class InvalidTimestamp { - public: - void run() { - auto_ptr<Scope> s( globalScriptEngine->newScope() ); + s->setObject("z", o, false); + s->invoke("return z", 0, 0); + out = s->getObject("__returnValue"); + ASSERT_EQUALS(5, out["a"].number()); + ASSERT_EQUALS(5.6, out["b"].number()); - // Timestamp 't' component cannot exceed max for int32_t. - // Use appendTimestamp(field, Date) to bypass OpTime construction. - BSONObj in; - { - BSONObjBuilder b; - b.appendTimestamp( "a", std::numeric_limits<unsigned long long>::max() ); - in = b.obj(); - } - s->setObject( "a" , in ); + ASSERT_EQUALS(NumberDouble, out["b"].type()); + ASSERT_EQUALS(NumberInt, out["a"].type()); - ASSERT_FALSE( s->exec( "x = tojson( a ); " ,"foo" , false , true , false ) ); - } - }; - class WeirdObjects { - public: + // -- C -- - BSONObj build( int depth ) { + { BSONObjBuilder b; - b.append( "0" , depth ); - if ( depth > 0 ) - b.appendArray( "1" , build( depth - 1 ) ); - return b.obj(); - } - void run() { - Scope * s = globalScriptEngine->newScope(); - - for ( int i=5; i<100 ; i += 10 ) { - s->setObject( "a" , build(i) , false ); - s->invokeSafe( "tojson( a )" , 0, 0 ); - - s->setObject( "a" , build(5) , true ); - s->invokeSafe( "tojson( a )" , 0, 0 ); + { + BSONObjBuilder c; + c.append("0", 5.5); + c.append("1", 6); + b.appendArray("a", c.obj()); } - delete s; + o = b.obj(); } - }; - - /** - * Test exec() timeout value terminates execution (SERVER-8053) - */ - class ExecTimeout { - public: - void run() { - scoped_ptr<Scope> scope(globalScriptEngine->newScope()); - // assert timeout occurred - ASSERT(!scope->exec("var a = 1; while (true) { ; }", - "ExecTimeout", false, true, false, 1)); + ASSERT_EQUALS(NumberDouble, o["a"].embeddedObjectUserCheck()["0"].type()); + ASSERT_EQUALS(NumberInt, o["a"].embeddedObjectUserCheck()["1"].type()); + + s->setObject("z", o, false); + out = s->getObject("z"); + + ASSERT_EQUALS(NumberDouble, out["a"].embeddedObjectUserCheck()["0"].type()); + ASSERT_EQUALS(NumberInt, out["a"].embeddedObjectUserCheck()["1"].type()); + + s->invokeSafe("z.z = 5;", 0, 0); + out = s->getObject("z"); + ASSERT_EQUALS(5, out["z"].number()); + ASSERT_EQUALS(NumberDouble, out["a"].embeddedObjectUserCheck()["0"].type()); + // Commenting so that v8 tests will work + // ASSERT_EQUALS( NumberDouble , out["a"].embeddedObjectUserCheck()["1"].type() ); // TODO: this is technically bad, but here to make sure that i understand the behavior + + + // Eliot says I don't have to worry about this case + + // // -- D -- + // + // o = fromjson( "{a:3.0,b:4.5}" ); + // ASSERT_EQUALS( NumberDouble , o["a"].type() ); + // ASSERT_EQUALS( NumberDouble , o["b"].type() ); + // + // s->setObject( "z" , o , false ); + // s->invoke( "return z" , BSONObj() ); + // out = s->getObject( "__returnValue" ); + // ASSERT_EQUALS( 3 , out["a"].number() ); + // ASSERT_EQUALS( 4.5 , out["b"].number() ); + // + // ASSERT_EQUALS( NumberDouble , out["b"].type() ); + // ASSERT_EQUALS( NumberDouble , out["a"].type() ); + // + + delete s; + } +}; + +class NumberLong { +public: + void run() { + auto_ptr<Scope> s(globalScriptEngine->newScope()); + BSONObjBuilder b; + long long val = (long long)(0xbabadeadbeefbaddULL); + b.append("a", val); + BSONObj in = b.obj(); + s->setObject("a", in); + BSONObj out = s->getObject("a"); + ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); + + ASSERT(s->exec("b = {b:a.a}", "foo", false, true, false)); + out = s->getObject("b"); + ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); + if (val != out.firstElement().numberLong()) { + cout << val << endl; + cout << out.firstElement().numberLong() << endl; + cout << out.toString() << endl; + ASSERT_EQUALS(val, out.firstElement().numberLong()); } - }; - /** - * Test exec() timeout value terminates execution (SERVER-8053) - */ - class ExecNoTimeout { - public: - void run() { - scoped_ptr<Scope> scope(globalScriptEngine->newScope()); - - // assert no timeout occurred - ASSERT(scope->exec("var a = function() { return 1; }", - "ExecNoTimeout", false, true, false, 5 * 60 * 1000)); - } - }; - - /** - * Test invoke() timeout value terminates execution (SERVER-8053) - */ - class InvokeTimeout { - public: - void run() { - scoped_ptr<Scope> scope(globalScriptEngine->newScope()); - - // scope timeout after 500ms - bool caught = false; - try { - scope->invokeSafe("function() { " - " while (true) { } " - "} ", - 0, 0, 1); - } catch (const DBException&) { - caught = true; - } - ASSERT(caught); + ASSERT(s->exec("c = {c:a.a.toString()}", "foo", false, true, false)); + out = s->getObject("c"); + stringstream ss; + ss << "NumberLong(\"" << val << "\")"; + ASSERT_EQUALS(ss.str(), out.firstElement().valuestr()); + + ASSERT(s->exec("d = {d:a.a.toNumber()}", "foo", false, true, false)); + out = s->getObject("d"); + ASSERT_EQUALS(NumberDouble, out.firstElement().type()); + ASSERT_EQUALS(double(val), out.firstElement().number()); + + ASSERT(s->exec("e = {e:a.a.floatApprox}", "foo", false, true, false)); + out = s->getObject("e"); + ASSERT_EQUALS(NumberDouble, out.firstElement().type()); + ASSERT_EQUALS(double(val), out.firstElement().number()); + + ASSERT(s->exec("f = {f:a.a.top}", "foo", false, true, false)); + out = s->getObject("f"); + ASSERT(NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type()); + + s->setObject("z", BSON("z" << (long long)(4))); + ASSERT(s->exec("y = {y:z.z.top}", "foo", false, true, false)); + out = s->getObject("y"); + ASSERT_EQUALS(Undefined, out.firstElement().type()); + + ASSERT(s->exec("x = {x:z.z.floatApprox}", "foo", false, true, false)); + out = s->getObject("x"); + ASSERT(NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type()); + ASSERT_EQUALS(double(4), out.firstElement().number()); + + ASSERT(s->exec("w = {w:z.z}", "foo", false, true, false)); + out = s->getObject("w"); + ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); + ASSERT_EQUALS(4, out.firstElement().numberLong()); + } +}; + +class NumberLong2 { +public: + void run() { + auto_ptr<Scope> s(globalScriptEngine->newScope()); + + BSONObj in; + { + BSONObjBuilder b; + b.append("a", 5); + b.append("b", (long long)5); + b.append("c", (long long)pow(2.0, 29)); + b.append("d", (long long)pow(2.0, 30)); + b.append("e", (long long)pow(2.0, 31)); + b.append("f", (long long)pow(2.0, 45)); + in = b.obj(); } - }; - - /** - * Test invoke() timeout value does not terminate execution (SERVER-8053) - */ - class InvokeNoTimeout { - public: - void run() { - scoped_ptr<Scope> scope(globalScriptEngine->newScope()); - - // invoke completes before timeout - scope->invokeSafe("function() { " - " for (var i=0; i<1; i++) { ; } " - "} ", - 0, 0, 5 * 60 * 1000); + s->setObject("a", in); + + ASSERT(s->exec("x = tojson( a ); ", "foo", false, true, false)); + string outString = s->getString("x"); + + ASSERT(s->exec((string) "y = " + outString, "foo2", false, true, false)); + BSONObj out = s->getObject("y"); + ASSERT_EQUALS(in, out); + } +}; + +class NumberLongUnderLimit { +public: + void run() { + auto_ptr<Scope> s(globalScriptEngine->newScope()); + + BSONObjBuilder b; + // limit is 2^53 + long long val = (long long)(9007199254740991ULL); + b.append("a", val); + BSONObj in = b.obj(); + s->setObject("a", in); + BSONObj out = s->getObject("a"); + ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); + + ASSERT(s->exec("b = {b:a.a}", "foo", false, true, false)); + out = s->getObject("b"); + ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); + if (val != out.firstElement().numberLong()) { + cout << val << endl; + cout << out.firstElement().numberLong() << endl; + cout << out.toString() << endl; + ASSERT_EQUALS(val, out.firstElement().numberLong()); } - }; - - class Utf8Check { - public: - Utf8Check() { reset(); } - ~Utf8Check() { reset(); } - void run() { - if( !globalScriptEngine->utf8Ok() ) { - mongo::unittest::log() << "warning: utf8 not supported" << endl; - return; - } - string utf8ObjSpec = "{'_id':'\\u0001\\u007f\\u07ff\\uffff'}"; - BSONObj utf8Obj = fromjson( utf8ObjSpec ); - - OperationContextImpl txn; - DBDirectClient client(&txn); - - client.insert( ns(), utf8Obj ); - client.eval( "unittest", "v = db.jstests.utf8check.findOne(); db.jstests.utf8check.remove( {} ); db.jstests.utf8check.insert( v );" ); - check( utf8Obj, client.findOne( ns(), BSONObj() ) ); + ASSERT(s->exec("c = {c:a.a.toString()}", "foo", false, true, false)); + out = s->getObject("c"); + stringstream ss; + ss << "NumberLong(\"" << val << "\")"; + ASSERT_EQUALS(ss.str(), out.firstElement().valuestr()); + + ASSERT(s->exec("d = {d:a.a.toNumber()}", "foo", false, true, false)); + out = s->getObject("d"); + ASSERT_EQUALS(NumberDouble, out.firstElement().type()); + ASSERT_EQUALS(double(val), out.firstElement().number()); + + ASSERT(s->exec("e = {e:a.a.floatApprox}", "foo", false, true, false)); + out = s->getObject("e"); + ASSERT_EQUALS(NumberDouble, out.firstElement().type()); + ASSERT_EQUALS(double(val), out.firstElement().number()); + + ASSERT(s->exec("f = {f:a.a.top}", "foo", false, true, false)); + out = s->getObject("f"); + ASSERT(Undefined == out.firstElement().type()); + } +}; + +class InvalidTimestamp { +public: + void run() { + auto_ptr<Scope> s(globalScriptEngine->newScope()); + + // Timestamp 't' component cannot exceed max for int32_t. + // Use appendTimestamp(field, Date) to bypass OpTime construction. + BSONObj in; + { + BSONObjBuilder b; + b.appendTimestamp("a", std::numeric_limits<unsigned long long>::max()); + in = b.obj(); } - private: - void check( const BSONObj &one, const BSONObj &two ) { - if ( one.woCompare( two ) != 0 ) { - static string fail = string( "Assertion failure expected " ) + one.toString() + ", got " + two.toString(); - FAIL( fail.c_str() ); - } + s->setObject("a", in); + + ASSERT_FALSE(s->exec("x = tojson( a ); ", "foo", false, true, false)); + } +}; + +class WeirdObjects { +public: + BSONObj build(int depth) { + BSONObjBuilder b; + b.append("0", depth); + if (depth > 0) + b.appendArray("1", build(depth - 1)); + return b.obj(); + } + + void run() { + Scope* s = globalScriptEngine->newScope(); + + for (int i = 5; i < 100; i += 10) { + s->setObject("a", build(i), false); + s->invokeSafe("tojson( a )", 0, 0); + + s->setObject("a", build(5), true); + s->invokeSafe("tojson( a )", 0, 0); } - void reset() { - OperationContextImpl txn; - DBDirectClient client(&txn); + delete s; + } +}; - client.dropCollection( ns() ); - } - - static const char *ns() { return "unittest.jstests.utf8check"; } - }; +/** + * Test exec() timeout value terminates execution (SERVER-8053) + */ +class ExecTimeout { +public: + void run() { + scoped_ptr<Scope> scope(globalScriptEngine->newScope()); - class LongUtf8String { - public: - LongUtf8String() { reset(); } - ~LongUtf8String() { reset(); } - void run() { - if( !globalScriptEngine->utf8Ok() ) - return; + // assert timeout occurred + ASSERT(!scope->exec("var a = 1; while (true) { ; }", "ExecTimeout", false, true, false, 1)); + } +}; - OperationContextImpl txn; - DBDirectClient client(&txn); +/** + * Test exec() timeout value terminates execution (SERVER-8053) + */ +class ExecNoTimeout { +public: + void run() { + scoped_ptr<Scope> scope(globalScriptEngine->newScope()); + + // assert no timeout occurred + ASSERT(scope->exec("var a = function() { return 1; }", + "ExecNoTimeout", + false, + true, + false, + 5 * 60 * 1000)); + } +}; - client.eval( "unittest", "db.jstests.longutf8string.save( {_id:'\\uffff\\uffff\\uffff\\uffff'} )" ); +/** + * Test invoke() timeout value terminates execution (SERVER-8053) + */ +class InvokeTimeout { +public: + void run() { + scoped_ptr<Scope> scope(globalScriptEngine->newScope()); + + // scope timeout after 500ms + bool caught = false; + try { + scope->invokeSafe( + "function() { " + " while (true) { } " + "} ", + 0, + 0, + 1); + } catch (const DBException&) { + caught = true; } - private: - void reset() { - OperationContextImpl txn; - DBDirectClient client(&txn); + ASSERT(caught); + } +}; - client.dropCollection( ns() ); +/** + * Test invoke() timeout value does not terminate execution (SERVER-8053) + */ +class InvokeNoTimeout { +public: + void run() { + scoped_ptr<Scope> scope(globalScriptEngine->newScope()); + + // invoke completes before timeout + scope->invokeSafe( + "function() { " + " for (var i=0; i<1; i++) { ; } " + "} ", + 0, + 0, + 5 * 60 * 1000); + } +}; + + +class Utf8Check { +public: + Utf8Check() { + reset(); + } + ~Utf8Check() { + reset(); + } + void run() { + if (!globalScriptEngine->utf8Ok()) { + mongo::unittest::log() << "warning: utf8 not supported" << endl; + return; } - - static const char *ns() { return "unittest.jstests.longutf8string"; } - }; - - class InvalidUTF8Check { - public: - void run() { - if( !globalScriptEngine->utf8Ok() ) - return; - - auto_ptr<Scope> s; - s.reset( globalScriptEngine->newScope() ); - - BSONObj b; - { - char crap[5]; - - crap[0] = (char) 128; - crap[1] = 17; - crap[2] = (char) 128; - crap[3] = 17; - crap[4] = 0; - - BSONObjBuilder bb; - bb.append( "x" , crap ); - b = bb.obj(); - } - - //cout << "ELIOT: " << b.jsonString() << endl; - // its ok if this is handled by js, just can't create a c++ exception - s->invoke( "x=this.x.length;" , 0, &b ); + string utf8ObjSpec = "{'_id':'\\u0001\\u007f\\u07ff\\uffff'}"; + BSONObj utf8Obj = fromjson(utf8ObjSpec); + + OperationContextImpl txn; + DBDirectClient client(&txn); + + client.insert(ns(), utf8Obj); + client.eval("unittest", + "v = db.jstests.utf8check.findOne(); db.jstests.utf8check.remove( {} ); " + "db.jstests.utf8check.insert( v );"); + check(utf8Obj, client.findOne(ns(), BSONObj())); + } + +private: + void check(const BSONObj& one, const BSONObj& two) { + if (one.woCompare(two) != 0) { + static string fail = + string("Assertion failure expected ") + one.toString() + ", got " + two.toString(); + FAIL(fail.c_str()); } - }; - - class CodeTests { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); - - { - BSONObjBuilder b; - b.append( "a" , 1 ); - b.appendCode( "b" , "function(){ out.b = 11; }" ); - b.appendCodeWScope( "c" , "function(){ out.c = 12; }" , BSONObj() ); - b.appendCodeWScope( "d" , "function(){ out.d = 13 + bleh; }" , BSON( "bleh" << 5 ) ); - s->setObject( "foo" , b.obj() ); - } - - s->invokeSafe( "out = {}; out.a = foo.a; foo.b(); foo.c();" , 0, 0 ); - BSONObj out = s->getObject( "out" ); - - ASSERT_EQUALS( 1 , out["a"].number() ); - ASSERT_EQUALS( 11 , out["b"].number() ); - ASSERT_EQUALS( 12 , out["c"].number() ); - - // Guess we don't care about this - //s->invokeSafe( "foo.d() " , BSONObj() ); - //out = s->getObject( "out" ); - //ASSERT_EQUALS( 18 , out["d"].number() ); - - - delete s; + } + + void reset() { + OperationContextImpl txn; + DBDirectClient client(&txn); + + client.dropCollection(ns()); + } + + static const char* ns() { + return "unittest.jstests.utf8check"; + } +}; + +class LongUtf8String { +public: + LongUtf8String() { + reset(); + } + ~LongUtf8String() { + reset(); + } + void run() { + if (!globalScriptEngine->utf8Ok()) + return; + + OperationContextImpl txn; + DBDirectClient client(&txn); + + client.eval("unittest", + "db.jstests.longutf8string.save( {_id:'\\uffff\\uffff\\uffff\\uffff'} )"); + } + +private: + void reset() { + OperationContextImpl txn; + DBDirectClient client(&txn); + + client.dropCollection(ns()); + } + + static const char* ns() { + return "unittest.jstests.longutf8string"; + } +}; + +class InvalidUTF8Check { +public: + void run() { + if (!globalScriptEngine->utf8Ok()) + return; + + auto_ptr<Scope> s; + s.reset(globalScriptEngine->newScope()); + + BSONObj b; + { + char crap[5]; + + crap[0] = (char)128; + crap[1] = 17; + crap[2] = (char)128; + crap[3] = 17; + crap[4] = 0; + + BSONObjBuilder bb; + bb.append("x", crap); + b = bb.obj(); } - }; - - namespace RoundTripTests { - - // Inherit from this class to test round tripping of JSON objects - class TestRoundTrip { - public: - virtual ~TestRoundTrip() {} - void run() { - // Insert in Javascript -> Find using DBDirectClient - - // Drop the collection - OperationContextImpl txn; - DBDirectClient client(&txn); - - client.dropCollection( "unittest.testroundtrip" ); - - // Insert in Javascript - stringstream jsInsert; - jsInsert << "db.testroundtrip.insert(" << jsonIn() << ")"; - ASSERT_TRUE( client.eval( "unittest" , jsInsert.str() ) ); - - // Find using DBDirectClient - BSONObj excludeIdProjection = BSON( "_id" << 0 ); - BSONObj directFind = client.findOne( "unittest.testroundtrip", - "", - &excludeIdProjection); - bsonEquals( bson(), directFind ); - - - // Insert using DBDirectClient -> Find in Javascript - - // Drop the collection - client.dropCollection( "unittest.testroundtrip" ); - - // Insert using DBDirectClient - client.insert( "unittest.testroundtrip" , bson() ); - - // Find in Javascript - stringstream jsFind; - jsFind << "dbref = db.testroundtrip.findOne( { } , { _id : 0 } )\n" - << "assert.eq(dbref, " << jsonOut() << ")"; - ASSERT_TRUE( client.eval( "unittest" , jsFind.str() ) ); - } - protected: - - // Methods that must be defined by child classes - virtual BSONObj bson() const = 0; - virtual string json() const = 0; - - // This can be overriden if a different meaning of equality besides woCompare is needed - virtual void bsonEquals( const BSONObj &expected, const BSONObj &actual ) { - if ( expected.woCompare( actual ) ) { - ::mongo::log() << "want:" << expected.jsonString() - << " size: " << expected.objsize() << endl; - ::mongo::log() << "got :" << actual.jsonString() - << " size: " << actual.objsize() << endl; - ::mongo::log() << expected.hexDump() << endl; - ::mongo::log() << actual.hexDump() << endl; - } - ASSERT( !expected.woCompare( actual ) ); - } - - // This can be overriden if the JSON representation is altered on the round trip - virtual string jsonIn() const { - return json(); - } - virtual string jsonOut() const { - return json(); - } - }; - - class DBRefTest : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - OID o; - memset( &o, 0, 12 ); - BSONObjBuilder subBuilder(b.subobjStart("a")); - subBuilder.append("$ref", "ns"); - subBuilder.append("$id", o); - subBuilder.done(); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; - } - - // A "fetch" function is added to the DBRef object when it is inserted using the - // constructor, so we need to compare the fields individually - virtual void bsonEquals( const BSONObj &expected, const BSONObj &actual ) { - ASSERT_EQUALS( expected["a"].type() , actual["a"].type() ); - ASSERT_EQUALS( expected["a"]["$id"].OID() , actual["a"]["$id"].OID() ); - ASSERT_EQUALS( expected["a"]["$ref"].String() , actual["a"]["$ref"].String() ); - } - }; - - class DBPointerTest : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - OID o; - memset( &o, 0, 12 ); - b.appendDBRef( "a" , "ns" , o ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : DBPointer( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; - } - }; - - class InformalDBRefTest : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - BSONObjBuilder subBuilder(b.subobjStart("a")); - subBuilder.append("$ref", "ns"); - subBuilder.append("$id", "000000000000000000000000"); - subBuilder.done(); - return b.obj(); - } - - // Don't need to return anything because we are overriding both jsonOut and jsonIn - virtual string json() const { return ""; } - - // Need to override these because the JSON doesn't actually round trip. - // An object with "$ref" and "$id" fields is handled specially and different on the way out. - virtual string jsonOut() const { - return "{ \"a\" : DBRef( \"ns\", \"000000000000000000000000\" ) }"; - } - virtual string jsonIn() const { - stringstream ss; - ss << "{ \"a\" : { \"$ref\" : \"ns\" , " << - "\"$id\" : \"000000000000000000000000\" } }"; - return ss.str(); - } - }; - - class InformalDBRefOIDTest : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - OID o; - memset( &o, 0, 12 ); - BSONObjBuilder subBuilder(b.subobjStart("a")); - subBuilder.append("$ref", "ns"); - subBuilder.append("$id", o); - subBuilder.done(); - return b.obj(); - } - - // Don't need to return anything because we are overriding both jsonOut and jsonIn - virtual string json() const { return ""; } - - // Need to override these because the JSON doesn't actually round trip. - // An object with "$ref" and "$id" fields is handled specially and different on the way out. - virtual string jsonOut() const { - return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; - } - virtual string jsonIn() const { - stringstream ss; - ss << "{ \"a\" : { \"$ref\" : \"ns\" , " << - "\"$id\" : ObjectId( \"000000000000000000000000\" ) } }"; - return ss.str(); - } - }; - - class InformalDBRefExtraFieldTest : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - OID o; - memset( &o, 0, 12 ); - BSONObjBuilder subBuilder(b.subobjStart("a")); - subBuilder.append("$ref", "ns"); - subBuilder.append("$id", o); - subBuilder.append("otherfield", "value"); - subBuilder.done(); - return b.obj(); - } - - // Don't need to return anything because we are overriding both jsonOut and jsonIn - virtual string json() const { return ""; } - - // Need to override these because the JSON doesn't actually round trip. - // An object with "$ref" and "$id" fields is handled specially and different on the way out. - virtual string jsonOut() const { - return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; - } - virtual string jsonIn() const { - stringstream ss; - ss << "{ \"a\" : { \"$ref\" : \"ns\" , " << - "\"$id\" : ObjectId( \"000000000000000000000000\" ) , " << - "\"otherfield\" : \"value\" } }"; - return ss.str(); - } - }; - - class Empty : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - return b.obj(); - } - virtual string json() const { - return "{}"; - } - }; - - class EmptyWithSpace : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - return b.obj(); - } - virtual string json() const { - return "{ }"; - } - }; - - class SingleString : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "a", "b" ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : \"b\" }"; - } - }; - - class EmptyStrings : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "", "" ); - return b.obj(); - } - virtual string json() const { - return "{ \"\" : \"\" }"; - } - }; - - class SingleNumber : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "a", 1 ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : 1 }"; - } - }; - - class RealNumber : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - double d; - ASSERT_OK(parseNumberFromString( "0.7", &d )); - b.append( "a", d ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : 0.7 }"; - } - }; - - class FancyNumber : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - double d; - ASSERT_OK(parseNumberFromString( "-4.4433e-2", &d )); - b.append( "a", d ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : -4.4433e-2 }"; - } - }; - - class TwoElements : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "a", 1 ); - b.append( "b", "foo" ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : 1, \"b\" : \"foo\" }"; - } - }; - - class Subobject : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "a", 1 ); - BSONObjBuilder c; - c.append( "z", b.done() ); - return c.obj(); - } - virtual string json() const { - return "{ \"z\" : { \"a\" : 1 } }"; - } - }; - - class DeeplyNestedObject : public TestRoundTrip { - virtual string buildJson(int depth) const { - if (depth == 0) { - return "{\"0\":true}"; - } - else { - std::stringstream ss; - ss << "{\"" << depth << "\":" << buildJson(depth - 1) << "}"; - depth--; - return ss.str(); - } - } - virtual BSONObj buildBson(int depth) const { - BSONObjBuilder builder; - if (depth == 0) { - builder.append( "0", true ); - return builder.obj(); - } - else { - std::stringstream ss; - ss << depth; - depth--; - builder.append(ss.str(), buildBson(depth)); - return builder.obj(); - } - } - virtual BSONObj bson() const { - return buildBson(35); - } - virtual string json() const { - return buildJson(35); - } - }; - - class ArrayEmpty : public TestRoundTrip { - virtual BSONObj bson() const { - vector< int > arr; - BSONObjBuilder b; - b.append( "a", arr ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : [] }"; - } - }; - - class Array : public TestRoundTrip { - virtual BSONObj bson() const { - vector< int > arr; - arr.push_back( 1 ); - arr.push_back( 2 ); - arr.push_back( 3 ); - BSONObjBuilder b; - b.append( "a", arr ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : [ 1, 2, 3 ] }"; - } - }; - - class True : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendBool( "a", true ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : true }"; - } - }; - class False : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendBool( "a", false ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : false }"; - } - }; + // cout << "ELIOT: " << b.jsonString() << endl; + // its ok if this is handled by js, just can't create a c++ exception + s->invoke("x=this.x.length;", 0, &b); + } +}; - class Null : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendNull( "a" ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : null }"; - } - }; +class CodeTests { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); - class Undefined : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendUndefined( "a" ); - return b.obj(); - } - - // Don't need to return anything because we are overriding both jsonOut and jsonIn - virtual string json() const { return ""; } - - // undefined values come out as null in the shell. See SERVER-6102. - virtual string jsonIn() const { - return "{ \"a\" : undefined }"; - } - virtual string jsonOut() const { - return "{ \"a\" : null }"; - } - }; - - class EscapedCharacters : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "a", "\" \\ / \b \f \n \r \t \v" ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : \"\\\" \\\\ \\/ \\b \\f \\n \\r \\t \\v\" }"; - } - }; - - class NonEscapedCharacters : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "a", "% { a z $ # ' " ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : \"\\% \\{ \\a \\z \\$ \\# \\' \\ \" }"; - } - }; - - class AllowedControlCharacter : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "a", "\x7f" ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : \"\x7f\" }"; - } - }; - - class NumbersInFieldName : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "b1", "b" ); - return b.obj(); - } - virtual string json() const { - return "{ b1 : \"b\" }"; - } - }; - - class EscapeFieldName : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "\n", "b" ); - return b.obj(); - } - virtual string json() const { - return "{ \"\\n\" : \"b\" }"; - } - }; - - class EscapedUnicodeToUtf8 : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - unsigned char u[ 7 ]; - u[ 0 ] = 0xe0 | 0x0a; - u[ 1 ] = 0x80; - u[ 2 ] = 0x80; - u[ 3 ] = 0xe0 | 0x0a; - u[ 4 ] = 0x80; - u[ 5 ] = 0x80; - u[ 6 ] = 0; - b.append( "a", (char *) u ); - BSONObj built = b.obj(); - ASSERT_EQUALS( string( (char *) u ), built.firstElement().valuestr() ); - return built; - } - virtual string json() const { - return "{ \"a\" : \"\\ua000\\uA000\" }"; - } - }; - - class Utf8AllOnes : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - unsigned char u[ 8 ]; - u[ 0 ] = 0x01; - - u[ 1 ] = 0x7f; - - u[ 2 ] = 0xdf; - u[ 3 ] = 0xbf; - - u[ 4 ] = 0xef; - u[ 5 ] = 0xbf; - u[ 6 ] = 0xbf; - - u[ 7 ] = 0; - - b.append( "a", (char *) u ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : \"\\u0001\\u007f\\u07ff\\uffff\" }"; - } - }; - - class Utf8FirstByteOnes : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - unsigned char u[ 6 ]; - u[ 0 ] = 0xdc; - u[ 1 ] = 0x80; - - u[ 2 ] = 0xef; - u[ 3 ] = 0xbc; - u[ 4 ] = 0x80; - - u[ 5 ] = 0; - - b.append( "a", (char *) u ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : \"\\u0700\\uff00\" }"; - } - }; - - class BinData : public TestRoundTrip { - virtual BSONObj bson() const { - char z[ 3 ]; - z[ 0 ] = 'a'; - z[ 1 ] = 'b'; - z[ 2 ] = 'c'; - BSONObjBuilder b; - b.appendBinData( "a", 3, BinDataGeneral, z ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : BinData( 0 , \"YWJj\" ) }"; - } - }; - - class BinDataPaddedSingle : public TestRoundTrip { - virtual BSONObj bson() const { - char z[ 2 ]; - z[ 0 ] = 'a'; - z[ 1 ] = 'b'; - BSONObjBuilder b; - b.appendBinData( "a", 2, BinDataGeneral, z ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : BinData( 0 , \"YWI=\" ) }"; - } - }; - - class BinDataPaddedDouble : public TestRoundTrip { - virtual BSONObj bson() const { - char z[ 1 ]; - z[ 0 ] = 'a'; - BSONObjBuilder b; - b.appendBinData( "a", 1, BinDataGeneral, z ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : BinData( 0 , \"YQ==\" ) }"; - } - }; - - class BinDataAllChars : public TestRoundTrip { - virtual BSONObj bson() const { - unsigned char z[] = { - 0x00, 0x10, 0x83, 0x10, 0x51, 0x87, 0x20, 0x92, 0x8B, 0x30, - 0xD3, 0x8F, 0x41, 0x14, 0x93, 0x51, 0x55, 0x97, 0x61, 0x96, - 0x9B, 0x71, 0xD7, 0x9F, 0x82, 0x18, 0xA3, 0x92, 0x59, 0xA7, - 0xA2, 0x9A, 0xAB, 0xB2, 0xDB, 0xAF, 0xC3, 0x1C, 0xB3, 0xD3, - 0x5D, 0xB7, 0xE3, 0x9E, 0xBB, 0xF3, 0xDF, 0xBF - }; - BSONObjBuilder b; - b.appendBinData( "a", 48, BinDataGeneral, z ); - return b.obj(); - } - virtual string json() const { - stringstream ss; - ss << "{ \"a\" : BinData( 0 , \"ABCDEFGHIJKLMNOPQRSTUVWXYZ" << - "abcdefghijklmnopqrstuvwxyz0123456789+/\" ) }"; - return ss.str(); - } - }; - - class Date : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendDate( "a", 0 ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : new Date( 0 ) }"; - } - }; - - class DateNonzero : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendDate( "a", 100 ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : new Date( 100 ) }"; - } - }; - - class DateNegative : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendDate( "a", -1 ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : new Date( -1 ) }"; - } - }; - - class Timestamp : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendTimestamp( "a", 20000ULL, 5 ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : Timestamp( 20, 5 ) }"; - } - }; - - class TimestampMax : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendMaxForType( "a", mongo::Timestamp ); - BSONObj o = b.obj(); - return o; - } - virtual string json() const { - OpTime opTime = OpTime::max(); - stringstream ss; - ss << "{ \"a\" : Timestamp( " << opTime.getSecs() << ", " << opTime.getInc() - << " ) }"; - return ss.str(); - } - }; - - class Regex : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendRegex( "a", "b", "" ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : /b/ }"; - } - }; - - class RegexWithQuotes : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.appendRegex( "a", "\"", "" ); - return b.obj(); - } - virtual string json() const { - return "{ \"a\" : /\"/ }"; - } - }; - - class UnquotedFieldName : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "a_b", 1 ); - return b.obj(); - } - virtual string json() const { - return "{ a_b : 1 }"; - } - }; - - class SingleQuotes : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "ab'c\"", "bb\b '\"" ); - return b.obj(); - } - virtual string json() const { - return "{ 'ab\\'c\"' : 'bb\\b \\'\"' }"; - } - }; - - class ObjectId : public TestRoundTrip { - virtual BSONObj bson() const { - OID id; - id.init( "deadbeeff00ddeadbeeff00d" ); - BSONObjBuilder b; - b.appendOID( "foo", &id ); - return b.obj(); - } - virtual string json() const { - return "{ \"foo\": ObjectId( \"deadbeeff00ddeadbeeff00d\" ) }"; - } - }; - - class NumberLong : public TestRoundTrip { - public: - virtual BSONObj bson() const { - return BSON( "long" << 4611686018427387904ll ); // 2**62 - } - virtual string json() const { - return "{ \"long\": NumberLong(4611686018427387904) }"; - } - }; - - class NumberInt : public TestRoundTrip { - public: - virtual BSONObj bson() const { - return BSON( "int" << static_cast<int>(100) ); - } - virtual string json() const { - return "{ \"int\": NumberInt(100) }"; - } - }; - - class Number : public TestRoundTrip { - public: - virtual BSONObj bson() const { - return BSON( "double" << 3.14 ); - } - virtual string json() const { - return "{ \"double\": Number(3.14) }"; - } - }; - - class UUID : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - unsigned char z[] = { - 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, - 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, - 0x00, 0x00, 0x00, 0x00 - }; - b.appendBinData( "a" , 16 , bdtUUID , z ); - return b.obj(); - } + { + BSONObjBuilder b; + b.append("a", 1); + b.appendCode("b", "function(){ out.b = 11; }"); + b.appendCodeWScope("c", "function(){ out.c = 12; }", BSONObj()); + b.appendCodeWScope("d", "function(){ out.d = 13 + bleh; }", BSON("bleh" << 5)); + s->setObject("foo", b.obj()); + } - // Don't need to return anything because we are overriding both jsonOut and jsonIn - virtual string json() const { return ""; } + s->invokeSafe("out = {}; out.a = foo.a; foo.b(); foo.c();", 0, 0); + BSONObj out = s->getObject("out"); - // The UUID constructor corresponds to a special BinData type - virtual string jsonIn() const { - return "{ \"a\" : UUID(\"abcdefabcdefabcdefabcdef00000000\") }"; - } - virtual string jsonOut() const { - return "{ \"a\" : BinData(3,\"q83vq83vq83vq83vAAAAAA==\") }"; - } - }; + ASSERT_EQUALS(1, out["a"].number()); + ASSERT_EQUALS(11, out["b"].number()); + ASSERT_EQUALS(12, out["c"].number()); - class HexData : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - unsigned char z[] = { - 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, - 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, - 0x00, 0x00, 0x00, 0x00 - }; - b.appendBinData( "a" , 16 , BinDataGeneral , z ); - return b.obj(); - } + // Guess we don't care about this + // s->invokeSafe( "foo.d() " , BSONObj() ); + // out = s->getObject( "out" ); + // ASSERT_EQUALS( 18 , out["d"].number() ); - // Don't need to return anything because we are overriding both jsonOut and jsonIn - virtual string json() const { return ""; } - // The HexData constructor creates a BinData type from a hex string - virtual string jsonIn() const { - return "{ \"a\" : HexData(0,\"abcdefabcdefabcdefabcdef00000000\") }"; - } - virtual string jsonOut() const { - return "{ \"a\" : BinData(0,\"q83vq83vq83vq83vAAAAAA==\") }"; - } - }; + delete s; + } +}; - class MD5 : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - unsigned char z[] = { - 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, - 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, - 0x00, 0x00, 0x00, 0x00 - }; - b.appendBinData( "a" , 16 , MD5Type , z ); - return b.obj(); - } +namespace RoundTripTests { - // Don't need to return anything because we are overriding both jsonOut and jsonIn - virtual string json() const { return ""; } +// Inherit from this class to test round tripping of JSON objects +class TestRoundTrip { +public: + virtual ~TestRoundTrip() {} + void run() { + // Insert in Javascript -> Find using DBDirectClient - // The HexData constructor creates a BinData type from a hex string - virtual string jsonIn() const { - return "{ \"a\" : MD5(\"abcdefabcdefabcdefabcdef00000000\") }"; - } - virtual string jsonOut() const { - return "{ \"a\" : BinData(5,\"q83vq83vq83vq83vAAAAAA==\") }"; - } - }; - - class NullString : public TestRoundTrip { - virtual BSONObj bson() const { - BSONObjBuilder b; - b.append( "x" , "a\0b" , 4 ); - return b.obj(); - } - virtual string json() const { - return "{ \"x\" : \"a\\u0000b\" }"; - } - }; + // Drop the collection + OperationContextImpl txn; + DBDirectClient client(&txn); - } // namespace RoundTripTests + client.dropCollection("unittest.testroundtrip"); - class BinDataType { - public: + // Insert in Javascript + stringstream jsInsert; + jsInsert << "db.testroundtrip.insert(" << jsonIn() << ")"; + ASSERT_TRUE(client.eval("unittest", jsInsert.str())); - void pp( const char * s , BSONElement e ) { - int len; - const char * data = e.binData( len ); - cout << s << ":" << e.binDataType() << "\t" << len << endl; - cout << "\t"; - for ( int i=0; i<len; i++ ) - cout << (int)(data[i]) << " "; - cout << endl; - } + // Find using DBDirectClient + BSONObj excludeIdProjection = BSON("_id" << 0); + BSONObj directFind = client.findOne("unittest.testroundtrip", "", &excludeIdProjection); + bsonEquals(bson(), directFind); - void run() { - Scope * s = globalScriptEngine->newScope(); - const char * foo = "asdas\0asdasd"; - const char * base64 = "YXNkYXMAYXNkYXNk"; + // Insert using DBDirectClient -> Find in Javascript - BSONObj in; - { - BSONObjBuilder b; - b.append( "a" , 7 ); - b.appendBinData( "b" , 12 , BinDataGeneral , foo ); - in = b.obj(); - s->setObject( "x" , in ); - } + // Drop the collection + client.dropCollection("unittest.testroundtrip"); - s->invokeSafe( "myb = x.b; print( myb ); printjson( myb );" , 0, 0 ); - s->invokeSafe( "y = { c : myb };" , 0, 0 ); - - BSONObj out = s->getObject( "y" ); - ASSERT_EQUALS( BinData , out["c"].type() ); -// pp( "in " , in["b"] ); -// pp( "out" , out["c"] ); - ASSERT_EQUALS( 0 , in["b"].woCompare( out["c"] , false ) ); - - // check that BinData js class is utilized - s->invokeSafe( "q = x.b.toString();", 0, 0 ); - stringstream expected; - expected << "BinData(" << BinDataGeneral << ",\"" << base64 << "\")"; - ASSERT_EQUALS( expected.str(), s->getString( "q" ) ); - - stringstream scriptBuilder; - scriptBuilder << "z = { c : new BinData( " << BinDataGeneral << ", \"" << base64 << "\" ) };"; - string script = scriptBuilder.str(); - s->invokeSafe( script.c_str(), 0, 0 ); - out = s->getObject( "z" ); -// pp( "out" , out["c"] ); - ASSERT_EQUALS( 0 , in["b"].woCompare( out["c"] , false ) ); - - s->invokeSafe( "a = { f: new BinData( 128, \"\" ) };", 0, 0 ); - out = s->getObject( "a" ); - int len = -1; - out[ "f" ].binData( len ); - ASSERT_EQUALS( 0, len ); - ASSERT_EQUALS( 128, out[ "f" ].binDataType() ); - - delete s; - } - }; + // Insert using DBDirectClient + client.insert("unittest.testroundtrip", bson()); - class VarTests { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); + // Find in Javascript + stringstream jsFind; + jsFind << "dbref = db.testroundtrip.findOne( { } , { _id : 0 } )\n" + << "assert.eq(dbref, " << jsonOut() << ")"; + ASSERT_TRUE(client.eval("unittest", jsFind.str())); + } - ASSERT( s->exec( "a = 5;" , "a" , false , true , false ) ); - ASSERT_EQUALS( 5 , s->getNumber("a" ) ); +protected: + // Methods that must be defined by child classes + virtual BSONObj bson() const = 0; + virtual string json() const = 0; - ASSERT( s->exec( "var b = 6;" , "b" , false , true , false ) ); - ASSERT_EQUALS( 6 , s->getNumber("b" ) ); - delete s; + // This can be overriden if a different meaning of equality besides woCompare is needed + virtual void bsonEquals(const BSONObj& expected, const BSONObj& actual) { + if (expected.woCompare(actual)) { + ::mongo::log() << "want:" << expected.jsonString() << " size: " << expected.objsize() + << endl; + ::mongo::log() << "got :" << actual.jsonString() << " size: " << actual.objsize() + << endl; + ::mongo::log() << expected.hexDump() << endl; + ::mongo::log() << actual.hexDump() << endl; } - }; - - class Speed1 { - public: - void run() { - BSONObj start = BSON( "x" << 5.0 ); - BSONObj empty; - - auto_ptr<Scope> s; - s.reset( globalScriptEngine->newScope() ); - - ScriptingFunction f = s->createFunction( "return this.x + 6;" ); - - Timer t; - double n = 0; - for ( ; n < 10000 ; n++ ) { - s->invoke( f , &empty, &start ); - ASSERT_EQUALS( 11 , s->getNumber( "__returnValue" ) ); - } - //cout << "speed1: " << ( n / t.millis() ) << " ops/ms" << endl; + ASSERT(!expected.woCompare(actual)); + } + + // This can be overriden if the JSON representation is altered on the round trip + virtual string jsonIn() const { + return json(); + } + virtual string jsonOut() const { + return json(); + } +}; + +class DBRefTest : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + OID o; + memset(&o, 0, 12); + BSONObjBuilder subBuilder(b.subobjStart("a")); + subBuilder.append("$ref", "ns"); + subBuilder.append("$id", o); + subBuilder.done(); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; + } + + // A "fetch" function is added to the DBRef object when it is inserted using the + // constructor, so we need to compare the fields individually + virtual void bsonEquals(const BSONObj& expected, const BSONObj& actual) { + ASSERT_EQUALS(expected["a"].type(), actual["a"].type()); + ASSERT_EQUALS(expected["a"]["$id"].OID(), actual["a"]["$id"].OID()); + ASSERT_EQUALS(expected["a"]["$ref"].String(), actual["a"]["$ref"].String()); + } +}; + +class DBPointerTest : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + OID o; + memset(&o, 0, 12); + b.appendDBRef("a", "ns", o); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : DBPointer( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; + } +}; + +class InformalDBRefTest : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + BSONObjBuilder subBuilder(b.subobjStart("a")); + subBuilder.append("$ref", "ns"); + subBuilder.append("$id", "000000000000000000000000"); + subBuilder.done(); + return b.obj(); + } + + // Don't need to return anything because we are overriding both jsonOut and jsonIn + virtual string json() const { + return ""; + } + + // Need to override these because the JSON doesn't actually round trip. + // An object with "$ref" and "$id" fields is handled specially and different on the way out. + virtual string jsonOut() const { + return "{ \"a\" : DBRef( \"ns\", \"000000000000000000000000\" ) }"; + } + virtual string jsonIn() const { + stringstream ss; + ss << "{ \"a\" : { \"$ref\" : \"ns\" , " + << "\"$id\" : \"000000000000000000000000\" } }"; + return ss.str(); + } +}; + +class InformalDBRefOIDTest : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + OID o; + memset(&o, 0, 12); + BSONObjBuilder subBuilder(b.subobjStart("a")); + subBuilder.append("$ref", "ns"); + subBuilder.append("$id", o); + subBuilder.done(); + return b.obj(); + } + + // Don't need to return anything because we are overriding both jsonOut and jsonIn + virtual string json() const { + return ""; + } + + // Need to override these because the JSON doesn't actually round trip. + // An object with "$ref" and "$id" fields is handled specially and different on the way out. + virtual string jsonOut() const { + return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; + } + virtual string jsonIn() const { + stringstream ss; + ss << "{ \"a\" : { \"$ref\" : \"ns\" , " + << "\"$id\" : ObjectId( \"000000000000000000000000\" ) } }"; + return ss.str(); + } +}; + +class InformalDBRefExtraFieldTest : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + OID o; + memset(&o, 0, 12); + BSONObjBuilder subBuilder(b.subobjStart("a")); + subBuilder.append("$ref", "ns"); + subBuilder.append("$id", o); + subBuilder.append("otherfield", "value"); + subBuilder.done(); + return b.obj(); + } + + // Don't need to return anything because we are overriding both jsonOut and jsonIn + virtual string json() const { + return ""; + } + + // Need to override these because the JSON doesn't actually round trip. + // An object with "$ref" and "$id" fields is handled specially and different on the way out. + virtual string jsonOut() const { + return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; + } + virtual string jsonIn() const { + stringstream ss; + ss << "{ \"a\" : { \"$ref\" : \"ns\" , " + << "\"$id\" : ObjectId( \"000000000000000000000000\" ) , " + << "\"otherfield\" : \"value\" } }"; + return ss.str(); + } +}; + +class Empty : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + return b.obj(); + } + virtual string json() const { + return "{}"; + } +}; + +class EmptyWithSpace : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + return b.obj(); + } + virtual string json() const { + return "{ }"; + } +}; + +class SingleString : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("a", "b"); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : \"b\" }"; + } +}; + +class EmptyStrings : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("", ""); + return b.obj(); + } + virtual string json() const { + return "{ \"\" : \"\" }"; + } +}; + +class SingleNumber : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("a", 1); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : 1 }"; + } +}; + +class RealNumber : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + double d; + ASSERT_OK(parseNumberFromString("0.7", &d)); + b.append("a", d); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : 0.7 }"; + } +}; + +class FancyNumber : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + double d; + ASSERT_OK(parseNumberFromString("-4.4433e-2", &d)); + b.append("a", d); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : -4.4433e-2 }"; + } +}; + +class TwoElements : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("a", 1); + b.append("b", "foo"); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : 1, \"b\" : \"foo\" }"; + } +}; + +class Subobject : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("a", 1); + BSONObjBuilder c; + c.append("z", b.done()); + return c.obj(); + } + virtual string json() const { + return "{ \"z\" : { \"a\" : 1 } }"; + } +}; + +class DeeplyNestedObject : public TestRoundTrip { + virtual string buildJson(int depth) const { + if (depth == 0) { + return "{\"0\":true}"; + } else { + std::stringstream ss; + ss << "{\"" << depth << "\":" << buildJson(depth - 1) << "}"; + depth--; + return ss.str(); } - }; - - class ScopeOut { - public: - void run() { - auto_ptr<Scope> s; - s.reset( globalScriptEngine->newScope() ); - - s->invokeSafe( "x = 5;" , 0, 0 ); - { - BSONObjBuilder b; - s->append( b , "z" , "x" ); - ASSERT_EQUALS( BSON( "z" << 5 ) , b.obj() ); - } - - s->invokeSafe( "x = function(){ return 17; }" , 0, 0 ); - BSONObj temp; - { - BSONObjBuilder b; - s->append( b , "z" , "x" ); - temp = b.obj(); - } - - s->invokeSafe( "foo = this.z();" , 0, &temp ); - ASSERT_EQUALS( 17 , s->getNumber( "foo" ) ); + } + virtual BSONObj buildBson(int depth) const { + BSONObjBuilder builder; + if (depth == 0) { + builder.append("0", true); + return builder.obj(); + } else { + std::stringstream ss; + ss << depth; + depth--; + builder.append(ss.str(), buildBson(depth)); + return builder.obj(); } - }; - - class RenameTest { - public: - void run() { - auto_ptr<Scope> s; - s.reset( globalScriptEngine->newScope() ); - - s->setNumber( "x" , 5 ); - ASSERT_EQUALS( 5 , s->getNumber( "x" ) ); - ASSERT_EQUALS( Undefined , s->type( "y" ) ); - - s->rename( "x" , "y" ); - ASSERT_EQUALS( 5 , s->getNumber( "y" ) ); - ASSERT_EQUALS( Undefined , s->type( "x" ) ); - - s->rename( "y" , "x" ); - ASSERT_EQUALS( 5 , s->getNumber( "x" ) ); - ASSERT_EQUALS( Undefined , s->type( "y" ) ); + } + virtual BSONObj bson() const { + return buildBson(35); + } + virtual string json() const { + return buildJson(35); + } +}; + +class ArrayEmpty : public TestRoundTrip { + virtual BSONObj bson() const { + vector<int> arr; + BSONObjBuilder b; + b.append("a", arr); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : [] }"; + } +}; + +class Array : public TestRoundTrip { + virtual BSONObj bson() const { + vector<int> arr; + arr.push_back(1); + arr.push_back(2); + arr.push_back(3); + BSONObjBuilder b; + b.append("a", arr); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : [ 1, 2, 3 ] }"; + } +}; + +class True : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendBool("a", true); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : true }"; + } +}; + +class False : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendBool("a", false); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : false }"; + } +}; + +class Null : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendNull("a"); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : null }"; + } +}; + +class Undefined : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendUndefined("a"); + return b.obj(); + } + + // Don't need to return anything because we are overriding both jsonOut and jsonIn + virtual string json() const { + return ""; + } + + // undefined values come out as null in the shell. See SERVER-6102. + virtual string jsonIn() const { + return "{ \"a\" : undefined }"; + } + virtual string jsonOut() const { + return "{ \"a\" : null }"; + } +}; + +class EscapedCharacters : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("a", "\" \\ / \b \f \n \r \t \v"); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : \"\\\" \\\\ \\/ \\b \\f \\n \\r \\t \\v\" }"; + } +}; + +class NonEscapedCharacters : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("a", "% { a z $ # ' "); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : \"\\% \\{ \\a \\z \\$ \\# \\' \\ \" }"; + } +}; + +class AllowedControlCharacter : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("a", "\x7f"); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : \"\x7f\" }"; + } +}; + +class NumbersInFieldName : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("b1", "b"); + return b.obj(); + } + virtual string json() const { + return "{ b1 : \"b\" }"; + } +}; + +class EscapeFieldName : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("\n", "b"); + return b.obj(); + } + virtual string json() const { + return "{ \"\\n\" : \"b\" }"; + } +}; + +class EscapedUnicodeToUtf8 : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + unsigned char u[7]; + u[0] = 0xe0 | 0x0a; + u[1] = 0x80; + u[2] = 0x80; + u[3] = 0xe0 | 0x0a; + u[4] = 0x80; + u[5] = 0x80; + u[6] = 0; + b.append("a", (char*)u); + BSONObj built = b.obj(); + ASSERT_EQUALS(string((char*)u), built.firstElement().valuestr()); + return built; + } + virtual string json() const { + return "{ \"a\" : \"\\ua000\\uA000\" }"; + } +}; + +class Utf8AllOnes : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + unsigned char u[8]; + u[0] = 0x01; + + u[1] = 0x7f; + + u[2] = 0xdf; + u[3] = 0xbf; + + u[4] = 0xef; + u[5] = 0xbf; + u[6] = 0xbf; + + u[7] = 0; + + b.append("a", (char*)u); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : \"\\u0001\\u007f\\u07ff\\uffff\" }"; + } +}; + +class Utf8FirstByteOnes : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + unsigned char u[6]; + u[0] = 0xdc; + u[1] = 0x80; + + u[2] = 0xef; + u[3] = 0xbc; + u[4] = 0x80; + + u[5] = 0; + + b.append("a", (char*)u); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : \"\\u0700\\uff00\" }"; + } +}; + +class BinData : public TestRoundTrip { + virtual BSONObj bson() const { + char z[3]; + z[0] = 'a'; + z[1] = 'b'; + z[2] = 'c'; + BSONObjBuilder b; + b.appendBinData("a", 3, BinDataGeneral, z); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : BinData( 0 , \"YWJj\" ) }"; + } +}; + +class BinDataPaddedSingle : public TestRoundTrip { + virtual BSONObj bson() const { + char z[2]; + z[0] = 'a'; + z[1] = 'b'; + BSONObjBuilder b; + b.appendBinData("a", 2, BinDataGeneral, z); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : BinData( 0 , \"YWI=\" ) }"; + } +}; + +class BinDataPaddedDouble : public TestRoundTrip { + virtual BSONObj bson() const { + char z[1]; + z[0] = 'a'; + BSONObjBuilder b; + b.appendBinData("a", 1, BinDataGeneral, z); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : BinData( 0 , \"YQ==\" ) }"; + } +}; + +class BinDataAllChars : public TestRoundTrip { + virtual BSONObj bson() const { + unsigned char z[] = {0x00, 0x10, 0x83, 0x10, 0x51, 0x87, 0x20, 0x92, 0x8B, 0x30, + 0xD3, 0x8F, 0x41, 0x14, 0x93, 0x51, 0x55, 0x97, 0x61, 0x96, + 0x9B, 0x71, 0xD7, 0x9F, 0x82, 0x18, 0xA3, 0x92, 0x59, 0xA7, + 0xA2, 0x9A, 0xAB, 0xB2, 0xDB, 0xAF, 0xC3, 0x1C, 0xB3, 0xD3, + 0x5D, 0xB7, 0xE3, 0x9E, 0xBB, 0xF3, 0xDF, 0xBF}; + BSONObjBuilder b; + b.appendBinData("a", 48, BinDataGeneral, z); + return b.obj(); + } + virtual string json() const { + stringstream ss; + ss << "{ \"a\" : BinData( 0 , \"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + << "abcdefghijklmnopqrstuvwxyz0123456789+/\" ) }"; + return ss.str(); + } +}; + +class Date : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendDate("a", 0); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : new Date( 0 ) }"; + } +}; + +class DateNonzero : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendDate("a", 100); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : new Date( 100 ) }"; + } +}; + +class DateNegative : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendDate("a", -1); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : new Date( -1 ) }"; + } +}; + +class Timestamp : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendTimestamp("a", 20000ULL, 5); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : Timestamp( 20, 5 ) }"; + } +}; + +class TimestampMax : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendMaxForType("a", mongo::Timestamp); + BSONObj o = b.obj(); + return o; + } + virtual string json() const { + OpTime opTime = OpTime::max(); + stringstream ss; + ss << "{ \"a\" : Timestamp( " << opTime.getSecs() << ", " << opTime.getInc() << " ) }"; + return ss.str(); + } +}; + +class Regex : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendRegex("a", "b", ""); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : /b/ }"; + } +}; + +class RegexWithQuotes : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.appendRegex("a", "\"", ""); + return b.obj(); + } + virtual string json() const { + return "{ \"a\" : /\"/ }"; + } +}; + +class UnquotedFieldName : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("a_b", 1); + return b.obj(); + } + virtual string json() const { + return "{ a_b : 1 }"; + } +}; + +class SingleQuotes : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("ab'c\"", "bb\b '\""); + return b.obj(); + } + virtual string json() const { + return "{ 'ab\\'c\"' : 'bb\\b \\'\"' }"; + } +}; + +class ObjectId : public TestRoundTrip { + virtual BSONObj bson() const { + OID id; + id.init("deadbeeff00ddeadbeeff00d"); + BSONObjBuilder b; + b.appendOID("foo", &id); + return b.obj(); + } + virtual string json() const { + return "{ \"foo\": ObjectId( \"deadbeeff00ddeadbeeff00d\" ) }"; + } +}; + +class NumberLong : public TestRoundTrip { +public: + virtual BSONObj bson() const { + return BSON("long" << 4611686018427387904ll); // 2**62 + } + virtual string json() const { + return "{ \"long\": NumberLong(4611686018427387904) }"; + } +}; + +class NumberInt : public TestRoundTrip { +public: + virtual BSONObj bson() const { + return BSON("int" << static_cast<int>(100)); + } + virtual string json() const { + return "{ \"int\": NumberInt(100) }"; + } +}; + +class Number : public TestRoundTrip { +public: + virtual BSONObj bson() const { + return BSON("double" << 3.14); + } + virtual string json() const { + return "{ \"double\": Number(3.14) }"; + } +}; + +class UUID : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + unsigned char z[] = {0xAB, + 0xCD, + 0xEF, + 0xAB, + 0xCD, + 0xEF, + 0xAB, + 0xCD, + 0xEF, + 0xAB, + 0xCD, + 0xEF, + 0x00, + 0x00, + 0x00, + 0x00}; + b.appendBinData("a", 16, bdtUUID, z); + return b.obj(); + } + + // Don't need to return anything because we are overriding both jsonOut and jsonIn + virtual string json() const { + return ""; + } + + // The UUID constructor corresponds to a special BinData type + virtual string jsonIn() const { + return "{ \"a\" : UUID(\"abcdefabcdefabcdefabcdef00000000\") }"; + } + virtual string jsonOut() const { + return "{ \"a\" : BinData(3,\"q83vq83vq83vq83vAAAAAA==\") }"; + } +}; + +class HexData : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + unsigned char z[] = {0xAB, + 0xCD, + 0xEF, + 0xAB, + 0xCD, + 0xEF, + 0xAB, + 0xCD, + 0xEF, + 0xAB, + 0xCD, + 0xEF, + 0x00, + 0x00, + 0x00, + 0x00}; + b.appendBinData("a", 16, BinDataGeneral, z); + return b.obj(); + } + + // Don't need to return anything because we are overriding both jsonOut and jsonIn + virtual string json() const { + return ""; + } + + // The HexData constructor creates a BinData type from a hex string + virtual string jsonIn() const { + return "{ \"a\" : HexData(0,\"abcdefabcdefabcdefabcdef00000000\") }"; + } + virtual string jsonOut() const { + return "{ \"a\" : BinData(0,\"q83vq83vq83vq83vAAAAAA==\") }"; + } +}; + +class MD5 : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + unsigned char z[] = {0xAB, + 0xCD, + 0xEF, + 0xAB, + 0xCD, + 0xEF, + 0xAB, + 0xCD, + 0xEF, + 0xAB, + 0xCD, + 0xEF, + 0x00, + 0x00, + 0x00, + 0x00}; + b.appendBinData("a", 16, MD5Type, z); + return b.obj(); + } + + // Don't need to return anything because we are overriding both jsonOut and jsonIn + virtual string json() const { + return ""; + } + + // The HexData constructor creates a BinData type from a hex string + virtual string jsonIn() const { + return "{ \"a\" : MD5(\"abcdefabcdefabcdefabcdef00000000\") }"; + } + virtual string jsonOut() const { + return "{ \"a\" : BinData(5,\"q83vq83vq83vq83vAAAAAA==\") }"; + } +}; + +class NullString : public TestRoundTrip { + virtual BSONObj bson() const { + BSONObjBuilder b; + b.append("x", "a\0b", 4); + return b.obj(); + } + virtual string json() const { + return "{ \"x\" : \"a\\u0000b\" }"; + } +}; + +} // namespace RoundTripTests + +class BinDataType { +public: + void pp(const char* s, BSONElement e) { + int len; + const char* data = e.binData(len); + cout << s << ":" << e.binDataType() << "\t" << len << endl; + cout << "\t"; + for (int i = 0; i < len; i++) + cout << (int)(data[i]) << " "; + cout << endl; + } + + void run() { + Scope* s = globalScriptEngine->newScope(); + + const char* foo = "asdas\0asdasd"; + const char* base64 = "YXNkYXMAYXNkYXNk"; + + BSONObj in; + { + BSONObjBuilder b; + b.append("a", 7); + b.appendBinData("b", 12, BinDataGeneral, foo); + in = b.obj(); + s->setObject("x", in); } - }; - - class InvalidStoredJS { - public: - void run() { - BSONObjBuilder query; - query.append( "_id" , "invalidstoredjs1" ); - - BSONObjBuilder update; - update.append( "_id" , "invalidstoredjs1" ); - update.appendCode( "value" , "function () { db.test.find().forEach(function(obj) { continue; }); }" ); - - OperationContextImpl txn; - DBDirectClient client(&txn); - client.update( "test.system.js" , query.obj() , update.obj() , true /* upsert */ ); - - scoped_ptr<Scope> s( globalScriptEngine->newScope() ); - client.eval( "test" , "invalidstoredjs1()" ); - - BSONObj info; - BSONElement ret; - ASSERT( client.eval( "test" , "return 5 + 12" , info , ret ) ); - ASSERT_EQUALS( 17 , ret.number() ); + s->invokeSafe("myb = x.b; print( myb ); printjson( myb );", 0, 0); + s->invokeSafe("y = { c : myb };", 0, 0); + + BSONObj out = s->getObject("y"); + ASSERT_EQUALS(BinData, out["c"].type()); + // pp( "in " , in["b"] ); + // pp( "out" , out["c"] ); + ASSERT_EQUALS(0, in["b"].woCompare(out["c"], false)); + + // check that BinData js class is utilized + s->invokeSafe("q = x.b.toString();", 0, 0); + stringstream expected; + expected << "BinData(" << BinDataGeneral << ",\"" << base64 << "\")"; + ASSERT_EQUALS(expected.str(), s->getString("q")); + + stringstream scriptBuilder; + scriptBuilder << "z = { c : new BinData( " << BinDataGeneral << ", \"" << base64 + << "\" ) };"; + string script = scriptBuilder.str(); + s->invokeSafe(script.c_str(), 0, 0); + out = s->getObject("z"); + // pp( "out" , out["c"] ); + ASSERT_EQUALS(0, in["b"].woCompare(out["c"], false)); + + s->invokeSafe("a = { f: new BinData( 128, \"\" ) };", 0, 0); + out = s->getObject("a"); + int len = -1; + out["f"].binData(len); + ASSERT_EQUALS(0, len); + ASSERT_EQUALS(128, out["f"].binDataType()); + + delete s; + } +}; + +class VarTests { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); + + ASSERT(s->exec("a = 5;", "a", false, true, false)); + ASSERT_EQUALS(5, s->getNumber("a")); + + ASSERT(s->exec("var b = 6;", "b", false, true, false)); + ASSERT_EQUALS(6, s->getNumber("b")); + delete s; + } +}; + +class Speed1 { +public: + void run() { + BSONObj start = BSON("x" << 5.0); + BSONObj empty; + + auto_ptr<Scope> s; + s.reset(globalScriptEngine->newScope()); + + ScriptingFunction f = s->createFunction("return this.x + 6;"); + + Timer t; + double n = 0; + for (; n < 10000; n++) { + s->invoke(f, &empty, &start); + ASSERT_EQUALS(11, s->getNumber("__returnValue")); } - }; - class NoReturnSpecified { - public: - void run() { - Scope * s = globalScriptEngine->newScope(); - - s->invoke("x=5;" , 0, 0 ); - ASSERT_EQUALS(5, s->getNumber("__returnValue")); - - s->invoke("x='test'", 0, 0); - ASSERT_EQUALS("test", s->getString("__returnValue")); - - s->invoke("x='return'", 0, 0); - ASSERT_EQUALS("return", s->getString("__returnValue")); - - s->invoke("return 'return'", 0, 0); - ASSERT_EQUALS("return", s->getString("__returnValue")); - - s->invoke("x = ' return '", 0, 0); - ASSERT_EQUALS(" return ", s->getString("__returnValue")); - - s->invoke("x = \" return \"", 0, 0); - ASSERT_EQUALS(" return ", s->getString("__returnValue")); - - s->invoke("x = \"' return '\"", 0, 0); - ASSERT_EQUALS("' return '", s->getString("__returnValue")); - - s->invoke("x = '\" return \"'", 0, 0); - ASSERT_EQUALS("\" return \"", s->getString("__returnValue")); - - s->invoke(";return 5", 0, 0); - ASSERT_EQUALS(5, s->getNumber("__returnValue")); - - s->invoke("String('return')", 0, 0); - ASSERT_EQUALS("return", s->getString("__returnValue")); - - s->invoke("String(' return ')", 0, 0); - ASSERT_EQUALS(" return ", s->getString("__returnValue")); - - // This should fail so we set the expected __returnValue to undefined - s->invoke(";x = 5", 0, 0); - ASSERT_EQUALS("undefined", s->getString("__returnValue")); - - s->invoke("String(\"'return\")", 0, 0); - ASSERT_EQUALS("'return", s->getString("__returnValue")); - - s->invoke("String('\"return')", 0, 0); - ASSERT_EQUALS("\"return", s->getString("__returnValue")); - - // A fail case - s->invoke("return$ = 0", 0, 0); - // Checks to confirm that the result is NaN - ASSERT(s->getNumber("__returnValue") != s->getNumber("__returnValue")); - } - }; - - class All : public Suite { - public: - All() : Suite( "js" ) { - // Initialize the Javascript interpreter - ScriptEngine::setup(); + // cout << "speed1: " << ( n / t.millis() ) << " ops/ms" << endl; + } +}; + +class ScopeOut { +public: + void run() { + auto_ptr<Scope> s; + s.reset(globalScriptEngine->newScope()); + + s->invokeSafe("x = 5;", 0, 0); + { + BSONObjBuilder b; + s->append(b, "z", "x"); + ASSERT_EQUALS(BSON("z" << 5), b.obj()); } - void setupTests() { - add< BuiltinTests >(); - add< BasicScope >(); - add< ResetScope >(); - add< FalseTests >(); - add< SimpleFunctions >(); - add< ExecLogError >(); - add< InvokeLogError >(); - add< ExecTimeout >(); - add< ExecNoTimeout >(); - add< InvokeTimeout >(); - add< InvokeNoTimeout >(); - - add< ObjectMapping >(); - add< ObjectDecoding >(); - add< JSOIDTests >(); - add< SetImplicit >(); - add< ObjectModReadonlyTests >(); - add< OtherJSTypes >(); - add< SpecialDBTypes >(); - add< TypeConservation >(); - add< NumberLong >(); - add< NumberLong2 >(); - add< InvalidTimestamp >(); - add< RenameTest >(); - - add< WeirdObjects >(); - add< CodeTests >(); - add< BinDataType >(); - - add< VarTests >(); - - add< Speed1 >(); - - add< InvalidUTF8Check >(); - add< Utf8Check >(); - add< LongUtf8String >(); - - add< ScopeOut >(); - add< InvalidStoredJS >(); - - add< NoReturnSpecified >(); - - add< RoundTripTests::DBRefTest >(); - add< RoundTripTests::DBPointerTest >(); - add< RoundTripTests::InformalDBRefTest >(); - add< RoundTripTests::InformalDBRefOIDTest >(); - add< RoundTripTests::InformalDBRefExtraFieldTest >(); - add< RoundTripTests::Empty >(); - add< RoundTripTests::EmptyWithSpace >(); - add< RoundTripTests::SingleString >(); - add< RoundTripTests::EmptyStrings >(); - add< RoundTripTests::SingleNumber >(); - add< RoundTripTests::RealNumber >(); - add< RoundTripTests::FancyNumber >(); - add< RoundTripTests::TwoElements >(); - add< RoundTripTests::Subobject >(); - add< RoundTripTests::DeeplyNestedObject >(); - add< RoundTripTests::ArrayEmpty >(); - add< RoundTripTests::Array >(); - add< RoundTripTests::True >(); - add< RoundTripTests::False >(); - add< RoundTripTests::Null >(); - add< RoundTripTests::Undefined >(); - add< RoundTripTests::EscapedCharacters >(); - add< RoundTripTests::NonEscapedCharacters >(); - add< RoundTripTests::AllowedControlCharacter >(); - add< RoundTripTests::NumbersInFieldName >(); - add< RoundTripTests::EscapeFieldName >(); - add< RoundTripTests::EscapedUnicodeToUtf8 >(); - add< RoundTripTests::Utf8AllOnes >(); - add< RoundTripTests::Utf8FirstByteOnes >(); - add< RoundTripTests::BinData >(); - add< RoundTripTests::BinDataPaddedSingle >(); - add< RoundTripTests::BinDataPaddedDouble >(); - add< RoundTripTests::BinDataAllChars >(); - add< RoundTripTests::Date >(); - add< RoundTripTests::DateNonzero >(); - add< RoundTripTests::DateNegative >(); - add< RoundTripTests::Timestamp >(); - add< RoundTripTests::TimestampMax >(); - add< RoundTripTests::Regex >(); - add< RoundTripTests::RegexWithQuotes >(); - add< RoundTripTests::UnquotedFieldName >(); - add< RoundTripTests::SingleQuotes >(); - add< RoundTripTests::ObjectId >(); - add< RoundTripTests::NumberLong >(); - add< RoundTripTests::NumberInt >(); - add< RoundTripTests::Number >(); - add< RoundTripTests::UUID >(); - add< RoundTripTests::HexData >(); - add< RoundTripTests::MD5 >(); - add< RoundTripTests::NullString >(); + s->invokeSafe("x = function(){ return 17; }", 0, 0); + BSONObj temp; + { + BSONObjBuilder b; + s->append(b, "z", "x"); + temp = b.obj(); } - }; - - SuiteInstance<All> myall; -} // namespace JavaJSTests + s->invokeSafe("foo = this.z();", 0, &temp); + ASSERT_EQUALS(17, s->getNumber("foo")); + } +}; +class RenameTest { +public: + void run() { + auto_ptr<Scope> s; + s.reset(globalScriptEngine->newScope()); + + s->setNumber("x", 5); + ASSERT_EQUALS(5, s->getNumber("x")); + ASSERT_EQUALS(Undefined, s->type("y")); + + s->rename("x", "y"); + ASSERT_EQUALS(5, s->getNumber("y")); + ASSERT_EQUALS(Undefined, s->type("x")); + + s->rename("y", "x"); + ASSERT_EQUALS(5, s->getNumber("x")); + ASSERT_EQUALS(Undefined, s->type("y")); + } +}; + + +class InvalidStoredJS { +public: + void run() { + BSONObjBuilder query; + query.append("_id", "invalidstoredjs1"); + + BSONObjBuilder update; + update.append("_id", "invalidstoredjs1"); + update.appendCode("value", + "function () { db.test.find().forEach(function(obj) { continue; }); }"); + + OperationContextImpl txn; + DBDirectClient client(&txn); + client.update("test.system.js", query.obj(), update.obj(), true /* upsert */); + + scoped_ptr<Scope> s(globalScriptEngine->newScope()); + client.eval("test", "invalidstoredjs1()"); + + BSONObj info; + BSONElement ret; + ASSERT(client.eval("test", "return 5 + 12", info, ret)); + ASSERT_EQUALS(17, ret.number()); + } +}; +class NoReturnSpecified { +public: + void run() { + Scope* s = globalScriptEngine->newScope(); + + s->invoke("x=5;", 0, 0); + ASSERT_EQUALS(5, s->getNumber("__returnValue")); + + s->invoke("x='test'", 0, 0); + ASSERT_EQUALS("test", s->getString("__returnValue")); + + s->invoke("x='return'", 0, 0); + ASSERT_EQUALS("return", s->getString("__returnValue")); + + s->invoke("return 'return'", 0, 0); + ASSERT_EQUALS("return", s->getString("__returnValue")); + + s->invoke("x = ' return '", 0, 0); + ASSERT_EQUALS(" return ", s->getString("__returnValue")); + + s->invoke("x = \" return \"", 0, 0); + ASSERT_EQUALS(" return ", s->getString("__returnValue")); + + s->invoke("x = \"' return '\"", 0, 0); + ASSERT_EQUALS("' return '", s->getString("__returnValue")); + + s->invoke("x = '\" return \"'", 0, 0); + ASSERT_EQUALS("\" return \"", s->getString("__returnValue")); + + s->invoke(";return 5", 0, 0); + ASSERT_EQUALS(5, s->getNumber("__returnValue")); + + s->invoke("String('return')", 0, 0); + ASSERT_EQUALS("return", s->getString("__returnValue")); + + s->invoke("String(' return ')", 0, 0); + ASSERT_EQUALS(" return ", s->getString("__returnValue")); + + // This should fail so we set the expected __returnValue to undefined + s->invoke(";x = 5", 0, 0); + ASSERT_EQUALS("undefined", s->getString("__returnValue")); + + s->invoke("String(\"'return\")", 0, 0); + ASSERT_EQUALS("'return", s->getString("__returnValue")); + + s->invoke("String('\"return')", 0, 0); + ASSERT_EQUALS("\"return", s->getString("__returnValue")); + + // A fail case + s->invoke("return$ = 0", 0, 0); + // Checks to confirm that the result is NaN + ASSERT(s->getNumber("__returnValue") != s->getNumber("__returnValue")); + } +}; + +class All : public Suite { +public: + All() : Suite("js") { + // Initialize the Javascript interpreter + ScriptEngine::setup(); + } + + void setupTests() { + add<BuiltinTests>(); + add<BasicScope>(); + add<ResetScope>(); + add<FalseTests>(); + add<SimpleFunctions>(); + add<ExecLogError>(); + add<InvokeLogError>(); + add<ExecTimeout>(); + add<ExecNoTimeout>(); + add<InvokeTimeout>(); + add<InvokeNoTimeout>(); + + add<ObjectMapping>(); + add<ObjectDecoding>(); + add<JSOIDTests>(); + add<SetImplicit>(); + add<ObjectModReadonlyTests>(); + add<OtherJSTypes>(); + add<SpecialDBTypes>(); + add<TypeConservation>(); + add<NumberLong>(); + add<NumberLong2>(); + add<InvalidTimestamp>(); + add<RenameTest>(); + + add<WeirdObjects>(); + add<CodeTests>(); + add<BinDataType>(); + + add<VarTests>(); + + add<Speed1>(); + + add<InvalidUTF8Check>(); + add<Utf8Check>(); + add<LongUtf8String>(); + + add<ScopeOut>(); + add<InvalidStoredJS>(); + + add<NoReturnSpecified>(); + + add<RoundTripTests::DBRefTest>(); + add<RoundTripTests::DBPointerTest>(); + add<RoundTripTests::InformalDBRefTest>(); + add<RoundTripTests::InformalDBRefOIDTest>(); + add<RoundTripTests::InformalDBRefExtraFieldTest>(); + add<RoundTripTests::Empty>(); + add<RoundTripTests::EmptyWithSpace>(); + add<RoundTripTests::SingleString>(); + add<RoundTripTests::EmptyStrings>(); + add<RoundTripTests::SingleNumber>(); + add<RoundTripTests::RealNumber>(); + add<RoundTripTests::FancyNumber>(); + add<RoundTripTests::TwoElements>(); + add<RoundTripTests::Subobject>(); + add<RoundTripTests::DeeplyNestedObject>(); + add<RoundTripTests::ArrayEmpty>(); + add<RoundTripTests::Array>(); + add<RoundTripTests::True>(); + add<RoundTripTests::False>(); + add<RoundTripTests::Null>(); + add<RoundTripTests::Undefined>(); + add<RoundTripTests::EscapedCharacters>(); + add<RoundTripTests::NonEscapedCharacters>(); + add<RoundTripTests::AllowedControlCharacter>(); + add<RoundTripTests::NumbersInFieldName>(); + add<RoundTripTests::EscapeFieldName>(); + add<RoundTripTests::EscapedUnicodeToUtf8>(); + add<RoundTripTests::Utf8AllOnes>(); + add<RoundTripTests::Utf8FirstByteOnes>(); + add<RoundTripTests::BinData>(); + add<RoundTripTests::BinDataPaddedSingle>(); + add<RoundTripTests::BinDataPaddedDouble>(); + add<RoundTripTests::BinDataAllChars>(); + add<RoundTripTests::Date>(); + add<RoundTripTests::DateNonzero>(); + add<RoundTripTests::DateNegative>(); + add<RoundTripTests::Timestamp>(); + add<RoundTripTests::TimestampMax>(); + add<RoundTripTests::Regex>(); + add<RoundTripTests::RegexWithQuotes>(); + add<RoundTripTests::UnquotedFieldName>(); + add<RoundTripTests::SingleQuotes>(); + add<RoundTripTests::ObjectId>(); + add<RoundTripTests::NumberLong>(); + add<RoundTripTests::NumberInt>(); + add<RoundTripTests::Number>(); + add<RoundTripTests::UUID>(); + add<RoundTripTests::HexData>(); + add<RoundTripTests::MD5>(); + add<RoundTripTests::NullString>(); + } +}; + +SuiteInstance<All> myall; + +} // namespace JavaJSTests |