1 // jstests.cpp 2 // 3 4 5 /** 6 * Copyright (C) 2018-present MongoDB, Inc. 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the Server Side Public License, version 1, 10 * as published by MongoDB, Inc. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * Server Side Public License for more details. 16 * 17 * You should have received a copy of the Server Side Public License 18 * along with this program. If not, see 19 * <http://www.mongodb.com/licensing/server-side-public-license>. 20 * 21 * As a special exception, the copyright holders give permission to link the 22 * code of portions of this program with the OpenSSL library under certain 23 * conditions as described in each individual source file and distribute 24 * linked combinations including the program with the OpenSSL library. You 25 * must comply with the Server Side Public License in all respects for 26 * all of the code used other than as permitted herein. If you modify file(s) 27 * with this exception, you may extend this exception to your version of the 28 * file(s), but you are not obligated to do so. If you do not wish to do so, 29 * delete this exception statement from your version. If you delete this 30 * exception statement from all source files in the program, then also delete 31 * it in the license file. 32 */ 33 34 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault 35 36 #include "mongo/platform/basic.h" 37 38 #include <iostream> 39 #include <limits> 40 41 #include "mongo/base/parse_number.h" 42 #include "mongo/db/client.h" 43 #include "mongo/db/dbdirectclient.h" 44 #include "mongo/db/json.h" 45 #include "mongo/dbtests/dbtests.h" 46 #include "mongo/platform/decimal128.h" 47 #include "mongo/scripting/engine.h" 48 #include "mongo/util/concurrency/thread_name.h" 49 #include "mongo/util/future.h" 50 #include "mongo/util/log.h" 51 #include "mongo/util/time_support.h" 52 #include "mongo/util/timer.h" 53 54 using std::cout; 55 using std::endl; 56 using std::string; 57 using std::stringstream; 58 using std::unique_ptr; 59 using std::vector; 60 61 namespace JSTests { 62 63 class BuiltinTests { 64 public: run()65 void run() { 66 // Run any tests included with the scripting engine 67 getGlobalScriptEngine()->runTest(); 68 } 69 }; 70 71 class BasicScope { 72 public: run()73 void run() { 74 unique_ptr<Scope> s; 75 s.reset(getGlobalScriptEngine()->newScope()); 76 77 s->setNumber("x", 5); 78 ASSERT(5 == s->getNumber("x")); 79 80 s->setNumber("x", 1.67); 81 ASSERT(1.67 == s->getNumber("x")); 82 83 s->setString("s", "eliot was here"); 84 ASSERT("eliot was here" == s->getString("s")); 85 86 s->setBoolean("b", true); 87 ASSERT(s->getBoolean("b")); 88 89 s->setBoolean("b", false); 90 ASSERT(!s->getBoolean("b")); 91 } 92 }; 93 94 class ResetScope { 95 public: run()96 void run() { 97 /* Currently reset does not clear data in v8 or spidermonkey scopes. See SECURITY-10 98 unique_ptr<Scope> s; 99 s.reset( getGlobalScriptEngine()->newScope() ); 100 101 s->setBoolean( "x" , true ); 102 ASSERT( s->getBoolean( "x" ) ); 103 104 s->reset(); 105 ASSERT( !s->getBoolean( "x" ) ); 106 */ 107 } 108 }; 109 110 class FalseTests { 111 public: run()112 void run() { 113 // Test falsy javascript values 114 unique_ptr<Scope> s; 115 s.reset(getGlobalScriptEngine()->newScope()); 116 117 ASSERT(!s->getBoolean("notSet")); 118 119 s->setString("emptyString", ""); 120 ASSERT(!s->getBoolean("emptyString")); 121 122 s->setNumber("notANumberVal", std::numeric_limits<double>::quiet_NaN()); 123 ASSERT(!s->getBoolean("notANumberVal")); 124 125 auto obj = BSONObjBuilder().appendNull("null").obj(); 126 s->setElement("nullVal", obj.getField("null"), obj); 127 ASSERT(!s->getBoolean("nullVal")); 128 129 s->setNumber("zeroVal", 0); 130 ASSERT(!s->getBoolean("zeroVal")); 131 } 132 }; 133 134 class SimpleFunctions { 135 public: run()136 void run() { 137 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 138 139 s->invoke("x=5;", 0, 0); 140 ASSERT(5 == s->getNumber("x")); 141 142 s->invoke("return 17;", 0, 0); 143 ASSERT(17 == s->getNumber("__returnValue")); 144 145 s->invoke("function(){ return 17; }", 0, 0); 146 ASSERT(17 == s->getNumber("__returnValue")); 147 148 s->setNumber("x", 1.76); 149 s->invoke("return x == 1.76; ", 0, 0); 150 ASSERT(s->getBoolean("__returnValue")); 151 152 s->setNumber("x", 1.76); 153 s->invoke("return x == 1.79; ", 0, 0); 154 ASSERT(!s->getBoolean("__returnValue")); 155 156 BSONObj obj = BSON("" << 11.0); 157 s->invoke("function( z ){ return 5 + z; }", &obj, 0); 158 ASSERT_EQUALS(16, s->getNumber("__returnValue")); 159 } 160 }; 161 162 /** Installs a tee for auditing log messages in the same thread. */ 163 class LogRecordingScope { 164 public: LogRecordingScope()165 LogRecordingScope() 166 : _logged(false), 167 _threadName(mongo::getThreadName().toString()), 168 _handle(mongo::logger::globalLogDomain()->attachAppender( 169 mongo::logger::MessageLogDomain::AppenderAutoPtr(new Tee(this)))) {} ~LogRecordingScope()170 ~LogRecordingScope() { 171 mongo::logger::globalLogDomain()->detachAppender(_handle); 172 } 173 /** @return most recent log entry. */ logged() const174 bool logged() const { 175 return _logged; 176 } 177 178 private: 179 class Tee : public mongo::logger::MessageLogDomain::EventAppender { 180 public: Tee(LogRecordingScope * scope)181 Tee(LogRecordingScope* scope) : _scope(scope) {} ~Tee()182 virtual ~Tee() {} append(const logger::MessageEventEphemeral & event)183 virtual Status append(const logger::MessageEventEphemeral& event) { 184 // Don't want to consider logging by background threads. 185 if (mongo::getThreadName() == _scope->_threadName) { 186 _scope->_logged = true; 187 } 188 return Status::OK(); 189 } 190 191 private: 192 LogRecordingScope* _scope; 193 }; 194 bool _logged; 195 const string _threadName; 196 mongo::logger::MessageLogDomain::AppenderHandle _handle; 197 }; 198 199 /** Error logging in Scope::exec(). */ 200 class ExecLogError { 201 public: run()202 void run() { 203 unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope()); 204 205 // No error is logged when reportError == false. 206 ASSERT(!scope->exec("notAFunction()", "foo", false, false, false)); 207 ASSERT(!_logger.logged()); 208 209 // No error is logged for a valid statement. 210 ASSERT(scope->exec("validStatement = true", "foo", false, true, false)); 211 ASSERT(!_logger.logged()); 212 213 // An error is logged for an invalid statement when reportError == true. 214 ASSERT(!scope->exec("notAFunction()", "foo", false, true, false)); 215 216 // Don't check if we're using SpiderMonkey. Our threading model breaks 217 // this test 218 // TODO: figure out a way to check for SpiderMonkey 219 auto ivs = getGlobalScriptEngine()->getInterpreterVersionString(); 220 std::string prefix("MozJS"); 221 if (ivs.compare(0, prefix.length(), prefix) != 0) { 222 ASSERT(_logger.logged()); 223 } 224 } 225 226 private: 227 LogRecordingScope _logger; 228 }; 229 230 /** Error logging in Scope::invoke(). */ 231 class InvokeLogError { 232 public: run()233 void run() { 234 unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope()); 235 236 // No error is logged for a valid statement. 237 ASSERT_EQUALS(0, scope->invoke("validStatement = true", 0, 0)); 238 ASSERT(!_logger.logged()); 239 240 // An error is logged for an invalid statement. 241 try { 242 scope->invoke("notAFunction()", 0, 0); 243 } catch (const DBException&) { 244 // ignore the exception; just test that we logged something 245 } 246 247 // Don't check if we're using SpiderMonkey. Our threading model breaks 248 // this test 249 // TODO: figure out a way to check for SpiderMonkey 250 auto ivs = getGlobalScriptEngine()->getInterpreterVersionString(); 251 std::string prefix("MozJS"); 252 if (ivs.compare(0, prefix.length(), prefix) != 0) { 253 ASSERT(_logger.logged()); 254 } 255 } 256 257 private: 258 LogRecordingScope _logger; 259 }; 260 261 class ObjectMapping { 262 public: run()263 void run() { 264 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 265 266 BSONObj o = BSON("x" << 17.0 << "y" 267 << "eliot" 268 << "z" 269 << "sara"); 270 s->setObject("blah", o); 271 272 s->invoke("return blah.x;", 0, 0); 273 ASSERT_EQUALS(17, s->getNumber("__returnValue")); 274 s->invoke("return blah.y;", 0, 0); 275 ASSERT_EQUALS("eliot", s->getString("__returnValue")); 276 277 s->invoke("return this.z;", 0, &o); 278 ASSERT_EQUALS("sara", s->getString("__returnValue")); 279 280 s->invoke("return this.z == 'sara';", 0, &o); 281 ASSERT_EQUALS(true, s->getBoolean("__returnValue")); 282 283 s->invoke("this.z == 'sara';", 0, &o); 284 ASSERT_EQUALS(true, s->getBoolean("__returnValue")); 285 286 s->invoke("this.z == 'asara';", 0, &o); 287 ASSERT_EQUALS(false, s->getBoolean("__returnValue")); 288 289 s->invoke("return this.x == 17;", 0, &o); 290 ASSERT_EQUALS(true, s->getBoolean("__returnValue")); 291 292 s->invoke("return this.x == 18;", 0, &o); 293 ASSERT_EQUALS(false, s->getBoolean("__returnValue")); 294 295 s->invoke("function(){ return this.x == 17; }", 0, &o); 296 ASSERT_EQUALS(true, s->getBoolean("__returnValue")); 297 298 s->invoke("function(){ return this.x == 18; }", 0, &o); 299 ASSERT_EQUALS(false, s->getBoolean("__returnValue")); 300 301 s->invoke("function (){ return this.x == 17; }", 0, &o); 302 ASSERT_EQUALS(true, s->getBoolean("__returnValue")); 303 304 s->invoke("function z(){ return this.x == 18; }", 0, &o); 305 ASSERT_EQUALS(false, s->getBoolean("__returnValue")); 306 307 s->invoke("function (){ this.x == 17; }", 0, &o); 308 ASSERT_EQUALS(false, s->getBoolean("__returnValue")); 309 310 s->invoke("function z(){ this.x == 18; }", 0, &o); 311 ASSERT_EQUALS(false, s->getBoolean("__returnValue")); 312 313 s->invoke("x = 5; for( ; x <10; x++){ a = 1; }", 0, &o); 314 ASSERT_EQUALS(10, s->getNumber("x")); 315 } 316 }; 317 318 class ObjectDecoding { 319 public: run()320 void run() { 321 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 322 323 s->invoke("z = { num : 1 };", 0, 0); 324 BSONObj out = s->getObject("z"); 325 ASSERT_EQUALS(1, out["num"].number()); 326 ASSERT_EQUALS(1, out.nFields()); 327 328 s->invoke("z = { x : 'eliot' };", 0, 0); 329 out = s->getObject("z"); 330 ASSERT_EQUALS((string) "eliot", out["x"].valuestr()); 331 ASSERT_EQUALS(1, out.nFields()); 332 333 BSONObj o = BSON("x" << 17); 334 s->setObject("blah", o); 335 out = s->getObject("blah"); 336 ASSERT_EQUALS(17, out["x"].number()); 337 } 338 }; 339 340 class JSOIDTests { 341 public: run()342 void run() { 343 #ifdef MOZJS 344 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 345 346 s->localConnect("blah"); 347 348 s->invoke("z = { _id : new ObjectId() , a : 123 };", 0, 0); 349 BSONObj out = s->getObject("z"); 350 ASSERT_EQUALS(123, out["a"].number()); 351 ASSERT_EQUALS(jstOID, out["_id"].type()); 352 353 OID save = out["_id"].__oid(); 354 355 s->setObject("a", out); 356 357 s->invoke("y = { _id : a._id , a : 124 };", 0, 0); 358 out = s->getObject("y"); 359 ASSERT_EQUALS(124, out["a"].number()); 360 ASSERT_EQUALS(jstOID, out["_id"].type()); 361 ASSERT_EQUALS(out["_id"].__oid().str(), save.str()); 362 363 s->invoke("y = { _id : new ObjectId( a._id ) , a : 125 };", 0, 0); 364 out = s->getObject("y"); 365 ASSERT_EQUALS(125, out["a"].number()); 366 ASSERT_EQUALS(jstOID, out["_id"].type()); 367 ASSERT_EQUALS(out["_id"].__oid().str(), save.str()); 368 #endif 369 } 370 }; 371 372 class SetImplicit { 373 public: run()374 void run() { 375 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 376 377 BSONObj o = BSON("foo" 378 << "bar"); 379 s->setObject("a.b", o); 380 ASSERT(s->getObject("a").isEmpty()); 381 382 BSONObj o2 = BSONObj(); 383 s->setObject("a", o2); 384 s->setObject("a.b", o); 385 ASSERT(s->getObject("a").isEmpty()); 386 387 o2 = fromjson("{b:{}}"); 388 s->setObject("a", o2); 389 s->setObject("a.b", o); 390 ASSERT(!s->getObject("a").isEmpty()); 391 } 392 }; 393 394 class ObjectModReadonlyTests { 395 public: run()396 void run() { 397 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 398 399 BSONObj o = BSON("x" << 17 << "y" 400 << "eliot" 401 << "z" 402 << "sara" 403 << "zz" 404 << BSONObj()); 405 s->setObject("blah", o, true); 406 407 BSONObj out; 408 409 ASSERT_THROWS(s->invoke("blah.y = 'e'", 0, 0), mongo::AssertionException); 410 ASSERT_THROWS(s->invoke("blah.a = 19;", 0, 0), mongo::AssertionException); 411 ASSERT_THROWS(s->invoke("blah.zz.a = 19;", 0, 0), mongo::AssertionException); 412 ASSERT_THROWS(s->invoke("blah.zz = { a : 19 };", 0, 0), mongo::AssertionException); 413 ASSERT_THROWS(s->invoke("delete blah['x']", 0, 0), mongo::AssertionException); 414 415 // read-only object itself can be overwritten 416 s->invoke("blah = {}", 0, 0); 417 out = s->getObject("blah"); 418 ASSERT(out.isEmpty()); 419 420 // test array - can't implement this in v8 421 // o = fromjson( "{a:[1,2,3]}" ); 422 // s->setObject( "blah", o, true ); 423 // out = s->getObject( "blah" ); 424 // s->invoke( "blah.a[ 0 ] = 4;", BSONObj() ); 425 // s->invoke( "delete blah['a'][ 2 ];", BSONObj() ); 426 // out = s->getObject( "blah" ); 427 // ASSERT_EQUALS( 1.0, out[ "a" ].embeddedObject()[ 0 ].number() ); 428 // ASSERT_EQUALS( 3.0, out[ "a" ].embeddedObject()[ 2 ].number() ); 429 } 430 }; 431 432 class OtherJSTypes { 433 public: run()434 void run() { 435 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 436 437 { 438 // date 439 BSONObj o; 440 { 441 BSONObjBuilder b; 442 b.appendDate("d", Date_t::fromMillisSinceEpoch(123456789)); 443 o = b.obj(); 444 } 445 s->setObject("x", o); 446 447 s->invoke("return x.d.getTime() != 12;", 0, 0); 448 ASSERT_EQUALS(true, s->getBoolean("__returnValue")); 449 450 s->invoke("z = x.d.getTime();", 0, 0); 451 ASSERT_EQUALS(123456789, s->getNumber("z")); 452 453 s->invoke("z = { z : x.d }", 0, 0); 454 BSONObj out = s->getObject("z"); 455 ASSERT(out["z"].type() == Date); 456 } 457 458 { 459 // regex 460 BSONObj o; 461 { 462 BSONObjBuilder b; 463 b.appendRegex("r", "^a", "i"); 464 o = b.obj(); 465 } 466 s->setObject("x", o); 467 468 s->invoke("z = x.r.test( 'b' );", 0, 0); 469 ASSERT_EQUALS(false, s->getBoolean("z")); 470 471 s->invoke("z = x.r.test( 'a' );", 0, 0); 472 ASSERT_EQUALS(true, s->getBoolean("z")); 473 474 s->invoke("z = x.r.test( 'ba' );", 0, 0); 475 ASSERT_EQUALS(false, s->getBoolean("z")); 476 477 s->invoke("z = { a : x.r };", 0, 0); 478 479 BSONObj out = s->getObject("z"); 480 ASSERT_EQUALS((string) "^a", out["a"].regex()); 481 ASSERT_EQUALS((string) "i", out["a"].regexFlags()); 482 483 // This regex used to cause a segfault because x isn't a valid flag for a js RegExp. 484 // Now it throws a JS exception. 485 BSONObj invalidRegex = BSON_ARRAY(BSON("regex" << BSONRegEx("asdf", "x"))); 486 const char* code = 487 "function (obj) {" 488 " var threw = false;" 489 " try {" 490 " obj.regex;" // should throw 491 " } catch(e) {" 492 " threw = true;" 493 " }" 494 " assert(threw);" 495 "}"; 496 ASSERT_EQUALS(s->invoke(code, &invalidRegex, NULL), 0); 497 } 498 499 // array 500 { 501 BSONObj o = fromjson("{r:[1,2,3]}"); 502 s->setObject("x", o, false); 503 BSONObj out = s->getObject("x"); 504 ASSERT_EQUALS(Array, out.firstElement().type()); 505 506 s->setObject("x", o, true); 507 out = s->getObject("x"); 508 ASSERT_EQUALS(Array, out.firstElement().type()); 509 } 510 511 // symbol 512 { 513 // test mutable object with symbol type 514 BSONObjBuilder builder; 515 builder.appendSymbol("sym", "value"); 516 BSONObj in = builder.done(); 517 s->setObject("x", in, false); 518 BSONObj out = s->getObject("x"); 519 ASSERT_EQUALS(Symbol, out.firstElement().type()); 520 521 // readonly 522 s->setObject("x", in, true); 523 out = s->getObject("x"); 524 ASSERT_EQUALS(Symbol, out.firstElement().type()); 525 } 526 } 527 }; 528 529 class SpecialDBTypes { 530 public: run()531 void run() { 532 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 533 534 BSONObjBuilder b; 535 b.appendTimestamp("a", 123456789); 536 b.appendMinKey("b"); 537 b.appendMaxKey("c"); 538 b.append("d", Timestamp(1234, 9876)); 539 540 541 { 542 BSONObj t = b.done(); 543 ASSERT_EQUALS(Date_t::fromMillisSinceEpoch(1234000), t["d"].timestampTime()); 544 ASSERT_EQUALS(9876U, t["d"].timestampInc()); 545 } 546 547 s->setObject("z", b.obj()); 548 549 ASSERT(s->invoke("y = { a : z.a , b : z.b , c : z.c , d: z.d }", 0, 0) == 0); 550 551 BSONObj out = s->getObject("y"); 552 ASSERT_EQUALS(bsonTimestamp, out["a"].type()); 553 ASSERT_EQUALS(MinKey, out["b"].type()); 554 ASSERT_EQUALS(MaxKey, out["c"].type()); 555 ASSERT_EQUALS(bsonTimestamp, out["d"].type()); 556 557 ASSERT_EQUALS(9876U, out["d"].timestampInc()); 558 ASSERT_EQUALS(Date_t::fromMillisSinceEpoch(1234000), out["d"].timestampTime()); 559 ASSERT_EQUALS(Date_t::fromMillisSinceEpoch(123456789), out["a"].date()); 560 } 561 }; 562 563 class TypeConservation { 564 public: run()565 void run() { 566 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 567 568 // -- A -- 569 570 BSONObj o; 571 { 572 BSONObjBuilder b; 573 b.append("a", (int)5); 574 b.append("b", 5.6); 575 o = b.obj(); 576 } 577 ASSERT_EQUALS(NumberInt, o["a"].type()); 578 ASSERT_EQUALS(NumberDouble, o["b"].type()); 579 580 s->setObject("z", o); 581 s->invoke("return z", 0, 0); 582 BSONObj out = s->getObject("__returnValue"); 583 ASSERT_EQUALS(5, out["a"].number()); 584 ASSERT_EQUALS(5.6, out["b"].number()); 585 586 ASSERT_EQUALS(NumberDouble, out["b"].type()); 587 ASSERT_EQUALS(NumberInt, out["a"].type()); 588 589 // -- B -- 590 591 { 592 BSONObjBuilder b; 593 b.append("a", (int)5); 594 b.append("b", 5.6); 595 o = b.obj(); 596 } 597 598 s->setObject("z", o, false); 599 s->invoke("return z", 0, 0); 600 out = s->getObject("__returnValue"); 601 ASSERT_EQUALS(5, out["a"].number()); 602 ASSERT_EQUALS(5.6, out["b"].number()); 603 604 ASSERT_EQUALS(NumberDouble, out["b"].type()); 605 ASSERT_EQUALS(NumberInt, out["a"].type()); 606 607 608 // -- C -- 609 610 { 611 BSONObjBuilder b; 612 613 { 614 BSONObjBuilder c; 615 c.append("0", 5.5); 616 c.append("1", 6); 617 b.appendArray("a", c.obj()); 618 } 619 620 o = b.obj(); 621 } 622 623 ASSERT_EQUALS(NumberDouble, o["a"].embeddedObjectUserCheck()["0"].type()); 624 ASSERT_EQUALS(NumberInt, o["a"].embeddedObjectUserCheck()["1"].type()); 625 626 s->setObject("z", o, false); 627 out = s->getObject("z"); 628 629 ASSERT_EQUALS(NumberDouble, out["a"].embeddedObjectUserCheck()["0"].type()); 630 ASSERT_EQUALS(NumberInt, out["a"].embeddedObjectUserCheck()["1"].type()); 631 632 s->invokeSafe("z.z = 5;", 0, 0); 633 out = s->getObject("z"); 634 ASSERT_EQUALS(5, out["z"].number()); 635 ASSERT_EQUALS(NumberDouble, out["a"].embeddedObjectUserCheck()["0"].type()); 636 // Commenting so that v8 tests will work 637 // TODO: this is technically bad, but here to make sure that i understand the behavior 638 // ASSERT_EQUALS( NumberDouble , out["a"].embeddedObjectUserCheck()["1"].type() ); 639 640 641 // Eliot says I don't have to worry about this case 642 643 // // -- D -- 644 // 645 // o = fromjson( "{a:3.0,b:4.5}" ); 646 // ASSERT_EQUALS( NumberDouble , o["a"].type() ); 647 // ASSERT_EQUALS( NumberDouble , o["b"].type() ); 648 // 649 // s->setObject( "z" , o , false ); 650 // s->invoke( "return z" , BSONObj() ); 651 // out = s->getObject( "__returnValue" ); 652 // ASSERT_EQUALS( 3 , out["a"].number() ); 653 // ASSERT_EQUALS( 4.5 , out["b"].number() ); 654 // 655 // ASSERT_EQUALS( NumberDouble , out["b"].type() ); 656 // ASSERT_EQUALS( NumberDouble , out["a"].type() ); 657 // 658 } 659 }; 660 661 class NumberLong { 662 public: run()663 void run() { 664 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 665 BSONObjBuilder b; 666 long long val = (long long)(0xbabadeadbeefbaddULL); 667 b.append("a", val); 668 BSONObj in = b.obj(); 669 s->setObject("a", in); 670 BSONObj out = s->getObject("a"); 671 ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); 672 673 ASSERT(s->exec("b = {b:a.a}", "foo", false, true, false)); 674 out = s->getObject("b"); 675 ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); 676 if (val != out.firstElement().numberLong()) { 677 cout << val << endl; 678 cout << out.firstElement().numberLong() << endl; 679 cout << out.toString() << endl; 680 ASSERT_EQUALS(val, out.firstElement().numberLong()); 681 } 682 683 ASSERT(s->exec("c = {c:a.a.toString()}", "foo", false, true, false)); 684 out = s->getObject("c"); 685 stringstream ss; 686 ss << "NumberLong(\"" << val << "\")"; 687 ASSERT_EQUALS(ss.str(), out.firstElement().valuestr()); 688 689 ASSERT(s->exec("d = {d:a.a.toNumber()}", "foo", false, true, false)); 690 out = s->getObject("d"); 691 ASSERT_EQUALS(NumberDouble, out.firstElement().type()); 692 ASSERT_EQUALS(double(val), out.firstElement().number()); 693 694 ASSERT(s->exec("e = {e:a.a.floatApprox}", "foo", false, true, false)); 695 out = s->getObject("e"); 696 ASSERT_EQUALS(NumberDouble, out.firstElement().type()); 697 ASSERT_EQUALS(double(val), out.firstElement().number()); 698 699 ASSERT(s->exec("f = {f:a.a.top}", "foo", false, true, false)); 700 out = s->getObject("f"); 701 ASSERT(NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type()); 702 703 s->setObject("z", BSON("z" << (long long)(4))); 704 ASSERT(s->exec("y = {y:z.z.top}", "foo", false, true, false)); 705 out = s->getObject("y"); 706 ASSERT_EQUALS(NumberDouble, out.firstElement().type()); 707 708 ASSERT(s->exec("x = {x:z.z.floatApprox}", "foo", false, true, false)); 709 out = s->getObject("x"); 710 ASSERT(NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type()); 711 ASSERT_EQUALS(double(4), out.firstElement().number()); 712 713 ASSERT(s->exec("w = {w:z.z}", "foo", false, true, false)); 714 out = s->getObject("w"); 715 ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); 716 ASSERT_EQUALS(4, out.firstElement().numberLong()); 717 } 718 }; 719 720 class NumberLong2 { 721 public: run()722 void run() { 723 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 724 725 BSONObj in; 726 { 727 BSONObjBuilder b; 728 b.append("a", 5); 729 b.append("b", (long long)5); 730 b.append("c", (long long)pow(2.0, 29)); 731 b.append("d", (long long)pow(2.0, 30)); 732 b.append("e", (long long)pow(2.0, 31)); 733 b.append("f", (long long)pow(2.0, 45)); 734 in = b.obj(); 735 } 736 s->setObject("a", in); 737 738 ASSERT(s->exec("x = tojson( a ); ", "foo", false, true, false)); 739 string outString = s->getString("x"); 740 741 ASSERT(s->exec((string) "y = " + outString, "foo2", false, true, false)); 742 BSONObj out = s->getObject("y"); 743 ASSERT_BSONOBJ_EQ(in, out); 744 } 745 }; 746 747 class NumberLongUnderLimit { 748 public: run()749 void run() { 750 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 751 752 BSONObjBuilder b; 753 // limit is 2^53 754 long long val = (long long)(9007199254740991ULL); 755 b.append("a", val); 756 BSONObj in = b.obj(); 757 s->setObject("a", in); 758 BSONObj out = s->getObject("a"); 759 ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); 760 761 ASSERT(s->exec("b = {b:a.a}", "foo", false, true, false)); 762 out = s->getObject("b"); 763 ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type()); 764 if (val != out.firstElement().numberLong()) { 765 cout << val << endl; 766 cout << out.firstElement().numberLong() << endl; 767 cout << out.toString() << endl; 768 ASSERT_EQUALS(val, out.firstElement().numberLong()); 769 } 770 771 ASSERT(s->exec("c = {c:a.a.toString()}", "foo", false, true, false)); 772 out = s->getObject("c"); 773 stringstream ss; 774 ss << "NumberLong(\"" << val << "\")"; 775 ASSERT_EQUALS(ss.str(), out.firstElement().valuestr()); 776 777 ASSERT(s->exec("d = {d:a.a.toNumber()}", "foo", false, true, false)); 778 out = s->getObject("d"); 779 ASSERT_EQUALS(NumberDouble, out.firstElement().type()); 780 ASSERT_EQUALS(double(val), out.firstElement().number()); 781 782 ASSERT(s->exec("e = {e:a.a.floatApprox}", "foo", false, true, false)); 783 out = s->getObject("e"); 784 ASSERT_EQUALS(NumberDouble, out.firstElement().type()); 785 ASSERT_EQUALS(double(val), out.firstElement().number()); 786 787 ASSERT(s->exec("f = {f:a.a.top}", "foo", false, true, false)); 788 out = s->getObject("f"); 789 ASSERT(Undefined == out.firstElement().type()); 790 } 791 }; 792 793 class NumberDecimal { 794 public: run()795 void run() { 796 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 797 BSONObjBuilder b; 798 Decimal128 val = Decimal128("2.010"); 799 b.append("a", val); 800 BSONObj in = b.obj(); 801 s->setObject("a", in); 802 803 // Test the scope object 804 BSONObj out = s->getObject("a"); 805 ASSERT_EQUALS(mongo::NumberDecimal, out.firstElement().type()); 806 ASSERT_TRUE(val.isEqual(out.firstElement().numberDecimal())); 807 808 ASSERT(s->exec("b = {b:a.a}", "foo", false, true, false)); 809 out = s->getObject("b"); 810 ASSERT_EQUALS(mongo::NumberDecimal, out.firstElement().type()); 811 ASSERT_TRUE(val.isEqual(out.firstElement().numberDecimal())); 812 813 // Test that the appropriate string output is generated 814 ASSERT(s->exec("c = {c:a.a.toString()}", "foo", false, true, false)); 815 out = s->getObject("c"); 816 stringstream ss; 817 ss << "NumberDecimal(\"" << val.toString() << "\")"; 818 ASSERT_EQUALS(ss.str(), out.firstElement().valuestr()); 819 } 820 }; 821 822 class NumberDecimalGetFromScope { 823 public: run()824 void run() { 825 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 826 ASSERT(s->exec("a = 5;", "a", false, true, false)); 827 ASSERT_TRUE(Decimal128(5).isEqual(s->getNumberDecimal("a"))); 828 } 829 }; 830 831 class NumberDecimalBigObject { 832 public: run()833 void run() { 834 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 835 836 BSONObj in; 837 { 838 BSONObjBuilder b; 839 b.append("a", 5); 840 b.append("b", Decimal128("1.5E-3000")); 841 b.append("c", Decimal128("1.5E-1")); 842 b.append("d", Decimal128("1.5E3000")); 843 b.append("e", Decimal128("Infinity")); 844 b.append("f", Decimal128("NaN")); 845 in = b.obj(); 846 } 847 s->setObject("a", in); 848 849 ASSERT(s->exec("x = tojson( a ); ", "foo", false, true, false)); 850 string outString = s->getString("x"); 851 852 ASSERT(s->exec((string) "y = " + outString, "foo2", false, true, false)); 853 BSONObj out = s->getObject("y"); 854 ASSERT_BSONOBJ_EQ(in, out); 855 } 856 }; 857 858 class MaxTimestamp { 859 public: run()860 void run() { 861 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 862 863 // Timestamp 't' component can exceed max for int32_t. 864 BSONObj in; 865 { 866 BSONObjBuilder b; 867 b.bb().appendNum(static_cast<char>(bsonTimestamp)); 868 b.bb().appendStr("a"); 869 b.bb().appendNum(std::numeric_limits<unsigned long long>::max()); 870 871 in = b.obj(); 872 } 873 s->setObject("a", in); 874 875 ASSERT(s->exec("x = tojson( a ); ", "foo", false, true, false)); 876 } 877 }; 878 879 class WeirdObjects { 880 public: build(int depth)881 BSONObj build(int depth) { 882 BSONObjBuilder b; 883 b.append("0", depth); 884 if (depth > 0) 885 b.appendArray("1", build(depth - 1)); 886 return b.obj(); 887 } 888 run()889 void run() { 890 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 891 892 for (int i = 5; i < 100; i += 10) { 893 s->setObject("a", build(i), false); 894 s->invokeSafe("tojson( a )", 0, 0); 895 896 s->setObject("a", build(5), true); 897 s->invokeSafe("tojson( a )", 0, 0); 898 } 899 } 900 }; 901 902 /** 903 * Test exec() timeout value terminates execution (SERVER-8053) 904 */ 905 class ExecTimeout { 906 public: run()907 void run() { 908 unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope()); 909 910 // assert timeout occurred 911 ASSERT(!scope->exec("var a = 1; while (true) { ; }", "ExecTimeout", false, true, false, 1)); 912 } 913 }; 914 915 /** 916 * Test exec() timeout value terminates execution (SERVER-8053) 917 */ 918 class ExecNoTimeout { 919 public: run()920 void run() { 921 unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope()); 922 923 // assert no timeout occurred 924 ASSERT(scope->exec("var a = function() { return 1; }", 925 "ExecNoTimeout", 926 false, 927 true, 928 false, 929 5 * 60 * 1000)); 930 } 931 }; 932 933 /** 934 * Test invoke() timeout value terminates execution (SERVER-8053) 935 */ 936 class InvokeTimeout { 937 public: run()938 void run() { 939 unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope()); 940 941 // scope timeout after 500ms 942 bool caught = false; 943 try { 944 scope->invokeSafe( 945 "function() { " 946 " while (true) { } " 947 "} ", 948 0, 949 0, 950 1); 951 } catch (const DBException&) { 952 caught = true; 953 } 954 ASSERT(caught); 955 } 956 }; 957 958 class SleepInterruption { 959 public: run()960 void run() { 961 std::shared_ptr<Scope> scope(getGlobalScriptEngine()->newScope()); 962 963 Promise<void> sleepPromise{}; 964 auto sleepFuture = sleepPromise.getFuture(); 965 966 Promise<void> awakenedPromise{}; 967 auto awakenedFuture = awakenedPromise.getFuture(); 968 969 // Spawn a thread which attempts to sleep indefinitely. 970 stdx::thread([ 971 preSleep = std::move(sleepPromise), 972 onAwake = std::move(awakenedPromise), 973 scope 974 ]() mutable { 975 preSleep.emplaceValue(); 976 onAwake.setWith([&] { 977 scope->exec( 978 "" 979 " try {" 980 " sleep(99999999999);" 981 " } finally {" 982 " throw \"FAILURE\";" 983 " }" 984 "", 985 "test", 986 false, 987 false, 988 true); 989 }); 990 }).detach(); 991 992 // Wait until just before the sleep begins. 993 sleepFuture.get(); 994 995 // Attempt to wait until Javascript enters the sleep. 996 // It's OK if we kill the function prematurely, before it begins sleeping. Either cause of 997 // death will emit an error with the Interrupted code. 998 sleepsecs(1); 999 1000 // Send the operation a kill signal. 1001 scope->kill(); 1002 1003 // Wait for the error. 1004 auto result = awakenedFuture.getNoThrow(); 1005 ASSERT_EQ(ErrorCodes::Interrupted, result); 1006 } 1007 }; 1008 1009 /** 1010 * Test invoke() timeout value does not terminate execution (SERVER-8053) 1011 */ 1012 class InvokeNoTimeout { 1013 public: run()1014 void run() { 1015 unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope()); 1016 1017 // invoke completes before timeout 1018 scope->invokeSafe( 1019 "function() { " 1020 " for (var i=0; i<1; i++) { ; } " 1021 "} ", 1022 0, 1023 0, 1024 5 * 60 * 1000); 1025 } 1026 }; 1027 1028 1029 class Utf8Check { 1030 public: Utf8Check()1031 Utf8Check() { 1032 reset(); 1033 } ~Utf8Check()1034 ~Utf8Check() { 1035 reset(); 1036 } run()1037 void run() { 1038 if (!getGlobalScriptEngine()->utf8Ok()) { 1039 mongo::unittest::log() << "warning: utf8 not supported" << endl; 1040 return; 1041 } 1042 string utf8ObjSpec = "{'_id':'\\u0001\\u007f\\u07ff\\uffff'}"; 1043 BSONObj utf8Obj = fromjson(utf8ObjSpec); 1044 1045 const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); 1046 OperationContext& opCtx = *opCtxPtr; 1047 DBDirectClient client(&opCtx); 1048 1049 client.insert(ns(), utf8Obj); 1050 client.eval("unittest", 1051 "v = db.jstests.utf8check.findOne(); db.jstests.utf8check.remove( {} ); " 1052 "db.jstests.utf8check.insert( v );"); 1053 check(utf8Obj, client.findOne(ns(), BSONObj())); 1054 } 1055 1056 private: check(const BSONObj & one,const BSONObj & two)1057 void check(const BSONObj& one, const BSONObj& two) { 1058 if (one.woCompare(two) != 0) { 1059 static string fail = 1060 string("Assertion failure expected ") + one.toString() + ", got " + two.toString(); 1061 FAIL(fail.c_str()); 1062 } 1063 } 1064 reset()1065 void reset() { 1066 const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); 1067 OperationContext& opCtx = *opCtxPtr; 1068 DBDirectClient client(&opCtx); 1069 1070 client.dropCollection(ns()); 1071 } 1072 ns()1073 static const char* ns() { 1074 return "unittest.jstests.utf8check"; 1075 } 1076 }; 1077 1078 class LongUtf8String { 1079 public: LongUtf8String()1080 LongUtf8String() { 1081 reset(); 1082 } ~LongUtf8String()1083 ~LongUtf8String() { 1084 reset(); 1085 } run()1086 void run() { 1087 if (!getGlobalScriptEngine()->utf8Ok()) 1088 return; 1089 1090 const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); 1091 OperationContext& opCtx = *opCtxPtr; 1092 DBDirectClient client(&opCtx); 1093 1094 client.eval("unittest", 1095 "db.jstests.longutf8string.save( {_id:'\\uffff\\uffff\\uffff\\uffff'} )"); 1096 } 1097 1098 private: reset()1099 void reset() { 1100 const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); 1101 OperationContext& opCtx = *opCtxPtr; 1102 DBDirectClient client(&opCtx); 1103 1104 client.dropCollection(ns()); 1105 } 1106 ns()1107 static const char* ns() { 1108 return "unittest.jstests.longutf8string"; 1109 } 1110 }; 1111 1112 class InvalidUTF8Check { 1113 public: run()1114 void run() { 1115 if (!getGlobalScriptEngine()->utf8Ok()) 1116 return; 1117 1118 unique_ptr<Scope> s; 1119 s.reset(getGlobalScriptEngine()->newScope()); 1120 1121 BSONObj b; 1122 { 1123 char crap[5]; 1124 1125 crap[0] = (char)128; 1126 crap[1] = 17; 1127 crap[2] = (char)128; 1128 crap[3] = 17; 1129 crap[4] = 0; 1130 1131 BSONObjBuilder bb; 1132 bb.append("x", crap); 1133 b = bb.obj(); 1134 } 1135 1136 // cout << "ELIOT: " << b.jsonString() << endl; 1137 // its ok if this is handled by js, just can't create a c++ exception 1138 s->invoke("x=this.x.length;", 0, &b); 1139 } 1140 }; 1141 1142 class CodeTests { 1143 public: run()1144 void run() { 1145 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 1146 1147 { 1148 BSONObjBuilder b; 1149 b.append("a", 1); 1150 b.appendCode("b", "function(){ out.b = 11; }"); 1151 b.appendCodeWScope("c", "function(){ out.c = 12; }", BSONObj()); 1152 b.appendCodeWScope("d", "function(){ out.d = 13 + bleh; }", BSON("bleh" << 5)); 1153 s->setObject("foo", b.obj()); 1154 } 1155 1156 s->invokeSafe("out = {}; out.a = foo.a; foo.b(); foo.c();", 0, 0); 1157 BSONObj out = s->getObject("out"); 1158 1159 ASSERT_EQUALS(1, out["a"].number()); 1160 ASSERT_EQUALS(11, out["b"].number()); 1161 ASSERT_EQUALS(12, out["c"].number()); 1162 1163 // Guess we don't care about this 1164 // s->invokeSafe( "foo.d() " , BSONObj() ); 1165 // out = s->getObject( "out" ); 1166 // ASSERT_EQUALS( 18 , out["d"].number() ); 1167 } 1168 }; 1169 1170 namespace RoundTripTests { 1171 1172 // Inherit from this class to test round tripping of JSON objects 1173 class TestRoundTrip { 1174 public: ~TestRoundTrip()1175 virtual ~TestRoundTrip() {} run()1176 void run() { 1177 // Insert in Javascript -> Find using DBDirectClient 1178 1179 // Drop the collection 1180 const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); 1181 OperationContext& opCtx = *opCtxPtr; 1182 DBDirectClient client(&opCtx); 1183 1184 client.dropCollection("unittest.testroundtrip"); 1185 1186 // Insert in Javascript 1187 stringstream jsInsert; 1188 jsInsert << "db.testroundtrip.insert(" << jsonIn() << ")"; 1189 ASSERT_TRUE(client.eval("unittest", jsInsert.str())); 1190 1191 // Find using DBDirectClient 1192 BSONObj excludeIdProjection = BSON("_id" << 0); 1193 BSONObj directFind = client.findOne("unittest.testroundtrip", "", &excludeIdProjection); 1194 bsonEquals(bson(), directFind); 1195 1196 1197 // Insert using DBDirectClient -> Find in Javascript 1198 1199 // Drop the collection 1200 client.dropCollection("unittest.testroundtrip"); 1201 1202 // Insert using DBDirectClient 1203 client.insert("unittest.testroundtrip", bson()); 1204 1205 // Find in Javascript 1206 stringstream jsFind; 1207 jsFind << "dbref = db.testroundtrip.findOne( { } , { _id : 0 } )\n" 1208 << "assert.eq(dbref, " << jsonOut() << ")"; 1209 ASSERT_TRUE(client.eval("unittest", jsFind.str())); 1210 } 1211 1212 protected: 1213 // Methods that must be defined by child classes 1214 virtual BSONObj bson() const = 0; 1215 virtual string json() const = 0; 1216 1217 // This can be overriden if a different meaning of equality besides woCompare is needed bsonEquals(const BSONObj & expected,const BSONObj & actual)1218 virtual void bsonEquals(const BSONObj& expected, const BSONObj& actual) { 1219 if (expected.woCompare(actual)) { 1220 ::mongo::log() << "want:" << expected.jsonString() << " size: " << expected.objsize() 1221 << endl; 1222 ::mongo::log() << "got :" << actual.jsonString() << " size: " << actual.objsize() 1223 << endl; 1224 ::mongo::log() << expected.hexDump() << endl; 1225 ::mongo::log() << actual.hexDump() << endl; 1226 } 1227 ASSERT(!expected.woCompare(actual)); 1228 } 1229 1230 // This can be overriden if the JSON representation is altered on the round trip jsonIn() const1231 virtual string jsonIn() const { 1232 return json(); 1233 } jsonOut() const1234 virtual string jsonOut() const { 1235 return json(); 1236 } 1237 }; 1238 1239 class DBRefTest : public TestRoundTrip { bson() const1240 virtual BSONObj bson() const { 1241 BSONObjBuilder b; 1242 OID o; 1243 memset(&o, 0, 12); 1244 BSONObjBuilder subBuilder(b.subobjStart("a")); 1245 subBuilder.append("$ref", "ns"); 1246 subBuilder.append("$id", o); 1247 subBuilder.done(); 1248 return b.obj(); 1249 } json() const1250 virtual string json() const { 1251 return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; 1252 } 1253 1254 // A "fetch" function is added to the DBRef object when it is inserted using the 1255 // constructor, so we need to compare the fields individually bsonEquals(const BSONObj & expected,const BSONObj & actual)1256 virtual void bsonEquals(const BSONObj& expected, const BSONObj& actual) { 1257 ASSERT_EQUALS(expected["a"].type(), actual["a"].type()); 1258 ASSERT_EQUALS(expected["a"]["$id"].OID(), actual["a"]["$id"].OID()); 1259 ASSERT_EQUALS(expected["a"]["$ref"].String(), actual["a"]["$ref"].String()); 1260 } 1261 }; 1262 1263 class DBPointerTest : public TestRoundTrip { bson() const1264 virtual BSONObj bson() const { 1265 BSONObjBuilder b; 1266 OID o; 1267 memset(&o, 0, 12); 1268 b.appendDBRef("a", "ns", o); 1269 return b.obj(); 1270 } json() const1271 virtual string json() const { 1272 return "{ \"a\" : DBPointer( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; 1273 } 1274 }; 1275 1276 class InformalDBRefTest : public TestRoundTrip { bson() const1277 virtual BSONObj bson() const { 1278 BSONObjBuilder b; 1279 BSONObjBuilder subBuilder(b.subobjStart("a")); 1280 subBuilder.append("$ref", "ns"); 1281 subBuilder.append("$id", "000000000000000000000000"); 1282 subBuilder.done(); 1283 return b.obj(); 1284 } 1285 1286 // Don't need to return anything because we are overriding both jsonOut and jsonIn json() const1287 virtual string json() const { 1288 return ""; 1289 } 1290 1291 // Need to override these because the JSON doesn't actually round trip. 1292 // An object with "$ref" and "$id" fields is handled specially and different on the way out. jsonOut() const1293 virtual string jsonOut() const { 1294 return "{ \"a\" : DBRef( \"ns\", \"000000000000000000000000\" ) }"; 1295 } jsonIn() const1296 virtual string jsonIn() const { 1297 stringstream ss; 1298 ss << "{ \"a\" : { \"$ref\" : \"ns\" , " 1299 << "\"$id\" : \"000000000000000000000000\" } }"; 1300 return ss.str(); 1301 } 1302 }; 1303 1304 class InformalDBRefOIDTest : public TestRoundTrip { bson() const1305 virtual BSONObj bson() const { 1306 BSONObjBuilder b; 1307 OID o; 1308 memset(&o, 0, 12); 1309 BSONObjBuilder subBuilder(b.subobjStart("a")); 1310 subBuilder.append("$ref", "ns"); 1311 subBuilder.append("$id", o); 1312 subBuilder.done(); 1313 return b.obj(); 1314 } 1315 1316 // Don't need to return anything because we are overriding both jsonOut and jsonIn json() const1317 virtual string json() const { 1318 return ""; 1319 } 1320 1321 // Need to override these because the JSON doesn't actually round trip. 1322 // An object with "$ref" and "$id" fields is handled specially and different on the way out. jsonOut() const1323 virtual string jsonOut() const { 1324 return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; 1325 } jsonIn() const1326 virtual string jsonIn() const { 1327 stringstream ss; 1328 ss << "{ \"a\" : { \"$ref\" : \"ns\" , " 1329 << "\"$id\" : ObjectId( \"000000000000000000000000\" ) } }"; 1330 return ss.str(); 1331 } 1332 }; 1333 1334 class InformalDBRefExtraFieldTest : public TestRoundTrip { bson() const1335 virtual BSONObj bson() const { 1336 BSONObjBuilder b; 1337 OID o; 1338 memset(&o, 0, 12); 1339 BSONObjBuilder subBuilder(b.subobjStart("a")); 1340 subBuilder.append("$ref", "ns"); 1341 subBuilder.append("$id", o); 1342 subBuilder.append("otherfield", "value"); 1343 subBuilder.done(); 1344 return b.obj(); 1345 } 1346 1347 // Don't need to return anything because we are overriding both jsonOut and jsonIn json() const1348 virtual string json() const { 1349 return ""; 1350 } 1351 1352 // Need to override these because the JSON doesn't actually round trip. 1353 // An object with "$ref" and "$id" fields is handled specially and different on the way out. jsonOut() const1354 virtual string jsonOut() const { 1355 return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }"; 1356 } jsonIn() const1357 virtual string jsonIn() const { 1358 stringstream ss; 1359 ss << "{ \"a\" : { \"$ref\" : \"ns\" , " 1360 << "\"$id\" : ObjectId( \"000000000000000000000000\" ) , " 1361 << "\"otherfield\" : \"value\" } }"; 1362 return ss.str(); 1363 } 1364 }; 1365 1366 class Empty : public TestRoundTrip { bson() const1367 virtual BSONObj bson() const { 1368 BSONObjBuilder b; 1369 return b.obj(); 1370 } json() const1371 virtual string json() const { 1372 return "{}"; 1373 } 1374 }; 1375 1376 class EmptyWithSpace : public TestRoundTrip { bson() const1377 virtual BSONObj bson() const { 1378 BSONObjBuilder b; 1379 return b.obj(); 1380 } json() const1381 virtual string json() const { 1382 return "{ }"; 1383 } 1384 }; 1385 1386 class SingleString : public TestRoundTrip { bson() const1387 virtual BSONObj bson() const { 1388 BSONObjBuilder b; 1389 b.append("a", "b"); 1390 return b.obj(); 1391 } json() const1392 virtual string json() const { 1393 return "{ \"a\" : \"b\" }"; 1394 } 1395 }; 1396 1397 class EmptyStrings : public TestRoundTrip { bson() const1398 virtual BSONObj bson() const { 1399 BSONObjBuilder b; 1400 b.append("", ""); 1401 return b.obj(); 1402 } json() const1403 virtual string json() const { 1404 return "{ \"\" : \"\" }"; 1405 } 1406 }; 1407 1408 class SingleNumber : public TestRoundTrip { bson() const1409 virtual BSONObj bson() const { 1410 BSONObjBuilder b; 1411 b.append("a", 1); 1412 return b.obj(); 1413 } json() const1414 virtual string json() const { 1415 return "{ \"a\" : 1 }"; 1416 } 1417 }; 1418 1419 class RealNumber : public TestRoundTrip { bson() const1420 virtual BSONObj bson() const { 1421 BSONObjBuilder b; 1422 double d; 1423 ASSERT_OK(parseNumberFromString("0.7", &d)); 1424 b.append("a", d); 1425 return b.obj(); 1426 } json() const1427 virtual string json() const { 1428 return "{ \"a\" : 0.7 }"; 1429 } 1430 }; 1431 1432 class FancyNumber : public TestRoundTrip { bson() const1433 virtual BSONObj bson() const { 1434 BSONObjBuilder b; 1435 double d; 1436 ASSERT_OK(parseNumberFromString("-4.4433e-2", &d)); 1437 b.append("a", d); 1438 return b.obj(); 1439 } json() const1440 virtual string json() const { 1441 return "{ \"a\" : -4.4433e-2 }"; 1442 } 1443 }; 1444 1445 class TwoElements : public TestRoundTrip { bson() const1446 virtual BSONObj bson() const { 1447 BSONObjBuilder b; 1448 b.append("a", 1); 1449 b.append("b", "foo"); 1450 return b.obj(); 1451 } json() const1452 virtual string json() const { 1453 return "{ \"a\" : 1, \"b\" : \"foo\" }"; 1454 } 1455 }; 1456 1457 class Subobject : public TestRoundTrip { bson() const1458 virtual BSONObj bson() const { 1459 BSONObjBuilder b; 1460 b.append("a", 1); 1461 BSONObjBuilder c; 1462 c.append("z", b.done()); 1463 return c.obj(); 1464 } json() const1465 virtual string json() const { 1466 return "{ \"z\" : { \"a\" : 1 } }"; 1467 } 1468 }; 1469 1470 class DeeplyNestedObject : public TestRoundTrip { buildJson(int depth) const1471 virtual string buildJson(int depth) const { 1472 if (depth == 0) { 1473 return "{\"0\":true}"; 1474 } else { 1475 std::stringstream ss; 1476 ss << "{\"" << depth << "\":" << buildJson(depth - 1) << "}"; 1477 depth--; 1478 return ss.str(); 1479 } 1480 } buildBson(int depth) const1481 virtual BSONObj buildBson(int depth) const { 1482 BSONObjBuilder builder; 1483 if (depth == 0) { 1484 builder.append("0", true); 1485 return builder.obj(); 1486 } else { 1487 std::stringstream ss; 1488 ss << depth; 1489 depth--; 1490 builder.append(ss.str(), buildBson(depth)); 1491 return builder.obj(); 1492 } 1493 } bson() const1494 virtual BSONObj bson() const { 1495 return buildBson(35); 1496 } json() const1497 virtual string json() const { 1498 return buildJson(35); 1499 } 1500 }; 1501 1502 class ArrayEmpty : public TestRoundTrip { bson() const1503 virtual BSONObj bson() const { 1504 vector<int> arr; 1505 BSONObjBuilder b; 1506 b.append("a", arr); 1507 return b.obj(); 1508 } json() const1509 virtual string json() const { 1510 return "{ \"a\" : [] }"; 1511 } 1512 }; 1513 1514 class Array : public TestRoundTrip { bson() const1515 virtual BSONObj bson() const { 1516 vector<int> arr; 1517 arr.push_back(1); 1518 arr.push_back(2); 1519 arr.push_back(3); 1520 BSONObjBuilder b; 1521 b.append("a", arr); 1522 return b.obj(); 1523 } json() const1524 virtual string json() const { 1525 return "{ \"a\" : [ 1, 2, 3 ] }"; 1526 } 1527 }; 1528 1529 class True : public TestRoundTrip { bson() const1530 virtual BSONObj bson() const { 1531 BSONObjBuilder b; 1532 b.appendBool("a", true); 1533 return b.obj(); 1534 } json() const1535 virtual string json() const { 1536 return "{ \"a\" : true }"; 1537 } 1538 }; 1539 1540 class False : public TestRoundTrip { bson() const1541 virtual BSONObj bson() const { 1542 BSONObjBuilder b; 1543 b.appendBool("a", false); 1544 return b.obj(); 1545 } json() const1546 virtual string json() const { 1547 return "{ \"a\" : false }"; 1548 } 1549 }; 1550 1551 class Null : public TestRoundTrip { bson() const1552 virtual BSONObj bson() const { 1553 BSONObjBuilder b; 1554 b.appendNull("a"); 1555 return b.obj(); 1556 } json() const1557 virtual string json() const { 1558 return "{ \"a\" : null }"; 1559 } 1560 }; 1561 1562 class Undefined : public TestRoundTrip { bson() const1563 virtual BSONObj bson() const { 1564 BSONObjBuilder b; 1565 b.appendUndefined("a"); 1566 return b.obj(); 1567 } 1568 json() const1569 virtual string json() const { 1570 return "{ \"a\" : undefined }"; 1571 } 1572 }; 1573 1574 class EscapedCharacters : public TestRoundTrip { bson() const1575 virtual BSONObj bson() const { 1576 BSONObjBuilder b; 1577 b.append("a", "\" \\ / \b \f \n \r \t \v"); 1578 return b.obj(); 1579 } json() const1580 virtual string json() const { 1581 return "{ \"a\" : \"\\\" \\\\ \\/ \\b \\f \\n \\r \\t \\v\" }"; 1582 } 1583 }; 1584 1585 class NonEscapedCharacters : public TestRoundTrip { bson() const1586 virtual BSONObj bson() const { 1587 BSONObjBuilder b; 1588 b.append("a", "% { a z $ # ' "); 1589 return b.obj(); 1590 } json() const1591 virtual string json() const { 1592 return "{ \"a\" : \"\\% \\{ \\a \\z \\$ \\# \\' \\ \" }"; 1593 } 1594 }; 1595 1596 class AllowedControlCharacter : public TestRoundTrip { bson() const1597 virtual BSONObj bson() const { 1598 BSONObjBuilder b; 1599 b.append("a", "\x7f"); 1600 return b.obj(); 1601 } json() const1602 virtual string json() const { 1603 return "{ \"a\" : \"\x7f\" }"; 1604 } 1605 }; 1606 1607 class NumbersInFieldName : public TestRoundTrip { bson() const1608 virtual BSONObj bson() const { 1609 BSONObjBuilder b; 1610 b.append("b1", "b"); 1611 return b.obj(); 1612 } json() const1613 virtual string json() const { 1614 return "{ b1 : \"b\" }"; 1615 } 1616 }; 1617 1618 class EscapeFieldName : public TestRoundTrip { bson() const1619 virtual BSONObj bson() const { 1620 BSONObjBuilder b; 1621 b.append("\n", "b"); 1622 return b.obj(); 1623 } json() const1624 virtual string json() const { 1625 return "{ \"\\n\" : \"b\" }"; 1626 } 1627 }; 1628 1629 class EscapedUnicodeToUtf8 : public TestRoundTrip { bson() const1630 virtual BSONObj bson() const { 1631 BSONObjBuilder b; 1632 unsigned char u[7]; 1633 u[0] = 0xe0 | 0x0a; 1634 u[1] = 0x80; 1635 u[2] = 0x80; 1636 u[3] = 0xe0 | 0x0a; 1637 u[4] = 0x80; 1638 u[5] = 0x80; 1639 u[6] = 0; 1640 b.append("a", (char*)u); 1641 BSONObj built = b.obj(); 1642 ASSERT_EQUALS(string((char*)u), built.firstElement().valuestr()); 1643 return built; 1644 } json() const1645 virtual string json() const { 1646 return "{ \"a\" : \"\\ua000\\uA000\" }"; 1647 } 1648 }; 1649 1650 class Utf8AllOnes : public TestRoundTrip { bson() const1651 virtual BSONObj bson() const { 1652 BSONObjBuilder b; 1653 unsigned char u[8]; 1654 u[0] = 0x01; 1655 1656 u[1] = 0x7f; 1657 1658 u[2] = 0xdf; 1659 u[3] = 0xbf; 1660 1661 u[4] = 0xef; 1662 u[5] = 0xbf; 1663 u[6] = 0xbf; 1664 1665 u[7] = 0; 1666 1667 b.append("a", (char*)u); 1668 return b.obj(); 1669 } json() const1670 virtual string json() const { 1671 return "{ \"a\" : \"\\u0001\\u007f\\u07ff\\uffff\" }"; 1672 } 1673 }; 1674 1675 class Utf8FirstByteOnes : public TestRoundTrip { bson() const1676 virtual BSONObj bson() const { 1677 BSONObjBuilder b; 1678 unsigned char u[6]; 1679 u[0] = 0xdc; 1680 u[1] = 0x80; 1681 1682 u[2] = 0xef; 1683 u[3] = 0xbc; 1684 u[4] = 0x80; 1685 1686 u[5] = 0; 1687 1688 b.append("a", (char*)u); 1689 return b.obj(); 1690 } json() const1691 virtual string json() const { 1692 return "{ \"a\" : \"\\u0700\\uff00\" }"; 1693 } 1694 }; 1695 1696 class BinData : public TestRoundTrip { bson() const1697 virtual BSONObj bson() const { 1698 char z[3]; 1699 z[0] = 'a'; 1700 z[1] = 'b'; 1701 z[2] = 'c'; 1702 BSONObjBuilder b; 1703 b.appendBinData("a", 3, BinDataGeneral, z); 1704 return b.obj(); 1705 } json() const1706 virtual string json() const { 1707 return "{ \"a\" : BinData( 0 , \"YWJj\" ) }"; 1708 } 1709 }; 1710 1711 class BinDataPaddedSingle : public TestRoundTrip { bson() const1712 virtual BSONObj bson() const { 1713 char z[2]; 1714 z[0] = 'a'; 1715 z[1] = 'b'; 1716 BSONObjBuilder b; 1717 b.appendBinData("a", 2, BinDataGeneral, z); 1718 return b.obj(); 1719 } json() const1720 virtual string json() const { 1721 return "{ \"a\" : BinData( 0 , \"YWI=\" ) }"; 1722 } 1723 }; 1724 1725 class BinDataPaddedDouble : public TestRoundTrip { bson() const1726 virtual BSONObj bson() const { 1727 char z[1]; 1728 z[0] = 'a'; 1729 BSONObjBuilder b; 1730 b.appendBinData("a", 1, BinDataGeneral, z); 1731 return b.obj(); 1732 } json() const1733 virtual string json() const { 1734 return "{ \"a\" : BinData( 0 , \"YQ==\" ) }"; 1735 } 1736 }; 1737 1738 class BinDataAllChars : public TestRoundTrip { bson() const1739 virtual BSONObj bson() const { 1740 unsigned char z[] = {0x00, 0x10, 0x83, 0x10, 0x51, 0x87, 0x20, 0x92, 0x8B, 0x30, 1741 0xD3, 0x8F, 0x41, 0x14, 0x93, 0x51, 0x55, 0x97, 0x61, 0x96, 1742 0x9B, 0x71, 0xD7, 0x9F, 0x82, 0x18, 0xA3, 0x92, 0x59, 0xA7, 1743 0xA2, 0x9A, 0xAB, 0xB2, 0xDB, 0xAF, 0xC3, 0x1C, 0xB3, 0xD3, 1744 0x5D, 0xB7, 0xE3, 0x9E, 0xBB, 0xF3, 0xDF, 0xBF}; 1745 BSONObjBuilder b; 1746 b.appendBinData("a", 48, BinDataGeneral, z); 1747 return b.obj(); 1748 } json() const1749 virtual string json() const { 1750 stringstream ss; 1751 ss << "{ \"a\" : BinData( 0 , \"ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1752 << "abcdefghijklmnopqrstuvwxyz0123456789+/\" ) }"; 1753 return ss.str(); 1754 } 1755 }; 1756 1757 class Date : public TestRoundTrip { bson() const1758 virtual BSONObj bson() const { 1759 BSONObjBuilder b; 1760 b.appendDate("a", Date_t()); 1761 return b.obj(); 1762 } json() const1763 virtual string json() const { 1764 return "{ \"a\" : new Date( 0 ) }"; 1765 } 1766 }; 1767 1768 class DateNonzero : public TestRoundTrip { bson() const1769 virtual BSONObj bson() const { 1770 BSONObjBuilder b; 1771 b.appendDate("a", Date_t::fromMillisSinceEpoch(100)); 1772 return b.obj(); 1773 } json() const1774 virtual string json() const { 1775 return "{ \"a\" : new Date( 100 ) }"; 1776 } 1777 }; 1778 1779 class DateNegative : public TestRoundTrip { bson() const1780 virtual BSONObj bson() const { 1781 BSONObjBuilder b; 1782 b.appendDate("a", Date_t::fromMillisSinceEpoch(-1)); 1783 return b.obj(); 1784 } json() const1785 virtual string json() const { 1786 return "{ \"a\" : new Date( -1 ) }"; 1787 } 1788 }; 1789 1790 class JSTimestamp : public TestRoundTrip { bson() const1791 virtual BSONObj bson() const { 1792 BSONObjBuilder b; 1793 b.append("a", Timestamp(20, 5)); 1794 return b.obj(); 1795 } json() const1796 virtual string json() const { 1797 return "{ \"a\" : Timestamp( 20, 5 ) }"; 1798 } 1799 }; 1800 1801 class TimestampMax : public TestRoundTrip { bson() const1802 virtual BSONObj bson() const { 1803 BSONObjBuilder b; 1804 b.appendMaxForType("a", mongo::bsonTimestamp); 1805 BSONObj o = b.obj(); 1806 return o; 1807 } json() const1808 virtual string json() const { 1809 Timestamp opTime = Timestamp::max(); 1810 stringstream ss; 1811 ss << "{ \"a\" : Timestamp( " << opTime.getSecs() << ", " << opTime.getInc() << " ) }"; 1812 return ss.str(); 1813 } 1814 }; 1815 1816 class Regex : public TestRoundTrip { bson() const1817 virtual BSONObj bson() const { 1818 BSONObjBuilder b; 1819 b.appendRegex("a", "b", ""); 1820 return b.obj(); 1821 } json() const1822 virtual string json() const { 1823 return "{ \"a\" : /b/ }"; 1824 } 1825 }; 1826 1827 class RegexWithQuotes : public TestRoundTrip { bson() const1828 virtual BSONObj bson() const { 1829 BSONObjBuilder b; 1830 b.appendRegex("a", "\"", ""); 1831 return b.obj(); 1832 } json() const1833 virtual string json() const { 1834 return "{ \"a\" : /\"/ }"; 1835 } 1836 }; 1837 1838 class UnquotedFieldName : public TestRoundTrip { bson() const1839 virtual BSONObj bson() const { 1840 BSONObjBuilder b; 1841 b.append("a_b", 1); 1842 return b.obj(); 1843 } json() const1844 virtual string json() const { 1845 return "{ a_b : 1 }"; 1846 } 1847 }; 1848 1849 class SingleQuotes : public TestRoundTrip { bson() const1850 virtual BSONObj bson() const { 1851 BSONObjBuilder b; 1852 b.append("ab'c\"", "bb\b '\""); 1853 return b.obj(); 1854 } json() const1855 virtual string json() const { 1856 return "{ 'ab\\'c\"' : 'bb\\b \\'\"' }"; 1857 } 1858 }; 1859 1860 class ObjectId : public TestRoundTrip { bson() const1861 virtual BSONObj bson() const { 1862 OID id; 1863 id.init("deadbeeff00ddeadbeeff00d"); 1864 BSONObjBuilder b; 1865 b.appendOID("foo", &id); 1866 return b.obj(); 1867 } json() const1868 virtual string json() const { 1869 return "{ \"foo\": ObjectId( \"deadbeeff00ddeadbeeff00d\" ) }"; 1870 } 1871 }; 1872 1873 class NumberLong : public TestRoundTrip { 1874 public: bson() const1875 virtual BSONObj bson() const { 1876 return BSON("long" << 4611686018427387904ll); // 2**62 1877 } json() const1878 virtual string json() const { 1879 return "{ \"long\": NumberLong(4611686018427387904) }"; 1880 } 1881 }; 1882 1883 class NumberInt : public TestRoundTrip { 1884 public: bson() const1885 virtual BSONObj bson() const { 1886 return BSON("int" << static_cast<int>(100)); 1887 } json() const1888 virtual string json() const { 1889 return "{ \"int\": NumberInt(100) }"; 1890 } 1891 }; 1892 1893 class Number : public TestRoundTrip { 1894 public: bson() const1895 virtual BSONObj bson() const { 1896 return BSON("double" << 3.14); 1897 } json() const1898 virtual string json() const { 1899 return "{ \"double\": Number(3.14) }"; 1900 } 1901 }; 1902 1903 class NumberDecimal : public TestRoundTrip { 1904 public: bson() const1905 virtual BSONObj bson() const { 1906 return BSON("decimal" << Decimal128("2.010")); 1907 } json() const1908 virtual string json() const { 1909 return "{ \"decimal\": NumberDecimal(\"+2.010\") }"; 1910 } 1911 }; 1912 1913 class NumberDecimalNegative : public TestRoundTrip { 1914 public: bson() const1915 virtual BSONObj bson() const { 1916 return BSON("decimal" << Decimal128("-4.018")); 1917 } json() const1918 virtual string json() const { 1919 return "{ \"decimal\": NumberDecimal(\"-4018E-3\") }"; 1920 } 1921 }; 1922 1923 class NumberDecimalMax : public TestRoundTrip { 1924 public: bson() const1925 virtual BSONObj bson() const { 1926 return BSON("decimal" << Decimal128("+9.999999999999999999999999999999999E6144")); 1927 } json() const1928 virtual string json() const { 1929 return "{ \"decimal\": NumberDecimal(\"+9999999999999999999999999999999999E6111\") }"; 1930 } 1931 }; 1932 1933 class NumberDecimalMin : public TestRoundTrip { 1934 public: bson() const1935 virtual BSONObj bson() const { 1936 return BSON("decimal" << Decimal128("0.000000000000000000000000000000001E-6143")); 1937 } json() const1938 virtual string json() const { 1939 return "{ \"decimal\": NumberDecimal(\"+1E-6176\") }"; 1940 } 1941 }; 1942 1943 class NumberDecimalPositiveZero : public TestRoundTrip { 1944 public: bson() const1945 virtual BSONObj bson() const { 1946 return BSON("decimal" << Decimal128("0")); 1947 } json() const1948 virtual string json() const { 1949 return "{ \"decimal\": NumberDecimal(\"0\") }"; 1950 } 1951 }; 1952 1953 class NumberDecimalNegativeZero : public TestRoundTrip { 1954 public: bson() const1955 virtual BSONObj bson() const { 1956 return BSON("decimal" << Decimal128("-0")); 1957 } json() const1958 virtual string json() const { 1959 return "{ \"decimal\": NumberDecimal(\"-0\") }"; 1960 } 1961 }; 1962 1963 class NumberDecimalPositiveNaN : public TestRoundTrip { 1964 public: bson() const1965 virtual BSONObj bson() const { 1966 return BSON("decimal" << Decimal128("NaN")); 1967 } json() const1968 virtual string json() const { 1969 return "{ \"decimal\": NumberDecimal(\"NaN\") }"; 1970 } 1971 }; 1972 1973 class NumberDecimalNegativeNaN : public TestRoundTrip { 1974 public: bson() const1975 virtual BSONObj bson() const { 1976 return BSON("decimal" << Decimal128("-NaN")); 1977 } json() const1978 virtual string json() const { 1979 return "{ \"decimal\": NumberDecimal(\"-NaN\") }"; 1980 } 1981 }; 1982 1983 class NumberDecimalPositiveInfinity : public TestRoundTrip { 1984 public: bson() const1985 virtual BSONObj bson() const { 1986 return BSON("decimal" << Decimal128("1E999999")); 1987 } json() const1988 virtual string json() const { 1989 return "{ \"decimal\": NumberDecimal(\"+Inf\") }"; 1990 } 1991 }; 1992 1993 class NumberDecimalNegativeInfinity : public TestRoundTrip { 1994 public: bson() const1995 virtual BSONObj bson() const { 1996 return BSON("decimal" << Decimal128("-1E999999")); 1997 } json() const1998 virtual string json() const { 1999 return "{ \"decimal\": NumberDecimal(\"-Inf\") }"; 2000 } 2001 }; 2002 2003 class NumberDecimalPrecision : public TestRoundTrip { 2004 public: bson() const2005 virtual BSONObj bson() const { 2006 return BSON("decimal" << Decimal128("5.00")); 2007 } json() const2008 virtual string json() const { 2009 return "{ \"decimal\": NumberDecimal(\"+500E-2\") }"; 2010 } 2011 }; 2012 2013 class UUID : public TestRoundTrip { bson() const2014 virtual BSONObj bson() const { 2015 BSONObjBuilder b; 2016 unsigned char z[] = {0xAB, 2017 0xCD, 2018 0xEF, 2019 0xAB, 2020 0xCD, 2021 0xEF, 2022 0xAB, 2023 0xCD, 2024 0xEF, 2025 0xAB, 2026 0xCD, 2027 0xEF, 2028 0x00, 2029 0x00, 2030 0x00, 2031 0x00}; 2032 b.appendBinData("a", 16, bdtUUID, z); 2033 return b.obj(); 2034 } 2035 2036 // Don't need to return anything because we are overriding both jsonOut and jsonIn json() const2037 virtual string json() const { 2038 return ""; 2039 } 2040 2041 // The UUID constructor corresponds to a special BinData type jsonIn() const2042 virtual string jsonIn() const { 2043 return "{ \"a\" : UUID(\"abcdefabcdefabcdefabcdef00000000\") }"; 2044 } jsonOut() const2045 virtual string jsonOut() const { 2046 return "{ \"a\" : BinData(3,\"q83vq83vq83vq83vAAAAAA==\") }"; 2047 } 2048 }; 2049 2050 class HexData : public TestRoundTrip { bson() const2051 virtual BSONObj bson() const { 2052 BSONObjBuilder b; 2053 unsigned char z[] = {0xAB, 2054 0xCD, 2055 0xEF, 2056 0xAB, 2057 0xCD, 2058 0xEF, 2059 0xAB, 2060 0xCD, 2061 0xEF, 2062 0xAB, 2063 0xCD, 2064 0xEF, 2065 0x00, 2066 0x00, 2067 0x00, 2068 0x00}; 2069 b.appendBinData("a", 16, BinDataGeneral, z); 2070 return b.obj(); 2071 } 2072 2073 // Don't need to return anything because we are overriding both jsonOut and jsonIn json() const2074 virtual string json() const { 2075 return ""; 2076 } 2077 2078 // The HexData constructor creates a BinData type from a hex string jsonIn() const2079 virtual string jsonIn() const { 2080 return "{ \"a\" : HexData(0,\"abcdefabcdefabcdefabcdef00000000\") }"; 2081 } jsonOut() const2082 virtual string jsonOut() const { 2083 return "{ \"a\" : BinData(0,\"q83vq83vq83vq83vAAAAAA==\") }"; 2084 } 2085 }; 2086 2087 class MD5 : public TestRoundTrip { bson() const2088 virtual BSONObj bson() const { 2089 BSONObjBuilder b; 2090 unsigned char z[] = {0xAB, 2091 0xCD, 2092 0xEF, 2093 0xAB, 2094 0xCD, 2095 0xEF, 2096 0xAB, 2097 0xCD, 2098 0xEF, 2099 0xAB, 2100 0xCD, 2101 0xEF, 2102 0x00, 2103 0x00, 2104 0x00, 2105 0x00}; 2106 b.appendBinData("a", 16, MD5Type, z); 2107 return b.obj(); 2108 } 2109 2110 // Don't need to return anything because we are overriding both jsonOut and jsonIn json() const2111 virtual string json() const { 2112 return ""; 2113 } 2114 2115 // The HexData constructor creates a BinData type from a hex string jsonIn() const2116 virtual string jsonIn() const { 2117 return "{ \"a\" : MD5(\"abcdefabcdefabcdefabcdef00000000\") }"; 2118 } jsonOut() const2119 virtual string jsonOut() const { 2120 return "{ \"a\" : BinData(5,\"q83vq83vq83vq83vAAAAAA==\") }"; 2121 } 2122 }; 2123 2124 class NullString : public TestRoundTrip { bson() const2125 virtual BSONObj bson() const { 2126 BSONObjBuilder b; 2127 b.append("x", "a\0b", 4); 2128 return b.obj(); 2129 } json() const2130 virtual string json() const { 2131 return "{ \"x\" : \"a\\u0000b\" }"; 2132 } 2133 }; 2134 2135 } // namespace RoundTripTests 2136 2137 class BinDataType { 2138 public: pp(const char * s,BSONElement e)2139 void pp(const char* s, BSONElement e) { 2140 int len; 2141 const char* data = e.binData(len); 2142 cout << s << ":" << e.binDataType() << "\t" << len << endl; 2143 cout << "\t"; 2144 for (int i = 0; i < len; i++) 2145 cout << (int)(data[i]) << " "; 2146 cout << endl; 2147 } 2148 run()2149 void run() { 2150 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 2151 2152 const char* foo = "asdas\0asdasd"; 2153 const char* base64 = "YXNkYXMAYXNkYXNk"; 2154 2155 BSONObj in; 2156 { 2157 BSONObjBuilder b; 2158 b.append("a", 7); 2159 b.appendBinData("b", 12, BinDataGeneral, foo); 2160 in = b.obj(); 2161 s->setObject("x", in); 2162 } 2163 2164 s->invokeSafe("myb = x.b; print( myb ); printjson( myb );", 0, 0); 2165 s->invokeSafe("y = { c : myb };", 0, 0); 2166 2167 BSONObj out = s->getObject("y"); 2168 ASSERT_EQUALS(BinData, out["c"].type()); 2169 // pp( "in " , in["b"] ); 2170 // pp( "out" , out["c"] ); 2171 ASSERT_EQUALS(0, in["b"].woCompare(out["c"], false)); 2172 2173 // check that BinData js class is utilized 2174 s->invokeSafe("q = x.b.toString();", 0, 0); 2175 stringstream expected; 2176 expected << "BinData(" << BinDataGeneral << ",\"" << base64 << "\")"; 2177 ASSERT_EQUALS(expected.str(), s->getString("q")); 2178 2179 stringstream scriptBuilder; 2180 scriptBuilder << "z = { c : new BinData( " << BinDataGeneral << ", \"" << base64 2181 << "\" ) };"; 2182 string script = scriptBuilder.str(); 2183 s->invokeSafe(script.c_str(), 0, 0); 2184 out = s->getObject("z"); 2185 // pp( "out" , out["c"] ); 2186 ASSERT_EQUALS(0, in["b"].woCompare(out["c"], false)); 2187 2188 s->invokeSafe("a = { f: new BinData( 128, \"\" ) };", 0, 0); 2189 out = s->getObject("a"); 2190 int len = -1; 2191 out["f"].binData(len); 2192 ASSERT_EQUALS(0, len); 2193 ASSERT_EQUALS(128, out["f"].binDataType()); 2194 } 2195 }; 2196 2197 class VarTests { 2198 public: run()2199 void run() { 2200 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 2201 2202 ASSERT(s->exec("a = 5;", "a", false, true, false)); 2203 ASSERT_EQUALS(5, s->getNumber("a")); 2204 2205 ASSERT(s->exec("var b = 6;", "b", false, true, false)); 2206 ASSERT_EQUALS(6, s->getNumber("b")); 2207 } 2208 }; 2209 2210 class Speed1 { 2211 public: run()2212 void run() { 2213 BSONObj start = BSON("x" << 5.0); 2214 BSONObj empty; 2215 2216 unique_ptr<Scope> s; 2217 s.reset(getGlobalScriptEngine()->newScope()); 2218 2219 ScriptingFunction f = s->createFunction("return this.x + 6;"); 2220 2221 Timer t; 2222 double n = 0; 2223 for (; n < 10000; n++) { 2224 s->invoke(f, &empty, &start); 2225 ASSERT_EQUALS(11, s->getNumber("__returnValue")); 2226 } 2227 // cout << "speed1: " << ( n / t.millis() ) << " ops/ms" << endl; 2228 } 2229 }; 2230 2231 class ScopeOut { 2232 public: run()2233 void run() { 2234 unique_ptr<Scope> s; 2235 s.reset(getGlobalScriptEngine()->newScope()); 2236 2237 s->invokeSafe("x = 5;", 0, 0); 2238 { 2239 BSONObjBuilder b; 2240 s->append(b, "z", "x"); 2241 ASSERT_BSONOBJ_EQ(BSON("z" << 5), b.obj()); 2242 } 2243 2244 s->invokeSafe("x = function(){ return 17; }", 0, 0); 2245 BSONObj temp; 2246 { 2247 BSONObjBuilder b; 2248 s->append(b, "z", "x"); 2249 temp = b.obj(); 2250 } 2251 2252 s->invokeSafe("foo = this.z();", 0, &temp); 2253 ASSERT_EQUALS(17, s->getNumber("foo")); 2254 } 2255 }; 2256 2257 class RenameTest { 2258 public: run()2259 void run() { 2260 unique_ptr<Scope> s; 2261 s.reset(getGlobalScriptEngine()->newScope()); 2262 2263 s->setNumber("x", 5); 2264 ASSERT_EQUALS(5, s->getNumber("x")); 2265 ASSERT_EQUALS(Undefined, s->type("y")); 2266 2267 s->rename("x", "y"); 2268 ASSERT_EQUALS(5, s->getNumber("y")); 2269 ASSERT_EQUALS(Undefined, s->type("x")); 2270 2271 s->rename("y", "x"); 2272 ASSERT_EQUALS(5, s->getNumber("x")); 2273 ASSERT_EQUALS(Undefined, s->type("y")); 2274 } 2275 }; 2276 2277 2278 class InvalidStoredJS { 2279 public: run()2280 void run() { 2281 BSONObjBuilder query; 2282 query.append("_id", "invalidstoredjs1"); 2283 2284 BSONObjBuilder update; 2285 update.append("_id", "invalidstoredjs1"); 2286 update.appendCode("value", 2287 "function () { db.test.find().forEach(function(obj) { continue; }); }"); 2288 2289 const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); 2290 OperationContext& opCtx = *opCtxPtr; 2291 DBDirectClient client(&opCtx); 2292 client.update("test.system.js", query.obj(), update.obj(), true /* upsert */); 2293 2294 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 2295 client.eval("test", "invalidstoredjs1()"); 2296 2297 BSONObj info; 2298 BSONElement ret; 2299 ASSERT(client.eval("test", "return 5 + 12", info, ret)); 2300 ASSERT_EQUALS(17, ret.number()); 2301 } 2302 }; 2303 2304 /** 2305 * This tests a bug discovered in SERVER-24054, where certain interesting nan patterns crash 2306 * spidermonkey by looking like non-double type puns. This verifies that we put that particular 2307 * interesting nan in and that we still get a nan out. 2308 */ 2309 class NovelNaN { 2310 public: run()2311 void run() { 2312 uint8_t bits[] = { 2313 16, 0, 0, 0, 0x01, 'a', '\0', 0x61, 0x79, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 2314 }; 2315 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 2316 2317 s->setObject("val", BSONObj(reinterpret_cast<char*>(bits)).getOwned()); 2318 2319 s->invoke("val[\"a\"];", 0, 0); 2320 ASSERT_TRUE(std::isnan(s->getNumber("__returnValue"))); 2321 } 2322 }; 2323 2324 class NoReturnSpecified { 2325 public: run()2326 void run() { 2327 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 2328 2329 s->invoke("x=5;", 0, 0); 2330 ASSERT_EQUALS(5, s->getNumber("__returnValue")); 2331 2332 s->invoke("x='test'", 0, 0); 2333 ASSERT_EQUALS("test", s->getString("__returnValue")); 2334 2335 s->invoke("x='return'", 0, 0); 2336 ASSERT_EQUALS("return", s->getString("__returnValue")); 2337 2338 s->invoke("return 'return'", 0, 0); 2339 ASSERT_EQUALS("return", s->getString("__returnValue")); 2340 2341 s->invoke("x = ' return '", 0, 0); 2342 ASSERT_EQUALS(" return ", s->getString("__returnValue")); 2343 2344 s->invoke("x = \" return \"", 0, 0); 2345 ASSERT_EQUALS(" return ", s->getString("__returnValue")); 2346 2347 s->invoke("x = \"' return '\"", 0, 0); 2348 ASSERT_EQUALS("' return '", s->getString("__returnValue")); 2349 2350 s->invoke("x = '\" return \"'", 0, 0); 2351 ASSERT_EQUALS("\" return \"", s->getString("__returnValue")); 2352 2353 s->invoke(";return 5", 0, 0); 2354 ASSERT_EQUALS(5, s->getNumber("__returnValue")); 2355 2356 s->invoke("String('return')", 0, 0); 2357 ASSERT_EQUALS("return", s->getString("__returnValue")); 2358 2359 s->invoke("String(' return ')", 0, 0); 2360 ASSERT_EQUALS(" return ", s->getString("__returnValue")); 2361 2362 s->invoke("String(\"'return\")", 0, 0); 2363 ASSERT_EQUALS("'return", s->getString("__returnValue")); 2364 2365 s->invoke("String('\"return')", 0, 0); 2366 ASSERT_EQUALS("\"return", s->getString("__returnValue")); 2367 } 2368 }; 2369 2370 class RecursiveInvoke { 2371 public: callback(const BSONObj & args,void * data)2372 static BSONObj callback(const BSONObj& args, void* data) { 2373 auto scope = static_cast<Scope*>(data); 2374 2375 scope->invoke("x = 10;", 0, 0); 2376 2377 return BSONObj(); 2378 } 2379 run()2380 void run() { 2381 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 2382 2383 s->injectNative("foo", callback, s.get()); 2384 s->invoke("var x = 1; foo();", 0, 0); 2385 ASSERT_EQUALS(s->getNumberInt("x"), 10); 2386 } 2387 }; 2388 2389 class ErrorCodeFromInvoke { 2390 public: run()2391 void run() { 2392 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 2393 2394 { 2395 bool threwException = false; 2396 try { 2397 s->invoke("\"use strict\"; x = 10;", 0, 0); 2398 } catch (...) { 2399 threwException = true; 2400 2401 auto status = exceptionToStatus(); 2402 2403 ASSERT_EQUALS(status.code(), ErrorCodes::JSInterpreterFailure); 2404 } 2405 2406 ASSERT(threwException); 2407 } 2408 2409 { 2410 bool threwException = false; 2411 try { 2412 s->invoke("UUID(1,2,3,4,5);", 0, 0); 2413 } catch (...) { 2414 threwException = true; 2415 2416 auto status = exceptionToStatus(); 2417 2418 ASSERT_EQUALS(status.code(), ErrorCodes::BadValue); 2419 } 2420 2421 ASSERT(threwException); 2422 } 2423 } 2424 }; 2425 2426 class RequiresOwnedObjects { 2427 public: run()2428 void run() { 2429 char buf[] = {5, 0, 0, 0, 0}; 2430 BSONObj unowned(buf); 2431 BSONObj owned = unowned.getOwned(); 2432 2433 ASSERT(!unowned.isOwned()); 2434 ASSERT(owned.isOwned()); 2435 2436 // Ensure that by default we can bind owned and unowned 2437 { 2438 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 2439 s->setObject("unowned", unowned, true); 2440 s->setObject("owned", owned, true); 2441 } 2442 2443 // After we set the flag, we should only be able to set owned 2444 { 2445 unique_ptr<Scope> s(getGlobalScriptEngine()->newScope()); 2446 s->requireOwnedObjects(); 2447 s->setObject("owned", owned, true); 2448 2449 bool threwException = false; 2450 try { 2451 s->setObject("unowned", unowned, true); 2452 } catch (...) { 2453 threwException = true; 2454 2455 auto status = exceptionToStatus(); 2456 2457 ASSERT_EQUALS(status.code(), ErrorCodes::BadValue); 2458 } 2459 2460 ASSERT(threwException); 2461 2462 // after resetting, we can set unowned's again 2463 s->reset(); 2464 s->setObject("unowned", unowned, true); 2465 } 2466 } 2467 }; 2468 2469 class All : public Suite { 2470 public: All()2471 All() : Suite("js") {} 2472 setupTests()2473 void setupTests() { 2474 add<BuiltinTests>(); 2475 add<BasicScope>(); 2476 add<ResetScope>(); 2477 add<FalseTests>(); 2478 add<SimpleFunctions>(); 2479 add<ExecLogError>(); 2480 add<InvokeLogError>(); 2481 add<ExecTimeout>(); 2482 add<ExecNoTimeout>(); 2483 add<InvokeTimeout>(); 2484 add<SleepInterruption>(); 2485 add<InvokeNoTimeout>(); 2486 2487 add<ObjectMapping>(); 2488 add<ObjectDecoding>(); 2489 add<JSOIDTests>(); 2490 add<SetImplicit>(); 2491 add<ObjectModReadonlyTests>(); 2492 add<OtherJSTypes>(); 2493 add<SpecialDBTypes>(); 2494 add<TypeConservation>(); 2495 add<NumberLong>(); 2496 add<NumberLong2>(); 2497 2498 add<NumberDecimal>(); 2499 add<NumberDecimalGetFromScope>(); 2500 add<NumberDecimalBigObject>(); 2501 2502 add<MaxTimestamp>(); 2503 add<RenameTest>(); 2504 2505 add<WeirdObjects>(); 2506 add<CodeTests>(); 2507 add<BinDataType>(); 2508 2509 add<VarTests>(); 2510 2511 add<Speed1>(); 2512 2513 add<InvalidUTF8Check>(); 2514 add<Utf8Check>(); 2515 add<LongUtf8String>(); 2516 2517 add<ScopeOut>(); 2518 add<InvalidStoredJS>(); 2519 2520 add<NovelNaN>(); 2521 2522 add<NoReturnSpecified>(); 2523 2524 add<RecursiveInvoke>(); 2525 add<ErrorCodeFromInvoke>(); 2526 add<RequiresOwnedObjects>(); 2527 2528 add<RoundTripTests::DBRefTest>(); 2529 add<RoundTripTests::DBPointerTest>(); 2530 add<RoundTripTests::InformalDBRefTest>(); 2531 add<RoundTripTests::InformalDBRefOIDTest>(); 2532 add<RoundTripTests::InformalDBRefExtraFieldTest>(); 2533 add<RoundTripTests::Empty>(); 2534 add<RoundTripTests::EmptyWithSpace>(); 2535 add<RoundTripTests::SingleString>(); 2536 add<RoundTripTests::EmptyStrings>(); 2537 add<RoundTripTests::SingleNumber>(); 2538 add<RoundTripTests::RealNumber>(); 2539 add<RoundTripTests::FancyNumber>(); 2540 add<RoundTripTests::TwoElements>(); 2541 add<RoundTripTests::Subobject>(); 2542 add<RoundTripTests::DeeplyNestedObject>(); 2543 add<RoundTripTests::ArrayEmpty>(); 2544 add<RoundTripTests::Array>(); 2545 add<RoundTripTests::True>(); 2546 add<RoundTripTests::False>(); 2547 add<RoundTripTests::Null>(); 2548 add<RoundTripTests::Undefined>(); 2549 add<RoundTripTests::EscapedCharacters>(); 2550 add<RoundTripTests::NonEscapedCharacters>(); 2551 add<RoundTripTests::AllowedControlCharacter>(); 2552 add<RoundTripTests::NumbersInFieldName>(); 2553 add<RoundTripTests::EscapeFieldName>(); 2554 add<RoundTripTests::EscapedUnicodeToUtf8>(); 2555 add<RoundTripTests::Utf8AllOnes>(); 2556 add<RoundTripTests::Utf8FirstByteOnes>(); 2557 add<RoundTripTests::BinData>(); 2558 add<RoundTripTests::BinDataPaddedSingle>(); 2559 add<RoundTripTests::BinDataPaddedDouble>(); 2560 add<RoundTripTests::BinDataAllChars>(); 2561 add<RoundTripTests::Date>(); 2562 add<RoundTripTests::DateNonzero>(); 2563 add<RoundTripTests::DateNegative>(); 2564 add<RoundTripTests::JSTimestamp>(); 2565 add<RoundTripTests::TimestampMax>(); 2566 add<RoundTripTests::Regex>(); 2567 add<RoundTripTests::RegexWithQuotes>(); 2568 add<RoundTripTests::UnquotedFieldName>(); 2569 add<RoundTripTests::SingleQuotes>(); 2570 add<RoundTripTests::ObjectId>(); 2571 add<RoundTripTests::NumberLong>(); 2572 add<RoundTripTests::NumberInt>(); 2573 add<RoundTripTests::Number>(); 2574 2575 add<RoundTripTests::NumberDecimal>(); 2576 add<RoundTripTests::NumberDecimalNegative>(); 2577 add<RoundTripTests::NumberDecimalMax>(); 2578 add<RoundTripTests::NumberDecimalMin>(); 2579 add<RoundTripTests::NumberDecimalPositiveZero>(); 2580 add<RoundTripTests::NumberDecimalNegativeZero>(); 2581 add<RoundTripTests::NumberDecimalPositiveNaN>(); 2582 add<RoundTripTests::NumberDecimalNegativeNaN>(); 2583 add<RoundTripTests::NumberDecimalPositiveInfinity>(); 2584 add<RoundTripTests::NumberDecimalNegativeInfinity>(); 2585 add<RoundTripTests::NumberDecimalPrecision>(); 2586 2587 add<RoundTripTests::UUID>(); 2588 add<RoundTripTests::HexData>(); 2589 add<RoundTripTests::MD5>(); 2590 add<RoundTripTests::NullString>(); 2591 } 2592 }; 2593 2594 SuiteInstance<All> myall; 2595 2596 } // namespace JSTests 2597