summaryrefslogtreecommitdiff
path: root/dbtests
diff options
context:
space:
mode:
authorAntonin Kral <a.kral@bobek.cz>2010-01-31 08:32:52 +0100
committerAntonin Kral <a.kral@bobek.cz>2010-01-31 08:32:52 +0100
commit4eefaf421bfeddf040d96a3dafb12e09673423d7 (patch)
treecb2e5ccc7f98158894f977ff131949da36673591 /dbtests
downloadmongodb-4eefaf421bfeddf040d96a3dafb12e09673423d7.tar.gz
Imported Upstream version 1.3.1
Diffstat (limited to 'dbtests')
-rw-r--r--dbtests/basictests.cpp251
-rw-r--r--dbtests/btreetests.cpp238
-rw-r--r--dbtests/clienttests.cpp98
-rw-r--r--dbtests/cursortests.cpp127
-rw-r--r--dbtests/dbtests.cpp27
-rw-r--r--dbtests/dbtests.h24
-rw-r--r--dbtests/framework.cpp361
-rw-r--r--dbtests/framework.h184
-rw-r--r--dbtests/jsobjtests.cpp1340
-rw-r--r--dbtests/jsontests.cpp1117
-rw-r--r--dbtests/jstests.cpp768
-rw-r--r--dbtests/matchertests.cpp128
-rw-r--r--dbtests/mockdbclient.h91
-rw-r--r--dbtests/namespacetests.cpp798
-rw-r--r--dbtests/pairingtests.cpp344
-rw-r--r--dbtests/pdfiletests.cpp328
-rw-r--r--dbtests/perf/perftest.cpp695
-rw-r--r--dbtests/queryoptimizertests.cpp1191
-rw-r--r--dbtests/querytests.cpp919
-rw-r--r--dbtests/repltests.cpp1050
-rw-r--r--dbtests/sharding.cpp56
-rw-r--r--dbtests/socktests.cpp44
-rw-r--r--dbtests/test.vcproj1931
-rw-r--r--dbtests/test.vcxproj586
-rw-r--r--dbtests/threadedtests.cpp135
-rw-r--r--dbtests/updatetests.cpp770
26 files changed, 13601 insertions, 0 deletions
diff --git a/dbtests/basictests.cpp b/dbtests/basictests.cpp
new file mode 100644
index 0000000..20dc6d7
--- /dev/null
+++ b/dbtests/basictests.cpp
@@ -0,0 +1,251 @@
+// basictests.cpp : basic unit tests
+//
+
+/**
+ * Copyright (C) 2009 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+
+#include "dbtests.h"
+#include "../util/base64.h"
+
+namespace BasicTests {
+
+ class Rarely {
+ public:
+ void run() {
+ int first = 0;
+ int second = 0;
+ int third = 0;
+ for( int i = 0; i < 128; ++i ) {
+ incRarely( first );
+ incRarely2( second );
+ ONCE ++third;
+ }
+ ASSERT_EQUALS( 1, first );
+ ASSERT_EQUALS( 1, second );
+ ASSERT_EQUALS( 1, third );
+ }
+ private:
+ void incRarely( int &c ) {
+ RARELY ++c;
+ }
+ void incRarely2( int &c ) {
+ RARELY ++c;
+ }
+ };
+
+ class Base64Tests {
+ public:
+
+ void roundTrip( string s ){
+ ASSERT_EQUALS( s , base64::decode( base64::encode( s ) ) );
+ }
+
+ void roundTrip( const unsigned char * _data , int len ){
+ const char *data = (const char *) _data;
+ string s = base64::encode( data , len );
+ string out = base64::decode( s );
+ ASSERT_EQUALS( out.size() , static_cast<size_t>(len) );
+ bool broke = false;
+ for ( int i=0; i<len; i++ ){
+ if ( data[i] != out[i] )
+ broke = true;
+ }
+ if ( ! broke )
+ return;
+
+ cout << s << endl;
+ for ( int i=0; i<len; i++ )
+ cout << hex << ( data[i] & 0xFF ) << dec << " ";
+ cout << endl;
+ for ( int i=0; i<len; i++ )
+ cout << hex << ( out[i] & 0xFF ) << dec << " ";
+ cout << endl;
+
+ ASSERT(0);
+ }
+
+ void run(){
+
+ ASSERT_EQUALS( "ZWxp" , base64::encode( "eli" , 3 ) );
+ ASSERT_EQUALS( "ZWxpb3Rz" , base64::encode( "eliots" , 6 ) );
+ ASSERT_EQUALS( "ZWxpb3Rz" , base64::encode( "eliots" ) );
+
+ ASSERT_EQUALS( "ZQ==" , base64::encode( "e" , 1 ) );
+ ASSERT_EQUALS( "ZWw=" , base64::encode( "el" , 2 ) );
+
+ roundTrip( "e" );
+ roundTrip( "el" );
+ roundTrip( "eli" );
+ roundTrip( "elio" );
+ roundTrip( "eliot" );
+ roundTrip( "eliots" );
+ roundTrip( "eliotsz" );
+
+ unsigned char z[] = { 0x1 , 0x2 , 0x3 , 0x4 };
+ roundTrip( z , 4 );
+
+ unsigned char y[] = {
+ 0x01, 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
+ };
+ roundTrip( y , 4 );
+ roundTrip( y , 40 );
+ }
+ };
+
+ namespace stringbuildertests {
+#define SBTGB(x) ss << (x); sb << (x);
+
+ class Base {
+ virtual void pop() = 0;
+
+ public:
+ Base(){}
+ virtual ~Base(){}
+
+ void run(){
+ pop();
+ ASSERT_EQUALS( ss.str() , sb.str() );
+ }
+
+ stringstream ss;
+ StringBuilder sb;
+ };
+
+ class simple1 : public Base {
+ void pop(){
+ SBTGB(1);
+ SBTGB("yo");
+ SBTGB(2);
+ }
+ };
+
+ class simple2 : public Base {
+ void pop(){
+ SBTGB(1);
+ SBTGB("yo");
+ SBTGB(2);
+ SBTGB( 12123123123LL );
+ SBTGB( "xxx" );
+ SBTGB( 5.4 );
+ SBTGB( 5.4312 );
+ SBTGB( "yyy" );
+ SBTGB( (short)5 );
+ SBTGB( (short)(1231231231231LL) );
+ }
+ };
+
+ class reset1 {
+ public:
+ void run(){
+ StringBuilder sb;
+ sb << "1" << "abc" << "5.17";
+ ASSERT_EQUALS( "1abc5.17" , sb.str() );
+ ASSERT_EQUALS( "1abc5.17" , sb.str() );
+ sb.reset();
+ ASSERT_EQUALS( "" , sb.str() );
+ sb << "999";
+ ASSERT_EQUALS( "999" , sb.str() );
+ }
+ };
+
+ class reset2 {
+ public:
+ void run(){
+ StringBuilder sb;
+ sb << "1" << "abc" << "5.17";
+ ASSERT_EQUALS( "1abc5.17" , sb.str() );
+ ASSERT_EQUALS( "1abc5.17" , sb.str() );
+ sb.reset(1);
+ ASSERT_EQUALS( "" , sb.str() );
+ sb << "999";
+ ASSERT_EQUALS( "999" , sb.str() );
+ }
+ };
+
+ }
+
+ class sleeptest {
+ public:
+ void run(){
+ Timer t;
+ sleepsecs( 1 );
+ ASSERT_EQUALS( 1 , t.seconds() );
+
+ t.reset();
+ sleepmicros( 1527123 );
+ ASSERT( t.micros() > 1000000 );
+ ASSERT( t.micros() < 2000000 );
+
+ t.reset();
+ sleepmillis( 1727 );
+ ASSERT( t.millis() >= 1000 );
+ ASSERT( t.millis() <= 2000 );
+
+ }
+
+ };
+
+ class AssertTests {
+ public:
+
+ int x;
+
+ AssertTests(){
+ x = 0;
+ }
+
+ string foo(){
+ x++;
+ return "";
+ }
+ void run(){
+ uassert( -1 , foo() , 1 );
+ ASSERT_EQUALS( 0 , x );
+ try {
+ uassert( -1 , foo() , 0 );
+ }
+ catch ( ... ){}
+ ASSERT_EQUALS( 1 , x );
+ }
+ };
+
+ class All : public Suite {
+ public:
+ All() : Suite( "basic" ){
+ }
+
+ void setupTests(){
+ add< Rarely >();
+ add< Base64Tests >();
+
+ add< stringbuildertests::simple1 >();
+ add< stringbuildertests::simple2 >();
+ add< stringbuildertests::reset1 >();
+ add< stringbuildertests::reset2 >();
+
+ add< sleeptest >();
+ add< AssertTests >();
+ }
+ } myall;
+
+} // namespace BasicTests
+
diff --git a/dbtests/btreetests.cpp b/dbtests/btreetests.cpp
new file mode 100644
index 0000000..5a0b15d
--- /dev/null
+++ b/dbtests/btreetests.cpp
@@ -0,0 +1,238 @@
+// btreetests.cpp : Btree unit tests
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+
+#include "../db/db.h"
+#include "../db/btree.h"
+
+#include "dbtests.h"
+
+namespace BtreeTests {
+
+ class Base {
+ public:
+ Base() {
+ {
+ bool f = false;
+ assert( f = true );
+ massert( 10402 , "assert is misdefined", f);
+ }
+
+ setClient( ns() );
+ BSONObjBuilder builder;
+ builder.append( "ns", ns() );
+ builder.append( "name", "testIndex" );
+ BSONObj bobj = builder.done();
+ idx_.info =
+ theDataFileMgr.insert( ns(), bobj.objdata(), bobj.objsize() );
+ idx_.head = BtreeBucket::addBucket( idx_ );
+ }
+ ~Base() {
+ // FIXME cleanup all btree buckets.
+ theDataFileMgr.deleteRecord( ns(), idx_.info.rec(), idx_.info );
+ ASSERT( theDataFileMgr.findAll( ns() )->eof() );
+ }
+ protected:
+ BtreeBucket* bt() const {
+ return idx_.head.btree();
+ }
+ DiskLoc dl() const {
+ return idx_.head;
+ }
+ IndexDetails& id() {
+ return idx_;
+ }
+ static const char* ns() {
+ return "unittests.btreetests";
+ }
+ // dummy, valid record loc
+ static DiskLoc recordLoc() {
+ return DiskLoc( 0, 2 );
+ }
+ void checkValid( int nKeys ) const {
+ ASSERT( bt() );
+ ASSERT( bt()->isHead() );
+ bt()->assertValid( order(), true );
+ ASSERT_EQUALS( nKeys, bt()->fullValidate( dl(), order() ) );
+ }
+ void insert( BSONObj &key ) {
+ bt()->bt_insert( dl(), recordLoc(), key, order(), true, id(), true );
+ }
+ void unindex( BSONObj &key ) {
+ bt()->unindex( dl(), id(), key, recordLoc() );
+ }
+ static BSONObj simpleKey( char c, int n = 1 ) {
+ BSONObjBuilder builder;
+ string val( n, c );
+ builder.append( "a", val );
+ return builder.obj();
+ }
+ void locate( BSONObj &key, int expectedPos,
+ bool expectedFound, const DiskLoc &expectedLocation,
+ int direction = 1 ) {
+ int pos;
+ bool found;
+ DiskLoc location =
+ bt()->locate( id(), dl(), key, order(), pos, found, recordLoc(), direction );
+ ASSERT_EQUALS( expectedFound, found );
+ ASSERT( location == expectedLocation );
+ ASSERT_EQUALS( expectedPos, pos );
+ }
+ BSONObj order() const {
+ return idx_.keyPattern();
+ }
+ private:
+ dblock lk_;
+ IndexDetails idx_;
+ };
+
+ class Create : public Base {
+ public:
+ void run() {
+ checkValid( 0 );
+ }
+ };
+
+ class SimpleInsertDelete : public Base {
+ public:
+ void run() {
+ BSONObj key = simpleKey( 'z' );
+ insert( key );
+
+ checkValid( 1 );
+ locate( key, 0, true, dl() );
+
+ unindex( key );
+
+ checkValid( 0 );
+ locate( key, 0, false, DiskLoc() );
+ }
+ };
+
+ class SplitUnevenBucketBase : public Base {
+ public:
+ virtual ~SplitUnevenBucketBase() {}
+ void run() {
+ for ( int i = 0; i < 10; ++i ) {
+ BSONObj shortKey = simpleKey( shortToken( i ), 1 );
+ insert( shortKey );
+ BSONObj longKey = simpleKey( longToken( i ), 800 );
+ insert( longKey );
+ }
+ checkValid( 20 );
+ }
+ protected:
+ virtual char shortToken( int i ) const = 0;
+ virtual char longToken( int i ) const = 0;
+ static char leftToken( int i ) {
+ return 'a' + i;
+ }
+ static char rightToken( int i ) {
+ return 'z' - i;
+ }
+ };
+
+ class SplitRightHeavyBucket : public SplitUnevenBucketBase {
+ private:
+ virtual char shortToken( int i ) const {
+ return leftToken( i );
+ }
+ virtual char longToken( int i ) const {
+ return rightToken( i );
+ }
+ };
+
+ class SplitLeftHeavyBucket : public SplitUnevenBucketBase {
+ private:
+ virtual char shortToken( int i ) const {
+ return rightToken( i );
+ }
+ virtual char longToken( int i ) const {
+ return leftToken( i );
+ }
+ };
+
+ class MissingLocate : public Base {
+ public:
+ void run() {
+ for ( int i = 0; i < 3; ++i ) {
+ BSONObj k = simpleKey( 'b' + 2 * i );
+ insert( k );
+ }
+
+ locate( 1, 'a', 'b', dl() );
+ locate( 1, 'c', 'd', dl() );
+ locate( 1, 'e', 'f', dl() );
+ locate( 1, 'g', 'g' + 1, DiskLoc() ); // of course, 'h' isn't in the index.
+
+ // old behavior
+ // locate( -1, 'a', 'b', dl() );
+ // locate( -1, 'c', 'd', dl() );
+ // locate( -1, 'e', 'f', dl() );
+ // locate( -1, 'g', 'f', dl() );
+
+ locate( -1, 'a', 'a' - 1, DiskLoc() ); // of course, 'a' - 1 isn't in the index
+ locate( -1, 'c', 'b', dl() );
+ locate( -1, 'e', 'd', dl() );
+ locate( -1, 'g', 'f', dl() );
+ }
+ private:
+ void locate( int direction, char token, char expectedMatch,
+ DiskLoc expectedLocation ) {
+ BSONObj k = simpleKey( token );
+ int expectedPos = ( expectedMatch - 'b' ) / 2;
+ Base::locate( k, expectedPos, false, expectedLocation, direction );
+ }
+ };
+
+ class MissingLocateMultiBucket : public Base {
+ public:
+ void run() {
+ for ( int i = 0; i < 10; ++i ) {
+ BSONObj k = key( 'b' + 2 * i );
+ insert( k );
+ }
+ BSONObj straddle = key( 'i' );
+ locate( straddle, 0, false, dl(), 1 );
+ straddle = key( 'k' );
+ locate( straddle, 0, false, dl(), -1 );
+ }
+ private:
+ BSONObj key( char c ) {
+ return simpleKey( c, 800 );
+ }
+ };
+
+ class All : public Suite {
+ public:
+ All() : Suite( "btree" ){
+ }
+
+ void setupTests(){
+ add< Create >();
+ add< SimpleInsertDelete >();
+ add< SplitRightHeavyBucket >();
+ add< SplitLeftHeavyBucket >();
+ add< MissingLocate >();
+ add< MissingLocateMultiBucket >();
+ }
+ } myall;
+}
+
diff --git a/dbtests/clienttests.cpp b/dbtests/clienttests.cpp
new file mode 100644
index 0000000..1dadd1a
--- /dev/null
+++ b/dbtests/clienttests.cpp
@@ -0,0 +1,98 @@
+// client.cpp
+
+#include "stdafx.h"
+#include "../client/dbclient.h"
+#include "dbtests.h"
+#include "../db/concurrency.h"
+
+namespace ClientTests {
+
+ class Base {
+ public:
+
+ Base( string coll ){
+ _ns = (string)"test." + coll;
+ }
+
+ virtual ~Base(){
+ db.dropCollection( _ns );
+ }
+
+ const char * ns(){ return _ns.c_str(); }
+
+ string _ns;
+ DBDirectClient db;
+ };
+
+
+ class DropIndex : public Base {
+ public:
+ DropIndex() : Base( "dropindex" ){}
+ void run(){
+ db.insert( ns() , BSON( "x" << 2 ) );
+ ASSERT_EQUALS( 1 , db.getIndexes( ns() )->itcount() );
+
+ db.ensureIndex( ns() , BSON( "x" << 1 ) );
+ ASSERT_EQUALS( 2 , db.getIndexes( ns() )->itcount() );
+
+ db.dropIndex( ns() , BSON( "x" << 1 ) );
+ ASSERT_EQUALS( 1 , db.getIndexes( ns() )->itcount() );
+
+ db.ensureIndex( ns() , BSON( "x" << 1 ) );
+ ASSERT_EQUALS( 2 , db.getIndexes( ns() )->itcount() );
+
+ db.dropIndexes( ns() );
+ ASSERT_EQUALS( 1 , db.getIndexes( ns() )->itcount() );
+ }
+ };
+
+ class ReIndex : public Base {
+ public:
+ ReIndex() : Base( "reindex" ){}
+ void run(){
+
+ db.insert( ns() , BSON( "x" << 2 ) );
+ ASSERT_EQUALS( 1 , db.getIndexes( ns() )->itcount() );
+
+ db.ensureIndex( ns() , BSON( "x" << 1 ) );
+ ASSERT_EQUALS( 2 , db.getIndexes( ns() )->itcount() );
+
+ db.reIndex( ns() );
+ ASSERT_EQUALS( 2 , db.getIndexes( ns() )->itcount() );
+ }
+
+ };
+
+ class ReIndex2 : public Base {
+ public:
+ ReIndex2() : Base( "reindex2" ){}
+ void run(){
+
+ db.insert( ns() , BSON( "x" << 2 ) );
+ ASSERT_EQUALS( 1 , db.getIndexes( ns() )->itcount() );
+
+ db.ensureIndex( ns() , BSON( "x" << 1 ) );
+ ASSERT_EQUALS( 2 , db.getIndexes( ns() )->itcount() );
+
+ BSONObj out;
+ ASSERT( db.runCommand( "test" , BSON( "reIndex" << "reindex2" ) , out ) );
+ ASSERT_EQUALS( 2 , out["nIndexes"].number() );
+ ASSERT_EQUALS( 2 , db.getIndexes( ns() )->itcount() );
+ }
+
+ };
+
+
+ class All : public Suite {
+ public:
+ All() : Suite( "client" ){
+ }
+
+ void setupTests(){
+ add<DropIndex>();
+ add<ReIndex>();
+ add<ReIndex2>();
+ }
+
+ } all;
+}
diff --git a/dbtests/cursortests.cpp b/dbtests/cursortests.cpp
new file mode 100644
index 0000000..28a4ba4
--- /dev/null
+++ b/dbtests/cursortests.cpp
@@ -0,0 +1,127 @@
+// cusrortests.cpp // cursor related unit tests
+//
+
+/**
+ * Copyright (C) 2009 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/db.h"
+#include "../db/clientcursor.h"
+#include "../db/instance.h"
+#include "../db/btree.h"
+#include "dbtests.h"
+
+namespace CursorTests {
+
+ namespace BtreeCursorTests {
+
+ class MultiRange {
+ public:
+ void run() {
+ dblock lk;
+ const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRange";
+ {
+ DBDirectClient c;
+ for( int i = 0; i < 10; ++i )
+ c.insert( ns, BSON( "a" << i ) );
+ ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) );
+ }
+ BoundList b;
+ b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 1 ), BSON( "" << 2 ) ) );
+ b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 4 ), BSON( "" << 6 ) ) );
+ setClient( ns );
+ BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->idx(1), b, 1 );
+ ASSERT_EQUALS( "BtreeCursor a_1 multi", c.toString() );
+ double expected[] = { 1, 2, 4, 5, 6 };
+ for( int i = 0; i < 5; ++i ) {
+ ASSERT( c.ok() );
+ ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() );
+ c.advance();
+ }
+ ASSERT( !c.ok() );
+ }
+ };
+
+ class MultiRangeGap {
+ public:
+ void run() {
+ dblock lk;
+ const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRangeGap";
+ {
+ DBDirectClient c;
+ for( int i = 0; i < 10; ++i )
+ c.insert( ns, BSON( "a" << i ) );
+ for( int i = 100; i < 110; ++i )
+ c.insert( ns, BSON( "a" << i ) );
+ ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) );
+ }
+ BoundList b;
+ b.push_back( pair< BSONObj, BSONObj >( BSON( "" << -50 ), BSON( "" << 2 ) ) );
+ b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 40 ), BSON( "" << 60 ) ) );
+ b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 109 ), BSON( "" << 200 ) ) );
+ setClient( ns );
+ BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->idx(1), b, 1 );
+ ASSERT_EQUALS( "BtreeCursor a_1 multi", c.toString() );
+ double expected[] = { 0, 1, 2, 109 };
+ for( int i = 0; i < 4; ++i ) {
+ ASSERT( c.ok() );
+ ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() );
+ c.advance();
+ }
+ ASSERT( !c.ok() );
+ }
+ };
+
+ class MultiRangeReverse {
+ public:
+ void run() {
+ dblock lk;
+ const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRangeReverse";
+ {
+ DBDirectClient c;
+ for( int i = 0; i < 10; ++i )
+ c.insert( ns, BSON( "a" << i ) );
+ ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) );
+ }
+ BoundList b;
+ b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 6 ), BSON( "" << 4 ) ) );
+ b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 2 ), BSON( "" << 1 ) ) );
+ setClient( ns );
+ BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->idx(1), b, -1 );
+ ASSERT_EQUALS( "BtreeCursor a_1 reverse multi", c.toString() );
+ double expected[] = { 6, 5, 4, 2, 1 };
+ for( int i = 0; i < 5; ++i ) {
+ ASSERT( c.ok() );
+ ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() );
+ c.advance();
+ }
+ ASSERT( !c.ok() );
+ }
+ };
+
+ } // namespace MultiBtreeCursorTests
+
+ class All : public Suite {
+ public:
+ All() : Suite( "cursor" ){}
+
+ void setupTests(){
+ add< BtreeCursorTests::MultiRange >();
+ add< BtreeCursorTests::MultiRangeGap >();
+ add< BtreeCursorTests::MultiRangeReverse >();
+ }
+ } myall;
+} // namespace CursorTests
diff --git a/dbtests/dbtests.cpp b/dbtests/dbtests.cpp
new file mode 100644
index 0000000..3821163
--- /dev/null
+++ b/dbtests/dbtests.cpp
@@ -0,0 +1,27 @@
+// dbtests.cpp : Runs db unit tests.
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+
+#include "dbtests.h"
+
+int main( int argc, char** argv ) {
+ return Suite::run(argc, argv, "/tmp/unittest");
+}
+
diff --git a/dbtests/dbtests.h b/dbtests/dbtests.h
new file mode 100644
index 0000000..3184a05
--- /dev/null
+++ b/dbtests/dbtests.h
@@ -0,0 +1,24 @@
+// dbtests.h : Test suite generator headers.
+//
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "framework.h"
+
+using namespace mongo;
+using namespace mongo::regression;
+
diff --git a/dbtests/framework.cpp b/dbtests/framework.cpp
new file mode 100644
index 0000000..6ed5e72
--- /dev/null
+++ b/dbtests/framework.cpp
@@ -0,0 +1,361 @@
+// framework.cpp
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+#include <boost/program_options.hpp>
+
+#undef assert
+#define assert xassert
+
+#include "framework.h"
+#include "../util/file_allocator.h"
+
+#ifndef _WIN32
+#include <cxxabi.h>
+#include <sys/file.h>
+#endif
+
+namespace po = boost::program_options;
+
+namespace mongo {
+
+ namespace regression {
+
+ map<string,Suite*> * mongo::regression::Suite::_suites = 0;
+
+ class Result {
+ public:
+ Result( string name ) : _name( name ) , _rc(0) , _tests(0) , _fails(0) , _asserts(0) {
+ }
+
+ string toString(){
+ stringstream ss;
+
+ char result[128];
+ sprintf(result, "%-20s | tests: %4d | fails: %4d | assert calls: %6d\n", _name.c_str(), _tests, _fails, _asserts);
+ ss << result;
+
+ for ( list<string>::iterator i=_messages.begin(); i!=_messages.end(); i++ ){
+ ss << "\t" << *i << "\n";
+ }
+
+ return ss.str();
+ }
+
+ int rc(){
+ return _rc;
+ }
+
+ string _name;
+
+ int _rc;
+ int _tests;
+ int _fails;
+ int _asserts;
+ list<string> _messages;
+
+ static Result * cur;
+ };
+
+ Result * Result::cur = 0;
+
+ Result * Suite::run(){
+ log(1) << "\t about to setupTests" << endl;
+ setupTests();
+ log(1) << "\t done setupTests" << endl;
+
+ Result * r = new Result( _name );
+ Result::cur = r;
+
+ /* see note in SavedContext */
+ //writelock lk("");
+
+ for ( list<TestCase*>::iterator i=_tests.begin(); i!=_tests.end(); i++ ){
+ TestCase * tc = *i;
+
+ r->_tests++;
+
+ bool passes = false;
+
+ log(1) << "\t going to run test: " << tc->getName() << endl;
+
+ stringstream err;
+ err << tc->getName() << "\t";
+
+ try {
+ tc->run();
+ passes = true;
+ }
+ catch ( MyAssertionException * ae ){
+ err << ae->ss.str();
+ delete( ae );
+ }
+ catch ( std::exception& e ){
+ err << " exception: " << e.what();
+ }
+ catch ( int x ){
+ err << " caught int : " << x << endl;
+ }
+ catch ( ... ){
+ cerr << "unknown exception in test: " << tc->getName() << endl;
+ }
+
+ if ( ! passes ){
+ string s = err.str();
+ log() << "FAIL: " << s << endl;
+ r->_fails++;
+ r->_messages.push_back( s );
+ }
+ }
+
+ if ( r->_fails )
+ r->_rc = 17;
+
+ log(1) << "\t DONE running tests" << endl;
+
+ return r;
+ }
+
+ void show_help_text(const char* name, po::options_description options) {
+ cout << "usage: " << name << " [options] [suite]..." << endl
+ << options << "suite: run the specified test suite(s) only" << endl;
+ }
+
+ int Suite::run( int argc , char** argv , string default_dbpath ) {
+ unsigned long long seed = time( 0 );
+ string dbpathSpec;
+
+ po::options_description shell_options("options");
+ po::options_description hidden_options("Hidden options");
+ po::options_description cmdline_options("Command line options");
+ po::positional_options_description positional_options;
+
+ shell_options.add_options()
+ ("help,h", "show this usage information")
+ ("dbpath", po::value<string>(&dbpathSpec)->default_value(default_dbpath),
+ "db data path for this test run. NOTE: the contents of this "
+ "directory will be overwritten if it already exists")
+ ("debug", "run tests with verbose output")
+ ("list,l", "list available test suites")
+ ("verbose,v", "verbose")
+ ("seed", po::value<unsigned long long>(&seed), "random number seed")
+ ;
+
+ hidden_options.add_options()
+ ("suites", po::value< vector<string> >(), "test suites to run")
+ ;
+
+ positional_options.add("suites", -1);
+
+ cmdline_options.add(shell_options).add(hidden_options);
+
+ po::variables_map params;
+ int command_line_style = (((po::command_line_style::unix_style ^
+ po::command_line_style::allow_guessing) |
+ po::command_line_style::allow_long_disguise) ^
+ po::command_line_style::allow_sticky);
+
+ try {
+ po::store(po::command_line_parser(argc, argv).options(cmdline_options).
+ positional(positional_options).
+ style(command_line_style).run(), params);
+ po::notify(params);
+ } catch (po::error &e) {
+ cout << "ERROR: " << e.what() << endl << endl;
+ show_help_text(argv[0], shell_options);
+ return EXIT_BADOPTIONS;
+ }
+
+ if (params.count("help")) {
+ show_help_text(argv[0], shell_options);
+ return EXIT_CLEAN;
+ }
+
+ if (params.count("debug") || params.count("verbose") ) {
+ logLevel = 1;
+ }
+
+ if (params.count("list")) {
+ for ( map<string,Suite*>::iterator i = _suites->begin() ; i != _suites->end(); i++ )
+ cout << i->first << endl;
+ return 0;
+ }
+
+ boost::filesystem::path p(dbpathSpec);
+
+ /* remove the contents of the test directory if it exists. */
+ if (boost::filesystem::exists(p)) {
+ if (!boost::filesystem::is_directory(p)) {
+ cout << "ERROR: path \"" << p.string() << "\" is not a directory" << endl << endl;
+ show_help_text(argv[0], shell_options);
+ return EXIT_BADOPTIONS;
+ }
+ boost::filesystem::directory_iterator end_iter;
+ for (boost::filesystem::directory_iterator dir_iter(p);
+ dir_iter != end_iter; ++dir_iter) {
+ boost::filesystem::remove_all(*dir_iter);
+ }
+ } else {
+ boost::filesystem::create_directory(p);
+ }
+
+ string dbpathString = p.native_directory_string();
+ dbpath = dbpathString.c_str();
+
+ cmdLine.prealloc = false;
+ cmdLine.smallfiles = true;
+ cmdLine.oplogSize = 10 * 1024 * 1024;
+ Client::initThread("testsuite");
+ acquirePathLock();
+
+ srand( (unsigned) seed );
+ printGitVersion();
+ printSysInfo();
+ out() << "random seed: " << seed << endl;
+
+ theFileAllocator().start();
+
+ vector<string> suites;
+ if (params.count("suites")) {
+ suites = params["suites"].as< vector<string> >();
+ }
+ int ret = run(suites);
+
+#if !defined(_WIN32) && !defined(__sunos__)
+ flock( lockFile, LOCK_UN );
+#endif
+
+ cc().shutdown();
+ dbexit( (ExitCode)ret ); // so everything shuts down cleanly
+ return ret;
+ }
+
+ int Suite::run( vector<string> suites ){
+ for ( unsigned int i = 0; i < suites.size(); i++ ) {
+ if ( _suites->find( suites[i] ) == _suites->end() ) {
+ cout << "invalid test [" << suites[i] << "], use --list to see valid names" << endl;
+ return -1;
+ }
+ }
+
+ list<string> torun(suites.begin(), suites.end());
+
+ if ( torun.size() == 0 )
+ for ( map<string,Suite*>::iterator i=_suites->begin() ; i!=_suites->end(); i++ )
+ torun.push_back( i->first );
+
+ list<Result*> results;
+
+ for ( list<string>::iterator i=torun.begin(); i!=torun.end(); i++ ){
+ string name = *i;
+ Suite * s = (*_suites)[name];
+ assert( s );
+
+ log() << "going to run suite: " << name << endl;
+ results.push_back( s->run() );
+ }
+
+ Logstream::get().flush();
+
+ cout << "**************************************************" << endl;
+ cout << "**************************************************" << endl;
+ cout << "**************************************************" << endl;
+
+ int rc = 0;
+
+ int tests = 0;
+ int fails = 0;
+ int asserts = 0;
+
+ for ( list<Result*>::iterator i=results.begin(); i!=results.end(); i++ ){
+ Result * r = *i;
+ cout << r->toString();
+ if ( abs( r->rc() ) > abs( rc ) )
+ rc = r->rc();
+
+ tests += r->_tests;
+ fails += r->_fails;
+ asserts += r->_asserts;
+ }
+
+ Result totals ("TOTALS");
+ totals._tests = tests;
+ totals._fails = fails;
+ totals._asserts = asserts;
+
+ cout << totals.toString(); // includes endl
+
+ return rc;
+ }
+
+ void Suite::registerSuite( string name , Suite * s ){
+ if ( ! _suites )
+ _suites = new map<string,Suite*>();
+ Suite*& m = (*_suites)[name];
+ uassert( 10162 , "already have suite with that name" , ! m );
+ m = s;
+ }
+
+ void assert_pass(){
+ Result::cur->_asserts++;
+ }
+
+ void assert_fail( const char * exp , const char * file , unsigned line ){
+ Result::cur->_asserts++;
+
+ MyAssertionException * e = new MyAssertionException();
+ e->ss << "ASSERT FAILED! " << file << ":" << line << endl;
+ throw e;
+ }
+
+ void fail( const char * exp , const char * file , unsigned line ){
+ assert(0);
+ }
+
+ string demangleName( const type_info& typeinfo ){
+#ifdef _WIN32
+ return typeinfo.name();
+#else
+ int status;
+
+ char * niceName = abi::__cxa_demangle(typeinfo.name(), 0, 0, &status);
+ if ( ! niceName )
+ return typeinfo.name();
+
+ string s = niceName;
+ free(niceName);
+ return s;
+#endif
+ }
+
+ MyAssertionException * MyAsserts::getBase(){
+ MyAssertionException * e = new MyAssertionException();
+ e->ss << _file << ":" << _line << " " << _aexp << " != " << _bexp << " ";
+ return e;
+ }
+
+ void MyAsserts::printLocation(){
+ log() << _file << ":" << _line << " " << _aexp << " != " << _bexp << " ";
+ }
+
+ void MyAsserts::_gotAssert(){
+ Result::cur->_asserts++;
+ }
+
+ }
+}
diff --git a/dbtests/framework.h b/dbtests/framework.h
new file mode 100644
index 0000000..710b880
--- /dev/null
+++ b/dbtests/framework.h
@@ -0,0 +1,184 @@
+// framework.h
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+
+ simple portable regression system
+ */
+
+#include "../stdafx.h"
+
+#define ASSERT_EXCEPTION(a,b) \
+ try { \
+ a; \
+ mongo::regression::assert_fail( #a , __FILE__ , __LINE__ ); \
+ } catch ( b& ){ \
+ mongo::regression::assert_pass(); \
+ }
+
+
+
+#define ASSERT_EQUALS(a,b) (mongo::regression::MyAsserts( #a , #b , __FILE__ , __LINE__ ) ).ae( (a) , (b) )
+#define ASSERT(x) (void)( (!(!(x))) ? mongo::regression::assert_pass() : mongo::regression::assert_fail( #x , __FILE__ , __LINE__ ) )
+#define FAIL(x) mongo::regression::fail( #x , __FILE__ , __LINE__ )
+
+#include "../db/instance.h"
+
+namespace mongo {
+
+ namespace regression {
+
+ class Result;
+
+ string demangleName( const type_info& typeinfo );
+
+ class TestCase {
+ public:
+ virtual ~TestCase(){}
+ virtual void run() = 0;
+ virtual string getName() = 0;
+ };
+
+ template< class T >
+ class TestHolderBase : public TestCase {
+ public:
+ TestHolderBase(){}
+ virtual ~TestHolderBase(){}
+ virtual void run(){
+ auto_ptr<T> t;
+ t.reset( create() );
+ t->run();
+ }
+ virtual T * create() = 0;
+ virtual string getName(){
+ return demangleName( typeid(T) );
+ }
+ };
+
+ template< class T >
+ class TestHolder0 : public TestHolderBase<T> {
+ public:
+ virtual T * create(){
+ return new T();
+ }
+ };
+
+ template< class T , typename A >
+ class TestHolder1 : public TestHolderBase<T> {
+ public:
+ TestHolder1( const A& a ) : _a(a){}
+ virtual T * create(){
+ return new T( _a );
+ }
+ const A& _a;
+ };
+
+ class Suite {
+ public:
+ Suite( string name ) : _name( name ){
+ registerSuite( name , this );
+ _ran = 0;
+ }
+
+ virtual ~Suite() {
+ if ( _ran ){
+ DBDirectClient c;
+ c.dropDatabase( "unittests" );
+ }
+ }
+
+ template<class T>
+ void add(){
+ _tests.push_back( new TestHolder0<T>() );
+ }
+
+ template<class T , typename A >
+ void add( const A& a ){
+ _tests.push_back( new TestHolder1<T,A>(a) );
+ }
+
+ Result * run();
+
+ static int run( vector<string> suites );
+ static int run( int argc , char ** argv , string default_dbpath );
+
+
+ protected:
+ virtual void setupTests() = 0;
+
+ private:
+ string _name;
+ list<TestCase*> _tests;
+ bool _ran;
+
+ static map<string,Suite*> * _suites;
+
+ void registerSuite( string name , Suite * s );
+ };
+
+ void assert_pass();
+ void assert_fail( const char * exp , const char * file , unsigned line );
+ void fail( const char * exp , const char * file , unsigned line );
+
+ class MyAssertionException : boost::noncopyable {
+ public:
+ MyAssertionException(){
+ ss << "assertion: ";
+ }
+ stringstream ss;
+ };
+
+
+
+ class MyAsserts {
+ public:
+ MyAsserts( const char * aexp , const char * bexp , const char * file , unsigned line )
+ : _aexp( aexp ) , _bexp( bexp ) , _file( file ) , _line( line ){
+
+ }
+
+ template<typename A,typename B>
+ void ae( A a , B b ){
+ _gotAssert();
+ if ( a == b )
+ return;
+
+ printLocation();
+
+ MyAssertionException * e = getBase();
+ e->ss << a << " != " << b << endl;
+ log() << e->ss.str() << endl;
+ throw e;
+ }
+
+ void printLocation();
+
+ private:
+
+ void _gotAssert();
+
+ MyAssertionException * getBase();
+
+ string _aexp;
+ string _bexp;
+ string _file;
+ unsigned _line;
+ };
+
+ }
+}
diff --git a/dbtests/jsobjtests.cpp b/dbtests/jsobjtests.cpp
new file mode 100644
index 0000000..0402426
--- /dev/null
+++ b/dbtests/jsobjtests.cpp
@@ -0,0 +1,1340 @@
+// jsobjtests.cpp - Tests for jsobj.{h,cpp} code
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/jsobj.h"
+#include "../db/jsobjmanipulator.h"
+#include "../db/json.h"
+#include "../db/repl.h"
+#include "../db/extsort.h"
+
+#include "dbtests.h"
+
+namespace JsobjTests {
+ class BufBuilderBasic {
+ public:
+ void run() {
+ BufBuilder b( 0 );
+ b.append( "foo" );
+ ASSERT_EQUALS( 4, b.len() );
+ ASSERT( strcmp( "foo", b.buf() ) == 0 );
+ }
+ };
+
+ class BSONElementBasic {
+ public:
+ void run() {
+ ASSERT_EQUALS( 1, BSONElement().size() );
+ }
+ };
+
+ namespace BSONObjTests {
+ class Create {
+ public:
+ void run() {
+ BSONObj b;
+ ASSERT_EQUALS( 0, b.nFields() );
+ }
+ };
+
+ class Base {
+ protected:
+ static BSONObj basic( const char *name, int val ) {
+ BSONObjBuilder b;
+ b.append( name, val );
+ return b.obj();
+ }
+ static BSONObj basic( const char *name, vector< int > val ) {
+ BSONObjBuilder b;
+ b.append( name, val );
+ return b.obj();
+ }
+ template< class T >
+ static BSONObj basic( const char *name, T val ) {
+ BSONObjBuilder b;
+ b.append( name, val );
+ return b.obj();
+ }
+ };
+
+ class WoCompareBasic : public Base {
+ public:
+ void run() {
+ ASSERT( basic( "a", 1 ).woCompare( basic( "a", 1 ) ) == 0 );
+ ASSERT( basic( "a", 2 ).woCompare( basic( "a", 1 ) ) > 0 );
+ ASSERT( basic( "a", 1 ).woCompare( basic( "a", 2 ) ) < 0 );
+ // field name comparison
+ ASSERT( basic( "a", 1 ).woCompare( basic( "b", 1 ) ) < 0 );
+ }
+ };
+
+ class NumericCompareBasic : public Base {
+ public:
+ void run() {
+ ASSERT( basic( "a", 1 ).woCompare( basic( "a", 1.0 ) ) == 0 );
+ }
+ };
+
+ class WoCompareEmbeddedObject : public Base {
+ public:
+ void run() {
+ ASSERT( basic( "a", basic( "b", 1 ) ).woCompare
+ ( basic( "a", basic( "b", 1.0 ) ) ) == 0 );
+ ASSERT( basic( "a", basic( "b", 1 ) ).woCompare
+ ( basic( "a", basic( "b", 2 ) ) ) < 0 );
+ }
+ };
+
+ class WoCompareEmbeddedArray : public Base {
+ public:
+ void run() {
+ vector< int > i;
+ i.push_back( 1 );
+ i.push_back( 2 );
+ vector< double > d;
+ d.push_back( 1 );
+ d.push_back( 2 );
+ ASSERT( basic( "a", i ).woCompare( basic( "a", d ) ) == 0 );
+
+ vector< int > j;
+ j.push_back( 1 );
+ j.push_back( 3 );
+ ASSERT( basic( "a", i ).woCompare( basic( "a", j ) ) < 0 );
+ }
+ };
+
+ class WoCompareOrdered : public Base {
+ public:
+ void run() {
+ ASSERT( basic( "a", 1 ).woCompare( basic( "a", 1 ), basic( "a", 1 ) ) == 0 );
+ ASSERT( basic( "a", 2 ).woCompare( basic( "a", 1 ), basic( "a", 1 ) ) > 0 );
+ ASSERT( basic( "a", 1 ).woCompare( basic( "a", 2 ), basic( "a", 1 ) ) < 0 );
+ ASSERT( basic( "a", 1 ).woCompare( basic( "a", 1 ), basic( "a", -1 ) ) == 0 );
+ ASSERT( basic( "a", 2 ).woCompare( basic( "a", 1 ), basic( "a", -1 ) ) < 0 );
+ ASSERT( basic( "a", 1 ).woCompare( basic( "a", 2 ), basic( "a", -1 ) ) > 0 );
+ }
+ };
+
+ class WoCompareDifferentLength : public Base {
+ public:
+ void run() {
+ ASSERT( BSON( "a" << 1 ).woCompare( BSON( "a" << 1 << "b" << 1 ) ) < 0 );
+ ASSERT( BSON( "a" << 1 << "b" << 1 ).woCompare( BSON( "a" << 1 ) ) > 0 );
+ }
+ };
+
+ class WoSortOrder : public Base {
+ public:
+ void run() {
+ ASSERT( BSON( "a" << 1 ).woSortOrder( BSON( "a" << 2 ), BSON( "b" << 1 << "a" << 1 ) ) < 0 );
+ ASSERT( fromjson( "{a:null}" ).woSortOrder( BSON( "b" << 1 ), BSON( "a" << 1 ) ) == 0 );
+ }
+ };
+
+ class MultiKeySortOrder : public Base {
+ public:
+ void run(){
+ ASSERT( BSON( "x" << "a" ).woCompare( BSON( "x" << "b" ) ) < 0 );
+ ASSERT( BSON( "x" << "b" ).woCompare( BSON( "x" << "a" ) ) > 0 );
+
+ ASSERT( BSON( "x" << "a" << "y" << "a" ).woCompare( BSON( "x" << "a" << "y" << "b" ) ) < 0 );
+ ASSERT( BSON( "x" << "a" << "y" << "a" ).woCompare( BSON( "x" << "b" << "y" << "a" ) ) < 0 );
+ ASSERT( BSON( "x" << "a" << "y" << "a" ).woCompare( BSON( "x" << "b" ) ) < 0 );
+
+ ASSERT( BSON( "x" << "c" ).woCompare( BSON( "x" << "b" << "y" << "h" ) ) > 0 );
+ ASSERT( BSON( "x" << "b" << "y" << "b" ).woCompare( BSON( "x" << "c" ) ) < 0 );
+
+ BSONObj key = BSON( "x" << 1 << "y" << 1 );
+
+ ASSERT( BSON( "x" << "c" ).woSortOrder( BSON( "x" << "b" << "y" << "h" ) , key ) > 0 );
+ ASSERT( BSON( "x" << "b" << "y" << "b" ).woCompare( BSON( "x" << "c" ) , key ) < 0 );
+
+ key = BSON( "" << 1 << "" << 1 );
+
+ ASSERT( BSON( "" << "c" ).woSortOrder( BSON( "" << "b" << "" << "h" ) , key ) > 0 );
+ ASSERT( BSON( "" << "b" << "" << "b" ).woCompare( BSON( "" << "c" ) , key ) < 0 );
+
+ {
+ BSONObjBuilder b;
+ b.append( "" , "c" );
+ b.appendNull( "" );
+ BSONObj o = b.obj();
+ ASSERT( o.woSortOrder( BSON( "" << "b" << "" << "h" ) , key ) > 0 );
+ ASSERT( BSON( "" << "b" << "" << "h" ).woSortOrder( o , key ) < 0 );
+
+ }
+
+
+ ASSERT( BSON( "" << "a" ).woCompare( BSON( "" << "a" << "" << "c" ) ) < 0 );
+ {
+ BSONObjBuilder b;
+ b.append( "" , "a" );
+ b.appendNull( "" );
+ ASSERT( b.obj().woCompare( BSON( "" << "a" << "" << "c" ) ) < 0 ); // SERVER-282
+ }
+
+ }
+ };
+
+ class TimestampTest : public Base {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendTimestamp( "a" );
+ BSONObj o = b.done();
+ o.toString();
+ ASSERT( o.valid() );
+ ASSERT_EQUALS( Timestamp, o.getField( "a" ).type() );
+ BSONObjIterator i( o );
+ ASSERT( i.moreWithEOO() );
+ ASSERT( i.more() );
+
+ BSONElement e = i.next();
+ ASSERT_EQUALS( Timestamp, e.type() );
+ ASSERT( i.moreWithEOO() );
+ ASSERT( ! i.more() );
+
+ e = i.next();
+ ASSERT( e.eoo() );
+
+ OpTime before = OpTime::now();
+ BSONElementManipulator( o.firstElement() ).initTimestamp();
+ OpTime after = OpTime::now();
+
+ OpTime test = OpTime( o.firstElement().date() );
+ ASSERT( before < test && test < after );
+
+ BSONElementManipulator( o.firstElement() ).initTimestamp();
+ test = OpTime( o.firstElement().date() );
+ ASSERT( before < test && test < after );
+ }
+ };
+
+ class Nan : public Base {
+ public:
+ void run() {
+ double inf = numeric_limits< double >::infinity();
+ double nan = numeric_limits< double >::quiet_NaN();
+ double nan2 = numeric_limits< double >::signaling_NaN();
+
+ ASSERT( BSON( "a" << inf ).woCompare( BSON( "a" << inf ) ) == 0 );
+ ASSERT( BSON( "a" << inf ).woCompare( BSON( "a" << 1 ) ) < 0 );
+ ASSERT( BSON( "a" << 1 ).woCompare( BSON( "a" << inf ) ) > 0 );
+
+ ASSERT( BSON( "a" << nan ).woCompare( BSON( "a" << nan ) ) == 0 );
+ ASSERT( BSON( "a" << nan ).woCompare( BSON( "a" << 1 ) ) < 0 );
+ ASSERT( BSON( "a" << 1 ).woCompare( BSON( "a" << nan ) ) > 0 );
+
+ ASSERT( BSON( "a" << nan2 ).woCompare( BSON( "a" << nan2 ) ) == 0 );
+ ASSERT( BSON( "a" << nan2 ).woCompare( BSON( "a" << 1 ) ) < 0 );
+ ASSERT( BSON( "a" << 1 ).woCompare( BSON( "a" << nan2 ) ) > 0 );
+
+ ASSERT( BSON( "a" << inf ).woCompare( BSON( "a" << nan ) ) == 0 );
+ ASSERT( BSON( "a" << inf ).woCompare( BSON( "a" << nan2 ) ) == 0 );
+ ASSERT( BSON( "a" << nan ).woCompare( BSON( "a" << nan2 ) ) == 0 );
+ }
+ };
+
+ namespace Validation {
+
+ class Base {
+ public:
+ virtual ~Base() {}
+ void run() {
+ ASSERT( valid().valid() );
+ ASSERT( !invalid().valid() );
+ }
+ protected:
+ virtual BSONObj valid() const { return BSONObj(); }
+ virtual BSONObj invalid() const { return BSONObj(); }
+ static char get( const BSONObj &o, int i ) {
+ return o.objdata()[ i ];
+ }
+ static void set( BSONObj &o, int i, char c ) {
+ const_cast< char * >( o.objdata() )[ i ] = c;
+ }
+ };
+
+ class BadType : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":1}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ set( ret, 4, 50 );
+ return ret;
+ }
+ };
+
+ class EooBeforeEnd : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":1}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ // (first byte of size)++
+ set( ret, 0, get( ret, 0 ) + 1 );
+ // re-read size for BSONObj::details
+ return ret.copy();
+ }
+ };
+
+ class Undefined : public Base {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendNull( "a" );
+ BSONObj o = b.done();
+ set( o, 4, mongo::Undefined );
+ ASSERT( o.valid() );
+ }
+ };
+
+ class TotalSizeTooSmall : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":1}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ // (first byte of size)--
+ set( ret, 0, get( ret, 0 ) - 1 );
+ // re-read size for BSONObj::details
+ return ret.copy();
+ }
+ };
+
+ class EooMissing : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":1}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ set( ret, ret.objsize() - 1, (char) 0xff );
+ // (first byte of size)--
+ set( ret, 0, get( ret, 0 ) - 1 );
+ // re-read size for BSONObj::details
+ return ret.copy();
+ }
+ };
+
+ class WrongStringSize : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":\"b\"}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ set( ret, 0, get( ret, 0 ) + 1 );
+ set( ret, 7, get( ret, 7 ) + 1 );
+ return ret.copy();
+ }
+ };
+
+ class ZeroStringSize : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":\"b\"}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ set( ret, 7, 0 );
+ return ret;
+ }
+ };
+
+ class NegativeStringSize : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":\"b\"}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ set( ret, 10, -100 );
+ return ret;
+ }
+ };
+
+ class WrongSubobjectSize : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":{\"b\":1}}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ set( ret, 0, get( ret, 0 ) + 1 );
+ set( ret, 7, get( ret, 7 ) + 1 );
+ return ret.copy();
+ }
+ };
+
+ class WrongDbrefNsSize : public Base {
+ BSONObj valid() const {
+ return fromjson( "{ \"a\": Dbref( \"b\", \"ffffffffffffffffffffffff\" ) }" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ set( ret, 0, get( ret, 0 ) + 1 );
+ set( ret, 7, get( ret, 7 ) + 1 );
+ return ret.copy();
+ };
+ };
+
+ class WrongSymbolSize : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":\"b\"}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ set( ret, 4, Symbol );
+ set( ret, 0, get( ret, 0 ) + 1 );
+ set( ret, 7, get( ret, 7 ) + 1 );
+ return ret.copy();
+ }
+ };
+
+ class WrongCodeSize : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":\"b\"}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ set( ret, 4, Code );
+ set( ret, 0, get( ret, 0 ) + 1 );
+ set( ret, 7, get( ret, 7 ) + 1 );
+ return ret.copy();
+ }
+ };
+
+ class NoFieldNameEnd : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":1}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ memset( const_cast< char * >( ret.objdata() ) + 5, 0xff, ret.objsize() - 5 );
+ return ret;
+ }
+ };
+
+ class BadRegex : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":/c/i}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ memset( const_cast< char * >( ret.objdata() ) + 7, 0xff, ret.objsize() - 7 );
+ return ret;
+ }
+ };
+
+ class BadRegexOptions : public Base {
+ BSONObj valid() const {
+ return fromjson( "{\"a\":/c/i}" );
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ memset( const_cast< char * >( ret.objdata() ) + 9, 0xff, ret.objsize() - 9 );
+ return ret;
+ }
+ };
+
+ class CodeWScopeBase : public Base {
+ BSONObj valid() const {
+ BSONObjBuilder b;
+ BSONObjBuilder scope;
+ scope.append( "a", "b" );
+ b.appendCodeWScope( "c", "d", scope.done() );
+ return b.obj();
+ }
+ BSONObj invalid() const {
+ BSONObj ret = valid();
+ modify( ret );
+ return ret;
+ }
+ protected:
+ virtual void modify( BSONObj &o ) const = 0;
+ };
+
+ class CodeWScopeSmallSize : public CodeWScopeBase {
+ void modify( BSONObj &o ) const {
+ set( o, 7, 7 );
+ }
+ };
+
+ class CodeWScopeZeroStrSize : public CodeWScopeBase {
+ void modify( BSONObj &o ) const {
+ set( o, 11, 0 );
+ }
+ };
+
+ class CodeWScopeSmallStrSize : public CodeWScopeBase {
+ void modify( BSONObj &o ) const {
+ set( o, 11, 1 );
+ }
+ };
+
+ class CodeWScopeNoSizeForObj : public CodeWScopeBase {
+ void modify( BSONObj &o ) const {
+ set( o, 7, 13 );
+ }
+ };
+
+ class CodeWScopeSmallObjSize : public CodeWScopeBase {
+ void modify( BSONObj &o ) const {
+ set( o, 17, 1 );
+ }
+ };
+
+ class CodeWScopeBadObject : public CodeWScopeBase {
+ void modify( BSONObj &o ) const {
+ set( o, 21, JSTypeMax + 1 );
+ }
+ };
+
+ class NoSize {
+ public:
+ NoSize( BSONType type ) : type_( type ) {}
+ void run() {
+ const char data[] = { 0x07, 0x00, 0x00, 0x00, char( type_ ), 'a', 0x00 };
+ BSONObj o( data );
+ ASSERT( !o.valid() );
+ }
+ private:
+ BSONType type_;
+ };
+
+ // Randomized BSON parsing test. See if we seg fault.
+ class Fuzz {
+ public:
+ Fuzz( double frequency ) : frequency_( frequency ) {}
+ void run() {
+ BSONObj a = fromjson( "{\"a\": 1, \"b\": \"c\"}" );
+ fuzz( a );
+ a.valid();
+
+ BSONObj b = fromjson( "{\"one\":2, \"two\":5, \"three\": {},"
+ "\"four\": { \"five\": { \"six\" : 11 } },"
+ "\"seven\": [ \"a\", \"bb\", \"ccc\", 5 ],"
+ "\"eight\": Dbref( \"rrr\", \"01234567890123456789aaaa\" ),"
+ "\"_id\": ObjectId( \"deadbeefdeadbeefdeadbeef\" ),"
+ "\"nine\": { \"$binary\": \"abc=\", \"$type\": \"02\" },"
+ "\"ten\": Date( 44 ), \"eleven\": /foooooo/i }" );
+ fuzz( b );
+ b.valid();
+ }
+ private:
+ void fuzz( BSONObj &o ) const {
+ for( int i = 4; i < o.objsize(); ++i )
+ for( unsigned char j = 1; j; j <<= 1 )
+ if ( rand() < int( RAND_MAX * frequency_ ) ) {
+ char *c = const_cast< char * >( o.objdata() ) + i;
+ if ( *c & j )
+ *c &= ~j;
+ else
+ *c |= j;
+ }
+ }
+ double frequency_;
+ };
+
+ } // namespace Validation
+
+ } // namespace BSONObjTests
+
+ namespace OIDTests {
+
+ class init1 {
+ public:
+ void run(){
+ OID a;
+ OID b;
+
+ a.init();
+ b.init();
+
+ ASSERT( a != b );
+ }
+ };
+
+ class initParse1 {
+ public:
+ void run(){
+
+ OID a;
+ OID b;
+
+ a.init();
+ b.init( a.str() );
+
+ ASSERT( a == b );
+ }
+ };
+
+ class append {
+ public:
+ void run(){
+ BSONObjBuilder b;
+ b.appendOID( "a" , 0 );
+ b.appendOID( "b" , 0 , false );
+ b.appendOID( "c" , 0 , true );
+ BSONObj o = b.obj();
+
+ ASSERT( o["a"].__oid().str() == "000000000000000000000000" );
+ ASSERT( o["b"].__oid().str() == "000000000000000000000000" );
+ ASSERT( o["c"].__oid().str() != "000000000000000000000000" );
+
+ }
+ };
+
+ class increasing {
+ public:
+ BSONObj g(){
+ BSONObjBuilder b;
+ b.appendOID( "_id" , 0 , true );
+ return b.obj();
+ }
+ void run(){
+ BSONObj a = g();
+ BSONObj b = g();
+
+ ASSERT( a.woCompare( b ) < 0 );
+
+ // yes, there is a 1/1000 chance this won't increase time(0)
+ // and therefore inaccurately say the function is behaving
+ // buf if its broken, it will fail 999/1000, so i think that's good enough
+ sleepsecs( 1 );
+ BSONObj c = g();
+ ASSERT( a.woCompare( c ) < 0 );
+ }
+ };
+ } // namespace OIDTests
+
+ namespace ValueStreamTests {
+
+ class LabelBase {
+ public:
+ virtual ~LabelBase() {}
+ void run() {
+ ASSERT( !expected().woCompare( actual() ) );
+ }
+ protected:
+ virtual BSONObj expected() = 0;
+ virtual BSONObj actual() = 0;
+ };
+
+ class LabelBasic : public LabelBase {
+ BSONObj expected() {
+ return BSON( "a" << ( BSON( "$gt" << 1 ) ) );
+ }
+ BSONObj actual() {
+ return BSON( "a" << GT << 1 );
+ }
+ };
+
+ class LabelShares : public LabelBase {
+ BSONObj expected() {
+ return BSON( "z" << "q" << "a" << ( BSON( "$gt" << 1 ) ) << "x" << "p" );
+ }
+ BSONObj actual() {
+ return BSON( "z" << "q" << "a" << GT << 1 << "x" << "p" );
+ }
+ };
+
+ class LabelDouble : public LabelBase {
+ BSONObj expected() {
+ return BSON( "a" << ( BSON( "$gt" << 1 << "$lte" << "x" ) ) );
+ }
+ BSONObj actual() {
+ return BSON( "a" << GT << 1 << LTE << "x" );
+ }
+ };
+
+ class LabelDoubleShares : public LabelBase {
+ BSONObj expected() {
+ return BSON( "z" << "q" << "a" << ( BSON( "$gt" << 1 << "$lte" << "x" ) ) << "x" << "p" );
+ }
+ BSONObj actual() {
+ return BSON( "z" << "q" << "a" << GT << 1 << LTE << "x" << "x" << "p" );
+ }
+ };
+
+ class LabelSize : public LabelBase {
+ BSONObj expected() {
+ return BSON( "a" << BSON( "$size" << 4 ) );
+ }
+ BSONObj actual() {
+ return BSON( "a" << mongo::SIZE << 4 );
+ }
+ };
+
+ class LabelMulti : public LabelBase {
+ BSONObj expected() {
+ return BSON( "z" << "q"
+ << "a" << BSON( "$gt" << 1 << "$lte" << "x" )
+ << "b" << BSON( "$ne" << 1 << "$ne" << "f" << "$ne" << 22.3 )
+ << "x" << "p" );
+ }
+ BSONObj actual() {
+ return BSON( "z" << "q"
+ << "a" << GT << 1 << LTE << "x"
+ << "b" << NE << 1 << NE << "f" << NE << 22.3
+ << "x" << "p" );
+ }
+ };
+
+ class Unallowed {
+ public:
+ void run() {
+ ASSERT_EXCEPTION( BSON( GT << 4 ), MsgAssertionException );
+ ASSERT_EXCEPTION( BSON( "a" << 1 << GT << 4 ), MsgAssertionException );
+ }
+ };
+
+ class ElementAppend {
+ public:
+ void run(){
+ BSONObj a = BSON( "a" << 17 );
+ BSONObj b = BSON( "b" << a["a"] );
+ ASSERT_EQUALS( NumberInt , a["a"].type() );
+ ASSERT_EQUALS( NumberInt , b["b"].type() );
+ ASSERT_EQUALS( 17 , b["b"].number() );
+ }
+ };
+
+ } // namespace ValueStreamTests
+
+ class SubObjectBuilder {
+ public:
+ void run() {
+ BSONObjBuilder b1;
+ b1.append( "a", "bcd" );
+ BSONObjBuilder b2( b1.subobjStart( "foo" ) );
+ b2.append( "ggg", 44.0 );
+ b2.done();
+ b1.append( "f", 10.0 );
+ BSONObj ret = b1.done();
+ ASSERT( ret.valid() );
+ ASSERT( ret.woCompare( fromjson( "{a:'bcd',foo:{ggg:44},f:10}" ) ) == 0 );
+ }
+ };
+
+ class DateBuilder {
+ public:
+ void run() {
+ BSONObj o = BSON("" << Date_t(1234567890));
+ ASSERT( o.firstElement().type() == Date );
+ ASSERT( o.firstElement().date() == Date_t(1234567890) );
+ }
+ };
+
+ class DateNowBuilder {
+ public:
+ void run() {
+ Date_t before = jsTime();
+ BSONObj o = BSON("now" << DATENOW);
+ Date_t after = jsTime();
+
+ ASSERT( o.valid() );
+
+ BSONElement e = o["now"];
+ ASSERT( e.type() == Date );
+ ASSERT( e.date() >= before );
+ ASSERT( e.date() <= after );
+ }
+ };
+
+ class TimeTBuilder {
+ public:
+ void run() {
+ Date_t before = jsTime();
+ time_t now = time(NULL);
+ Date_t after = jsTime();
+
+ BSONObjBuilder b;
+ b.appendTimeT("now", now);
+ BSONObj o = b.obj();
+
+ ASSERT( o.valid() );
+
+ BSONElement e = o["now"];
+ ASSERT( e.type() == Date );
+ ASSERT( e.date()/1000 >= before/1000 );
+ ASSERT( e.date()/1000 <= after/1000 );
+ }
+ };
+
+ class MinMaxElementTest {
+ public:
+
+ BSONObj min( int t ){
+ BSONObjBuilder b;
+ b.appendMinForType( "a" , t );
+ return b.obj();
+ }
+
+ BSONObj max( int t ){
+ BSONObjBuilder b;
+ b.appendMaxForType( "a" , t );
+ return b.obj();
+ }
+
+ void run(){
+ for ( int t=1; t<JSTypeMax; t++ ){
+ stringstream ss;
+ ss << "type: " << t;
+ string s = ss.str();
+ massert( 10403 , s , min( t ).woCompare( max( t ) ) < 0 );
+ massert( 10404 , s , max( t ).woCompare( min( t ) ) > 0 );
+ massert( 10405 , s , min( t ).woCompare( min( t ) ) == 0 );
+ massert( 10406 , s , max( t ).woCompare( max( t ) ) == 0 );
+ massert( 10407 , s , abs( min( t ).firstElement().canonicalType() - max( t ).firstElement().canonicalType() ) <= 10 );
+ }
+ }
+
+
+
+ };
+
+ class ExtractFieldsTest {
+ public:
+ void run(){
+ BSONObj x = BSON( "a" << 10 << "b" << 11 );
+ assert( BSON( "a" << 10 ).woCompare( x.extractFields( BSON( "a" << 1 ) ) ) == 0 );
+ assert( BSON( "b" << 11 ).woCompare( x.extractFields( BSON( "b" << 1 ) ) ) == 0 );
+ assert( x.woCompare( x.extractFields( BSON( "a" << 1 << "b" << 1 ) ) ) == 0 );
+
+ assert( (string)"a" == x.extractFields( BSON( "a" << 1 << "c" << 1 ) ).firstElement().fieldName() );
+ }
+ };
+
+ class ComparatorTest {
+ public:
+ BSONObj one( string s ){
+ return BSON( "x" << s );
+ }
+ BSONObj two( string x , string y ){
+ BSONObjBuilder b;
+ b.append( "x" , x );
+ if ( y.size() )
+ b.append( "y" , y );
+ else
+ b.appendNull( "y" );
+ return b.obj();
+ }
+
+ void test( BSONObj order , BSONObj l , BSONObj r , bool wanted ){
+ BSONObjCmp c( order );
+ bool got = c(l,r);
+ if ( got == wanted )
+ return;
+ cout << " order: " << order << " l: " << l << "r: " << r << " wanted: " << wanted << " got: " << got << endl;
+ }
+
+ void lt( BSONObj order , BSONObj l , BSONObj r ){
+ test( order , l , r , 1 );
+ }
+
+ void run(){
+ BSONObj s = BSON( "x" << 1 );
+ BSONObj c = BSON( "x" << 1 << "y" << 1 );
+ test( s , one( "A" ) , one( "B" ) , 1 );
+ test( s , one( "B" ) , one( "A" ) , 0 );
+
+ test( c , two( "A" , "A" ) , two( "A" , "B" ) , 1 );
+ test( c , two( "A" , "A" ) , two( "B" , "A" ) , 1 );
+ test( c , two( "B" , "A" ) , two( "A" , "B" ) , 0 );
+
+ lt( c , one("A") , two( "A" , "A" ) );
+ lt( c , one("A") , one( "B" ) );
+ lt( c , two("A","") , two( "B" , "A" ) );
+
+ lt( c , two("B","A") , two( "C" , "A" ) );
+ lt( c , two("B","A") , one( "C" ) );
+ lt( c , two("B","A") , two( "C" , "" ) );
+
+ }
+ };
+
+ namespace external_sort {
+ class Basic1 {
+ public:
+ void run(){
+ BSONObjExternalSorter sorter;
+ sorter.add( BSON( "x" << 10 ) , 5 , 1);
+ sorter.add( BSON( "x" << 2 ) , 3 , 1 );
+ sorter.add( BSON( "x" << 5 ) , 6 , 1 );
+ sorter.add( BSON( "x" << 5 ) , 7 , 1 );
+
+ sorter.sort();
+
+ auto_ptr<BSONObjExternalSorter::Iterator> i = sorter.iterator();
+ int num=0;
+ while ( i->more() ){
+ pair<BSONObj,DiskLoc> p = i->next();
+ if ( num == 0 )
+ assert( p.first["x"].number() == 2 );
+ else if ( num <= 2 ){
+ assert( p.first["x"].number() == 5 );
+ }
+ else if ( num == 3 )
+ assert( p.first["x"].number() == 10 );
+ else
+ ASSERT( 0 );
+ num++;
+ }
+
+
+ ASSERT_EQUALS( 0 , sorter.numFiles() );
+ }
+ };
+
+ class Basic2 {
+ public:
+ void run(){
+ BSONObjExternalSorter sorter( BSONObj() , 10 );
+ sorter.add( BSON( "x" << 10 ) , 5 , 11 );
+ sorter.add( BSON( "x" << 2 ) , 3 , 1 );
+ sorter.add( BSON( "x" << 5 ) , 6 , 1 );
+ sorter.add( BSON( "x" << 5 ) , 7 , 1 );
+
+ sorter.sort();
+
+ auto_ptr<BSONObjExternalSorter::Iterator> i = sorter.iterator();
+ int num=0;
+ while ( i->more() ){
+ pair<BSONObj,DiskLoc> p = i->next();
+ if ( num == 0 ){
+ assert( p.first["x"].number() == 2 );
+ ASSERT_EQUALS( p.second.toString() , "3:1" );
+ }
+ else if ( num <= 2 )
+ assert( p.first["x"].number() == 5 );
+ else if ( num == 3 ){
+ assert( p.first["x"].number() == 10 );
+ ASSERT_EQUALS( p.second.toString() , "5:b" );
+ }
+ else
+ ASSERT( 0 );
+ num++;
+ }
+
+ }
+ };
+
+ class Basic3 {
+ public:
+ void run(){
+ BSONObjExternalSorter sorter( BSONObj() , 10 );
+ sorter.sort();
+
+ auto_ptr<BSONObjExternalSorter::Iterator> i = sorter.iterator();
+ assert( ! i->more() );
+
+ }
+ };
+
+
+ class ByDiskLock {
+ public:
+ void run(){
+ BSONObjExternalSorter sorter;
+ sorter.add( BSON( "x" << 10 ) , 5 , 4);
+ sorter.add( BSON( "x" << 2 ) , 3 , 0 );
+ sorter.add( BSON( "x" << 5 ) , 6 , 2 );
+ sorter.add( BSON( "x" << 5 ) , 7 , 3 );
+ sorter.add( BSON( "x" << 5 ) , 2 , 1 );
+
+ sorter.sort();
+
+ auto_ptr<BSONObjExternalSorter::Iterator> i = sorter.iterator();
+ int num=0;
+ while ( i->more() ){
+ pair<BSONObj,DiskLoc> p = i->next();
+ if ( num == 0 )
+ assert( p.first["x"].number() == 2 );
+ else if ( num <= 3 ){
+ assert( p.first["x"].number() == 5 );
+ }
+ else if ( num == 4 )
+ assert( p.first["x"].number() == 10 );
+ else
+ ASSERT( 0 );
+ ASSERT_EQUALS( num , p.second.getOfs() );
+ num++;
+ }
+
+
+ }
+ };
+
+
+ class Big1 {
+ public:
+ void run(){
+ BSONObjExternalSorter sorter( BSONObj() , 2000 );
+ for ( int i=0; i<10000; i++ ){
+ sorter.add( BSON( "x" << rand() % 10000 ) , 5 , i );
+ }
+
+ sorter.sort();
+
+ auto_ptr<BSONObjExternalSorter::Iterator> i = sorter.iterator();
+ int num=0;
+ double prev = 0;
+ while ( i->more() ){
+ pair<BSONObj,DiskLoc> p = i->next();
+ num++;
+ double cur = p.first["x"].number();
+ assert( cur >= prev );
+ prev = cur;
+ }
+ assert( num == 10000 );
+ }
+ };
+
+ class Big2 {
+ public:
+ void run(){
+ const int total = 100000;
+ BSONObjExternalSorter sorter( BSONObj() , total * 2 );
+ for ( int i=0; i<total; i++ ){
+ sorter.add( BSON( "a" << "b" ) , 5 , i );
+ }
+
+ sorter.sort();
+
+ auto_ptr<BSONObjExternalSorter::Iterator> i = sorter.iterator();
+ int num=0;
+ double prev = 0;
+ while ( i->more() ){
+ pair<BSONObj,DiskLoc> p = i->next();
+ num++;
+ double cur = p.first["x"].number();
+ assert( cur >= prev );
+ prev = cur;
+ }
+ assert( num == total );
+ ASSERT( sorter.numFiles() > 2 );
+ }
+ };
+
+ class D1 {
+ public:
+ void run(){
+
+ BSONObjBuilder b;
+ b.appendNull("");
+ BSONObj x = b.obj();
+
+ BSONObjExternalSorter sorter;
+ sorter.add(x, DiskLoc(3,7));
+ sorter.add(x, DiskLoc(4,7));
+ sorter.add(x, DiskLoc(2,7));
+ sorter.add(x, DiskLoc(1,7));
+ sorter.add(x, DiskLoc(3,77));
+
+ sorter.sort();
+
+ auto_ptr<BSONObjExternalSorter::Iterator> i = sorter.iterator();
+ while( i->more() ) {
+ BSONObjExternalSorter::Data d = i->next();
+ cout << d.second.toString() << endl;
+ cout << d.first.objsize() << endl;
+ cout<<"SORTER next:" << d.first.toString() << endl;
+ }
+ }
+ };
+ }
+
+ class CompatBSON {
+ public:
+
+#define JSONBSONTEST(j,s,m) ASSERT_EQUALS( fromjson( j ).objsize() , s ); ASSERT_EQUALS( fromjson( j ).md5() , m );
+#define RAWBSONTEST(j,s,m) ASSERT_EQUALS( j.objsize() , s ); ASSERT_EQUALS( j.md5() , m );
+
+ void run(){
+
+ JSONBSONTEST( "{ 'x' : true }" , 9 , "6fe24623e4efc5cf07f027f9c66b5456" );
+ JSONBSONTEST( "{ 'x' : null }" , 8 , "12d43430ff6729af501faf0638e68888" );
+ JSONBSONTEST( "{ 'x' : 5.2 }" , 16 , "aaeeac4a58e9c30eec6b0b0319d0dff2" );
+ JSONBSONTEST( "{ 'x' : 'eliot' }" , 18 , "331a3b8b7cbbe0706c80acdb45d4ebbe" );
+ JSONBSONTEST( "{ 'x' : 5.2 , 'y' : 'truth' , 'z' : 1.1 }" , 40 , "7c77b3a6e63e2f988ede92624409da58" );
+ JSONBSONTEST( "{ 'a' : { 'b' : 1.1 } }" , 24 , "31887a4b9d55cd9f17752d6a8a45d51f" );
+ JSONBSONTEST( "{ 'x' : 5.2 , 'y' : { 'a' : 'eliot' , b : true } , 'z' : null }" , 44 , "b3de8a0739ab329e7aea138d87235205" );
+ JSONBSONTEST( "{ 'x' : 5.2 , 'y' : [ 'a' , 'eliot' , 'b' , true ] , 'z' : null }" , 62 , "cb7bad5697714ba0cbf51d113b6a0ee8" );
+
+ RAWBSONTEST( BSON( "x" << 4 ) , 12 , "d1ed8dbf79b78fa215e2ded74548d89d" );
+
+ }
+ };
+
+ class CompareDottedFieldNamesTest {
+ public:
+ void t( FieldCompareResult res , const string& l , const string& r ){
+ ASSERT_EQUALS( res , compareDottedFieldNames( l , r ) );
+ ASSERT_EQUALS( -1 * res , compareDottedFieldNames( r , l ) );
+ }
+
+ void run(){
+ t( SAME , "x" , "x" );
+ t( SAME , "x.a" , "x.a" );
+ t( LEFT_BEFORE , "a" , "b" );
+ t( RIGHT_BEFORE , "b" , "a" );
+
+ t( LEFT_SUBFIELD , "a.x" , "a" );
+ }
+ };
+
+ struct NestedDottedConversions{
+ void t(const BSONObj& nest, const BSONObj& dot){
+ ASSERT_EQUALS( nested2dotted(nest), dot);
+ ASSERT_EQUALS( nest, dotted2nested(dot));
+ }
+
+ void run(){
+ t( BSON("a" << BSON("b" << 1)), BSON("a.b" << 1) );
+ t( BSON("a" << BSON("b" << 1 << "c" << 1)), BSON("a.b" << 1 << "a.c" << 1) );
+ t( BSON("a" << BSON("b" << 1 << "c" << 1) << "d" << 1), BSON("a.b" << 1 << "a.c" << 1 << "d" << 1) );
+ t( BSON("a" << BSON("b" << 1 << "c" << 1 << "e" << BSON("f" << 1)) << "d" << 1), BSON("a.b" << 1 << "a.c" << 1 << "a.e.f" << 1 << "d" << 1) );
+ }
+ };
+
+ struct BSONArrayBuilderTest{
+ void run(){
+ int i = 0;
+ BSONObjBuilder objb;
+ BSONArrayBuilder arrb;
+
+ objb << objb.numStr(i++) << 100;
+ arrb << 100;
+
+ objb << objb.numStr(i++) << 1.0;
+ arrb << 1.0;
+
+ objb << objb.numStr(i++) << "Hello";
+ arrb << "Hello";
+
+ objb << objb.numStr(i++) << string("World");
+ arrb << string("World");
+
+ objb << objb.numStr(i++) << BSON( "a" << 1 << "b" << "foo" );
+ arrb << BSON( "a" << 1 << "b" << "foo" );
+
+ objb << objb.numStr(i++) << BSON( "a" << 1)["a"];
+ arrb << BSON( "a" << 1)["a"];
+
+ OID oid;
+ oid.init();
+ objb << objb.numStr(i++) << oid;
+ arrb << oid;
+
+ BSONObj obj = objb.obj();
+ BSONArray arr = arrb.arr();
+
+ ASSERT_EQUALS(obj, arr);
+
+ BSONObj o = BSON( "obj" << obj << "arr" << arr << "arr2" << BSONArray(obj) );
+ ASSERT_EQUALS(o["obj"].type(), Object);
+ ASSERT_EQUALS(o["arr"].type(), Array);
+ ASSERT_EQUALS(o["arr2"].type(), Array);
+ }
+ };
+
+ struct ArrayMacroTest{
+ void run(){
+ BSONArray arr = BSON_ARRAY( "hello" << 1 << BSON( "foo" << BSON_ARRAY( "bar" << "baz" << "qux" ) ) );
+ BSONObj obj = BSON( "0" << "hello"
+ << "1" << 1
+ << "2" << BSON( "foo" << BSON_ARRAY( "bar" << "baz" << "qux" ) ) );
+
+ ASSERT_EQUALS(arr, obj);
+ ASSERT_EQUALS(arr["2"].type(), Object);
+ ASSERT_EQUALS(arr["2"].embeddedObject()["foo"].type(), Array);
+ }
+ };
+
+ class NumberParsing {
+ public:
+ void run(){
+ BSONObjBuilder a;
+ BSONObjBuilder b;
+
+ a.append( "a" , (int)1 );
+ ASSERT( b.appendAsNumber( "a" , "1" ) );
+
+ a.append( "b" , 1.1 );
+ ASSERT( b.appendAsNumber( "b" , "1.1" ) );
+
+ a.append( "c" , (int)-1 );
+ ASSERT( b.appendAsNumber( "c" , "-1" ) );
+
+ a.append( "d" , -1.1 );
+ ASSERT( b.appendAsNumber( "d" , "-1.1" ) );
+
+ a.append( "e" , (long long)32131231231232313LL );
+ ASSERT( b.appendAsNumber( "e" , "32131231231232313" ) );
+
+ ASSERT( ! b.appendAsNumber( "f" , "zz" ) );
+ ASSERT( ! b.appendAsNumber( "f" , "5zz" ) );
+ ASSERT( ! b.appendAsNumber( "f" , "zz5" ) );
+
+ ASSERT_EQUALS( a.obj() , b.obj() );
+ }
+ };
+
+ class bson2settest {
+ public:
+ void run(){
+ BSONObj o = BSON( "z" << 1 << "a" << 2 << "m" << 3 << "c" << 4 );
+ BSONObjIteratorSorted i( o );
+ stringstream ss;
+ while ( i.more() )
+ ss << i.next().fieldName();
+ ASSERT_EQUALS( "acmz" , ss.str() );
+
+ {
+ Timer t;
+ for ( int i=0; i<10000; i++ ){
+ BSONObjIteratorSorted j( o );
+ int l = 0;
+ while ( j.more() )
+ l += strlen( j.next().fieldName() );
+ }
+ unsigned long long tm = t.micros();
+ cout << "time: " << tm << endl;
+ }
+ }
+
+ };
+
+ class checkForStorageTests {
+ public:
+
+ void good( string s ){
+ BSONObj o = fromjson( s );
+ if ( o.okForStorage() )
+ return;
+ throw UserException( 12528 , (string)"should be ok for storage:" + s );
+ }
+
+ void bad( string s ){
+ BSONObj o = fromjson( s );
+ if ( ! o.okForStorage() )
+ return;
+ throw UserException( 12529 , (string)"should NOT be ok for storage:" + s );
+ }
+
+ void run(){
+ good( "{x:1}" );
+ bad( "{'x.y':1}" );
+
+ good( "{x:{a:2}}" );
+ bad( "{x:{'$a':2}}" );
+ }
+ };
+
+ class All : public Suite {
+ public:
+ All() : Suite( "jsobj" ){
+ }
+
+ void setupTests(){
+ add< BufBuilderBasic >();
+ add< BSONElementBasic >();
+ add< BSONObjTests::Create >();
+ add< BSONObjTests::WoCompareBasic >();
+ add< BSONObjTests::NumericCompareBasic >();
+ add< BSONObjTests::WoCompareEmbeddedObject >();
+ add< BSONObjTests::WoCompareEmbeddedArray >();
+ add< BSONObjTests::WoCompareOrdered >();
+ add< BSONObjTests::WoCompareDifferentLength >();
+ add< BSONObjTests::WoSortOrder >();
+ add< BSONObjTests::MultiKeySortOrder > ();
+ add< BSONObjTests::TimestampTest >();
+ add< BSONObjTests::Nan >();
+ add< BSONObjTests::Validation::BadType >();
+ add< BSONObjTests::Validation::EooBeforeEnd >();
+ add< BSONObjTests::Validation::Undefined >();
+ add< BSONObjTests::Validation::TotalSizeTooSmall >();
+ add< BSONObjTests::Validation::EooMissing >();
+ add< BSONObjTests::Validation::WrongStringSize >();
+ add< BSONObjTests::Validation::ZeroStringSize >();
+ add< BSONObjTests::Validation::NegativeStringSize >();
+ add< BSONObjTests::Validation::WrongSubobjectSize >();
+ add< BSONObjTests::Validation::WrongDbrefNsSize >();
+ add< BSONObjTests::Validation::WrongSymbolSize >();
+ add< BSONObjTests::Validation::WrongCodeSize >();
+ add< BSONObjTests::Validation::NoFieldNameEnd >();
+ add< BSONObjTests::Validation::BadRegex >();
+ add< BSONObjTests::Validation::BadRegexOptions >();
+ add< BSONObjTests::Validation::CodeWScopeSmallSize >();
+ add< BSONObjTests::Validation::CodeWScopeZeroStrSize >();
+ add< BSONObjTests::Validation::CodeWScopeSmallStrSize >();
+ add< BSONObjTests::Validation::CodeWScopeNoSizeForObj >();
+ add< BSONObjTests::Validation::CodeWScopeSmallObjSize >();
+ add< BSONObjTests::Validation::CodeWScopeBadObject >();
+ add< BSONObjTests::Validation::NoSize >( Symbol );
+ add< BSONObjTests::Validation::NoSize >( Code );
+ add< BSONObjTests::Validation::NoSize >( String );
+ add< BSONObjTests::Validation::NoSize >( CodeWScope );
+ add< BSONObjTests::Validation::NoSize >( DBRef );
+ add< BSONObjTests::Validation::NoSize >( Object );
+ add< BSONObjTests::Validation::NoSize >( Array );
+ add< BSONObjTests::Validation::NoSize >( BinData );
+ add< BSONObjTests::Validation::Fuzz >( .5 );
+ add< BSONObjTests::Validation::Fuzz >( .1 );
+ add< BSONObjTests::Validation::Fuzz >( .05 );
+ add< BSONObjTests::Validation::Fuzz >( .01 );
+ add< BSONObjTests::Validation::Fuzz >( .001 );
+ add< OIDTests::init1 >();
+ add< OIDTests::initParse1 >();
+ add< OIDTests::append >();
+ add< OIDTests::increasing >();
+ add< ValueStreamTests::LabelBasic >();
+ add< ValueStreamTests::LabelShares >();
+ add< ValueStreamTests::LabelDouble >();
+ add< ValueStreamTests::LabelDoubleShares >();
+ add< ValueStreamTests::LabelSize >();
+ add< ValueStreamTests::LabelMulti >();
+ add< ValueStreamTests::Unallowed >();
+ add< ValueStreamTests::ElementAppend >();
+ add< SubObjectBuilder >();
+ add< DateBuilder >();
+ add< DateNowBuilder >();
+ add< TimeTBuilder >();
+ add< MinMaxElementTest >();
+ add< ComparatorTest >();
+ add< ExtractFieldsTest >();
+ add< external_sort::Basic1 >();
+ add< external_sort::Basic2 >();
+ add< external_sort::Basic3 >();
+ add< external_sort::ByDiskLock >();
+ add< external_sort::Big1 >();
+ add< external_sort::Big2 >();
+ add< external_sort::D1 >();
+ add< CompatBSON >();
+ add< CompareDottedFieldNamesTest >();
+ add< NestedDottedConversions >();
+ add< BSONArrayBuilderTest >();
+ add< ArrayMacroTest >();
+ add< NumberParsing >();
+ add< bson2settest >();
+ add< checkForStorageTests >();
+
+ }
+ } myall;
+
+} // namespace JsobjTests
+
diff --git a/dbtests/jsontests.cpp b/dbtests/jsontests.cpp
new file mode 100644
index 0000000..68c6b5c
--- /dev/null
+++ b/dbtests/jsontests.cpp
@@ -0,0 +1,1117 @@
+// jsontests.cpp - Tests for json.{h,cpp} code and BSONObj::jsonString()
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/jsobj.h"
+#include "../db/json.h"
+
+#include "dbtests.h"
+
+#include <limits>
+
+namespace JsonTests {
+ namespace JsonStringTests {
+
+ class Empty {
+ public:
+ void run() {
+ ASSERT_EQUALS( "{}", BSONObj().jsonString( Strict ) );
+ }
+ };
+
+ class SingleStringMember {
+ public:
+ void run() {
+ ASSERT_EQUALS( "{ \"a\" : \"b\" }", BSON( "a" << "b" ).jsonString( Strict ) );
+ }
+ };
+
+ class EscapedCharacters {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "a", "\" \\ / \b \f \n \r \t" );
+ ASSERT_EQUALS( "{ \"a\" : \"\\\" \\\\ \\/ \\b \\f \\n \\r \\t\" }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ // per http://www.ietf.org/rfc/rfc4627.txt, control characters are
+ // (U+0000 through U+001F). U+007F is not mentioned as a control character.
+ class AdditionalControlCharacters {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "a", "\x1 \x1f" );
+ ASSERT_EQUALS( "{ \"a\" : \"\\u0001 \\u001f\" }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class ExtendedAscii {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "a", "\x80" );
+ ASSERT_EQUALS( "{ \"a\" : \"\x80\" }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class EscapeFieldName {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "\t", "b" );
+ ASSERT_EQUALS( "{ \"\\t\" : \"b\" }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class SingleIntMember {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "a", 1 );
+ ASSERT_EQUALS( "{ \"a\" : 1 }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class SingleNumberMember {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "a", 1.5 );
+ ASSERT_EQUALS( "{ \"a\" : 1.5 }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class InvalidNumbers {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "a", numeric_limits< double >::infinity() );
+ ASSERT_EXCEPTION( b.done().jsonString( Strict ), AssertionException );
+
+ BSONObjBuilder c;
+ c.append( "a", numeric_limits< double >::quiet_NaN() );
+ ASSERT_EXCEPTION( c.done().jsonString( Strict ), AssertionException );
+
+ BSONObjBuilder d;
+ d.append( "a", numeric_limits< double >::signaling_NaN() );
+ ASSERT_EXCEPTION( d.done().jsonString( Strict ), AssertionException );
+ }
+ };
+
+ class NumberPrecision {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "a", 123456789 );
+ ASSERT_EQUALS( "{ \"a\" : 123456789 }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class NegativeNumber {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "a", -1 );
+ ASSERT_EQUALS( "{ \"a\" : -1 }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class SingleBoolMember {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendBool( "a", true );
+ ASSERT_EQUALS( "{ \"a\" : true }", b.done().jsonString( Strict ) );
+
+ BSONObjBuilder c;
+ c.appendBool( "a", false );
+ ASSERT_EQUALS( "{ \"a\" : false }", c.done().jsonString( Strict ) );
+ }
+ };
+
+ class SingleNullMember {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendNull( "a" );
+ ASSERT_EQUALS( "{ \"a\" : null }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class SingleObjectMember {
+ public:
+ void run() {
+ BSONObjBuilder b, c;
+ b.append( "a", c.done() );
+ ASSERT_EQUALS( "{ \"a\" : {} }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class TwoMembers {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "a", 1 );
+ b.append( "b", 2 );
+ ASSERT_EQUALS( "{ \"a\" : 1, \"b\" : 2 }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class EmptyArray {
+ public:
+ void run() {
+ vector< int > arr;
+ BSONObjBuilder b;
+ b.append( "a", arr );
+ ASSERT_EQUALS( "{ \"a\" : [] }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class Array {
+ public:
+ void run() {
+ vector< int > arr;
+ arr.push_back( 1 );
+ arr.push_back( 2 );
+ BSONObjBuilder b;
+ b.append( "a", arr );
+ ASSERT_EQUALS( "{ \"a\" : [ 1, 2 ] }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class DBRef {
+ public:
+ void run() {
+ OID oid;
+ memset( &oid, 0xff, 12 );
+ BSONObjBuilder b;
+ b.appendDBRef( "a", "namespace", oid );
+ BSONObj built = b.done();
+ ASSERT_EQUALS( "{ \"a\" : { \"$ref\" : \"namespace\", \"$id\" : \"ffffffffffffffffffffffff\" } }",
+ built.jsonString( Strict ) );
+ ASSERT_EQUALS( "{ \"a\" : { \"$ref\" : \"namespace\", \"$id\" : \"ffffffffffffffffffffffff\" } }",
+ built.jsonString( JS ) );
+ ASSERT_EQUALS( "{ \"a\" : Dbref( \"namespace\", \"ffffffffffffffffffffffff\" ) }",
+ built.jsonString( TenGen ) );
+ }
+ };
+
+ class DBRefZero {
+ public:
+ void run() {
+ OID oid;
+ memset( &oid, 0, 12 );
+ BSONObjBuilder b;
+ b.appendDBRef( "a", "namespace", oid );
+ ASSERT_EQUALS( "{ \"a\" : { \"$ref\" : \"namespace\", \"$id\" : \"000000000000000000000000\" } }",
+ b.done().jsonString( Strict ) );
+ }
+ };
+
+ class ObjectId {
+ public:
+ void run() {
+ OID oid;
+ memset( &oid, 0xff, 12 );
+ BSONObjBuilder b;
+ b.appendOID( "a", &oid );
+ BSONObj built = b.done();
+ ASSERT_EQUALS( "{ \"a\" : { \"$oid\" : \"ffffffffffffffffffffffff\" } }",
+ built.jsonString( Strict ) );
+ ASSERT_EQUALS( "{ \"a\" : ObjectId( \"ffffffffffffffffffffffff\" ) }",
+ built.jsonString( TenGen ) );
+ }
+ };
+
+ class BinData {
+ public:
+ void run() {
+ char z[ 3 ];
+ z[ 0 ] = 'a';
+ z[ 1 ] = 'b';
+ z[ 2 ] = 'c';
+ BSONObjBuilder b;
+ b.appendBinData( "a", 3, ByteArray, z );
+ ASSERT_EQUALS( "{ \"a\" : { \"$binary\" : \"YWJj\", \"$type\" : \"02\" } }",
+ b.done().jsonString( Strict ) );
+
+ BSONObjBuilder c;
+ c.appendBinData( "a", 2, ByteArray, z );
+ ASSERT_EQUALS( "{ \"a\" : { \"$binary\" : \"YWI=\", \"$type\" : \"02\" } }",
+ c.done().jsonString( Strict ) );
+
+ BSONObjBuilder d;
+ d.appendBinData( "a", 1, ByteArray, z );
+ ASSERT_EQUALS( "{ \"a\" : { \"$binary\" : \"YQ==\", \"$type\" : \"02\" } }",
+ d.done().jsonString( Strict ) );
+ }
+ };
+
+ class Symbol {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendSymbol( "a", "b" );
+ ASSERT_EQUALS( "{ \"a\" : \"b\" }", b.done().jsonString( Strict ) );
+ }
+ };
+
+ class Date {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendDate( "a", 0 );
+ BSONObj built = b.done();
+ ASSERT_EQUALS( "{ \"a\" : { \"$date\" : 0 } }", built.jsonString( Strict ) );
+ ASSERT_EQUALS( "{ \"a\" : Date( 0 ) }", built.jsonString( TenGen ) );
+ ASSERT_EQUALS( "{ \"a\" : Date( 0 ) }", built.jsonString( JS ) );
+ }
+ };
+
+ class Regex {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendRegex( "a", "abc", "i" );
+ BSONObj built = b.done();
+ ASSERT_EQUALS( "{ \"a\" : { \"$regex\" : \"abc\", \"$options\" : \"i\" } }",
+ built.jsonString( Strict ) );
+ ASSERT_EQUALS( "{ \"a\" : /abc/i }", built.jsonString( TenGen ) );
+ ASSERT_EQUALS( "{ \"a\" : /abc/i }", built.jsonString( JS ) );
+ }
+ };
+
+ class RegexEscape {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendRegex( "a", "/\"", "i" );
+ BSONObj built = b.done();
+ ASSERT_EQUALS( "{ \"a\" : { \"$regex\" : \"\\/\\\"\", \"$options\" : \"i\" } }",
+ built.jsonString( Strict ) );
+ ASSERT_EQUALS( "{ \"a\" : /\\/\\\"/i }", built.jsonString( TenGen ) );
+ ASSERT_EQUALS( "{ \"a\" : /\\/\\\"/i }", built.jsonString( JS ) );
+ }
+ };
+
+ class RegexManyOptions {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendRegex( "a", "z", "abcgimx" );
+ BSONObj built = b.done();
+ ASSERT_EQUALS( "{ \"a\" : { \"$regex\" : \"z\", \"$options\" : \"abcgimx\" } }",
+ built.jsonString( Strict ) );
+ ASSERT_EQUALS( "{ \"a\" : /z/gim }", built.jsonString( TenGen ) );
+ ASSERT_EQUALS( "{ \"a\" : /z/gim }", built.jsonString( JS ) );
+ }
+ };
+
+ class CodeTests {
+ public:
+ void run(){
+ BSONObjBuilder b;
+ b.appendCode( "x" , "function(){ return 1; }" );
+ BSONObj o = b.obj();
+ ASSERT_EQUALS( "{ \"x\" : function(){ return 1; } }" , o.jsonString() );
+ }
+ };
+
+ class TimestampTests {
+ public:
+ void run(){
+ BSONObjBuilder b;
+ b.appendTimestamp( "x" , 4000 , 10 );
+ BSONObj o = b.obj();
+ ASSERT_EQUALS( "{ \"x\" : { \"t\" : 4000 , \"i\" : 10 } }" , o.jsonString() );
+ }
+ };
+
+
+ } // namespace JsonStringTests
+
+ namespace FromJsonTests {
+
+ class Base {
+ public:
+ virtual ~Base() {}
+ void run() {
+ ASSERT( fromjson( json() ).valid() );
+ assertEquals( bson(), fromjson( json() ) );
+ assertEquals( bson(), fromjson( bson().jsonString( Strict ) ) );
+ assertEquals( bson(), fromjson( bson().jsonString( TenGen ) ) );
+ assertEquals( bson(), fromjson( bson().jsonString( JS ) ) );
+ }
+ protected:
+ virtual BSONObj bson() const = 0;
+ virtual string json() const = 0;
+ private:
+ static void assertEquals( const BSONObj &expected, const BSONObj &actual ) {
+ if ( expected.woCompare( actual ) ) {
+ out() << "want:" << expected.jsonString() << " size: " << expected.objsize() << endl;
+ out() << "got :" << actual.jsonString() << " size: " << actual.objsize() << endl;
+ out() << expected.hexDump() << endl;
+ out() << actual.hexDump() << endl;
+ }
+ ASSERT( !expected.woCompare( actual ) );
+ }
+ };
+
+ class Bad {
+ public:
+ virtual ~Bad() {}
+ void run() {
+ ASSERT_EXCEPTION( fromjson( json() ), MsgAssertionException );
+ }
+ protected:
+ virtual string json() const = 0;
+ };
+
+ class Empty : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{}";
+ }
+ };
+
+ class EmptyWithSpace : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ }";
+ }
+ };
+
+ class SingleString : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.append( "a", "b" );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : \"b\" }";
+ }
+ };
+
+ class EmptyStrings : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.append( "", "" );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"\" : \"\" }";
+ }
+ };
+
+ class ReservedFieldName : public Bad {
+ virtual string json() const {
+ return "{ \"$oid\" : \"b\" }";
+ }
+ };
+
+ class OkDollarFieldName : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.append( "$where", 1 );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"$where\" : 1 }";
+ }
+ };
+
+ class SingleNumber : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.append( "a", 1 );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : 1 }";
+ }
+ };
+
+ class FancyNumber {
+ public:
+ virtual ~FancyNumber() {}
+ void run() {
+ ASSERT_EQUALS( int( 1000000 * bson().firstElement().number() ),
+ int( 1000000 * fromjson( json() ).firstElement().number() ) );
+ }
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.append( "a", -4.4433e-2 );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : -4.4433e-2 }";
+ }
+ };
+
+ class TwoElements : public Base {
+ 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 Base {
+ 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 ArrayEmpty : public Base {
+ 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 Base {
+ 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 Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.appendBool( "a", true );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : true }";
+ }
+ };
+
+ class False : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.appendBool( "a", false );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : false }";
+ }
+ };
+
+ class Null : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.appendNull( "a" );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : null }";
+ }
+ };
+
+ class EscapedCharacters : public Base {
+ 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 Base {
+ 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 Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.append( "a", "\x7f" );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : \"\x7f\" }";
+ }
+ };
+
+ class EscapeFieldName : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.append( "\n", "b" );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"\\n\" : \"b\" }";
+ }
+ };
+
+ class EscapedUnicodeToUtf8 : public Base {
+ 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 Base {
+ 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 Base {
+ 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 DBRef : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ OID o;
+ memset( &o, 0, 12 );
+ b.appendDBRef( "a", "foo", o );
+ return b.obj();
+ }
+ // NOTE Testing other formats handled by by Base class.
+ virtual string json() const {
+ return "{ \"a\" : { \"$ref\" : \"foo\", \"$id\" : \"000000000000000000000000\" } }";
+ }
+ };
+
+ class NewDBRef : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ OID o;
+ memset( &o, 0, 12 );
+ b.append( "$ref", "items" );
+ b.appendOID( "$id", &o );
+ BSONObjBuilder c;
+ c.append( "refval", b.done() );
+ return c.obj();
+ }
+ virtual string json() const {
+ return "{ \"refval\" : { \"$ref\" : \"items\", \"$id\" : ObjectId( \"000000000000000000000000\" ) } }";
+ }
+ };
+
+ class Oid : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.appendOID( "_id" );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"_id\" : { \"$oid\" : \"000000000000000000000000\" } }";
+ }
+ };
+
+ class Oid2 : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ OID o;
+ memset( &o, 0x0f, 12 );
+ b.appendOID( "_id", &o );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"_id\" : ObjectId( \"0f0f0f0f0f0f0f0f0f0f0f0f\" ) }";
+ }
+ };
+
+ class StringId : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.append("_id", "000000000000000000000000");
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"_id\" : \"000000000000000000000000\" }";
+ }
+ };
+
+ class BinData : public Base {
+ virtual BSONObj bson() const {
+ char z[ 3 ];
+ z[ 0 ] = 'a';
+ z[ 1 ] = 'b';
+ z[ 2 ] = 'c';
+ BSONObjBuilder b;
+ b.appendBinData( "a", 3, ByteArray, z );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : { \"$binary\" : \"YWJj\", \"$type\" : \"02\" } }";
+ }
+ };
+
+ class BinDataPaddedSingle : public Base {
+ virtual BSONObj bson() const {
+ char z[ 2 ];
+ z[ 0 ] = 'a';
+ z[ 1 ] = 'b';
+ BSONObjBuilder b;
+ b.appendBinData( "a", 2, ByteArray, z );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : { \"$binary\" : \"YWI=\", \"$type\" : \"02\" } }";
+ }
+ };
+
+ class BinDataPaddedDouble : public Base {
+ virtual BSONObj bson() const {
+ char z[ 1 ];
+ z[ 0 ] = 'a';
+ BSONObjBuilder b;
+ b.appendBinData( "a", 1, ByteArray, z );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : { \"$binary\" : \"YQ==\", \"$type\" : \"02\" } }";
+ }
+ };
+
+ class BinDataAllChars : public Base {
+ 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, ByteArray, z );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : { \"$binary\" : \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\", \"$type\" : \"02\" } }";
+ }
+ };
+
+ class Date : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.appendDate( "a", 0 );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : { \"$date\" : 0 } }";
+ }
+ };
+
+ class DateNonzero : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.appendDate( "a", 100 );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : { \"$date\" : 100 } }";
+ }
+ };
+
+ class DateTooLong : public Bad {
+ virtual string json() const {
+ stringstream ss;
+ ss << "{ \"a\" : { \"$date\" : " << ~(0LL) << "0" << " } }";
+ return ss.str();
+ }
+ };
+
+ class Regex : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.appendRegex( "a", "b", "i" );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : { \"$regex\" : \"b\", \"$options\" : \"i\" } }";
+ }
+ };
+
+ class RegexEscape : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.appendRegex( "a", "\t", "i" );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : { \"$regex\" : \"\\t\", \"$options\" : \"i\" } }";
+ }
+ };
+
+ class RegexWithQuotes : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.appendRegex( "a", "\"", "" );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"a\" : /\"/ }";
+ }
+ };
+
+ class RegexInvalidOption : public Bad {
+ virtual string json() const {
+ return "{ \"a\" : { \"$regex\" : \"b\", \"$options\" : \"1\" } }";
+ }
+ };
+
+ class RegexInvalidOption2 : public Bad {
+ virtual string json() const {
+ return "{ \"a\" : /b/c }";
+ }
+ };
+
+ class Malformed : public Bad {
+ string json() const {
+ return "{";
+ }
+ };
+
+ class UnquotedFieldName : public Base {
+ virtual BSONObj bson() const {
+ BSONObjBuilder b;
+ b.append( "a_b", 1 );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ a_b : 1 }";
+ }
+ };
+
+ class UnquotedFieldNameDollar : public Base {
+ 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 Base {
+ 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 Base {
+ virtual BSONObj bson() const {
+ OID id;
+ id.init( "deadbeeff00ddeadbeeff00d" );
+ BSONObjBuilder b;
+ b.appendOID( "_id", &id );
+ return b.obj();
+ }
+ virtual string json() const {
+ return "{ \"_id\": ObjectId( \"deadbeeff00ddeadbeeff00d\" ) }";
+ }
+ };
+
+ class ObjectId2 : public Base {
+ 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 NumericTypes : public Base {
+ public:
+ void run(){
+ Base::run();
+
+ BSONObj o = fromjson(json());
+
+ ASSERT(o["int"].type() == NumberInt);
+ ASSERT(o["long"].type() == NumberLong);
+ ASSERT(o["double"].type() == NumberDouble);
+
+ ASSERT(o["long"].numberLong() == 9223372036854775807ll);
+ }
+
+ virtual BSONObj bson() const {
+ return BSON( "int" << 123
+ << "long" << 9223372036854775807ll // 2**63 - 1
+ << "double" << 3.14
+ );
+ }
+ virtual string json() const {
+ return "{ \"int\": 123, \"long\": 9223372036854775807, \"double\": 3.14 }";
+ }
+ };
+
+ class NegativeNumericTypes : public Base {
+ public:
+ void run(){
+ Base::run();
+
+ BSONObj o = fromjson(json());
+
+ ASSERT(o["int"].type() == NumberInt);
+ ASSERT(o["long"].type() == NumberLong);
+ ASSERT(o["double"].type() == NumberDouble);
+
+ ASSERT(o["long"].numberLong() == -9223372036854775807ll);
+ }
+
+ virtual BSONObj bson() const {
+ return BSON( "int" << -123
+ << "long" << -9223372036854775807ll // -1 * (2**63 - 1)
+ << "double" << -3.14
+ );
+ }
+ virtual string json() const {
+ return "{ \"int\": -123, \"long\": -9223372036854775807, \"double\": -3.14 }";
+ }
+ };
+
+ class EmbeddedDatesBase : public Base {
+ public:
+
+ virtual void run(){
+ BSONObj o = fromjson( json() );
+ ASSERT_EQUALS( 3 , (o["time.valid"].type()) );
+ BSONObj e = o["time.valid"].embeddedObjectUserCheck();
+ ASSERT_EQUALS( 9 , e["$gt"].type() );
+ ASSERT_EQUALS( 9 , e["$lt"].type() );
+ Base::run();
+ }
+
+ BSONObj bson() const {
+ BSONObjBuilder e;
+ e.appendDate( "$gt" , 1257829200000LL );
+ e.appendDate( "$lt" , 1257829200100LL );
+
+ BSONObjBuilder b;
+ b.append( "time.valid" , e.obj() );
+ return b.obj();
+ }
+ virtual string json() const = 0;
+ };
+
+ struct EmbeddedDatesFormat1 : EmbeddedDatesBase {
+ string json() const {
+ return "{ \"time.valid\" : { $gt : { \"$date\" : 1257829200000 } , $lt : { \"$date\" : 1257829200100 } } }";
+ }
+ };
+ struct EmbeddedDatesFormat2 : EmbeddedDatesBase {
+ string json() const {
+ return "{ \"time.valid\" : { $gt : Date(1257829200000) , $lt : Date( 1257829200100 ) } }";
+ }
+ };
+ struct EmbeddedDatesFormat3 : EmbeddedDatesBase {
+ string json() const {
+ return "{ \"time.valid\" : { $gt : new Date(1257829200000) , $lt : new Date( 1257829200100 ) } }";
+ }
+ };
+
+
+ } // namespace FromJsonTests
+
+ class All : public Suite {
+ public:
+ All() : Suite( "json" ){
+ }
+
+ void setupTests(){
+ add< JsonStringTests::Empty >();
+ add< JsonStringTests::SingleStringMember >();
+ add< JsonStringTests::EscapedCharacters >();
+ add< JsonStringTests::AdditionalControlCharacters >();
+ add< JsonStringTests::ExtendedAscii >();
+ add< JsonStringTests::EscapeFieldName >();
+ add< JsonStringTests::SingleIntMember >();
+ add< JsonStringTests::SingleNumberMember >();
+ add< JsonStringTests::InvalidNumbers >();
+ add< JsonStringTests::NumberPrecision >();
+ add< JsonStringTests::NegativeNumber >();
+ add< JsonStringTests::SingleBoolMember >();
+ add< JsonStringTests::SingleNullMember >();
+ add< JsonStringTests::SingleObjectMember >();
+ add< JsonStringTests::TwoMembers >();
+ add< JsonStringTests::EmptyArray >();
+ add< JsonStringTests::Array >();
+ add< JsonStringTests::DBRef >();
+ add< JsonStringTests::DBRefZero >();
+ add< JsonStringTests::ObjectId >();
+ add< JsonStringTests::BinData >();
+ add< JsonStringTests::Symbol >();
+ add< JsonStringTests::Date >();
+ add< JsonStringTests::Regex >();
+ add< JsonStringTests::RegexEscape >();
+ add< JsonStringTests::RegexManyOptions >();
+ add< JsonStringTests::CodeTests >();
+ add< JsonStringTests::TimestampTests >();
+
+ add< FromJsonTests::Empty >();
+ add< FromJsonTests::EmptyWithSpace >();
+ add< FromJsonTests::SingleString >();
+ add< FromJsonTests::EmptyStrings >();
+ add< FromJsonTests::ReservedFieldName >();
+ add< FromJsonTests::OkDollarFieldName >();
+ add< FromJsonTests::SingleNumber >();
+ add< FromJsonTests::FancyNumber >();
+ add< FromJsonTests::TwoElements >();
+ add< FromJsonTests::Subobject >();
+ add< FromJsonTests::ArrayEmpty >();
+ add< FromJsonTests::Array >();
+ add< FromJsonTests::True >();
+ add< FromJsonTests::False >();
+ add< FromJsonTests::Null >();
+ add< FromJsonTests::EscapedCharacters >();
+ add< FromJsonTests::NonEscapedCharacters >();
+ add< FromJsonTests::AllowedControlCharacter >();
+ add< FromJsonTests::EscapeFieldName >();
+ add< FromJsonTests::EscapedUnicodeToUtf8 >();
+ add< FromJsonTests::Utf8AllOnes >();
+ add< FromJsonTests::Utf8FirstByteOnes >();
+ add< FromJsonTests::DBRef >();
+ add< FromJsonTests::NewDBRef >();
+ add< FromJsonTests::Oid >();
+ add< FromJsonTests::Oid2 >();
+ add< FromJsonTests::StringId >();
+ add< FromJsonTests::BinData >();
+ add< FromJsonTests::BinDataPaddedSingle >();
+ add< FromJsonTests::BinDataPaddedDouble >();
+ add< FromJsonTests::BinDataAllChars >();
+ add< FromJsonTests::Date >();
+ add< FromJsonTests::DateNonzero >();
+ add< FromJsonTests::DateTooLong >();
+ add< FromJsonTests::Regex >();
+ add< FromJsonTests::RegexEscape >();
+ add< FromJsonTests::RegexWithQuotes >();
+ add< FromJsonTests::RegexInvalidOption >();
+ add< FromJsonTests::RegexInvalidOption2 >();
+ add< FromJsonTests::Malformed >();
+ add< FromJsonTests::UnquotedFieldName >();
+ add< FromJsonTests::UnquotedFieldNameDollar >();
+ add< FromJsonTests::SingleQuotes >();
+ add< FromJsonTests::ObjectId >();
+ add< FromJsonTests::ObjectId2 >();
+ add< FromJsonTests::NumericTypes >();
+ add< FromJsonTests::NegativeNumericTypes >();
+ add< FromJsonTests::EmbeddedDatesFormat1 >();
+ add< FromJsonTests::EmbeddedDatesFormat2 >();
+ add< FromJsonTests::EmbeddedDatesFormat3 >();
+ }
+ } myall;
+
+} // namespace JsonTests
+
diff --git a/dbtests/jstests.cpp b/dbtests/jstests.cpp
new file mode 100644
index 0000000..86b0218
--- /dev/null
+++ b/dbtests/jstests.cpp
@@ -0,0 +1,768 @@
+// javajstests.cpp
+//
+
+/**
+ * Copyright (C) 2009 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/instance.h"
+
+#include "../stdafx.h"
+#include "../scripting/engine.h"
+
+#include "dbtests.h"
+
+namespace mongo {
+ bool dbEval(const char *ns, BSONObj& cmd, BSONObjBuilder& result, string& errmsg);
+} // namespace mongo
+
+namespace JSTests {
+
+ class Fundamental {
+ public:
+ void run() {
+ // By calling JavaJSImpl() inside run(), we ensure the unit test framework's
+ // signal handlers are pre-installed from JNI's perspective. This allows
+ // JNI to catch signals generated within the JVM and forward other signals
+ // as appropriate.
+ ScriptEngine::setup();
+ globalScriptEngine->runTest();
+ }
+ };
+
+ class BasicScope {
+ public:
+ void run(){
+ auto_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" ) );
+
+ if ( 0 ){
+ s->setBoolean( "b" , false );
+ ASSERT( ! s->getBoolean( "b" ) );
+ }
+ }
+ };
+
+ class ResetScope {
+ public:
+ void run(){
+ // Not worrying about this for now SERVER-446.
+ /*
+ 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(){
+ Scope * s = globalScriptEngine->newScope();
+
+ ASSERT( ! s->getBoolean( "x" ) );
+
+ s->setString( "z" , "" );
+ ASSERT( ! s->getBoolean( "z" ) );
+
+
+ delete s ;
+ }
+ };
+
+ class SimpleFunctions {
+ public:
+ void run(){
+ Scope * s = globalScriptEngine->newScope();
+
+ s->invoke( "x=5;" , BSONObj() );
+ ASSERT( 5 == s->getNumber( "x" ) );
+
+ s->invoke( "return 17;" , BSONObj() );
+ ASSERT( 17 == s->getNumber( "return" ) );
+
+ s->invoke( "function(){ return 17; }" , BSONObj() );
+ ASSERT( 17 == s->getNumber( "return" ) );
+
+ s->setNumber( "x" , 1.76 );
+ s->invoke( "return x == 1.76; " , BSONObj() );
+ ASSERT( s->getBoolean( "return" ) );
+
+ s->setNumber( "x" , 1.76 );
+ s->invoke( "return x == 1.79; " , BSONObj() );
+ ASSERT( ! s->getBoolean( "return" ) );
+
+ s->invoke( "function( z ){ return 5 + z; }" , BSON( "" << 11 ) );
+ ASSERT_EQUALS( 16 , s->getNumber( "return" ) );
+
+ delete s;
+ }
+ };
+
+ class ObjectMapping {
+ public:
+ void run(){
+ Scope * s = globalScriptEngine->newScope();
+
+ BSONObj o = BSON( "x" << 17 << "y" << "eliot" << "z" << "sara" );
+ s->setObject( "blah" , o );
+
+ s->invoke( "return blah.x;" , BSONObj() );
+ ASSERT_EQUALS( 17 , s->getNumber( "return" ) );
+ s->invoke( "return blah.y;" , BSONObj() );
+ ASSERT_EQUALS( "eliot" , s->getString( "return" ) );
+
+ s->setThis( & o );
+ s->invoke( "return this.z;" , BSONObj() );
+ ASSERT_EQUALS( "sara" , s->getString( "return" ) );
+
+ s->invoke( "return this.z == 'sara';" , BSONObj() );
+ ASSERT_EQUALS( true , s->getBoolean( "return" ) );
+
+ s->invoke( "this.z == 'sara';" , BSONObj() );
+ ASSERT_EQUALS( true , s->getBoolean( "return" ) );
+
+ s->invoke( "this.z == 'asara';" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "return" ) );
+
+ s->invoke( "return this.x == 17;" , BSONObj() );
+ ASSERT_EQUALS( true , s->getBoolean( "return" ) );
+
+ s->invoke( "return this.x == 18;" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "return" ) );
+
+ s->invoke( "function(){ return this.x == 17; }" , BSONObj() );
+ ASSERT_EQUALS( true , s->getBoolean( "return" ) );
+
+ s->invoke( "function(){ return this.x == 18; }" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "return" ) );
+
+ s->invoke( "function (){ return this.x == 17; }" , BSONObj() );
+ ASSERT_EQUALS( true , s->getBoolean( "return" ) );
+
+ s->invoke( "function z(){ return this.x == 18; }" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "return" ) );
+
+ s->invoke( "function (){ this.x == 17; }" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "return" ) );
+
+ s->invoke( "function z(){ this.x == 18; }" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "return" ) );
+
+ s->invoke( "x = 5; for( ; x <10; x++){ a = 1; }" , BSONObj() );
+ ASSERT_EQUALS( 10 , s->getNumber( "x" ) );
+
+ delete s;
+ }
+ };
+
+ class ObjectDecoding {
+ public:
+ void run(){
+ Scope * s = globalScriptEngine->newScope();
+
+ s->invoke( "z = { num : 1 };" , BSONObj() );
+ BSONObj out = s->getObject( "z" );
+ ASSERT_EQUALS( 1 , out["num"].number() );
+ ASSERT_EQUALS( 1 , out.nFields() );
+
+ s->invoke( "z = { x : 'eliot' };" , BSONObj() );
+ 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() );
+
+ delete s;
+ }
+ };
+
+ class JSOIDTests {
+ public:
+ void run(){
+#ifdef MOZJS
+ Scope * s = globalScriptEngine->newScope();
+
+ s->localConnect( "blah" );
+
+ s->invoke( "z = { _id : new ObjectId() , a : 123 };" , BSONObj() );
+ BSONObj out = s->getObject( "z" );
+ ASSERT_EQUALS( 123 , out["a"].number() );
+ ASSERT_EQUALS( jstOID , out["_id"].type() );
+
+ OID save = out["_id"].__oid();
+
+ s->setObject( "a" , out );
+
+ s->invoke( "y = { _id : a._id , a : 124 };" , BSONObj() );
+ 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 };" , BSONObj() );
+ 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;
+#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'", BSONObj() );
+ BSONObj out = s->getObject( "blah" );
+ ASSERT( strlen( out["y"].valuestr() ) > 1 );
+
+ s->invoke( "blah.a = 19;" , BSONObj() );
+ out = s->getObject( "blah" );
+ ASSERT( out["a"].eoo() );
+
+ s->invoke( "blah.zz.a = 19;" , BSONObj() );
+ 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']" , BSONObj() );
+ out = s->getObject( "blah" );
+ ASSERT( !out["x"].eoo() );
+
+ // read-only object itself can be overwritten
+ s->invoke( "blah = {}", BSONObj() );
+ 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;" , BSONObj() );
+ ASSERT_EQUALS( true, s->getBoolean( "return" ) );
+
+ s->invoke( "z = x.d.getTime();" , BSONObj() );
+ ASSERT_EQUALS( 123456789 , s->getNumber( "z" ) );
+
+ s->invoke( "z = { z : x.d }" , BSONObj() );
+ 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' );" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "z" ) );
+
+ s->invoke( "z = x.r.test( 'a' );" , BSONObj() );
+ ASSERT_EQUALS( true , s->getBoolean( "z" ) );
+
+ s->invoke( "z = x.r.test( 'ba' );" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "z" ) );
+
+ s->invoke( "z = { a : x.r };" , BSONObj() );
+
+ BSONObj out = s->getObject("z");
+ ASSERT_EQUALS( (string)"^a" , out["a"].regex() );
+ ASSERT_EQUALS( (string)"i" , out["a"].regexFlags() );
+
+ }
+
+ // 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() );
+ }
+
+ 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 }" , BSONObj() ) == 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 --
+
+ BSONObj o;
+ {
+ BSONObjBuilder b ;
+ 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() );
+
+ s->setObject( "z" , o );
+ s->invoke( "return z" , BSONObj() );
+ BSONObj out = s->getObject( "return" );
+ ASSERT_EQUALS( 5 , out["a"].number() );
+ ASSERT_EQUALS( 5.6 , out["b"].number() );
+
+ ASSERT_EQUALS( NumberDouble , out["b"].type() );
+ ASSERT_EQUALS( NumberInt , out["a"].type() );
+
+ // -- B --
+
+ {
+ BSONObjBuilder b ;
+ b.append( "a" , (int)5 );
+ b.append( "b" , 5.6 );
+ o = b.obj();
+ }
+
+ s->setObject( "z" , o , false );
+ s->invoke( "return z" , BSONObj() );
+ out = s->getObject( "return" );
+ ASSERT_EQUALS( 5 , out["a"].number() );
+ ASSERT_EQUALS( 5.6 , out["b"].number() );
+
+ ASSERT_EQUALS( NumberDouble , out["b"].type() );
+ ASSERT_EQUALS( NumberInt , out["a"].type() );
+
+
+ // -- C --
+
+ {
+ BSONObjBuilder b ;
+
+ {
+ BSONObjBuilder c;
+ c.append( "0" , 5.5 );
+ c.append( "1" , 6 );
+ b.appendArray( "a" , c.obj() );
+ }
+
+ o = b.obj();
+ }
+
+ 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;" , BSONObj() );
+ 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( "return" );
+// 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 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();
+
+ s->localConnect( "blah" );
+
+ for ( int i=5; i<100 ; i += 10 ){
+ s->setObject( "a" , build(i) , false );
+ s->invokeSafe( "tojson( a )" , BSONObj() );
+
+ s->setObject( "a" , build(5) , true );
+ s->invokeSafe( "tojson( a )" , BSONObj() );
+ }
+
+ delete s;
+ }
+ };
+
+
+ void dummy_function_to_force_dbeval_cpp_linking() {
+ BSONObj cmd;
+ BSONObjBuilder result;
+ string errmsg;
+ dbEval( "", cmd, result, errmsg);
+ }
+
+ DBDirectClient client;
+
+ class Utf8Check {
+ public:
+ Utf8Check() { reset(); }
+ ~Utf8Check() { reset(); }
+ void run() {
+ if( !globalScriptEngine->utf8Ok() ) {
+ log() << "warning: utf8 not supported" << endl;
+ return;
+ }
+ string utf8ObjSpec = "{'_id':'\\u0001\\u007f\\u07ff\\uffff'}";
+ BSONObj utf8Obj = fromjson( utf8ObjSpec );
+ 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 " ) + string( one ) + ", got " + string( two );
+ FAIL( fail.c_str() );
+ }
+ }
+ void reset() {
+ client.dropCollection( ns() );
+ }
+ static const char *ns() { return "unittest.jstests.utf8check"; }
+ };
+
+ class LongUtf8String {
+ public:
+ LongUtf8String() { reset(); }
+ ~LongUtf8String() { reset(); }
+ void run() {
+ if( !globalScriptEngine->utf8Ok() )
+ return;
+ client.eval( "unittest", "db.jstests.longutf8string.save( {_id:'\\uffff\\uffff\\uffff\\uffff'} )" );
+ }
+ private:
+ void reset() {
+ client.dropCollection( ns() );
+ }
+ static const char *ns() { return "unittest.jstests.longutf8string"; }
+ };
+
+ 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();" , BSONObj() );
+ 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;
+ }
+ };
+
+ class DBRefTest {
+ public:
+ DBRefTest(){
+ _a = "unittest.dbref.a";
+ _b = "unittest.dbref.b";
+ reset();
+ }
+ ~DBRefTest(){
+ //reset();
+ }
+
+ void run(){
+
+ client.insert( _a , BSON( "a" << "17" ) );
+
+ {
+ BSONObj fromA = client.findOne( _a , BSONObj() );
+ cout << "Froma : " << fromA << endl;
+ BSONObjBuilder b;
+ b.append( "b" , 18 );
+ b.appendDBRef( "c" , "dbref.a" , fromA["_id"].__oid() );
+ client.insert( _b , b.obj() );
+ }
+
+ ASSERT( client.eval( "unittest" , "x = db.dbref.b.findOne(); assert.eq( 17 , x.c.fetch().a , 'ref working' );" ) );
+
+ // BSON DBRef <=> JS DBPointer
+ ASSERT( client.eval( "unittest", "x = db.dbref.b.findOne(); db.dbref.b.drop(); x.c = new DBPointer( x.c.ns, x.c.id ); db.dbref.b.insert( x );" ) );
+ ASSERT_EQUALS( DBRef, client.findOne( "unittest.dbref.b", "" )[ "c" ].type() );
+
+ // BSON Object <=> JS DBRef
+ ASSERT( client.eval( "unittest", "x = db.dbref.b.findOne(); db.dbref.b.drop(); x.c = new DBRef( x.c.ns, x.c.id ); db.dbref.b.insert( x );" ) );
+ ASSERT_EQUALS( Object, client.findOne( "unittest.dbref.b", "" )[ "c" ].type() );
+ ASSERT_EQUALS( string( "dbref.a" ), client.findOne( "unittest.dbref.b", "" )[ "c" ].embeddedObject().getStringField( "$ref" ) );
+ }
+
+ void reset(){
+ client.dropCollection( _a );
+ client.dropCollection( _b );
+ }
+
+ const char * _a;
+ const char * _b;
+ };
+
+ 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();
+ s->localConnect( "asd" );
+ const char * foo = "asdas\0asdasd";
+
+
+ BSONObj in;
+ {
+ BSONObjBuilder b;
+ b.append( "a" , 7 );
+ b.appendBinData( "b" , 12 , ByteArray , foo );
+ in = b.obj();
+ s->setObject( "x" , in );
+ }
+
+ s->invokeSafe( "myb = x.b; print( myb ); printjson( myb );" , BSONObj() );
+ s->invokeSafe( "y = { c : myb };" , BSONObj() );
+
+ BSONObj out = s->getObject( "y" );
+ ASSERT_EQUALS( BinData , out["c"].type() );
+ //blah( "in " , in["b"] );
+ //blah( "out" , out["c"] );
+ ASSERT_EQUALS( 0 , in["b"].woCompare( out["c"] , false ) );
+
+ // check that BinData js class is utilized
+ s->invokeSafe( "q = tojson( x.b );", BSONObj() );
+ ASSERT_EQUALS( "BinData", s->getString( "q" ).substr( 0, 7 ) );
+
+ 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 All : public Suite {
+ public:
+ All() : Suite( "js" ) {
+ }
+
+ void setupTests(){
+ add< Fundamental >();
+ add< BasicScope >();
+ add< ResetScope >();
+ add< FalseTests >();
+ add< SimpleFunctions >();
+
+ add< ObjectMapping >();
+ add< ObjectDecoding >();
+ add< JSOIDTests >();
+ add< SetImplicit >();
+ add< ObjectModReadonlyTests >();
+ add< OtherJSTypes >();
+ add< SpecialDBTypes >();
+ add< TypeConservation >();
+
+ add< WeirdObjects >();
+ add< Utf8Check >();
+ add< LongUtf8String >();
+ add< CodeTests >();
+ add< DBRefTest >();
+ add< BinDataType >();
+
+ add< VarTests >();
+ }
+ } myall;
+
+} // namespace JavaJSTests
+
diff --git a/dbtests/matchertests.cpp b/dbtests/matchertests.cpp
new file mode 100644
index 0000000..e71988e
--- /dev/null
+++ b/dbtests/matchertests.cpp
@@ -0,0 +1,128 @@
+// matchertests.cpp : matcher unit tests
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/matcher.h"
+
+#include "../db/json.h"
+
+#include "dbtests.h"
+
+namespace MatcherTests {
+
+ class Basic {
+ public:
+ void run() {
+ BSONObj query = fromjson( "{\"a\":\"b\"}" );
+ Matcher m( query );
+ ASSERT( m.matches( fromjson( "{\"a\":\"b\"}" ) ) );
+ }
+ };
+
+ class DoubleEqual {
+ public:
+ void run() {
+ BSONObj query = fromjson( "{\"a\":5}" );
+ Matcher m( query );
+ ASSERT( m.matches( fromjson( "{\"a\":5}" ) ) );
+ }
+ };
+
+ class MixedNumericEqual {
+ public:
+ void run() {
+ BSONObjBuilder query;
+ query.append( "a", 5 );
+ Matcher m( query.done() );
+ ASSERT( m.matches( fromjson( "{\"a\":5}" ) ) );
+ }
+ };
+
+ class MixedNumericGt {
+ public:
+ void run() {
+ BSONObj query = fromjson( "{\"a\":{\"$gt\":4}}" );
+ Matcher m( query );
+ BSONObjBuilder b;
+ b.append( "a", 5 );
+ ASSERT( m.matches( b.done() ) );
+ }
+ };
+
+ class MixedNumericIN {
+ public:
+ void run(){
+ BSONObj query = fromjson( "{ a : { $in : [4,6] } }" );
+ ASSERT_EQUALS( 4 , query["a"].embeddedObject()["$in"].embeddedObject()["0"].number() );
+ ASSERT_EQUALS( NumberInt , query["a"].embeddedObject()["$in"].embeddedObject()["0"].type() );
+
+ Matcher m( query );
+
+ {
+ BSONObjBuilder b;
+ b.append( "a" , 4.0 );
+ ASSERT( m.matches( b.done() ) );
+ }
+
+ {
+ BSONObjBuilder b;
+ b.append( "a" , 5 );
+ ASSERT( ! m.matches( b.done() ) );
+ }
+
+
+ {
+ BSONObjBuilder b;
+ b.append( "a" , 4 );
+ ASSERT( m.matches( b.done() ) );
+ }
+
+ }
+ };
+
+
+ class Size {
+ public:
+ void run() {
+ Matcher m( fromjson( "{a:{$size:4}}" ) );
+ ASSERT( m.matches( fromjson( "{a:[1,2,3,4]}" ) ) );
+ ASSERT( !m.matches( fromjson( "{a:[1,2,3]}" ) ) );
+ ASSERT( !m.matches( fromjson( "{a:[1,2,3,'a','b']}" ) ) );
+ ASSERT( !m.matches( fromjson( "{a:[[1,2,3,4]]}" ) ) );
+ }
+ };
+
+
+ class All : public Suite {
+ public:
+ All() : Suite( "matcher" ){
+ }
+
+ void setupTests(){
+ add< Basic >();
+ add< DoubleEqual >();
+ add< MixedNumericEqual >();
+ add< MixedNumericGt >();
+ add< MixedNumericIN >();
+ add< Size >();
+ }
+ } dball;
+
+} // namespace MatcherTests
+
diff --git a/dbtests/mockdbclient.h b/dbtests/mockdbclient.h
new file mode 100644
index 0000000..26f6250
--- /dev/null
+++ b/dbtests/mockdbclient.h
@@ -0,0 +1,91 @@
+// mockdbclient.h - mocked out client for testing.
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "../client/dbclient.h"
+#include "../db/commands.h"
+
+class MockDBClientConnection : public DBClientConnection {
+public:
+ MockDBClientConnection() : connect_() {}
+ virtual
+ BSONObj findOne(const string &ns, Query query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0) {
+ return one_;
+ }
+ virtual
+ bool connect(const string &serverHostname, string& errmsg) {
+ return connect_;
+ }
+ virtual
+ bool isMaster(bool& isMaster, BSONObj *info=0) {
+ return isMaster_;
+ }
+ void one( const BSONObj &one ) {
+ one_ = one;
+ }
+ void connect( bool val ) {
+ connect_ = val;
+ }
+ void setIsMaster( bool val ) {
+ isMaster_ = val;
+ }
+private:
+ BSONObj one_;
+ bool connect_;
+ bool isMaster_;
+};
+
+class DirectDBClientConnection : public DBClientConnection {
+public:
+ struct ConnectionCallback {
+ virtual ~ConnectionCallback() {}
+ virtual void beforeCommand() {}
+ virtual void afterCommand() {}
+ };
+ DirectDBClientConnection( ReplPair *rp, ConnectionCallback *cc = 0 ) :
+ rp_( rp ),
+ cc_( cc ) {
+ }
+ virtual BSONObj findOne(const string &ns, Query query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0) {
+ if ( cc_ ) cc_->beforeCommand();
+ SetGlobalReplPair s( rp_ );
+ BSONObjBuilder result;
+ result.append( "ok", Command::runAgainstRegistered( "admin.$cmd", query.obj, result ) ? 1.0 : 0.0 );
+ if ( cc_ ) cc_->afterCommand();
+ return result.obj();
+ }
+ virtual bool connect( const string &serverHostname, string& errmsg ) {
+ return true;
+ }
+private:
+ ReplPair *rp_;
+ ConnectionCallback *cc_;
+ class SetGlobalReplPair {
+ public:
+ SetGlobalReplPair( ReplPair *rp ) {
+ backup_ = replPair;
+ replPair = rp;
+ }
+ ~SetGlobalReplPair() {
+ replPair = backup_;
+ }
+ private:
+ ReplPair *backup_;
+ };
+};
diff --git a/dbtests/namespacetests.cpp b/dbtests/namespacetests.cpp
new file mode 100644
index 0000000..c820ca6
--- /dev/null
+++ b/dbtests/namespacetests.cpp
@@ -0,0 +1,798 @@
+// namespacetests.cpp : namespace.{h,cpp} unit tests.
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Where IndexDetails defined.
+#include "stdafx.h"
+#include "../db/namespace.h"
+
+#include "../db/db.h"
+#include "../db/json.h"
+
+#include "dbtests.h"
+
+namespace NamespaceTests {
+ namespace IndexDetailsTests {
+ class Base {
+ dblock lk;
+ public:
+ Base() {
+ setClient( ns() );
+ }
+ virtual ~Base() {
+ if ( id_.info.isNull() )
+ return;
+ theDataFileMgr.deleteRecord( ns(), id_.info.rec(), id_.info );
+ ASSERT( theDataFileMgr.findAll( ns() )->eof() );
+ }
+ protected:
+ void create() {
+ NamespaceDetailsTransient::get_w( ns() ).deletedIndex();
+ BSONObjBuilder builder;
+ builder.append( "ns", ns() );
+ builder.append( "name", "testIndex" );
+ builder.append( "key", key() );
+ BSONObj bobj = builder.done();
+ id_.info = theDataFileMgr.insert( ns(), bobj.objdata(), bobj.objsize() );
+ // head not needed for current tests
+ // idx_.head = BtreeBucket::addHead( id_ );
+ }
+ static const char* ns() {
+ return "unittests.indexdetailstests";
+ }
+ IndexDetails& id() {
+ return id_;
+ }
+ virtual BSONObj key() const {
+ BSONObjBuilder k;
+ k.append( "a", 1 );
+ return k.obj();
+ }
+ BSONObj aDotB() const {
+ BSONObjBuilder k;
+ k.append( "a.b", 1 );
+ return k.obj();
+ }
+ BSONObj aAndB() const {
+ BSONObjBuilder k;
+ k.append( "a", 1 );
+ k.append( "b", 1 );
+ return k.obj();
+ }
+ static vector< int > shortArray() {
+ vector< int > a;
+ a.push_back( 1 );
+ a.push_back( 2 );
+ a.push_back( 3 );
+ return a;
+ }
+ static BSONObj simpleBC( int i ) {
+ BSONObjBuilder b;
+ b.append( "b", i );
+ b.append( "c", 4 );
+ return b.obj();
+ }
+ static void checkSize( int expected, const BSONObjSetDefaultOrder &objs ) {
+ ASSERT_EQUALS( BSONObjSetDefaultOrder::size_type( expected ), objs.size() );
+ }
+ static void assertEquals( const BSONObj &a, const BSONObj &b ) {
+ if ( a.woCompare( b ) != 0 ) {
+ out() << "expected: " << a.toString()
+ << ", got: " << b.toString() << endl;
+ }
+ ASSERT( a.woCompare( b ) == 0 );
+ }
+ BSONObj nullObj() const {
+ BSONObjBuilder b;
+ b.appendNull( "" );
+ return b.obj();
+ }
+ private:
+ dblock lk_;
+ IndexDetails id_;
+ };
+
+ class Create : public Base {
+ public:
+ void run() {
+ create();
+ ASSERT_EQUALS( "testIndex", id().indexName() );
+ ASSERT_EQUALS( ns(), id().parentNS() );
+ assertEquals( key(), id().keyPattern() );
+ }
+ };
+
+ class GetKeysFromObjectSimple : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjBuilder b, e;
+ b.append( "b", 4 );
+ b.append( "a", 5 );
+ e.append( "", 5 );
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( b.done(), keys );
+ checkSize( 1, keys );
+ assertEquals( e.obj(), *keys.begin() );
+ }
+ };
+
+ class GetKeysFromObjectDotted : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjBuilder a, e, b;
+ b.append( "b", 4 );
+ a.append( "a", b.done() );
+ a.append( "c", "foo" );
+ e.append( "", 4 );
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( a.done(), keys );
+ checkSize( 1, keys );
+ ASSERT_EQUALS( e.obj(), *keys.begin() );
+ }
+ private:
+ virtual BSONObj key() const {
+ return aDotB();
+ }
+ };
+
+ class GetKeysFromArraySimple : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjBuilder b;
+ b.append( "a", shortArray()) ;
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( b.done(), keys );
+ checkSize( 3, keys );
+ int j = 1;
+ for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
+ BSONObjBuilder b;
+ b.append( "", j );
+ assertEquals( b.obj(), *i );
+ }
+ }
+ };
+
+ class GetKeysFromArrayFirstElement : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjBuilder b;
+ b.append( "a", shortArray() );
+ b.append( "b", 2 );
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( b.done(), keys );
+ checkSize( 3, keys );
+ int j = 1;
+ for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
+ BSONObjBuilder b;
+ b.append( "", j );
+ b.append( "", 2 );
+ assertEquals( b.obj(), *i );
+ }
+ }
+ private:
+ virtual BSONObj key() const {
+ return aAndB();
+ }
+ };
+
+ class GetKeysFromArraySecondElement : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjBuilder b;
+ b.append( "first", 5 );
+ b.append( "a", shortArray()) ;
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( b.done(), keys );
+ checkSize( 3, keys );
+ int j = 1;
+ for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
+ BSONObjBuilder b;
+ b.append( "", 5 );
+ b.append( "", j );
+ assertEquals( b.obj(), *i );
+ }
+ }
+ private:
+ virtual BSONObj key() const {
+ BSONObjBuilder k;
+ k.append( "first", 1 );
+ k.append( "a", 1 );
+ return k.obj();
+ }
+ };
+
+ class GetKeysFromSecondLevelArray : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjBuilder b;
+ b.append( "b", shortArray() );
+ BSONObjBuilder a;
+ a.append( "a", b.done() );
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( a.done(), keys );
+ checkSize( 3, keys );
+ int j = 1;
+ for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
+ BSONObjBuilder b;
+ b.append( "", j );
+ assertEquals( b.obj(), *i );
+ }
+ }
+ private:
+ virtual BSONObj key() const {
+ return aDotB();
+ }
+ };
+
+ class ParallelArraysBasic : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjBuilder b;
+ b.append( "a", shortArray() );
+ b.append( "b", shortArray() );
+
+ BSONObjSetDefaultOrder keys;
+ ASSERT_EXCEPTION( id().getKeysFromObject( b.done(), keys ),
+ UserException );
+ }
+ private:
+ virtual BSONObj key() const {
+ return aAndB();
+ }
+ };
+
+ class ArraySubobjectBasic : public Base {
+ public:
+ void run() {
+ create();
+ vector< BSONObj > elts;
+ for ( int i = 1; i < 4; ++i )
+ elts.push_back( simpleBC( i ) );
+ BSONObjBuilder b;
+ b.append( "a", elts );
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( b.done(), keys );
+ checkSize( 3, keys );
+ int j = 1;
+ for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
+ BSONObjBuilder b;
+ b.append( "", j );
+ assertEquals( b.obj(), *i );
+ }
+ }
+ private:
+ virtual BSONObj key() const {
+ return aDotB();
+ }
+ };
+
+ class ArraySubobjectMultiFieldIndex : public Base {
+ public:
+ void run() {
+ create();
+ vector< BSONObj > elts;
+ for ( int i = 1; i < 4; ++i )
+ elts.push_back( simpleBC( i ) );
+ BSONObjBuilder b;
+ b.append( "a", elts );
+ b.append( "d", 99 );
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( b.done(), keys );
+ checkSize( 3, keys );
+ int j = 1;
+ for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
+ BSONObjBuilder c;
+ c.append( "", j );
+ c.append( "", 99 );
+ assertEquals( c.obj(), *i );
+ }
+ }
+ private:
+ virtual BSONObj key() const {
+ BSONObjBuilder k;
+ k.append( "a.b", 1 );
+ k.append( "d", 1 );
+ return k.obj();
+ }
+ };
+
+ class ArraySubobjectSingleMissing : public Base {
+ public:
+ void run() {
+ create();
+ vector< BSONObj > elts;
+ BSONObjBuilder s;
+ s.append( "foo", 41 );
+ elts.push_back( s.obj() );
+ for ( int i = 1; i < 4; ++i )
+ elts.push_back( simpleBC( i ) );
+ BSONObjBuilder b;
+ b.append( "a", elts );
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( b.done(), keys );
+ checkSize( 4, keys );
+ BSONObjSetDefaultOrder::iterator i = keys.begin();
+ assertEquals( nullObj(), *i++ );
+ for ( int j = 1; j < 4; ++i, ++j ) {
+ BSONObjBuilder b;
+ b.append( "", j );
+ assertEquals( b.obj(), *i );
+ }
+ }
+ private:
+ virtual BSONObj key() const {
+ return aDotB();
+ }
+ };
+
+ class ArraySubobjectMissing : public Base {
+ public:
+ void run() {
+ create();
+ vector< BSONObj > elts;
+ BSONObjBuilder s;
+ s.append( "foo", 41 );
+ for ( int i = 1; i < 4; ++i )
+ elts.push_back( s.done() );
+ BSONObjBuilder b;
+ b.append( "a", elts );
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( b.done(), keys );
+ checkSize( 1, keys );
+ assertEquals( nullObj(), *keys.begin() );
+ }
+ private:
+ virtual BSONObj key() const {
+ return aDotB();
+ }
+ };
+
+ class MissingField : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( BSON( "b" << 1 ), keys );
+ checkSize( 1, keys );
+ assertEquals( nullObj(), *keys.begin() );
+ }
+ private:
+ virtual BSONObj key() const {
+ return BSON( "a" << 1 );
+ }
+ };
+
+ class SubobjectMissing : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( fromjson( "{a:[1,2]}" ), keys );
+ checkSize( 1, keys );
+ assertEquals( nullObj(), *keys.begin() );
+ }
+ private:
+ virtual BSONObj key() const {
+ return aDotB();
+ }
+ };
+
+ class CompoundMissing : public Base {
+ public:
+ void run(){
+ create();
+
+ {
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( fromjson( "{x:'a',y:'b'}" ) , keys );
+ checkSize( 1 , keys );
+ assertEquals( BSON( "" << "a" << "" << "b" ) , *keys.begin() );
+ }
+
+ {
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( fromjson( "{x:'a'}" ) , keys );
+ checkSize( 1 , keys );
+ BSONObjBuilder b;
+ b.append( "" , "a" );
+ b.appendNull( "" );
+ assertEquals( b.obj() , *keys.begin() );
+ }
+
+ }
+
+ private:
+ virtual BSONObj key() const {
+ return BSON( "x" << 1 << "y" << 1 );
+ }
+
+ };
+
+ class ArraySubelementComplex : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( fromjson( "{a:[{b:[2]}]}" ), keys );
+ checkSize( 1, keys );
+ assertEquals( BSON( "" << 2 ), *keys.begin() );
+ }
+ private:
+ virtual BSONObj key() const {
+ return aDotB();
+ }
+ };
+
+ class ParallelArraysComplex : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjSetDefaultOrder keys;
+ ASSERT_EXCEPTION( id().getKeysFromObject( fromjson( "{a:[{b:[1],c:[2]}]}" ), keys ),
+ UserException );
+ }
+ private:
+ virtual BSONObj key() const {
+ return fromjson( "{'a.b':1,'a.c':1}" );
+ }
+ };
+
+ class AlternateMissing : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( fromjson( "{a:[{b:1},{c:2}]}" ), keys );
+ checkSize( 2, keys );
+ BSONObjSetDefaultOrder::iterator i = keys.begin();
+ {
+ BSONObjBuilder e;
+ e.appendNull( "" );
+ e.append( "", 2 );
+ assertEquals( e.obj(), *i++ );
+ }
+
+ {
+ BSONObjBuilder e;
+ e.append( "", 1 );
+ e.appendNull( "" );
+ assertEquals( e.obj(), *i++ );
+ }
+ }
+ private:
+ virtual BSONObj key() const {
+ return fromjson( "{'a.b':1,'a.c':1}" );
+ }
+ };
+
+ class MultiComplex : public Base {
+ public:
+ void run() {
+ create();
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( fromjson( "{a:[{b:1},{b:[1,2,3]}]}" ), keys );
+ checkSize( 3, keys );
+ }
+ private:
+ virtual BSONObj key() const {
+ return aDotB();
+ }
+ };
+
+ class EmptyArray : Base {
+ public:
+ void run(){
+ create();
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( fromjson( "{a:[1,2]}" ), keys );
+ checkSize(2, keys );
+ keys.clear();
+
+ id().getKeysFromObject( fromjson( "{a:[1]}" ), keys );
+ checkSize(1, keys );
+ keys.clear();
+
+ id().getKeysFromObject( fromjson( "{a:null}" ), keys );
+ checkSize(1, keys );
+ keys.clear();
+
+ id().getKeysFromObject( fromjson( "{a:[]}" ), keys );
+ checkSize(1, keys );
+ keys.clear();
+ }
+ };
+
+ class MultiEmptyArray : Base {
+ public:
+ void run(){
+ create();
+
+ BSONObjSetDefaultOrder keys;
+ id().getKeysFromObject( fromjson( "{a:1,b:[1,2]}" ), keys );
+ checkSize(2, keys );
+ keys.clear();
+
+ id().getKeysFromObject( fromjson( "{a:1,b:[1]}" ), keys );
+ checkSize(1, keys );
+ keys.clear();
+
+ id().getKeysFromObject( fromjson( "{a:1,b:null}" ), keys );
+ cout << "YO : " << *(keys.begin()) << endl;
+ checkSize(1, keys );
+ keys.clear();
+
+ id().getKeysFromObject( fromjson( "{a:1,b:[]}" ), keys );
+ checkSize(1, keys );
+ cout << "YO : " << *(keys.begin()) << endl;
+ ASSERT_EQUALS( NumberInt , keys.begin()->firstElement().type() );
+ keys.clear();
+ }
+
+ protected:
+ BSONObj key() const {
+ return aAndB();
+ }
+ };
+
+
+ } // namespace IndexDetailsTests
+
+ namespace NamespaceDetailsTests {
+
+ class Base {
+ dblock lk;
+ public:
+ Base( const char *ns = "unittests.NamespaceDetailsTests" ) : ns_( ns ) {}
+ virtual ~Base() {
+ if ( !nsd() )
+ return;
+ string s( ns() );
+ string errmsg;
+ BSONObjBuilder result;
+ dropCollection( s, errmsg, result );
+ }
+ protected:
+ void create() {
+ dblock lk;
+ setClient( ns() );
+ string err;
+ ASSERT( userCreateNS( ns(), fromjson( spec() ), err, false ) );
+ }
+ virtual string spec() const {
+ return "{\"capped\":true,\"size\":512}";
+ }
+ int nRecords() const {
+ int count = 0;
+ for ( DiskLoc i = nsd()->firstExtent; !i.isNull(); i = i.ext()->xnext ) {
+ int fileNo = i.ext()->firstRecord.a();
+ if ( fileNo == -1 )
+ continue;
+ for ( int j = i.ext()->firstRecord.getOfs(); j != DiskLoc::NullOfs;
+ j = DiskLoc( fileNo, j ).rec()->nextOfs ) {
+ ++count;
+ }
+ }
+ ASSERT_EQUALS( count, nsd()->nrecords );
+ return count;
+ }
+ int nExtents() const {
+ int count = 0;
+ for ( DiskLoc i = nsd()->firstExtent; !i.isNull(); i = i.ext()->xnext )
+ ++count;
+ return count;
+ }
+ static int min( int a, int b ) {
+ return a < b ? a : b;
+ }
+ const char *ns() const {
+ return ns_;
+ }
+ NamespaceDetails *nsd() const {
+ return nsdetails( ns() );
+ }
+ static BSONObj bigObj() {
+ string as( 187, 'a' );
+ BSONObjBuilder b;
+ b.append( "a", as );
+ return b.obj();
+ }
+ private:
+ const char *ns_;
+ };
+
+ class Create : public Base {
+ public:
+ void run() {
+ create();
+ ASSERT( nsd() );
+ ASSERT_EQUALS( 0, nRecords() );
+ ASSERT( nsd()->firstExtent == nsd()->capExtent );
+ DiskLoc initial = DiskLoc();
+ initial.setInvalid();
+ ASSERT( initial == nsd()->capFirstNewRecord );
+ }
+ };
+
+ class SingleAlloc : public Base {
+ public:
+ void run() {
+ create();
+ BSONObj b = bigObj();
+ ASSERT( !theDataFileMgr.insert( ns(), b.objdata(), b.objsize() ).isNull() );
+ ASSERT_EQUALS( 1, nRecords() );
+ }
+ };
+
+ class Realloc : public Base {
+ public:
+ void run() {
+ create();
+ BSONObj b = bigObj();
+
+ DiskLoc l[ 6 ];
+ for ( int i = 0; i < 6; ++i ) {
+ l[ i ] = theDataFileMgr.insert( ns(), b.objdata(), b.objsize() );
+ ASSERT( !l[ i ].isNull() );
+ ASSERT_EQUALS( 1 + i % 2, nRecords() );
+ if ( i > 1 )
+ ASSERT( l[ i ] == l[ i - 2 ] );
+ }
+ }
+ };
+
+ class TwoExtent : public Base {
+ public:
+ void run() {
+ create();
+ ASSERT_EQUALS( 2, nExtents() );
+ BSONObj b = bigObj();
+
+ DiskLoc l[ 8 ];
+ for ( int i = 0; i < 8; ++i ) {
+ l[ i ] = theDataFileMgr.insert( ns(), b.objdata(), b.objsize() );
+ ASSERT( !l[ i ].isNull() );
+ ASSERT_EQUALS( i < 2 ? i + 1 : 3 + i % 2, nRecords() );
+ if ( i > 3 )
+ ASSERT( l[ i ] == l[ i - 4 ] );
+ }
+
+ // Too big
+ BSONObjBuilder bob;
+ bob.append( "a", string( 787, 'a' ) );
+ BSONObj bigger = bob.done();
+ ASSERT( theDataFileMgr.insert( ns(), bigger.objdata(), bigger.objsize() ).isNull() );
+ ASSERT_EQUALS( 0, nRecords() );
+ }
+ private:
+ virtual string spec() const {
+ return "{\"capped\":true,\"size\":512,\"$nExtents\":2}";
+ }
+ };
+
+ class Migrate : public Base {
+ public:
+ void run() {
+ create();
+ nsd()->deletedList[ 2 ] = nsd()->deletedList[ 0 ].drec()->nextDeleted.drec()->nextDeleted;
+ nsd()->deletedList[ 0 ].drec()->nextDeleted.drec()->nextDeleted = DiskLoc();
+ nsd()->deletedList[ 1 ].Null();
+ NamespaceDetails *d = nsd();
+ zero( &d->capExtent );
+ zero( &d->capFirstNewRecord );
+
+ nsd();
+
+ ASSERT( nsd()->firstExtent == nsd()->capExtent );
+ ASSERT( nsd()->capExtent.getOfs() != 0 );
+ ASSERT( !nsd()->capFirstNewRecord.isValid() );
+ int nDeleted = 0;
+ for ( DiskLoc i = nsd()->deletedList[ 0 ]; !i.isNull(); i = i.drec()->nextDeleted, ++nDeleted );
+ ASSERT_EQUALS( 10, nDeleted );
+ ASSERT( nsd()->deletedList[ 1 ].isNull() );
+ }
+ private:
+ static void zero( DiskLoc *d ) {
+ memset( d, 0, sizeof( DiskLoc ) );
+ }
+ virtual string spec() const {
+ return "{\"capped\":true,\"size\":512,\"$nExtents\":10}";
+ }
+ };
+
+ // This isn't a particularly useful test, and because it doesn't clean up
+ // after itself, /tmp/unittest needs to be cleared after running.
+ // class BigCollection : public Base {
+ // public:
+ // BigCollection() : Base( "NamespaceDetailsTests_BigCollection" ) {}
+ // void run() {
+ // create();
+ // ASSERT_EQUALS( 2, nExtents() );
+ // }
+ // private:
+ // virtual string spec() const {
+ // // NOTE 256 added to size in _userCreateNS()
+ // long long big = MongoDataFile::maxSize() - MDFHeader::headerSize();
+ // stringstream ss;
+ // ss << "{\"capped\":true,\"size\":" << big << "}";
+ // return ss.str();
+ // }
+ // };
+
+ class Size {
+ public:
+ void run() {
+ ASSERT_EQUALS( 496U, sizeof( NamespaceDetails ) );
+ }
+ };
+
+ } // namespace NamespaceDetailsTests
+
+ class All : public Suite {
+ public:
+ All() : Suite( "namespace" ){
+ }
+
+ void setupTests(){
+ add< IndexDetailsTests::Create >();
+ add< IndexDetailsTests::GetKeysFromObjectSimple >();
+ add< IndexDetailsTests::GetKeysFromObjectDotted >();
+ add< IndexDetailsTests::GetKeysFromArraySimple >();
+ add< IndexDetailsTests::GetKeysFromArrayFirstElement >();
+ add< IndexDetailsTests::GetKeysFromArraySecondElement >();
+ add< IndexDetailsTests::GetKeysFromSecondLevelArray >();
+ add< IndexDetailsTests::ParallelArraysBasic >();
+ add< IndexDetailsTests::ArraySubobjectBasic >();
+ add< IndexDetailsTests::ArraySubobjectMultiFieldIndex >();
+ add< IndexDetailsTests::ArraySubobjectSingleMissing >();
+ add< IndexDetailsTests::ArraySubobjectMissing >();
+ add< IndexDetailsTests::ArraySubelementComplex >();
+ add< IndexDetailsTests::ParallelArraysComplex >();
+ add< IndexDetailsTests::AlternateMissing >();
+ add< IndexDetailsTests::MultiComplex >();
+ add< IndexDetailsTests::EmptyArray >();
+ add< IndexDetailsTests::MultiEmptyArray >();
+ add< IndexDetailsTests::MissingField >();
+ add< IndexDetailsTests::SubobjectMissing >();
+ add< IndexDetailsTests::CompoundMissing >();
+ add< NamespaceDetailsTests::Create >();
+ add< NamespaceDetailsTests::SingleAlloc >();
+ add< NamespaceDetailsTests::Realloc >();
+ add< NamespaceDetailsTests::TwoExtent >();
+ add< NamespaceDetailsTests::Migrate >();
+ // add< NamespaceDetailsTests::BigCollection >();
+ add< NamespaceDetailsTests::Size >();
+ }
+ } myall;
+} // namespace NamespaceTests
+
diff --git a/dbtests/pairingtests.cpp b/dbtests/pairingtests.cpp
new file mode 100644
index 0000000..b3e772b
--- /dev/null
+++ b/dbtests/pairingtests.cpp
@@ -0,0 +1,344 @@
+// pairingtests.cpp : Pairing unit tests.
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/replset.h"
+#include "dbtests.h"
+#include "mockdbclient.h"
+#include "../db/cmdline.h"
+
+namespace mongo {
+ extern PairSync *pairSync;
+} // namespace mongo
+
+namespace PairingTests {
+ class Base {
+ protected:
+ Base() {
+ backup = pairSync;
+ setSynced();
+ }
+ ~Base() {
+ pairSync = backup;
+ dblock lk;
+ Helpers::emptyCollection( "local.pair.sync" );
+ if ( pairSync->initialSyncCompleted() ) {
+ // save to db
+ pairSync->setInitialSyncCompleted();
+ }
+ }
+ static void setSynced() {
+ init();
+ pairSync = synced;
+ pairSync->setInitialSyncCompletedLocking();
+ ASSERT( pairSync->initialSyncCompleted() );
+ }
+ static void setNotSynced() {
+ init();
+ pairSync = notSynced;
+ ASSERT( !pairSync->initialSyncCompleted() );
+ }
+ static void flipSync() {
+ if ( pairSync->initialSyncCompleted() )
+ setNotSynced();
+ else
+ setSynced();
+ }
+ private:
+ static void init() {
+ dblock lk;
+ Helpers::emptyCollection( "local.pair.sync" );
+ if ( synced != 0 && notSynced != 0 )
+ return;
+ notSynced = new PairSync();
+ notSynced->init();
+ synced = new PairSync();
+ synced->init();
+ synced->setInitialSyncCompleted();
+ Helpers::emptyCollection( "local.pair.sync" );
+ }
+ PairSync *backup;
+ static PairSync *synced;
+ static PairSync *notSynced;
+ };
+ PairSync *Base::synced = 0;
+ PairSync *Base::notSynced = 0;
+
+ namespace ReplPairTests {
+ class Create : public Base {
+ public:
+ void run() {
+ ReplPair rp1( "foo", "bar" );
+ checkFields( rp1, "foo", "foo", CmdLine::DefaultDBPort, "bar" );
+
+ ReplPair rp2( "foo:1", "bar" );
+ checkFields( rp2, "foo:1", "foo", 1, "bar" );
+
+ // FIXME Should we accept this input?
+ ReplPair rp3( "", "bar" );
+ checkFields( rp3, "", "", CmdLine::DefaultDBPort, "bar" );
+
+ ASSERT_EXCEPTION( ReplPair( "foo:", "bar" ),
+ UserException );
+
+ ASSERT_EXCEPTION( ReplPair( "foo:0", "bar" ),
+ UserException );
+
+ ASSERT_EXCEPTION( ReplPair( "foo:10000000", "bar" ),
+ UserException );
+
+ ASSERT_EXCEPTION( ReplPair( "foo", "" ),
+ UserException );
+ }
+ private:
+ void checkFields( const ReplPair &rp,
+ const string &remote,
+ const string &remoteHost,
+ int remotePort,
+ const string &arbHost ) {
+ ASSERT( rp.state == ReplPair::State_Negotiating );
+ ASSERT_EQUALS( remote, rp.remote );
+ ASSERT_EQUALS( remoteHost, rp.remoteHost );
+ ASSERT_EQUALS( remotePort, rp.remotePort );
+ ASSERT_EQUALS( arbHost, rp.arbHost );
+ }
+ };
+
+ class Dominant : public Base {
+ public:
+ Dominant() : oldPort_( cmdLine.port ) {
+ cmdLine.port = 10;
+ }
+ ~Dominant() {
+ cmdLine.port = oldPort_;
+ }
+ void run() {
+ ASSERT( ReplPair( "b:9", "-" ).dominant( "b" ) );
+ ASSERT( !ReplPair( "b:10", "-" ).dominant( "b" ) );
+ ASSERT( ReplPair( "b", "-" ).dominant( "c" ) );
+ ASSERT( !ReplPair( "b", "-" ).dominant( "a" ) );
+ }
+ private:
+ int oldPort_;
+ };
+
+ class SetMaster {
+ public:
+ void run() {
+ ReplPair rp( "a", "b" );
+ rp.setMaster( ReplPair::State_CantArb, "foo" );
+ ASSERT( rp.state == ReplPair::State_CantArb );
+ ASSERT_EQUALS( "foo", rp.info );
+ rp.setMaster( ReplPair::State_Confused, "foo" );
+ ASSERT( rp.state == ReplPair::State_Confused );
+ }
+ };
+
+ class Negotiate : public Base {
+ public:
+ void run() {
+ ReplPair rp( "a", "b" );
+ MockDBClientConnection cc;
+
+ cc.one( res( 0, 0 ) );
+ rp.negotiate( &cc, "dummy" );
+ ASSERT( rp.state == ReplPair::State_Confused );
+
+ rp.state = ReplPair::State_Negotiating;
+ cc.one( res( 1, 2 ) );
+ rp.negotiate( &cc, "dummy" );
+ ASSERT( rp.state == ReplPair::State_Negotiating );
+
+ cc.one( res( 1, ReplPair::State_Slave ) );
+ rp.negotiate( &cc, "dummy" );
+ ASSERT( rp.state == ReplPair::State_Slave );
+
+ cc.one( res( 1, ReplPair::State_Master ) );
+ rp.negotiate( &cc, "dummy" );
+ ASSERT( rp.state == ReplPair::State_Master );
+ }
+ private:
+ BSONObj res( int ok, int youAre ) {
+ BSONObjBuilder b;
+ b.append( "ok", ok );
+ b.append( "you_are", youAre );
+ return b.obj();
+ }
+ };
+
+ class Arbitrate : public Base {
+ public:
+ void run() {
+ ReplPair rp1( "a", "-" );
+ rp1.arbitrate();
+ ASSERT( rp1.state == ReplPair::State_Master );
+
+ TestableReplPair rp2( false, BSONObj() );
+ rp2.arbitrate();
+ ASSERT( rp2.state == ReplPair::State_CantArb );
+
+ TestableReplPair rp3( true, fromjson( "{ok:0}" ) );
+ rp3.arbitrate();
+ ASSERT( rp3.state == ReplPair::State_Confused );
+
+ TestableReplPair rp4( true, fromjson( "{ok:1,you_are:1}" ) );
+ rp4.arbitrate();
+ ASSERT( rp4.state == ReplPair::State_Master );
+
+ TestableReplPair rp5( true, fromjson( "{ok:1,you_are:0}" ) );
+ rp5.arbitrate();
+ ASSERT( rp5.state == ReplPair::State_Slave );
+
+ TestableReplPair rp6( true, fromjson( "{ok:1,you_are:-1}" ) );
+ rp6.arbitrate();
+ // unchanged from initial value
+ ASSERT( rp6.state == ReplPair::State_Negotiating );
+ }
+ private:
+ class TestableReplPair : public ReplPair {
+ public:
+ TestableReplPair( bool connect, const BSONObj &one ) :
+ ReplPair( "a", "z" ),
+ connect_( connect ),
+ one_( one ) {
+ }
+ virtual
+ DBClientConnection *newClientConnection() const {
+ MockDBClientConnection * c = new MockDBClientConnection();
+ c->connect( connect_ );
+ c->one( one_ );
+ return c;
+ }
+ private:
+ bool connect_;
+ BSONObj one_;
+ };
+ };
+ } // namespace ReplPairTests
+
+ class DirectConnectBase : public Base {
+ public:
+ virtual ~DirectConnectBase() {}
+ protected:
+ void negotiate( ReplPair &a, ReplPair &b ) {
+ auto_ptr< DBClientConnection > c( new DirectDBClientConnection( &b, cc() ) );
+ a.negotiate( c.get(), "dummy" );
+ }
+ virtual DirectDBClientConnection::ConnectionCallback *cc() {
+ return 0;
+ }
+ void checkNegotiation( const char *host1, const char *arb1, int state1, int newState1,
+ const char *host2, const char *arb2, int state2, int newState2 ) {
+ ReplPair one( host1, arb1 );
+ one.state = state1;
+ ReplPair two( host2, arb2 );
+ two.state = state2;
+ negotiate( one, two );
+ ASSERT( one.state == newState1 );
+ ASSERT( two.state == newState2 );
+ }
+ };
+
+ class Negotiate : public DirectConnectBase {
+ public:
+ void run() {
+ checkNegotiation( "a", "-", ReplPair::State_Negotiating, ReplPair::State_Negotiating,
+ "b", "-", ReplPair::State_Negotiating, ReplPair::State_Negotiating );
+ checkNegotiation( "b", "-", ReplPair::State_Negotiating, ReplPair::State_Slave,
+ "a", "-", ReplPair::State_Negotiating, ReplPair::State_Master );
+
+ checkNegotiation( "b", "-", ReplPair::State_Master, ReplPair::State_Master,
+ "a", "-", ReplPair::State_Negotiating, ReplPair::State_Slave );
+
+ // No change when negotiate() called on a.
+ checkNegotiation( "a", "-", ReplPair::State_Master, ReplPair::State_Master,
+ "b", "-", ReplPair::State_Master, ReplPair::State_Master );
+ // Resolve Master - Master.
+ checkNegotiation( "b", "-", ReplPair::State_Master, ReplPair::State_Slave,
+ "a", "-", ReplPair::State_Master, ReplPair::State_Master );
+
+ // FIXME Move from negotiating to master?
+ checkNegotiation( "b", "-", ReplPair::State_Slave, ReplPair::State_Slave,
+ "a", "-", ReplPair::State_Negotiating, ReplPair::State_Master );
+ }
+ };
+
+ class NegotiateWithCatchup : public DirectConnectBase {
+ public:
+ void run() {
+ // a caught up, b not
+ setNotSynced();
+ checkNegotiation( "b", "-", ReplPair::State_Negotiating, ReplPair::State_Slave,
+ "a", "-", ReplPair::State_Negotiating, ReplPair::State_Master );
+ // b caught up, a not
+ setSynced();
+ checkNegotiation( "b", "-", ReplPair::State_Negotiating, ReplPair::State_Master,
+ "a", "-", ReplPair::State_Negotiating, ReplPair::State_Slave );
+
+ // a caught up, b not
+ setNotSynced();
+ checkNegotiation( "b", "-", ReplPair::State_Slave, ReplPair::State_Slave,
+ "a", "-", ReplPair::State_Negotiating, ReplPair::State_Master );
+ // b caught up, a not
+ setSynced();
+ checkNegotiation( "b", "-", ReplPair::State_Slave, ReplPair::State_Master,
+ "a", "-", ReplPair::State_Negotiating, ReplPair::State_Slave );
+ }
+ private:
+ class NegateCatchup : public DirectDBClientConnection::ConnectionCallback {
+ virtual void beforeCommand() {
+ Base::flipSync();
+ }
+ virtual void afterCommand() {
+ Base::flipSync();
+ }
+ };
+ virtual DirectDBClientConnection::ConnectionCallback *cc() {
+ return &cc_;
+ }
+ NegateCatchup cc_;
+ };
+
+ class NobodyCaughtUp : public DirectConnectBase {
+ public:
+ void run() {
+ setNotSynced();
+ checkNegotiation( "b", "-", ReplPair::State_Negotiating, ReplPair::State_Negotiating,
+ "a", "-", ReplPair::State_Negotiating, ReplPair::State_Slave );
+ }
+ };
+
+ class All : public Suite {
+ public:
+ All() : Suite( "pairing" ){
+ }
+
+ void setupTests(){
+ add< ReplPairTests::Create >();
+ add< ReplPairTests::Dominant >();
+ add< ReplPairTests::SetMaster >();
+ add< ReplPairTests::Negotiate >();
+ add< ReplPairTests::Arbitrate >();
+ add< Negotiate >();
+ add< NegotiateWithCatchup >();
+ add< NobodyCaughtUp >();
+ }
+ } myall;
+} // namespace PairingTests
+
diff --git a/dbtests/pdfiletests.cpp b/dbtests/pdfiletests.cpp
new file mode 100644
index 0000000..17659c0
--- /dev/null
+++ b/dbtests/pdfiletests.cpp
@@ -0,0 +1,328 @@
+// pdfiletests.cpp : pdfile unit tests.
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/pdfile.h"
+
+#include "../db/db.h"
+#include "../db/json.h"
+
+#include "dbtests.h"
+
+namespace PdfileTests {
+
+ namespace ScanCapped {
+
+ class Base {
+ public:
+ Base() {
+ setClient( ns() );
+ }
+ virtual ~Base() {
+ if ( !nsd() )
+ return;
+ string n( ns() );
+ dropNS( n );
+ }
+ void run() {
+ stringstream spec;
+ spec << "{\"capped\":true,\"size\":2000,\"$nExtents\":" << nExtents() << "}";
+ string err;
+ ASSERT( userCreateNS( ns(), fromjson( spec.str() ), err, false ) );
+ prepare();
+ int j = 0;
+ for ( auto_ptr< Cursor > i = theDataFileMgr.findAll( ns() );
+ i->ok(); i->advance(), ++j )
+ ASSERT_EQUALS( j, i->current().firstElement().number() );
+ ASSERT_EQUALS( count(), j );
+
+ j = count() - 1;
+ for ( auto_ptr< Cursor > i =
+ findTableScan( ns(), fromjson( "{\"$natural\":-1}" ) );
+ i->ok(); i->advance(), --j )
+ ASSERT_EQUALS( j, i->current().firstElement().number() );
+ ASSERT_EQUALS( -1, j );
+ }
+ protected:
+ virtual void prepare() = 0;
+ virtual int count() const = 0;
+ virtual int nExtents() const {
+ return 0;
+ }
+ // bypass standard alloc/insert routines to use the extent we want.
+ static DiskLoc insert( DiskLoc ext, int i ) {
+ BSONObjBuilder b;
+ b.append( "a", i );
+ BSONObj o = b.done();
+ int len = o.objsize();
+ Extent *e = ext.ext();
+ int ofs;
+ if ( e->lastRecord.isNull() )
+ ofs = ext.getOfs() + ( e->extentData - (char *)e );
+ else
+ ofs = e->lastRecord.getOfs() + e->lastRecord.rec()->lengthWithHeaders;
+ DiskLoc dl( ext.a(), ofs );
+ Record *r = dl.rec();
+ r->lengthWithHeaders = Record::HeaderSize + len;
+ r->extentOfs = e->myLoc.getOfs();
+ r->nextOfs = DiskLoc::NullOfs;
+ r->prevOfs = e->lastRecord.isNull() ? DiskLoc::NullOfs : e->lastRecord.getOfs();
+ memcpy( r->data, o.objdata(), len );
+ if ( e->firstRecord.isNull() )
+ e->firstRecord = dl;
+ else
+ e->lastRecord.rec()->nextOfs = ofs;
+ e->lastRecord = dl;
+ return dl;
+ }
+ static const char *ns() {
+ return "unittests.ScanCapped";
+ }
+ static NamespaceDetails *nsd() {
+ return nsdetails( ns() );
+ }
+ private:
+ dblock lk_;
+ };
+
+ class Empty : public Base {
+ virtual void prepare() {}
+ virtual int count() const {
+ return 0;
+ }
+ };
+
+ class EmptyLooped : public Base {
+ virtual void prepare() {
+ nsd()->capFirstNewRecord = DiskLoc();
+ }
+ virtual int count() const {
+ return 0;
+ }
+ };
+
+ class EmptyMultiExtentLooped : public Base {
+ virtual void prepare() {
+ nsd()->capFirstNewRecord = DiskLoc();
+ }
+ virtual int count() const {
+ return 0;
+ }
+ virtual int nExtents() const {
+ return 3;
+ }
+ };
+
+ class Single : public Base {
+ virtual void prepare() {
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 0 );
+ }
+ virtual int count() const {
+ return 1;
+ }
+ };
+
+ class NewCapFirst : public Base {
+ virtual void prepare() {
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 0 );
+ insert( nsd()->capExtent, 1 );
+ }
+ virtual int count() const {
+ return 2;
+ }
+ };
+
+ class NewCapLast : public Base {
+ virtual void prepare() {
+ insert( nsd()->capExtent, 0 );
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 1 );
+ }
+ virtual int count() const {
+ return 2;
+ }
+ };
+
+ class NewCapMiddle : public Base {
+ virtual void prepare() {
+ insert( nsd()->capExtent, 0 );
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 1 );
+ insert( nsd()->capExtent, 2 );
+ }
+ virtual int count() const {
+ return 3;
+ }
+ };
+
+ class FirstExtent : public Base {
+ virtual void prepare() {
+ insert( nsd()->capExtent, 0 );
+ insert( nsd()->lastExtent, 1 );
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 2 );
+ insert( nsd()->capExtent, 3 );
+ }
+ virtual int count() const {
+ return 4;
+ }
+ virtual int nExtents() const {
+ return 2;
+ }
+ };
+
+ class LastExtent : public Base {
+ virtual void prepare() {
+ nsd()->capExtent = nsd()->lastExtent;
+ insert( nsd()->capExtent, 0 );
+ insert( nsd()->firstExtent, 1 );
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 2 );
+ insert( nsd()->capExtent, 3 );
+ }
+ virtual int count() const {
+ return 4;
+ }
+ virtual int nExtents() const {
+ return 2;
+ }
+ };
+
+ class MidExtent : public Base {
+ virtual void prepare() {
+ nsd()->capExtent = nsd()->firstExtent.ext()->xnext;
+ insert( nsd()->capExtent, 0 );
+ insert( nsd()->lastExtent, 1 );
+ insert( nsd()->firstExtent, 2 );
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 3 );
+ insert( nsd()->capExtent, 4 );
+ }
+ virtual int count() const {
+ return 5;
+ }
+ virtual int nExtents() const {
+ return 3;
+ }
+ };
+
+ class AloneInExtent : public Base {
+ virtual void prepare() {
+ nsd()->capExtent = nsd()->firstExtent.ext()->xnext;
+ insert( nsd()->lastExtent, 0 );
+ insert( nsd()->firstExtent, 1 );
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 2 );
+ }
+ virtual int count() const {
+ return 3;
+ }
+ virtual int nExtents() const {
+ return 3;
+ }
+ };
+
+ class FirstInExtent : public Base {
+ virtual void prepare() {
+ nsd()->capExtent = nsd()->firstExtent.ext()->xnext;
+ insert( nsd()->lastExtent, 0 );
+ insert( nsd()->firstExtent, 1 );
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 2 );
+ insert( nsd()->capExtent, 3 );
+ }
+ virtual int count() const {
+ return 4;
+ }
+ virtual int nExtents() const {
+ return 3;
+ }
+ };
+
+ class LastInExtent : public Base {
+ virtual void prepare() {
+ nsd()->capExtent = nsd()->firstExtent.ext()->xnext;
+ insert( nsd()->capExtent, 0 );
+ insert( nsd()->lastExtent, 1 );
+ insert( nsd()->firstExtent, 2 );
+ nsd()->capFirstNewRecord = insert( nsd()->capExtent, 3 );
+ }
+ virtual int count() const {
+ return 4;
+ }
+ virtual int nExtents() const {
+ return 3;
+ }
+ };
+
+ } // namespace ScanCapped
+
+ namespace Insert {
+ class Base {
+ public:
+ Base() {
+ setClient( ns() );
+ }
+ virtual ~Base() {
+ if ( !nsd() )
+ return;
+ string n( ns() );
+ dropNS( n );
+ }
+ protected:
+ static const char *ns() {
+ return "unittests.pdfiletests.Insert";
+ }
+ static NamespaceDetails *nsd() {
+ return nsdetails( ns() );
+ }
+ private:
+ dblock lk_;
+ };
+
+ class UpdateDate : public Base {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendTimestamp( "a" );
+ BSONObj o = b.done();
+ ASSERT( 0 == o.getField( "a" ).date() );
+ theDataFileMgr.insert( ns(), o );
+ ASSERT( 0 != o.getField( "a" ).date() );
+ }
+ };
+ } // namespace Insert
+
+ class All : public Suite {
+ public:
+ All() : Suite( "pdfile" ){}
+
+ void setupTests(){
+ add< ScanCapped::Empty >();
+ add< ScanCapped::EmptyLooped >();
+ add< ScanCapped::EmptyMultiExtentLooped >();
+ add< ScanCapped::Single >();
+ add< ScanCapped::NewCapFirst >();
+ add< ScanCapped::NewCapLast >();
+ add< ScanCapped::NewCapMiddle >();
+ add< ScanCapped::FirstExtent >();
+ add< ScanCapped::LastExtent >();
+ add< ScanCapped::MidExtent >();
+ add< ScanCapped::AloneInExtent >();
+ add< ScanCapped::FirstInExtent >();
+ add< ScanCapped::LastInExtent >();
+ add< Insert::UpdateDate >();
+ }
+ } myall;
+
+} // namespace PdfileTests
+
diff --git a/dbtests/perf/perftest.cpp b/dbtests/perf/perftest.cpp
new file mode 100644
index 0000000..6fe9d6a
--- /dev/null
+++ b/dbtests/perf/perftest.cpp
@@ -0,0 +1,695 @@
+// perftest.cpp : Run db performance tests.
+//
+
+/**
+ * Copyright (C) 2009 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+
+#include "../../client/dbclient.h"
+#include "../../db/instance.h"
+#include "../../db/query.h"
+#include "../../db/queryoptimizer.h"
+#include "../../util/file_allocator.h"
+
+#include "../framework.h"
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+namespace mongo {
+ extern string dbpath;
+} // namespace mongo
+
+
+using namespace mongo;
+using namespace mongo::regression;
+
+DBClientBase *client_;
+
+// Each test runs with a separate db, so no test does any of the startup
+// (ie allocation) work for another test.
+template< class T >
+string testDb( T *t = 0 ) {
+ string name = mongo::regression::demangleName( typeid( T ) );
+ // Make filesystem safe.
+ for( string::iterator i = name.begin(); i != name.end(); ++i )
+ if ( *i == ':' )
+ *i = '_';
+ return name;
+}
+
+template< class T >
+string testNs( T *t ) {
+ stringstream ss;
+ ss << testDb( t ) << ".perftest";
+ return ss.str();
+}
+
+template <class T>
+class Runner {
+public:
+ void run() {
+ T test;
+ string name = testDb( &test );
+ boost::posix_time::ptime start = boost::posix_time::microsec_clock::universal_time();
+ test.run();
+ boost::posix_time::ptime end = boost::posix_time::microsec_clock::universal_time();
+ long long micro = ( end - start ).total_microseconds();
+ cout << "{'" << name << "': "
+ << micro / 1000000
+ << "."
+ << setw( 6 ) << setfill( '0' ) << micro % 1000000
+ << "}" << endl;
+ }
+ ~Runner() {
+ theFileAllocator().waitUntilFinished();
+ client_->dropDatabase( testDb< T >().c_str() );
+ }
+};
+
+class RunnerSuite : public Suite {
+public:
+ RunnerSuite( string name ) : Suite( name ){}
+protected:
+ template< class T >
+ void add() {
+ Suite::add< Runner< T > >();
+ }
+};
+
+namespace Insert {
+ class IdIndex {
+ public:
+ void run() {
+ string ns = testNs( this );
+ for( int i = 0; i < 100000; ++i ) {
+ client_->insert( ns.c_str(), BSON( "_id" << i ) );
+ }
+ }
+ };
+
+ class TwoIndex {
+ public:
+ TwoIndex() : ns_( testNs( this ) ) {
+ client_->ensureIndex( ns_, BSON( "_id" << 1 ), "my_id" );
+ }
+ void run() {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i ) );
+ }
+ string ns_;
+ };
+
+ class TenIndex {
+ public:
+ TenIndex() : ns_( testNs( this ) ) {
+ const char *names = "aaaaaaaaa";
+ for( int i = 0; i < 9; ++i ) {
+ client_->resetIndexCache();
+ client_->ensureIndex( ns_.c_str(), BSON( "_id" << 1 ), false, names + i );
+ }
+ }
+ void run() {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i ) );
+ }
+ string ns_;
+ };
+
+ class Capped {
+ public:
+ Capped() : ns_( testNs( this ) ) {
+ client_->createCollection( ns_.c_str(), 100000, true );
+ }
+ void run() {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i ) );
+ }
+ string ns_;
+ };
+
+ class OneIndexReverse {
+ public:
+ OneIndexReverse() : ns_( testNs( this ) ) {
+ client_->ensureIndex( ns_, BSON( "_id" << 1 ) );
+ }
+ void run() {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << ( 100000 - 1 - i ) ) );
+ }
+ string ns_;
+ };
+
+ class OneIndexHighLow {
+ public:
+ OneIndexHighLow() : ns_( testNs( this ) ) {
+ client_->ensureIndex( ns_, BSON( "_id" << 1 ) );
+ }
+ void run() {
+ for( int i = 0; i < 100000; ++i ) {
+ int j = 50000 + ( ( i % 2 == 0 ) ? 1 : -1 ) * ( i / 2 + 1 );
+ client_->insert( ns_.c_str(), BSON( "_id" << j ) );
+ }
+ }
+ string ns_;
+ };
+
+ class All : public RunnerSuite {
+ public:
+ All() : RunnerSuite( "insert" ){}
+
+ void setupTests(){
+ add< IdIndex >();
+ add< TwoIndex >();
+ add< TenIndex >();
+ add< Capped >();
+ add< OneIndexReverse >();
+ add< OneIndexHighLow >();
+ }
+ } all;
+} // namespace Insert
+
+namespace Update {
+ class Smaller {
+ public:
+ Smaller() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i << "b" << 2 ) );
+ }
+ void run() {
+ for( int i = 0; i < 100000; ++i )
+ client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "_id" << i ) );
+ }
+ string ns_;
+ };
+
+ class Bigger {
+ public:
+ Bigger() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i ) );
+ }
+ void run() {
+ for( int i = 0; i < 100000; ++i )
+ client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "_id" << i << "b" << 2 ) );
+ }
+ string ns_;
+ };
+
+ class Inc {
+ public:
+ Inc() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 10000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i << "i" << 0 ) );
+ }
+ void run() {
+ for( int j = 0; j < 10; ++j )
+ for( int i = 0; i < 10000; ++i )
+ client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "$inc" << BSON( "i" << 1 ) ) );
+ }
+ string ns_;
+ };
+
+ class Set {
+ public:
+ Set() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 10000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i << "i" << 0 ) );
+ }
+ void run() {
+ for( int j = 1; j < 11; ++j )
+ for( int i = 0; i < 10000; ++i )
+ client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "$set" << BSON( "i" << j ) ) );
+ }
+ string ns_;
+ };
+
+ class SetGrow {
+ public:
+ SetGrow() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 10000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i << "i" << "" ) );
+ }
+ void run() {
+ for( int j = 9; j > -1; --j )
+ for( int i = 0; i < 10000; ++i )
+ client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "$set" << BSON( "i" << "aaaaaaaaaa"[j] ) ) );
+ }
+ string ns_;
+ };
+
+ class All : public RunnerSuite {
+ public:
+ All() : RunnerSuite( "update" ){}
+ void setupTests(){
+ add< Smaller >();
+ add< Bigger >();
+ add< Inc >();
+ add< Set >();
+ add< SetGrow >();
+ }
+ } all;
+} // namespace Update
+
+namespace BSON {
+
+ const char *sample =
+ "{\"one\":2, \"two\":5, \"three\": {},"
+ "\"four\": { \"five\": { \"six\" : 11 } },"
+ "\"seven\": [ \"a\", \"bb\", \"ccc\", 5 ],"
+ "\"eight\": Dbref( \"rrr\", \"01234567890123456789aaaa\" ),"
+ "\"_id\": ObjectId( \"deadbeefdeadbeefdeadbeef\" ),"
+ "\"nine\": { \"$binary\": \"abc=\", \"$type\": \"02\" },"
+ "\"ten\": Date( 44 ), \"eleven\": /foooooo/i }";
+
+ const char *shopwikiSample =
+ "{ '_id' : '289780-80f85380b5c1d4a0ad75d1217673a4a2' , 'site_id' : 289780 , 'title'"
+ ": 'Jubilee - Margaret Walker' , 'image_url' : 'http://www.heartlanddigsandfinds.c"
+ "om/store/graphics/Product_Graphics/Product_8679.jpg' , 'url' : 'http://www.heartla"
+ "nddigsandfinds.com/store/store_product_detail.cfm?Product_ID=8679&Category_ID=2&Su"
+ "b_Category_ID=910' , 'url_hash' : 3450626119933116345 , 'last_update' : null , '"
+ "features' : { '$imagePrefetchDate' : '2008Aug30 22:39' , '$image.color.rgb' : '5a7"
+ "574' , 'Price' : '$10.99' , 'Description' : 'Author--s 1st Novel. A Houghton Miffl"
+ "in Literary Fellowship Award novel by the esteemed poet and novelist who has demon"
+ "strated a lifelong commitment to the heritage of black culture. An acclaimed story"
+ "of Vyry, a negro slave during the 19th Century, facing the biggest challenge of h"
+ "er lifetime - that of gaining her freedom, fighting for all the things she had nev"
+ "er known before. The author, great-granddaughter of Vyry, reveals what the Civil W"
+ "ar in America meant to the Negroes. Slavery W' , '$priceHistory-1' : '2008Dec03 $1"
+ "0.99' , 'Brand' : 'Walker' , '$brands_in_title' : 'Walker' , '--path' : '//HTML[1]"
+ "/BODY[1]/TABLE[1]/TR[1]/TD[1]/P[1]/TABLE[1]/TR[1]/TD[1]/TABLE[1]/TR[2]/TD[2]/TABLE"
+ "[1]/TR[1]/TD[1]/P[1]/TABLE[1]/TR[1]' , '~location' : 'en_US' , '$crawled' : '2009J"
+ "an11 03:22' , '$priceHistory-2' : '2008Nov15 $10.99' , '$priceHistory-0' : '2008De"
+ "c24 $10.99'}}";
+
+ class Parse {
+ public:
+ void run() {
+ for( int i = 0; i < 10000; ++i )
+ fromjson( sample );
+ }
+ };
+
+ class ShopwikiParse {
+ public:
+ void run() {
+ for( int i = 0; i < 10000; ++i )
+ fromjson( shopwikiSample );
+ }
+ };
+
+ class Json {
+ public:
+ Json() : o_( fromjson( sample ) ) {}
+ void run() {
+ for( int i = 0; i < 10000; ++i )
+ o_.jsonString();
+ }
+ BSONObj o_;
+ };
+
+ class ShopwikiJson {
+ public:
+ ShopwikiJson() : o_( fromjson( shopwikiSample ) ) {}
+ void run() {
+ for( int i = 0; i < 10000; ++i )
+ o_.jsonString();
+ }
+ BSONObj o_;
+ };
+
+ class All : public RunnerSuite {
+ public:
+ All() : RunnerSuite( "bson" ){}
+ void setupTests(){
+ add< Parse >();
+ add< ShopwikiParse >();
+ add< Json >();
+ add< ShopwikiJson >();
+ }
+ } all;
+
+} // namespace BSON
+
+namespace Index {
+
+ class Int {
+ public:
+ Int() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "a" << i ) );
+ }
+ void run() {
+ client_->ensureIndex( ns_, BSON( "a" << 1 ) );
+ }
+ string ns_;
+ };
+
+ class ObjectId {
+ public:
+ ObjectId() : ns_( testNs( this ) ) {
+ OID id;
+ for( int i = 0; i < 100000; ++i ) {
+ id.init();
+ client_->insert( ns_.c_str(), BSON( "a" << id ) );
+ }
+ }
+ void run() {
+ client_->ensureIndex( ns_, BSON( "a" << 1 ) );
+ }
+ string ns_;
+ };
+
+ class String {
+ public:
+ String() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 100000; ++i ) {
+ stringstream ss;
+ ss << i;
+ client_->insert( ns_.c_str(), BSON( "a" << ss.str() ) );
+ }
+ }
+ void run() {
+ client_->ensureIndex( ns_, BSON( "a" << 1 ) );
+ }
+ string ns_;
+ };
+
+ class Object {
+ public:
+ Object() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 100000; ++i ) {
+ client_->insert( ns_.c_str(), BSON( "a" << BSON( "a" << i ) ) );
+ }
+ }
+ void run() {
+ client_->ensureIndex( ns_, BSON( "a" << 1 ) );
+ }
+ string ns_;
+ };
+
+ class All : public RunnerSuite {
+ public:
+ All() : RunnerSuite( "index" ){}
+ void setupTests(){
+ add< Int >();
+ add< ObjectId >();
+ add< String >();
+ add< Object >();
+ }
+ } all;
+
+} // namespace Index
+
+namespace QueryTests {
+
+ class NoMatch {
+ public:
+ NoMatch() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i ) );
+ }
+ void run() {
+ client_->findOne( ns_.c_str(), QUERY( "_id" << 100000 ) );
+ }
+ string ns_;
+ };
+
+ class NoMatchIndex {
+ public:
+ NoMatchIndex() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i ) );
+ }
+ void run() {
+ client_->findOne( ns_.c_str(),
+ QUERY( "a" << "b" ).hint( BSON( "_id" << 1 ) ) );
+ }
+ string ns_;
+ };
+
+ class NoMatchLong {
+ public:
+ NoMatchLong() : ns_( testNs( this ) ) {
+ const char *names = "aaaaaaaaaa";
+ for( int i = 0; i < 100000; ++i ) {
+ BSONObjBuilder b;
+ for( int j = 0; j < 10; ++j )
+ b << ( names + j ) << i;
+ client_->insert( ns_.c_str(), b.obj() );
+ }
+ }
+ void run() {
+ client_->findOne( ns_.c_str(), QUERY( "a" << 100000 ) );
+ }
+ string ns_;
+ };
+
+ class SortOrdered {
+ public:
+ SortOrdered() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 50000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << i ) );
+ }
+ void run() {
+ auto_ptr< DBClientCursor > c =
+ client_->query( ns_.c_str(), Query( BSONObj() ).sort( BSON( "_id" << 1 ) ) );
+ int i = 0;
+ for( ; c->more(); c->nextSafe(), ++i );
+ ASSERT_EQUALS( 50000, i );
+ }
+ string ns_;
+ };
+
+ class SortReverse {
+ public:
+ SortReverse() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 50000; ++i )
+ client_->insert( ns_.c_str(), BSON( "_id" << ( 50000 - 1 - i ) ) );
+ }
+ void run() {
+ auto_ptr< DBClientCursor > c =
+ client_->query( ns_.c_str(), Query( BSONObj() ).sort( BSON( "_id" << 1 ) ) );
+ int i = 0;
+ for( ; c->more(); c->nextSafe(), ++i );
+ ASSERT_EQUALS( 50000, i );
+ }
+ string ns_;
+ };
+
+ class GetMore {
+ public:
+ GetMore() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "a" << i ) );
+ c_ = client_->query( ns_.c_str(), Query() );
+ }
+ void run() {
+ int i = 0;
+ for( ; c_->more(); c_->nextSafe(), ++i );
+ ASSERT_EQUALS( 100000, i );
+ }
+ string ns_;
+ auto_ptr< DBClientCursor > c_;
+ };
+
+ class GetMoreIndex {
+ public:
+ GetMoreIndex() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_.c_str(), BSON( "a" << i ) );
+ client_->ensureIndex( ns_, BSON( "a" << 1 ) );
+ c_ = client_->query( ns_.c_str(), QUERY( "a" << GT << -1 ).hint( BSON( "a" << 1 ) ) );
+ }
+ void run() {
+ int i = 0;
+ for( ; c_->more(); c_->nextSafe(), ++i );
+ ASSERT_EQUALS( 100000, i );
+ }
+ string ns_;
+ auto_ptr< DBClientCursor > c_;
+ };
+
+ class GetMoreKeyMatchHelps {
+ public:
+ GetMoreKeyMatchHelps() : ns_( testNs( this ) ) {
+ for( int i = 0; i < 1000000; ++i )
+ client_->insert( ns_.c_str(), BSON( "a" << i << "b" << i % 10 << "c" << "d" ) );
+ client_->ensureIndex( ns_, BSON( "a" << 1 << "b" << 1 ) );
+ c_ = client_->query( ns_.c_str(), QUERY( "a" << GT << -1 << "b" << 0 ).hint( BSON( "a" << 1 << "b" << 1 ) ) );
+ }
+ void run() {
+ int i = 0;
+ for( ; c_->more(); c_->nextSafe(), ++i );
+ ASSERT_EQUALS( 100000, i );
+ }
+ string ns_;
+ auto_ptr< DBClientCursor > c_;
+ };
+
+ class All : public RunnerSuite {
+ public:
+ All() : RunnerSuite( "query" ){}
+ void setupTests(){
+ add< NoMatch >();
+ add< NoMatchIndex >();
+ add< NoMatchLong >();
+ add< SortOrdered >();
+ add< SortReverse >();
+ add< GetMore >();
+ add< GetMoreIndex >();
+ add< GetMoreKeyMatchHelps >();
+ }
+ } all;
+
+} // namespace QueryTests
+
+namespace Count {
+
+ class Count {
+ public:
+ Count() : ns_( testNs( this ) ) {
+ BSONObj obj = BSON( "a" << 1 );
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_, obj );
+ }
+ void run() {
+ ASSERT_EQUALS( 100000U, client_->count( ns_, BSON( "a" << 1 ) ) );
+ }
+ string ns_;
+ };
+
+ class CountIndex {
+ public:
+ CountIndex() : ns_( testNs( this ) ) {
+ BSONObj obj = BSON( "a" << 1 );
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_, obj );
+ client_->ensureIndex( ns_, obj );
+ }
+ void run() {
+ // 'simple' match does not work for numbers
+ ASSERT_EQUALS( 100000U, client_->count( ns_, BSON( "a" << 1 ) ) );
+ }
+ string ns_;
+ };
+
+ class CountSimpleIndex {
+ public:
+ CountSimpleIndex() : ns_( testNs( this ) ) {
+ BSONObj obj = BSON( "a" << "b" );
+ for( int i = 0; i < 100000; ++i )
+ client_->insert( ns_, obj );
+ client_->ensureIndex( ns_, obj );
+ }
+ void run() {
+ ASSERT_EQUALS( 100000U, client_->count( ns_, BSON( "a" << "b" ) ) );
+ }
+ string ns_;
+ };
+
+ class All : public RunnerSuite {
+ public:
+ All() : RunnerSuite( "count" ){}
+ void setupTests(){
+ add< Count >();
+ add< CountIndex >();
+ add< CountSimpleIndex >();
+ }
+ } all;
+
+} // namespace Count
+
+namespace Plan {
+
+ class Hint {
+ public:
+ Hint() : ns_( testNs( this ) ) {
+ const char *names = "aaaaaaaaa";
+ for( int i = 0; i < 9; ++i ) {
+ client_->resetIndexCache();
+ client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i );
+ }
+ lk_.reset( new dblock );
+ setClient( ns_.c_str() );
+ hint_ = BSON( "hint" << BSON( "a" << 1 ) );
+ hintElt_ = hint_.firstElement();
+ }
+ void run() {
+ for( int i = 0; i < 10000; ++i )
+ QueryPlanSet s( ns_.c_str(), BSONObj(), BSONObj(), &hintElt_ );
+ }
+ string ns_;
+ auto_ptr< dblock > lk_;
+ BSONObj hint_;
+ BSONElement hintElt_;
+ };
+
+ class Sort {
+ public:
+ Sort() : ns_( testNs( this ) ) {
+ const char *names = "aaaaaaaaaa";
+ for( int i = 0; i < 10; ++i ) {
+ client_->resetIndexCache();
+ client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i );
+ }
+ lk_.reset( new dblock );
+ setClient( ns_.c_str() );
+ }
+ void run() {
+ for( int i = 0; i < 10000; ++i )
+ QueryPlanSet s( ns_.c_str(), BSONObj(), BSON( "a" << 1 ) );
+ }
+ string ns_;
+ auto_ptr< dblock > lk_;
+ };
+
+ class Query {
+ public:
+ Query() : ns_( testNs( this ) ) {
+ const char *names = "aaaaaaaaaa";
+ for( int i = 0; i < 10; ++i ) {
+ client_->resetIndexCache();
+ client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i );
+ }
+ lk_.reset( new dblock );
+ setClient( ns_.c_str() );
+ }
+ void run() {
+ for( int i = 0; i < 10000; ++i )
+ QueryPlanSet s( ns_.c_str(), BSON( "a" << 1 ), BSONObj() );
+ }
+ string ns_;
+ auto_ptr< dblock > lk_;
+ };
+
+ class All : public RunnerSuite {
+ public:
+ All() : RunnerSuite("plan" ){}
+ void setupTests(){
+ add< Hint >();
+ add< Sort >();
+ add< Query >();
+ }
+ } all;
+
+} // namespace Plan
+
+int main( int argc, char **argv ) {
+ logLevel = -1;
+ client_ = new DBDirectClient();
+
+ return Suite::run(argc, argv, "/data/db/perftest");
+}
diff --git a/dbtests/queryoptimizertests.cpp b/dbtests/queryoptimizertests.cpp
new file mode 100644
index 0000000..c9465f3
--- /dev/null
+++ b/dbtests/queryoptimizertests.cpp
@@ -0,0 +1,1191 @@
+// queryoptimizertests.cpp : query optimizer unit tests
+//
+
+/**
+ * Copyright (C) 2009 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/queryoptimizer.h"
+
+#include "../db/db.h"
+#include "../db/dbhelpers.h"
+#include "../db/instance.h"
+#include "../db/query.h"
+
+#include "dbtests.h"
+
+namespace mongo {
+ extern BSONObj id_obj;
+ auto_ptr< QueryResult > runQuery(Message& m, QueryMessage& q ){
+ CurOp op;
+ return runQuery( m , q , op );
+ }
+} // namespace mongo
+
+namespace QueryOptimizerTests {
+
+ namespace FieldRangeTests {
+ class Base {
+ public:
+ virtual ~Base() {}
+ void run() {
+ FieldRangeSet s( "ns", query() );
+ checkElt( lower(), s.range( "a" ).min() );
+ checkElt( upper(), s.range( "a" ).max() );
+ ASSERT_EQUALS( lowerInclusive(), s.range( "a" ).minInclusive() );
+ ASSERT_EQUALS( upperInclusive(), s.range( "a" ).maxInclusive() );
+ }
+ protected:
+ virtual BSONObj query() = 0;
+ virtual BSONElement lower() { return minKey.firstElement(); }
+ virtual bool lowerInclusive() { return true; }
+ virtual BSONElement upper() { return maxKey.firstElement(); }
+ virtual bool upperInclusive() { return true; }
+ private:
+ static void checkElt( BSONElement expected, BSONElement actual ) {
+ if ( expected.woCompare( actual, false ) ) {
+ stringstream ss;
+ ss << "expected: " << expected << ", got: " << actual;
+ FAIL( ss.str() );
+ }
+ }
+ };
+
+
+ class NumericBase : public Base {
+ public:
+ NumericBase(){
+ o = BSON( "min" << -numeric_limits<double>::max() << "max" << numeric_limits<double>::max() );
+ }
+
+ virtual BSONElement lower() { return o["min"]; }
+ virtual BSONElement upper() { return o["max"]; }
+ private:
+ BSONObj o;
+ };
+
+ class Empty : public Base {
+ virtual BSONObj query() { return BSONObj(); }
+ };
+
+ class Eq : public Base {
+ public:
+ Eq() : o_( BSON( "a" << 1 ) ) {}
+ virtual BSONObj query() { return o_; }
+ virtual BSONElement lower() { return o_.firstElement(); }
+ virtual BSONElement upper() { return o_.firstElement(); }
+ BSONObj o_;
+ };
+
+ class DupEq : public Eq {
+ public:
+ virtual BSONObj query() { return BSON( "a" << 1 << "b" << 2 << "a" << 1 ); }
+ };
+
+ class Lt : public NumericBase {
+ public:
+ Lt() : o_( BSON( "-" << 1 ) ) {}
+ virtual BSONObj query() { return BSON( "a" << LT << 1 ); }
+ virtual BSONElement upper() { return o_.firstElement(); }
+ virtual bool upperInclusive() { return false; }
+ BSONObj o_;
+ };
+
+ class Lte : public Lt {
+ virtual BSONObj query() { return BSON( "a" << LTE << 1 ); }
+ virtual bool upperInclusive() { return true; }
+ };
+
+ class Gt : public NumericBase {
+ public:
+ Gt() : o_( BSON( "-" << 1 ) ) {}
+ virtual BSONObj query() { return BSON( "a" << GT << 1 ); }
+ virtual BSONElement lower() { return o_.firstElement(); }
+ virtual bool lowerInclusive() { return false; }
+ BSONObj o_;
+ };
+
+ class Gte : public Gt {
+ virtual BSONObj query() { return BSON( "a" << GTE << 1 ); }
+ virtual bool lowerInclusive() { return true; }
+ };
+
+ class TwoLt : public Lt {
+ virtual BSONObj query() { return BSON( "a" << LT << 1 << LT << 5 ); }
+ };
+
+ class TwoGt : public Gt {
+ virtual BSONObj query() { return BSON( "a" << GT << 0 << GT << 1 ); }
+ };
+
+ class EqGte : public Eq {
+ virtual BSONObj query() { return BSON( "a" << 1 << "a" << GTE << 1 ); }
+ };
+
+ class EqGteInvalid {
+ public:
+ void run() {
+ FieldRangeSet fbs( "ns", BSON( "a" << 1 << "a" << GTE << 2 ) );
+ ASSERT( !fbs.matchPossible() );
+ }
+ };
+
+ class Regex : public Base {
+ public:
+ Regex() : o1_( BSON( "" << "abc" ) ), o2_( BSON( "" << "abd" ) ) {}
+ virtual BSONObj query() {
+ BSONObjBuilder b;
+ b.appendRegex( "a", "^abc" );
+ return b.obj();
+ }
+ virtual BSONElement lower() { return o1_.firstElement(); }
+ virtual BSONElement upper() { return o2_.firstElement(); }
+ virtual bool upperInclusive() { return false; }
+ BSONObj o1_, o2_;
+ };
+
+ class RegexObj : public Base {
+ public:
+ RegexObj() : o1_( BSON( "" << "abc" ) ), o2_( BSON( "" << "abd" ) ) {}
+ virtual BSONObj query() { return BSON("a" << BSON("$regex" << "^abc")); }
+ virtual BSONElement lower() { return o1_.firstElement(); }
+ virtual BSONElement upper() { return o2_.firstElement(); }
+ virtual bool upperInclusive() { return false; }
+ BSONObj o1_, o2_;
+ };
+
+ class UnhelpfulRegex : public Base {
+ virtual BSONObj query() {
+ BSONObjBuilder b;
+ b.appendRegex( "a", "abc" );
+ return b.obj();
+ }
+ };
+
+ class In : public Base {
+ public:
+ In() : o1_( BSON( "-" << -3 ) ), o2_( BSON( "-" << 44 ) ) {}
+ virtual BSONObj query() {
+ vector< int > vals;
+ vals.push_back( 4 );
+ vals.push_back( 8 );
+ vals.push_back( 44 );
+ vals.push_back( -1 );
+ vals.push_back( -3 );
+ vals.push_back( 0 );
+ BSONObjBuilder bb;
+ bb.append( "$in", vals );
+ BSONObjBuilder b;
+ b.append( "a", bb.done() );
+ return b.obj();
+ }
+ virtual BSONElement lower() { return o1_.firstElement(); }
+ virtual BSONElement upper() { return o2_.firstElement(); }
+ BSONObj o1_, o2_;
+ };
+
+ class Equality {
+ public:
+ void run() {
+ FieldRangeSet s( "ns", BSON( "a" << 1 ) );
+ ASSERT( s.range( "a" ).equality() );
+ FieldRangeSet s2( "ns", BSON( "a" << GTE << 1 << LTE << 1 ) );
+ ASSERT( s2.range( "a" ).equality() );
+ FieldRangeSet s3( "ns", BSON( "a" << GT << 1 << LTE << 1 ) );
+ ASSERT( !s3.range( "a" ).equality() );
+ FieldRangeSet s4( "ns", BSON( "a" << GTE << 1 << LT << 1 ) );
+ ASSERT( !s4.range( "a" ).equality() );
+ FieldRangeSet s5( "ns", BSON( "a" << GTE << 1 << LTE << 1 << GT << 1 ) );
+ ASSERT( !s5.range( "a" ).equality() );
+ FieldRangeSet s6( "ns", BSON( "a" << GTE << 1 << LTE << 1 << LT << 1 ) );
+ ASSERT( !s6.range( "a" ).equality() );
+ }
+ };
+
+ class SimplifiedQuery {
+ public:
+ void run() {
+ FieldRangeSet fbs( "ns", BSON( "a" << GT << 1 << GT << 5 << LT << 10 << "b" << 4 << "c" << LT << 4 << LT << 6 << "d" << GTE << 0 << GT << 0 << "e" << GTE << 0 << LTE << 10 ) );
+ BSONObj simple = fbs.simplifiedQuery();
+ cout << "simple: " << simple << endl;
+ ASSERT( !simple.getObjectField( "a" ).woCompare( fromjson( "{$gt:5,$lt:10}" ) ) );
+ ASSERT_EQUALS( 4, simple.getIntField( "b" ) );
+ ASSERT( !simple.getObjectField( "c" ).woCompare( BSON("$gte" << -numeric_limits<double>::max() << "$lt" << 4 ) ) );
+ ASSERT( !simple.getObjectField( "d" ).woCompare( BSON("$gt" << 0 << "$lte" << numeric_limits<double>::max() ) ) );
+ ASSERT( !simple.getObjectField( "e" ).woCompare( fromjson( "{$gte:0,$lte:10}" ) ) );
+ }
+ };
+
+ class QueryPatternTest {
+ public:
+ void run() {
+ ASSERT( p( BSON( "a" << 1 ) ) == p( BSON( "a" << 1 ) ) );
+ ASSERT( p( BSON( "a" << 1 ) ) == p( BSON( "a" << 5 ) ) );
+ ASSERT( p( BSON( "a" << 1 ) ) != p( BSON( "b" << 1 ) ) );
+ ASSERT( p( BSON( "a" << 1 ) ) != p( BSON( "a" << LTE << 1 ) ) );
+ ASSERT( p( BSON( "a" << 1 ) ) != p( BSON( "a" << 1 << "b" << 2 ) ) );
+ ASSERT( p( BSON( "a" << 1 << "b" << 3 ) ) != p( BSON( "a" << 1 ) ) );
+ ASSERT( p( BSON( "a" << LT << 1 ) ) == p( BSON( "a" << LTE << 5 ) ) );
+ ASSERT( p( BSON( "a" << LT << 1 << GTE << 0 ) ) == p( BSON( "a" << LTE << 5 << GTE << 0 ) ) );
+ ASSERT( p( BSON( "a" << 1 ) ) < p( BSON( "a" << 1 << "b" << 1 ) ) );
+ ASSERT( !( p( BSON( "a" << 1 << "b" << 1 ) ) < p( BSON( "a" << 1 ) ) ) );
+ ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) == p( BSON( "a" << 4 ), BSON( "b" << "a" ) ) );
+ ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) == p( BSON( "a" << 4 ), BSON( "b" << -1 ) ) );
+ ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "c" << 1 ) ) );
+ ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 << "c" << -1 ) ) == p( BSON( "a" << 4 ), BSON( "b" << -1 << "c" << 1 ) ) );
+ ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 << "c" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "b" << 1 ) ) );
+ ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "b" << 1 << "c" << 1 ) ) );
+ }
+ private:
+ static QueryPattern p( const BSONObj &query, const BSONObj &sort = BSONObj() ) {
+ return FieldRangeSet( "", query ).pattern( sort );
+ }
+ };
+
+ class NoWhere {
+ public:
+ void run() {
+ ASSERT_EQUALS( 0, FieldRangeSet( "ns", BSON( "$where" << 1 ) ).nNontrivialRanges() );
+ }
+ };
+
+ class Numeric {
+ public:
+ void run() {
+ FieldRangeSet f( "", BSON( "a" << 1 ) );
+ ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 2.0 ).firstElement() ) < 0 );
+ ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 0.0 ).firstElement() ) > 0 );
+ }
+ };
+
+ class InLowerBound {
+ public:
+ void run() {
+ FieldRangeSet f( "", fromjson( "{a:{$gt:4,$in:[1,2,3,4,5,6]}}" ) );
+ ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 5.0 ).firstElement(), false ) == 0 );
+ ASSERT( f.range( "a" ).max().woCompare( BSON( "a" << 6.0 ).firstElement(), false ) == 0 );
+ }
+ };
+
+ class InUpperBound {
+ public:
+ void run() {
+ FieldRangeSet f( "", fromjson( "{a:{$lt:4,$in:[1,2,3,4,5,6]}}" ) );
+ ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 1.0 ).firstElement(), false ) == 0 );
+ ASSERT( f.range( "a" ).max().woCompare( BSON( "a" << 3.0 ).firstElement(), false ) == 0 );
+ }
+ };
+
+ class MultiBound {
+ public:
+ void run() {
+ FieldRangeSet frs1( "", fromjson( "{a:{$in:[1,3,5,7,9]}}" ) );
+ FieldRangeSet frs2( "", fromjson( "{a:{$in:[2,3,5,8,9]}}" ) );
+ FieldRange fr1 = frs1.range( "a" );
+ FieldRange fr2 = frs2.range( "a" );
+ fr1 &= fr2;
+ ASSERT( fr1.min().woCompare( BSON( "a" << 3.0 ).firstElement(), false ) == 0 );
+ ASSERT( fr1.max().woCompare( BSON( "a" << 9.0 ).firstElement(), false ) == 0 );
+ vector< FieldInterval > intervals = fr1.intervals();
+ vector< FieldInterval >::const_iterator j = intervals.begin();
+ double expected[] = { 3, 5, 9 };
+ for( int i = 0; i < 3; ++i, ++j ) {
+ ASSERT_EQUALS( expected[ i ], j->lower_.bound_.number() );
+ ASSERT( j->lower_.inclusive_ );
+ ASSERT( j->lower_ == j->upper_ );
+ }
+ ASSERT( j == intervals.end() );
+ }
+ };
+
+ } // namespace FieldRangeTests
+
+ namespace QueryPlanTests {
+ class Base {
+ public:
+ Base() : indexNum_( 0 ) {
+ setClient( ns() );
+ string err;
+ userCreateNS( ns(), BSONObj(), err, false );
+ }
+ ~Base() {
+ if ( !nsd() )
+ return;
+ string s( ns() );
+ dropNS( s );
+ }
+ protected:
+ static const char *ns() { return "unittests.QueryPlanTests"; }
+ static NamespaceDetails *nsd() { return nsdetails( ns() ); }
+ IndexDetails *index( const BSONObj &key ) {
+ stringstream ss;
+ ss << indexNum_++;
+ string name = ss.str();
+ client_.resetIndexCache();
+ client_.ensureIndex( ns(), key, false, name.c_str() );
+ NamespaceDetails *d = nsd();
+ for( int i = 0; i < d->nIndexes; ++i ) {
+ if ( d->idx(i).keyPattern() == key /*indexName() == name*/ || ( d->idx(i).isIdIndex() && IndexDetails::isIdIndexPattern( key ) ) )
+ return &d->idx(i);
+ }
+ assert( false );
+ return 0;
+ }
+ int indexno( const BSONObj &key ) {
+ return nsd()->idxNo( *index(key) );
+ }
+ BSONObj startKey( const QueryPlan &p ) const {
+ BoundList bl = p.indexBounds();
+ return bl[ 0 ].first.getOwned();
+ }
+ BSONObj endKey( const QueryPlan &p ) const {
+ BoundList bl = p.indexBounds();
+ return bl[ bl.size() - 1 ].second.getOwned();
+ }
+ private:
+ dblock lk_;
+ int indexNum_;
+ static DBDirectClient client_;
+ };
+ DBDirectClient Base::client_;
+
+ // There's a limit of 10 indexes total, make sure not to exceed this in a given test.
+#define INDEXNO(x) nsd()->idxNo( *this->index( BSON(x) ) )
+#define INDEX(x) this->index( BSON(x) )
+ auto_ptr< FieldRangeSet > FieldRangeSet_GLOBAL;
+#define FBS(x) ( FieldRangeSet_GLOBAL.reset( new FieldRangeSet( ns(), x ) ), *FieldRangeSet_GLOBAL )
+
+ class NoIndex : public Base {
+ public:
+ void run() {
+ QueryPlan p( nsd(), -1, FBS( BSONObj() ), BSONObj() );
+ ASSERT( !p.optimal() );
+ ASSERT( !p.scanAndOrderRequired() );
+ ASSERT( !p.exactKeyMatch() );
+ }
+ };
+
+ class SimpleOrder : public Base {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendMinKey( "" );
+ BSONObj start = b.obj();
+ BSONObjBuilder b2;
+ b2.appendMaxKey( "" );
+ BSONObj end = b2.obj();
+
+ QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
+ ASSERT( !p.scanAndOrderRequired() );
+ ASSERT( !startKey( p ).woCompare( start ) );
+ ASSERT( !endKey( p ).woCompare( end ) );
+ QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 << "b" << 1 ) );
+ ASSERT( !p2.scanAndOrderRequired() );
+ QueryPlan p3( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "b" << 1 ) );
+ ASSERT( p3.scanAndOrderRequired() );
+ ASSERT( !startKey( p3 ).woCompare( start ) );
+ ASSERT( !endKey( p3 ).woCompare( end ) );
+ }
+ };
+
+ class MoreIndexThanNeeded : public Base {
+ public:
+ void run() {
+ QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
+ ASSERT( !p.scanAndOrderRequired() );
+ }
+ };
+
+ class IndexSigns : public Base {
+ public:
+ void run() {
+ QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << -1 ) , FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ) );
+ ASSERT( !p.scanAndOrderRequired() );
+ ASSERT_EQUALS( 1, p.direction() );
+ QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ) );
+ ASSERT( p2.scanAndOrderRequired() );
+ ASSERT_EQUALS( 0, p2.direction() );
+ QueryPlan p3( nsd(), indexno( id_obj ), FBS( BSONObj() ), BSON( "_id" << 1 ) );
+ ASSERT( !p3.scanAndOrderRequired() );
+ ASSERT_EQUALS( 1, p3.direction() );
+ }
+ };
+
+ class IndexReverse : public Base {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.appendMinKey( "" );
+ b.appendMaxKey( "" );
+ BSONObj start = b.obj();
+ BSONObjBuilder b2;
+ b2.appendMaxKey( "" );
+ b2.appendMinKey( "" );
+ BSONObj end = b2.obj();
+ QueryPlan p( nsd(), INDEXNO( "a" << -1 << "b" << 1 ),FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ) );
+ ASSERT( !p.scanAndOrderRequired() );
+ ASSERT_EQUALS( -1, p.direction() );
+ ASSERT( !startKey( p ).woCompare( start ) );
+ ASSERT( !endKey( p ).woCompare( end ) );
+ QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << -1 << "b" << -1 ) );
+ ASSERT( !p2.scanAndOrderRequired() );
+ ASSERT_EQUALS( -1, p2.direction() );
+ QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << -1 ), FBS( BSONObj() ), BSON( "a" << -1 << "b" << -1 ) );
+ ASSERT( p3.scanAndOrderRequired() );
+ ASSERT_EQUALS( 0, p3.direction() );
+ }
+ };
+
+ class NoOrder : public Base {
+ public:
+ void run() {
+ BSONObjBuilder b;
+ b.append( "", 3 );
+ b.appendMinKey( "" );
+ BSONObj start = b.obj();
+ BSONObjBuilder b2;
+ b2.append( "", 3 );
+ b2.appendMaxKey( "" );
+ BSONObj end = b2.obj();
+ QueryPlan p( nsd(), INDEXNO( "a" << -1 << "b" << 1 ), FBS( BSON( "a" << 3 ) ), BSONObj() );
+ ASSERT( !p.scanAndOrderRequired() );
+ ASSERT( !startKey( p ).woCompare( start ) );
+ ASSERT( !endKey( p ).woCompare( end ) );
+ QueryPlan p2( nsd(), INDEXNO( "a" << -1 << "b" << 1 ), FBS( BSON( "a" << 3 ) ), BSONObj() );
+ ASSERT( !p2.scanAndOrderRequired() );
+ ASSERT( !startKey( p ).woCompare( start ) );
+ ASSERT( !endKey( p ).woCompare( end ) );
+ }
+ };
+
+ class EqualWithOrder : public Base {
+ public:
+ void run() {
+ QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 4 ) ), BSON( "b" << 1 ) );
+ ASSERT( !p.scanAndOrderRequired() );
+ QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ) );
+ ASSERT( !p2.scanAndOrderRequired() );
+ QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ) );
+ ASSERT( p3.scanAndOrderRequired() );
+ }
+ };
+
+ class Optimal : public Base {
+ public:
+ void run() {
+ QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
+ ASSERT( p.optimal() );
+ QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
+ ASSERT( p2.optimal() );
+ QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 ) ), BSON( "a" << 1 ) );
+ ASSERT( p3.optimal() );
+ QueryPlan p4( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 ) ), BSON( "a" << 1 ) );
+ ASSERT( !p4.optimal() );
+ QueryPlan p5( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 ) ), BSON( "b" << 1 ) );
+ ASSERT( p5.optimal() );
+ QueryPlan p6( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 ) ), BSON( "b" << 1 ) );
+ ASSERT( !p6.optimal() );
+ QueryPlan p7( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 << "b" << 1 ) ), BSON( "a" << 1 ) );
+ ASSERT( p7.optimal() );
+ QueryPlan p8( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ) );
+ ASSERT( p8.optimal() );
+ QueryPlan p9( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ) );
+ ASSERT( p9.optimal() );
+ }
+ };
+
+ class MoreOptimal : public Base {
+ public:
+ void run() {
+ QueryPlan p10( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << 1 ) ), BSONObj() );
+ ASSERT( p10.optimal() );
+ QueryPlan p11( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSONObj() );
+ ASSERT( p11.optimal() );
+ QueryPlan p12( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << LT << 1 ) ), BSONObj() );
+ ASSERT( p12.optimal() );
+ QueryPlan p13( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << LT << 1 ) ), BSON( "a" << 1 ) );
+ ASSERT( p13.optimal() );
+ }
+ };
+
+ class KeyMatch : public Base {
+ public:
+ void run() {
+ QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
+ ASSERT( !p.exactKeyMatch() );
+ QueryPlan p2( nsd(), INDEXNO( "b" << 1 << "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
+ ASSERT( !p2.exactKeyMatch() );
+ QueryPlan p3( nsd(), INDEXNO( "b" << 1 << "a" << 1 ), FBS( BSON( "b" << "z" ) ), BSON( "a" << 1 ) );
+ ASSERT( !p3.exactKeyMatch() );
+ QueryPlan p4( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FBS( BSON( "c" << "y" << "b" << "z" ) ), BSON( "a" << 1 ) );
+ ASSERT( !p4.exactKeyMatch() );
+ QueryPlan p5( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FBS( BSON( "c" << "y" << "b" << "z" ) ), BSONObj() );
+ ASSERT( !p5.exactKeyMatch() );
+ QueryPlan p6( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FBS( BSON( "c" << LT << "y" << "b" << GT << "z" ) ), BSONObj() );
+ ASSERT( !p6.exactKeyMatch() );
+ QueryPlan p7( nsd(), INDEXNO( "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
+ ASSERT( !p7.exactKeyMatch() );
+ QueryPlan p8( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << "y" << "a" << "z" ) ), BSONObj() );
+ ASSERT( p8.exactKeyMatch() );
+ QueryPlan p9( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << "z" ) ), BSON( "a" << 1 ) );
+ ASSERT( p9.exactKeyMatch() );
+ }
+ };
+
+ class MoreKeyMatch : public Base {
+ public:
+ void run() {
+ QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << "r" << "b" << NE << "q" ) ), BSON( "a" << 1 ) );
+ ASSERT( !p.exactKeyMatch() );
+ }
+ };
+
+ class ExactKeyQueryTypes : public Base {
+ public:
+ void run() {
+ QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << "b" ) ), BSONObj() );
+ ASSERT( p.exactKeyMatch() );
+ QueryPlan p2( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << 4 ) ), BSONObj() );
+ ASSERT( !p2.exactKeyMatch() );
+ QueryPlan p3( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << BSON( "c" << "d" ) ) ), BSONObj() );
+ ASSERT( !p3.exactKeyMatch() );
+ BSONObjBuilder b;
+ b.appendRegex( "a", "^ddd" );
+ QueryPlan p4( nsd(), INDEXNO( "a" << 1 ), FBS( b.obj() ), BSONObj() );
+ ASSERT( !p4.exactKeyMatch() );
+ QueryPlan p5( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << "z" << "b" << 4 ) ), BSONObj() );
+ ASSERT( !p5.exactKeyMatch() );
+ }
+ };
+
+ class Unhelpful : public Base {
+ public:
+ void run() {
+ QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 ) ), BSONObj() );
+ ASSERT( !p.range( "a" ).nontrivial() );
+ ASSERT( p.unhelpful() );
+ QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 << "c" << 1 ) ), BSON( "a" << 1 ) );
+ ASSERT( !p2.scanAndOrderRequired() );
+ ASSERT( !p2.range( "a" ).nontrivial() );
+ ASSERT( !p2.unhelpful() );
+ QueryPlan p3( nsd(), INDEXNO( "b" << 1 ), FBS( BSON( "b" << 1 << "c" << 1 ) ), BSONObj() );
+ ASSERT( p3.range( "b" ).nontrivial() );
+ ASSERT( !p3.unhelpful() );
+ QueryPlan p4( nsd(), INDEXNO( "b" << 1 << "c" << 1 ), FBS( BSON( "c" << 1 << "d" << 1 ) ), BSONObj() );
+ ASSERT( !p4.range( "b" ).nontrivial() );
+ ASSERT( p4.unhelpful() );
+ }
+ };
+
+ } // namespace QueryPlanTests
+
+ namespace QueryPlanSetTests {
+ class Base {
+ public:
+ Base() {
+ setClient( ns() );
+ string err;
+ userCreateNS( ns(), BSONObj(), err, false );
+ }
+ ~Base() {
+ if ( !nsd() )
+ return;
+ NamespaceDetailsTransient::_get( ns() ).clearQueryCache();
+ string s( ns() );
+ dropNS( s );
+ }
+ static void assembleRequest( const string &ns, BSONObj query, int nToReturn, int nToSkip, BSONObj *fieldsToReturn, int queryOptions, Message &toSend ) {
+ // see query.h for the protocol we are using here.
+ BufBuilder b;
+ int opts = queryOptions;
+ b.append(opts);
+ b.append(ns.c_str());
+ b.append(nToSkip);
+ b.append(nToReturn);
+ query.appendSelfToBufBuilder(b);
+ if ( fieldsToReturn )
+ fieldsToReturn->appendSelfToBufBuilder(b);
+ toSend.setData(dbQuery, b.buf(), b.len());
+ }
+ protected:
+ static const char *ns() { return "unittests.QueryPlanSetTests"; }
+ static NamespaceDetails *nsd() { return nsdetails( ns() ); }
+ private:
+ dblock lk_;
+ };
+
+ class NoIndexes : public Base {
+ public:
+ void run() {
+ QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
+ ASSERT_EQUALS( 1, s.nPlans() );
+ }
+ };
+
+ class Optimal : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "b_2" );
+ QueryPlanSet s( ns(), BSON( "a" << 4 ), BSONObj() );
+ ASSERT_EQUALS( 1, s.nPlans() );
+ }
+ };
+
+ class NoOptimal : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
+ ASSERT_EQUALS( 3, s.nPlans() );
+ }
+ };
+
+ class NoSpec : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ QueryPlanSet s( ns(), BSONObj(), BSONObj() );
+ ASSERT_EQUALS( 1, s.nPlans() );
+ }
+ };
+
+ class HintSpec : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ BSONObj b = BSON( "hint" << BSON( "a" << 1 ) );
+ BSONElement e = b.firstElement();
+ QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e );
+ ASSERT_EQUALS( 1, s.nPlans() );
+ }
+ };
+
+ class HintName : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ BSONObj b = BSON( "hint" << "a_1" );
+ BSONElement e = b.firstElement();
+ QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e );
+ ASSERT_EQUALS( 1, s.nPlans() );
+ }
+ };
+
+ class NaturalHint : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ BSONObj b = BSON( "hint" << BSON( "$natural" << 1 ) );
+ BSONElement e = b.firstElement();
+ QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e );
+ ASSERT_EQUALS( 1, s.nPlans() );
+ }
+ };
+
+ class NaturalSort : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "b_2" );
+ QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "$natural" << 1 ) );
+ ASSERT_EQUALS( 1, s.nPlans() );
+ }
+ };
+
+ class BadHint : public Base {
+ public:
+ void run() {
+ BSONObj b = BSON( "hint" << "a_1" );
+ BSONElement e = b.firstElement();
+ ASSERT_EXCEPTION( QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e ),
+ AssertionException );
+ }
+ };
+
+ class Count : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ string err;
+ ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
+ BSONObj one = BSON( "a" << 1 );
+ BSONObj fourA = BSON( "a" << 4 );
+ BSONObj fourB = BSON( "a" << 4 );
+ theDataFileMgr.insert( ns(), one );
+ ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
+ theDataFileMgr.insert( ns(), fourA );
+ ASSERT_EQUALS( 1, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
+ theDataFileMgr.insert( ns(), fourB );
+ ASSERT_EQUALS( 2, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
+ ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSONObj() ), err ) );
+ ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 ) ), err ) );
+ // missing ns
+ ASSERT_EQUALS( -1, runCount( "unittests.missingNS", BSONObj(), err ) );
+ // impossible match
+ ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 << LT << -1 ) ), err ) );
+ }
+ };
+
+ class QueryMissingNs : public Base {
+ public:
+ void run() {
+ Message m;
+ assembleRequest( "unittests.missingNS", BSONObj(), 0, 0, 0, 0, m );
+ stringstream ss;
+
+ DbMessage d(m);
+ QueryMessage q(d);
+ ASSERT_EQUALS( 0, runQuery( m, q)->nReturned );
+ }
+ };
+
+ class UnhelpfulIndex : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ QueryPlanSet s( ns(), BSON( "a" << 1 << "c" << 2 ), BSONObj() );
+ ASSERT_EQUALS( 2, s.nPlans() );
+ }
+ };
+
+ class SingleException : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
+ ASSERT_EQUALS( 3, s.nPlans() );
+ bool threw = false;
+ auto_ptr< TestOp > t( new TestOp( true, threw ) );
+ boost::shared_ptr< TestOp > done = s.runOp( *t );
+ ASSERT( threw );
+ ASSERT( done->complete() );
+ ASSERT( done->exceptionMessage().empty() );
+ ASSERT( !done->error() );
+ }
+ private:
+ class TestOp : public QueryOp {
+ public:
+ TestOp( bool iThrow, bool &threw ) : iThrow_( iThrow ), threw_( threw ), i_(), youThrow_( false ) {}
+ virtual void init() {}
+ virtual void next() {
+ if ( iThrow_ )
+ threw_ = true;
+ massert( 10408 , "throw", !iThrow_ );
+ if ( ++i_ > 10 )
+ setComplete();
+ }
+ virtual QueryOp *clone() const {
+ QueryOp *op = new TestOp( youThrow_, threw_ );
+ youThrow_ = !youThrow_;
+ return op;
+ }
+ virtual bool mayRecordPlan() const { return true; }
+ private:
+ bool iThrow_;
+ bool &threw_;
+ int i_;
+ mutable bool youThrow_;
+ };
+ };
+
+ class AllException : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
+ ASSERT_EQUALS( 3, s.nPlans() );
+ auto_ptr< TestOp > t( new TestOp() );
+ boost::shared_ptr< TestOp > done = s.runOp( *t );
+ ASSERT( !done->complete() );
+ ASSERT_EQUALS( "throw", done->exceptionMessage() );
+ ASSERT( done->error() );
+ }
+ private:
+ class TestOp : public QueryOp {
+ public:
+ virtual void init() {}
+ virtual void next() {
+ massert( 10409 , "throw", false );
+ }
+ virtual QueryOp *clone() const {
+ return new TestOp();
+ }
+ virtual bool mayRecordPlan() const { return true; }
+ };
+ };
+
+ class SaveGoodIndex : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
+ nPlans( 3 );
+ runQuery();
+ nPlans( 1 );
+ nPlans( 1 );
+ Helpers::ensureIndex( ns(), BSON( "c" << 1 ), false, "c_1" );
+ nPlans( 3 );
+ runQuery();
+ nPlans( 1 );
+
+ {
+ DBDirectClient client;
+ for( int i = 0; i < 34; ++i ) {
+ client.insert( ns(), BSON( "i" << i ) );
+ client.update( ns(), QUERY( "i" << i ), BSON( "i" << i + 1 ) );
+ client.remove( ns(), BSON( "i" << i + 1 ) );
+ }
+ }
+ nPlans( 3 );
+
+ QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
+ NoRecordTestOp original;
+ s.runOp( original );
+ nPlans( 3 );
+
+ BSONObj hint = fromjson( "{hint:{$natural:1}}" );
+ BSONElement hintElt = hint.firstElement();
+ QueryPlanSet s2( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ), &hintElt );
+ TestOp newOriginal;
+ s2.runOp( newOriginal );
+ nPlans( 3 );
+
+ QueryPlanSet s3( ns(), BSON( "a" << 4 ), BSON( "b" << 1 << "c" << 1 ) );
+ TestOp newerOriginal;
+ s3.runOp( newerOriginal );
+ nPlans( 3 );
+
+ runQuery();
+ nPlans( 1 );
+ }
+ private:
+ void nPlans( int n ) {
+ QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
+ ASSERT_EQUALS( n, s.nPlans() );
+ }
+ void runQuery() {
+ QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
+ TestOp original;
+ s.runOp( original );
+ }
+ class TestOp : public QueryOp {
+ public:
+ virtual void init() {}
+ virtual void next() {
+ setComplete();
+ }
+ virtual QueryOp *clone() const {
+ return new TestOp();
+ }
+ virtual bool mayRecordPlan() const { return true; }
+ };
+ class NoRecordTestOp : public TestOp {
+ virtual bool mayRecordPlan() const { return false; }
+ virtual QueryOp *clone() const { return new NoRecordTestOp(); }
+ };
+ };
+
+ class TryAllPlansOnErr : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+
+ QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
+ ScanOnlyTestOp op;
+ s.runOp( op );
+ ASSERT( fromjson( "{$natural:1}" ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( s.fbs().pattern( BSON( "b" << 1 ) ) ) ) == 0 );
+ ASSERT_EQUALS( 1, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( s.fbs().pattern( BSON( "b" << 1 ) ) ) );
+
+ QueryPlanSet s2( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
+ TestOp op2;
+ ASSERT( s2.runOp( op2 )->complete() );
+ }
+ private:
+ class TestOp : public QueryOp {
+ public:
+ virtual void init() {}
+ virtual void next() {
+ if ( qp().indexKey().firstElement().fieldName() == string( "$natural" ) )
+ massert( 10410 , "throw", false );
+ setComplete();
+ }
+ virtual QueryOp *clone() const {
+ return new TestOp();
+ }
+ virtual bool mayRecordPlan() const { return true; }
+ };
+ class ScanOnlyTestOp : public TestOp {
+ virtual void next() {
+ if ( qp().indexKey().firstElement().fieldName() == string( "$natural" ) )
+ setComplete();
+ massert( 10411 , "throw", false );
+ }
+ virtual QueryOp *clone() const {
+ return new ScanOnlyTestOp();
+ }
+ };
+ };
+
+ class FindOne : public Base {
+ public:
+ void run() {
+ BSONObj one = BSON( "a" << 1 );
+ theDataFileMgr.insert( ns(), one );
+ BSONObj result;
+ ASSERT( Helpers::findOne( ns(), BSON( "a" << 1 ), result ) );
+ ASSERT_EXCEPTION( Helpers::findOne( ns(), BSON( "a" << 1 ), result, true ), AssertionException );
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ ASSERT( Helpers::findOne( ns(), BSON( "a" << 1 ), result, true ) );
+ }
+ };
+
+ class Delete : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ for( int i = 0; i < 200; ++i ) {
+ BSONObj two = BSON( "a" << 2 );
+ theDataFileMgr.insert( ns(), two );
+ }
+ BSONObj one = BSON( "a" << 1 );
+ theDataFileMgr.insert( ns(), one );
+ deleteObjects( ns(), BSON( "a" << 1 ), false );
+ ASSERT( BSON( "a" << 1 ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "a" << 1 ) ).pattern() ) ) == 0 );
+ ASSERT_EQUALS( 2, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "a" << 1 ) ).pattern() ) );
+ }
+ };
+
+ class DeleteOneScan : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "_id" << 1 ), false, "_id_1" );
+ BSONObj one = BSON( "_id" << 3 << "a" << 1 );
+ BSONObj two = BSON( "_id" << 2 << "a" << 1 );
+ BSONObj three = BSON( "_id" << 1 << "a" << -1 );
+ theDataFileMgr.insert( ns(), one );
+ theDataFileMgr.insert( ns(), two );
+ theDataFileMgr.insert( ns(), three );
+ deleteObjects( ns(), BSON( "_id" << GT << 0 << "a" << GT << 0 ), true );
+ for( auto_ptr< Cursor > c = theDataFileMgr.findAll( ns() ); c->ok(); c->advance() )
+ ASSERT( 3 != c->current().getIntField( "_id" ) );
+ }
+ };
+
+ class DeleteOneIndex : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a" );
+ BSONObj one = BSON( "a" << 2 << "_id" << 0 );
+ BSONObj two = BSON( "a" << 1 << "_id" << 1 );
+ BSONObj three = BSON( "a" << 0 << "_id" << 2 );
+ theDataFileMgr.insert( ns(), one );
+ theDataFileMgr.insert( ns(), two );
+ theDataFileMgr.insert( ns(), three );
+ deleteObjects( ns(), BSON( "a" << GTE << 0 << "_id" << GT << 0 ), true );
+ for( auto_ptr< Cursor > c = theDataFileMgr.findAll( ns() ); c->ok(); c->advance() )
+ ASSERT( 2 != c->current().getIntField( "_id" ) );
+ }
+ };
+
+ class TryOtherPlansBeforeFinish : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ for( int i = 0; i < 100; ++i ) {
+ for( int j = 0; j < 2; ++j ) {
+ BSONObj temp = BSON( "a" << 100 - i - 1 << "b" << i );
+ theDataFileMgr.insert( ns(), temp );
+ }
+ }
+ Message m;
+ // Need to return at least 2 records to cause plan to be recorded.
+ assembleRequest( ns(), QUERY( "b" << 0 << "a" << GTE << 0 ).obj, 2, 0, 0, 0, m );
+ stringstream ss;
+ {
+ DbMessage d(m);
+ QueryMessage q(d);
+ runQuery( m, q);
+ }
+ ASSERT( BSON( "$natural" << 1 ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ) ).pattern() ) ) == 0 );
+
+ Message m2;
+ assembleRequest( ns(), QUERY( "b" << 99 << "a" << GTE << 0 ).obj, 2, 0, 0, 0, m2 );
+ {
+ DbMessage d(m2);
+ QueryMessage q(d);
+ runQuery( m2, q);
+ }
+ ASSERT( BSON( "a" << 1 ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ) ).pattern() ) ) == 0 );
+ ASSERT_EQUALS( 2, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ) ).pattern() ) );
+ }
+ };
+
+ class InQueryIntervals : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
+ for( int i = 0; i < 10; ++i ) {
+ BSONObj temp = BSON( "a" << i );
+ theDataFileMgr.insert( ns(), temp );
+ }
+ BSONObj hint = fromjson( "{$hint:{a:1}}" );
+ BSONElement hintElt = hint.firstElement();
+ QueryPlanSet s( ns(), fromjson( "{a:{$in:[2,3,6,9,11]}}" ), BSONObj(), &hintElt );
+ QueryPlan qp( nsd(), 1, s.fbs(), BSONObj() );
+ auto_ptr< Cursor > c = qp.newCursor();
+ double expected[] = { 2, 3, 6, 9 };
+ for( int i = 0; i < 4; ++i, c->advance() ) {
+ ASSERT_EQUALS( expected[ i ], c->current().getField( "a" ).number() );
+ }
+ ASSERT( !c->ok() );
+
+ // now check reverse
+ {
+ QueryPlanSet s( ns(), fromjson( "{a:{$in:[2,3,6,9,11]}}" ), BSON( "a" << -1 ), &hintElt );
+ QueryPlan qp( nsd(), 1, s.fbs(), BSON( "a" << -1 ) );
+ auto_ptr< Cursor > c = qp.newCursor();
+ double expected[] = { 9, 6, 3, 2 };
+ for( int i = 0; i < 4; ++i, c->advance() ) {
+ ASSERT_EQUALS( expected[ i ], c->current().getField( "a" ).number() );
+ }
+ ASSERT( !c->ok() );
+ }
+ }
+ };
+
+ class EqualityThenIn : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ), false, "a_1_b_1" );
+ for( int i = 0; i < 10; ++i ) {
+ BSONObj temp = BSON( "a" << 5 << "b" << i );
+ theDataFileMgr.insert( ns(), temp );
+ }
+ BSONObj hint = fromjson( "{$hint:{a:1,b:1}}" );
+ BSONElement hintElt = hint.firstElement();
+ QueryPlanSet s( ns(), fromjson( "{a:5,b:{$in:[2,3,6,9,11]}}" ), BSONObj(), &hintElt );
+ QueryPlan qp( nsd(), 1, s.fbs(), BSONObj() );
+ auto_ptr< Cursor > c = qp.newCursor();
+ double expected[] = { 2, 3, 6, 9 };
+ for( int i = 0; i < 4; ++i, c->advance() ) {
+ ASSERT_EQUALS( expected[ i ], c->current().getField( "b" ).number() );
+ }
+ ASSERT( !c->ok() );
+ }
+ };
+
+ class NotEqualityThenIn : public Base {
+ public:
+ void run() {
+ Helpers::ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ), false, "a_1_b_1" );
+ for( int i = 0; i < 10; ++i ) {
+ BSONObj temp = BSON( "a" << 5 << "b" << i );
+ theDataFileMgr.insert( ns(), temp );
+ }
+ BSONObj hint = fromjson( "{$hint:{a:1,b:1}}" );
+ BSONElement hintElt = hint.firstElement();
+ QueryPlanSet s( ns(), fromjson( "{a:{$gte:5},b:{$in:[2,3,6,9,11]}}" ), BSONObj(), &hintElt );
+ QueryPlan qp( nsd(), 1, s.fbs(), BSONObj() );
+ auto_ptr< Cursor > c = qp.newCursor();
+ for( int i = 2; i < 10; ++i, c->advance() ) {
+ ASSERT_EQUALS( i, c->current().getField( "b" ).number() );
+ }
+ ASSERT( !c->ok() );
+ }
+ };
+
+ } // namespace QueryPlanSetTests
+
+ class All : public Suite {
+ public:
+ All() : Suite( "queryoptimizer" ){}
+
+ void setupTests(){
+ add< FieldRangeTests::Empty >();
+ add< FieldRangeTests::Eq >();
+ add< FieldRangeTests::DupEq >();
+ add< FieldRangeTests::Lt >();
+ add< FieldRangeTests::Lte >();
+ add< FieldRangeTests::Gt >();
+ add< FieldRangeTests::Gte >();
+ add< FieldRangeTests::TwoLt >();
+ add< FieldRangeTests::TwoGt >();
+ add< FieldRangeTests::EqGte >();
+ add< FieldRangeTests::EqGteInvalid >();
+ add< FieldRangeTests::Regex >();
+ add< FieldRangeTests::RegexObj >();
+ add< FieldRangeTests::UnhelpfulRegex >();
+ add< FieldRangeTests::In >();
+ add< FieldRangeTests::Equality >();
+ add< FieldRangeTests::SimplifiedQuery >();
+ add< FieldRangeTests::QueryPatternTest >();
+ add< FieldRangeTests::NoWhere >();
+ add< FieldRangeTests::Numeric >();
+ add< FieldRangeTests::InLowerBound >();
+ add< FieldRangeTests::InUpperBound >();
+ add< FieldRangeTests::MultiBound >();
+ add< QueryPlanTests::NoIndex >();
+ add< QueryPlanTests::SimpleOrder >();
+ add< QueryPlanTests::MoreIndexThanNeeded >();
+ add< QueryPlanTests::IndexSigns >();
+ add< QueryPlanTests::IndexReverse >();
+ add< QueryPlanTests::NoOrder >();
+ add< QueryPlanTests::EqualWithOrder >();
+ add< QueryPlanTests::Optimal >();
+ add< QueryPlanTests::MoreOptimal >();
+ add< QueryPlanTests::KeyMatch >();
+ add< QueryPlanTests::MoreKeyMatch >();
+ add< QueryPlanTests::ExactKeyQueryTypes >();
+ add< QueryPlanTests::Unhelpful >();
+ add< QueryPlanSetTests::NoIndexes >();
+ add< QueryPlanSetTests::Optimal >();
+ add< QueryPlanSetTests::NoOptimal >();
+ add< QueryPlanSetTests::NoSpec >();
+ add< QueryPlanSetTests::HintSpec >();
+ add< QueryPlanSetTests::HintName >();
+ add< QueryPlanSetTests::NaturalHint >();
+ add< QueryPlanSetTests::NaturalSort >();
+ add< QueryPlanSetTests::BadHint >();
+ add< QueryPlanSetTests::Count >();
+ add< QueryPlanSetTests::QueryMissingNs >();
+ add< QueryPlanSetTests::UnhelpfulIndex >();
+ add< QueryPlanSetTests::SingleException >();
+ add< QueryPlanSetTests::AllException >();
+ add< QueryPlanSetTests::SaveGoodIndex >();
+ add< QueryPlanSetTests::TryAllPlansOnErr >();
+ add< QueryPlanSetTests::FindOne >();
+ add< QueryPlanSetTests::Delete >();
+ add< QueryPlanSetTests::DeleteOneScan >();
+ add< QueryPlanSetTests::DeleteOneIndex >();
+ add< QueryPlanSetTests::TryOtherPlansBeforeFinish >();
+ add< QueryPlanSetTests::InQueryIntervals >();
+ add< QueryPlanSetTests::EqualityThenIn >();
+ add< QueryPlanSetTests::NotEqualityThenIn >();
+ }
+ } myall;
+
+} // namespace QueryOptimizerTests
+
diff --git a/dbtests/querytests.cpp b/dbtests/querytests.cpp
new file mode 100644
index 0000000..4681bf0
--- /dev/null
+++ b/dbtests/querytests.cpp
@@ -0,0 +1,919 @@
+// querytests.cpp : query.{h,cpp} unit tests.
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/query.h"
+
+#include "../db/db.h"
+#include "../db/instance.h"
+#include "../db/json.h"
+#include "../db/lasterror.h"
+
+#include "dbtests.h"
+
+namespace QueryTests {
+
+ class Base {
+ dblock lk;
+ public:
+ Base() {
+ dblock lk;
+ setClient( ns() );
+ addIndex( fromjson( "{\"a\":1}" ) );
+ }
+ ~Base() {
+ try {
+ auto_ptr< Cursor > c = theDataFileMgr.findAll( ns() );
+ vector< DiskLoc > toDelete;
+ for(; c->ok(); c->advance() )
+ toDelete.push_back( c->currLoc() );
+ for( vector< DiskLoc >::iterator i = toDelete.begin(); i != toDelete.end(); ++i )
+ theDataFileMgr.deleteRecord( ns(), i->rec(), *i, false );
+ } catch ( ... ) {
+ FAIL( "Exception while cleaning up records" );
+ }
+ }
+ protected:
+ static const char *ns() {
+ return "unittests.querytests";
+ }
+ static void addIndex( const BSONObj &key ) {
+ BSONObjBuilder b;
+ b.append( "name", "index" );
+ b.append( "ns", ns() );
+ b.append( "key", key );
+ BSONObj o = b.done();
+ stringstream indexNs;
+ indexNs << "unittests.system.indexes";
+ theDataFileMgr.insert( indexNs.str().c_str(), o.objdata(), o.objsize() );
+ }
+ static void insert( const char *s ) {
+ insert( fromjson( s ) );
+ }
+ static void insert( const BSONObj &o ) {
+ theDataFileMgr.insert( ns(), o.objdata(), o.objsize() );
+ }
+ };
+
+ class CountBasic : public Base {
+ public:
+ void run() {
+ insert( "{\"a\":\"b\"}" );
+ BSONObj cmd = fromjson( "{\"query\":{}}" );
+ string err;
+ ASSERT_EQUALS( 1, runCount( ns(), cmd, err ) );
+ }
+ };
+
+ class CountQuery : public Base {
+ public:
+ void run() {
+ insert( "{\"a\":\"b\"}" );
+ insert( "{\"a\":\"b\",\"x\":\"y\"}" );
+ insert( "{\"a\":\"c\"}" );
+ BSONObj cmd = fromjson( "{\"query\":{\"a\":\"b\"}}" );
+ string err;
+ ASSERT_EQUALS( 2, runCount( ns(), cmd, err ) );
+ }
+ };
+
+ class CountFields : public Base {
+ public:
+ void run() {
+ insert( "{\"a\":\"b\"}" );
+ insert( "{\"c\":\"d\"}" );
+ BSONObj cmd = fromjson( "{\"query\":{},\"fields\":{\"a\":1}}" );
+ string err;
+ ASSERT_EQUALS( 2, runCount( ns(), cmd, err ) );
+ }
+ };
+
+ class CountQueryFields : public Base {
+ public:
+ void run() {
+ insert( "{\"a\":\"b\"}" );
+ insert( "{\"a\":\"c\"}" );
+ insert( "{\"d\":\"e\"}" );
+ BSONObj cmd = fromjson( "{\"query\":{\"a\":\"b\"},\"fields\":{\"a\":1}}" );
+ string err;
+ ASSERT_EQUALS( 1, runCount( ns(), cmd, err ) );
+ }
+ };
+
+ class CountIndexedRegex : public Base {
+ public:
+ void run() {
+ insert( "{\"a\":\"b\"}" );
+ insert( "{\"a\":\"c\"}" );
+ BSONObj cmd = fromjson( "{\"query\":{\"a\":/^b/}}" );
+ string err;
+ ASSERT_EQUALS( 1, runCount( ns(), cmd, err ) );
+ }
+ };
+
+ class ClientBase {
+ public:
+ // NOTE: Not bothering to backup the old error record.
+ ClientBase() {
+ mongo::lastError.reset( new LastError() );
+ }
+ ~ClientBase() {
+ mongo::lastError.release();
+ }
+ protected:
+ static void insert( const char *ns, BSONObj o ) {
+ client_.insert( ns, o );
+ }
+ static void update( const char *ns, BSONObj q, BSONObj o, bool upsert = 0 ) {
+ client_.update( ns, Query( q ), o, upsert );
+ }
+ static bool error() {
+ return !client_.getPrevError().getField( "err" ).isNull();
+ }
+ DBDirectClient &client() const { return client_; }
+
+ static DBDirectClient client_;
+ };
+ DBDirectClient ClientBase::client_;
+
+ class BoundedKey : public ClientBase {
+ public:
+ void run() {
+ const char *ns = "unittests.querytests.BoundedKey";
+ insert( ns, BSON( "a" << 1 ) );
+ BSONObjBuilder a;
+ a.appendMaxKey( "$lt" );
+ BSONObj limit = a.done();
+ ASSERT( !client().findOne( ns, QUERY( "a" << limit ) ).isEmpty() );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ ASSERT( !client().findOne( ns, QUERY( "a" << limit ).hint( BSON( "a" << 1 ) ) ).isEmpty() );
+ }
+ };
+
+ class GetMore : public ClientBase {
+ public:
+ ~GetMore() {
+ client().dropCollection( "unittests.querytests.GetMore" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.GetMore";
+ insert( ns, BSON( "a" << 1 ) );
+ insert( ns, BSON( "a" << 2 ) );
+ insert( ns, BSON( "a" << 3 ) );
+ auto_ptr< DBClientCursor > cursor = client().query( ns, BSONObj(), 2 );
+ long long cursorId = cursor->getCursorId();
+ cursor->decouple();
+ cursor.reset();
+ cursor = client().getMore( ns, cursorId );
+ ASSERT( cursor->more() );
+ ASSERT_EQUALS( 3, cursor->next().getIntField( "a" ) );
+ }
+ };
+
+ class ReturnOneOfManyAndTail : public ClientBase {
+ public:
+ ~ReturnOneOfManyAndTail() {
+ client().dropCollection( "unittests.querytests.ReturnOneOfManyAndTail" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.ReturnOneOfManyAndTail";
+ insert( ns, BSON( "a" << 0 ) );
+ insert( ns, BSON( "a" << 1 ) );
+ insert( ns, BSON( "a" << 2 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, QUERY( "a" << GT << 0 ).hint( BSON( "$natural" << 1 ) ), 1, 0, 0, QueryOption_CursorTailable );
+ // If only one result requested, a cursor is not saved.
+ ASSERT_EQUALS( 0, c->getCursorId() );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 1, c->next().getIntField( "a" ) );
+ }
+ };
+
+ class TailNotAtEnd : public ClientBase {
+ public:
+ ~TailNotAtEnd() {
+ client().dropCollection( "unittests.querytests.TailNotAtEnd" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.TailNotAtEnd";
+ insert( ns, BSON( "a" << 0 ) );
+ insert( ns, BSON( "a" << 1 ) );
+ insert( ns, BSON( "a" << 2 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, Query().hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable );
+ ASSERT( 0 != c->getCursorId() );
+ while( c->more() )
+ c->next();
+ ASSERT( 0 != c->getCursorId() );
+ insert( ns, BSON( "a" << 3 ) );
+ insert( ns, BSON( "a" << 4 ) );
+ insert( ns, BSON( "a" << 5 ) );
+ insert( ns, BSON( "a" << 6 ) );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 3, c->next().getIntField( "a" ) );
+ }
+ };
+
+ class EmptyTail : public ClientBase {
+ public:
+ ~EmptyTail() {
+ client().dropCollection( "unittests.querytests.EmptyTail" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.EmptyTail";
+ ASSERT_EQUALS( 0, client().query( ns, Query().hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable )->getCursorId() );
+ insert( ns, BSON( "a" << 0 ) );
+ ASSERT( 0 != client().query( ns, QUERY( "a" << 1 ).hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable )->getCursorId() );
+ }
+ };
+
+ class TailableDelete : public ClientBase {
+ public:
+ ~TailableDelete() {
+ client().dropCollection( "unittests.querytests.TailableDelete" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.TailableDelete";
+ insert( ns, BSON( "a" << 0 ) );
+ insert( ns, BSON( "a" << 1 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, Query().hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable );
+ c->next();
+ c->next();
+ ASSERT( !c->more() );
+ client().remove( ns, QUERY( "a" << 1 ) );
+ insert( ns, BSON( "a" << 2 ) );
+ ASSERT( !c->more() );
+ ASSERT_EQUALS( 0, c->getCursorId() );
+ }
+ };
+
+ class TailableInsertDelete : public ClientBase {
+ public:
+ ~TailableInsertDelete() {
+ client().dropCollection( "unittests.querytests.TailableInsertDelete" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.TailableInsertDelete";
+ insert( ns, BSON( "a" << 0 ) );
+ insert( ns, BSON( "a" << 1 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, Query().hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable );
+ c->next();
+ c->next();
+ ASSERT( !c->more() );
+ insert( ns, BSON( "a" << 2 ) );
+ client().remove( ns, QUERY( "a" << 1 ) );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 2, c->next().getIntField( "a" ) );
+ ASSERT( !c->more() );
+ }
+ };
+
+ class OplogReplayMode : public ClientBase {
+ public:
+ ~OplogReplayMode() {
+ client().dropCollection( "unittests.querytests.OplogReplayMode" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.OplogReplayMode";
+ insert( ns, BSON( "a" << 3 ) );
+ insert( ns, BSON( "a" << 0 ) );
+ insert( ns, BSON( "a" << 1 ) );
+ insert( ns, BSON( "a" << 2 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, QUERY( "a" << GT << 1 ).hint( BSON( "$natural" << 1 ) ), 0, 0, 0, QueryOption_OplogReplay );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 2, c->next().getIntField( "a" ) );
+ ASSERT( !c->more() );
+ }
+ };
+
+ class BasicCount : public ClientBase {
+ public:
+ ~BasicCount() {
+ client().dropCollection( "unittests.querytests.BasicCount" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.BasicCount";
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ count( 0 );
+ insert( ns, BSON( "a" << 3 ) );
+ count( 0 );
+ insert( ns, BSON( "a" << 4 ) );
+ count( 1 );
+ insert( ns, BSON( "a" << 5 ) );
+ count( 1 );
+ insert( ns, BSON( "a" << 4 ) );
+ count( 2 );
+ }
+ private:
+ void count( unsigned long long c ) const {
+ ASSERT_EQUALS( c, client().count( "unittests.querytests.BasicCount", BSON( "a" << 4 ) ) );
+ }
+ };
+
+ class ArrayId : public ClientBase {
+ public:
+ ~ArrayId() {
+ client().dropCollection( "unittests.querytests.ArrayId" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.ArrayId";
+ client().ensureIndex( ns, BSON( "_id" << 1 ) );
+ ASSERT( !error() );
+ client().insert( ns, fromjson( "{'_id':[1,2]}" ) );
+ ASSERT( error() );
+ }
+ };
+
+ class UnderscoreNs : public ClientBase {
+ public:
+ ~UnderscoreNs() {
+ client().dropCollection( "unittests.querytests._UnderscoreNs" );
+ }
+ void run() {
+ ASSERT( !error() );
+ const char *ns = "unittests.querytests._UnderscoreNs";
+ ASSERT( client().findOne( ns, "{}" ).isEmpty() );
+ client().insert( ns, BSON( "a" << 1 ) );
+ ASSERT_EQUALS( 1, client().findOne( ns, "{}" ).getIntField( "a" ) );
+ ASSERT( !error() );
+ }
+ };
+
+ class EmptyFieldSpec : public ClientBase {
+ public:
+ ~EmptyFieldSpec() {
+ client().dropCollection( "unittests.querytests.EmptyFieldSpec" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.EmptyFieldSpec";
+ client().insert( ns, BSON( "a" << 1 ) );
+ ASSERT( !client().findOne( ns, "" ).isEmpty() );
+ BSONObj empty;
+ ASSERT( !client().findOne( ns, "", &empty ).isEmpty() );
+ }
+ };
+
+ class MultiNe : public ClientBase {
+ public:
+ ~MultiNe() {
+ client().dropCollection( "unittests.querytests.Ne" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.Ne";
+ client().insert( ns, fromjson( "{a:[1,2]}" ) );
+ ASSERT( client().findOne( ns, fromjson( "{a:{$ne:1}}" ) ).isEmpty() );
+ BSONObj spec = fromjson( "{a:{$ne:1,$ne:2}}" );
+ ASSERT( client().findOne( ns, spec ).isEmpty() );
+ }
+ };
+
+ class EmbeddedNe : public ClientBase {
+ public:
+ ~EmbeddedNe() {
+ client().dropCollection( "unittests.querytests.NestedNe" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.NestedNe";
+ client().insert( ns, fromjson( "{a:[{b:1},{b:2}]}" ) );
+ ASSERT( client().findOne( ns, fromjson( "{'a.b':{$ne:1}}" ) ).isEmpty() );
+ }
+ };
+
+ class AutoResetIndexCache : public ClientBase {
+ public:
+ ~AutoResetIndexCache() {
+ client().dropCollection( "unittests.querytests.AutoResetIndexCache" );
+ }
+ static const char *ns() { return "unittests.querytests.AutoResetIndexCache"; }
+ static const char *idxNs() { return "unittests.system.indexes"; }
+ void index() const { ASSERT( !client().findOne( idxNs(), BSON( "name" << NE << "_id_" ) ).isEmpty() ); }
+ void noIndex() const { ASSERT( client().findOne( idxNs(), BSON( "name" << NE << "_id_" ) ).isEmpty() ); }
+ void checkIndex() {
+ client().ensureIndex( ns(), BSON( "a" << 1 ) );
+ index();
+ }
+ void run() {
+ client().dropDatabase( "unittests" );
+ noIndex();
+ checkIndex();
+ client().dropCollection( ns() );
+ noIndex();
+ checkIndex();
+ client().dropDatabase( "unittests" );
+ noIndex();
+ checkIndex();
+ }
+ };
+
+ class UniqueIndex : public ClientBase {
+ public:
+ ~UniqueIndex() {
+ client().dropCollection( "unittests.querytests.UniqueIndex" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.UniqueIndex";
+ client().ensureIndex( ns, BSON( "a" << 1 ), true );
+ client().insert( ns, BSON( "a" << 4 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 4 << "b" << 3 ) );
+ ASSERT_EQUALS( 1U, client().count( ns, BSONObj() ) );
+ client().dropCollection( ns );
+ client().ensureIndex( ns, BSON( "b" << 1 ), true );
+ client().insert( ns, BSON( "a" << 4 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 4 << "b" << 3 ) );
+ ASSERT_EQUALS( 2U, client().count( ns, BSONObj() ) );
+ }
+ };
+
+ class UniqueIndexPreexistingData : public ClientBase {
+ public:
+ ~UniqueIndexPreexistingData() {
+ client().dropCollection( "unittests.querytests.UniqueIndexPreexistingData" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.UniqueIndexPreexistingData";
+ client().insert( ns, BSON( "a" << 4 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 4 << "b" << 3 ) );
+ client().ensureIndex( ns, BSON( "a" << 1 ), true );
+ ASSERT_EQUALS( 0U, client().count( "unittests.system.indexes", BSON( "ns" << ns << "name" << NE << "_id_" ) ) );
+ }
+ };
+
+ class SubobjectInArray : public ClientBase {
+ public:
+ ~SubobjectInArray() {
+ client().dropCollection( "unittests.querytests.SubobjectInArray" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.SubobjectInArray";
+ client().insert( ns, fromjson( "{a:[{b:{c:1}}]}" ) );
+ ASSERT( !client().findOne( ns, BSON( "a.b.c" << 1 ) ).isEmpty() );
+ ASSERT( !client().findOne( ns, fromjson( "{'a.c':null}" ) ).isEmpty() );
+ }
+ };
+
+ class Size : public ClientBase {
+ public:
+ ~Size() {
+ client().dropCollection( "unittests.querytests.Size" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.Size";
+ client().insert( ns, fromjson( "{a:[1,2,3]}" ) );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ ASSERT( client().query( ns, QUERY( "a" << mongo::SIZE << 3 ).hint( BSON( "a" << 1 ) ) )->more() );
+ }
+ };
+
+ class FullArray : public ClientBase {
+ public:
+ ~FullArray() {
+ client().dropCollection( "unittests.querytests.IndexedArray" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.IndexedArray";
+ client().insert( ns, fromjson( "{a:[1,2,3]}" ) );
+ ASSERT( client().query( ns, Query( "{a:[1,2,3]}" ) )->more() );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ ASSERT( client().query( ns, Query( "{a:{$in:[1,[1,2,3]]}}" ).hint( BSON( "a" << 1 ) ) )->more() );
+ ASSERT( client().query( ns, Query( "{a:[1,2,3]}" ).hint( BSON( "a" << 1 ) ) )->more() ); // SERVER-146
+ }
+ };
+
+ class InsideArray : public ClientBase {
+ public:
+ ~InsideArray() {
+ client().dropCollection( "unittests.querytests.InsideArray" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.InsideArray";
+ client().insert( ns, fromjson( "{a:[[1],2]}" ) );
+ check( "$natural" );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ check( "a" ); // SERVER-146
+ }
+ private:
+ void check( const string &hintField ) {
+ const char *ns = "unittests.querytests.InsideArray";
+ ASSERT( client().query( ns, Query( "{a:[[1],2]}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ ASSERT( client().query( ns, Query( "{a:[1]}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ ASSERT( client().query( ns, Query( "{a:2}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ ASSERT( !client().query( ns, Query( "{a:1}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ }
+ };
+
+ class IndexInsideArrayCorrect : public ClientBase {
+ public:
+ ~IndexInsideArrayCorrect() {
+ client().dropCollection( "unittests.querytests.IndexInsideArrayCorrect" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.IndexInsideArrayCorrect";
+ client().insert( ns, fromjson( "{'_id':1,a:[1]}" ) );
+ client().insert( ns, fromjson( "{'_id':2,a:[[1]]}" ) );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ ASSERT_EQUALS( 1, client().query( ns, Query( "{a:[1]}" ).hint( BSON( "a" << 1 ) ) )->next().getIntField( "_id" ) );
+ }
+ };
+
+ class SubobjArr : public ClientBase {
+ public:
+ ~SubobjArr() {
+ client().dropCollection( "unittests.querytests.SubobjArr" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.SubobjArr";
+ client().insert( ns, fromjson( "{a:[{b:[1]}]}" ) );
+ check( "$natural" );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ check( "a" );
+ }
+ private:
+ void check( const string &hintField ) {
+ const char *ns = "unittests.querytests.SubobjArr";
+ ASSERT( client().query( ns, Query( "{'a.b':1}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ ASSERT( !client().query( ns, Query( "{'a.b':[1]}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ }
+ };
+
+ class MinMax : public ClientBase {
+ public:
+ MinMax() : ns( "unittests.querytests.MinMax" ) {}
+ ~MinMax() {
+ client().dropCollection( "unittests.querytests.MinMax" );
+ }
+ void run() {
+ client().ensureIndex( ns, BSON( "a" << 1 << "b" << 1 ) );
+ client().insert( ns, BSON( "a" << 1 << "b" << 1 ) );
+ client().insert( ns, BSON( "a" << 1 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 2 << "b" << 1 ) );
+ client().insert( ns, BSON( "a" << 2 << "b" << 2 ) );
+
+ ASSERT_EQUALS( 4, count( client().query( ns, BSONObj() ) ) );
+ BSONObj hints[] = { BSONObj(), BSON( "a" << 1 << "b" << 1 ) };
+ for( int i = 0; i < 2; ++i ) {
+ check( 0, 0, 3, 3, 4, hints[ i ] );
+ check( 1, 1, 2, 2, 3, hints[ i ] );
+ check( 1, 2, 2, 2, 2, hints[ i ] );
+ check( 1, 2, 2, 1, 1, hints[ i ] );
+
+ auto_ptr< DBClientCursor > c = query( 1, 2, 2, 2, hints[ i ] );
+ BSONObj obj = c->next();
+ ASSERT_EQUALS( 1, obj.getIntField( "a" ) );
+ ASSERT_EQUALS( 2, obj.getIntField( "b" ) );
+ obj = c->next();
+ ASSERT_EQUALS( 2, obj.getIntField( "a" ) );
+ ASSERT_EQUALS( 1, obj.getIntField( "b" ) );
+ ASSERT( !c->more() );
+ }
+ }
+ private:
+ auto_ptr< DBClientCursor > query( int minA, int minB, int maxA, int maxB, const BSONObj &hint ) {
+ Query q;
+ q = q.minKey( BSON( "a" << minA << "b" << minB ) ).maxKey( BSON( "a" << maxA << "b" << maxB ) );
+ if ( !hint.isEmpty() )
+ q.hint( hint );
+ return client().query( ns, q );
+ }
+ void check( int minA, int minB, int maxA, int maxB, int expectedCount, const BSONObj &hint = empty_ ) {
+ ASSERT_EQUALS( expectedCount, count( query( minA, minB, maxA, maxB, hint ) ) );
+ }
+ int count( auto_ptr< DBClientCursor > c ) {
+ int ret = 0;
+ while( c->more() ) {
+ ++ret;
+ c->next();
+ }
+ return ret;
+ }
+ const char *ns;
+ static BSONObj empty_;
+ };
+ BSONObj MinMax::empty_;
+
+ class DirectLocking : public ClientBase {
+ public:
+ void run() {
+ dblock lk;
+ setClient( "unittests.DirectLocking" );
+ client().remove( "a.b", BSONObj() );
+ ASSERT_EQUALS( "unittests", cc().database()->name );
+ }
+ const char *ns;
+ };
+
+ class FastCountIn : public ClientBase {
+ public:
+ ~FastCountIn() {
+ client().dropCollection( "unittests.querytests.FastCountIn" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.FastCountIn";
+ client().insert( ns, BSON( "i" << "a" ) );
+ client().ensureIndex( ns, BSON( "i" << 1 ) );
+ ASSERT_EQUALS( 1U, client().count( ns, fromjson( "{i:{$in:['a']}}" ) ) );
+ }
+ };
+
+ class EmbeddedArray : public ClientBase {
+ public:
+ ~EmbeddedArray() {
+ client().dropCollection( "unittests.querytests.EmbeddedArray" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.EmbeddedArray";
+ client().insert( ns, fromjson( "{foo:{bar:['spam']}}" ) );
+ client().insert( ns, fromjson( "{foo:{bar:['spam','eggs']}}" ) );
+ client().insert( ns, fromjson( "{bar:['spam']}" ) );
+ client().insert( ns, fromjson( "{bar:['spam','eggs']}" ) );
+ ASSERT_EQUALS( 2U, client().count( ns, BSON( "bar" << "spam" ) ) );
+ ASSERT_EQUALS( 2U, client().count( ns, BSON( "foo.bar" << "spam" ) ) );
+ }
+ };
+
+ class DifferentNumbers : public ClientBase {
+ public:
+ ~DifferentNumbers(){
+ client().dropCollection( "unittests.querytests.DifferentNumbers" );
+ }
+ void t( const char * ns ){
+ auto_ptr< DBClientCursor > cursor = client().query( ns, Query().sort( "7" ) );
+ while ( cursor->more() ){
+ BSONObj o = cursor->next();
+ cout << " foo " << o << endl;
+ }
+
+ }
+ void run() {
+ const char *ns = "unittests.querytests.DifferentNumbers";
+ { BSONObjBuilder b; b.append( "7" , (int)4 ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.append( "7" , (long long)2 ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.appendNull( "7" ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.append( "7" , "b" ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.appendNull( "8" ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.append( "7" , (double)3.7 ); client().insert( ns , b.obj() ); }
+
+ t(ns);
+ client().ensureIndex( ns , BSON( "7" << 1 ) );
+ t(ns);
+ }
+ };
+
+ class CollectionBase : public ClientBase {
+ public:
+
+ CollectionBase( string leaf ){
+ _ns = "unittests.querytests.";
+ _ns += leaf;
+ }
+
+ virtual ~CollectionBase(){
+ client().dropCollection( ns() );
+ }
+
+ int count(){
+ return (int) client().count( ns() );
+ }
+
+ const char * ns(){
+ return _ns.c_str();
+ }
+
+ private:
+ string _ns;
+ };
+
+ class SymbolStringSame : public CollectionBase {
+ public:
+ SymbolStringSame() : CollectionBase( "symbolstringsame" ){}
+
+ void run(){
+ { BSONObjBuilder b; b.appendSymbol( "x" , "eliot" ); b.append( "z" , 17 ); client().insert( ns() , b.obj() ); }
+ ASSERT_EQUALS( 17 , client().findOne( ns() , BSONObj() )["z"].number() );
+ {
+ BSONObjBuilder b;
+ b.appendSymbol( "x" , "eliot" );
+ ASSERT_EQUALS( 17 , client().findOne( ns() , b.obj() )["z"].number() );
+ }
+ ASSERT_EQUALS( 17 , client().findOne( ns() , BSON( "x" << "eliot" ) )["z"].number() );
+ client().ensureIndex( ns() , BSON( "x" << 1 ) );
+ ASSERT_EQUALS( 17 , client().findOne( ns() , BSON( "x" << "eliot" ) )["z"].number() );
+ }
+ };
+
+ class TailableCappedRaceCondition : public CollectionBase {
+ public:
+
+ TailableCappedRaceCondition() : CollectionBase( "tailablecappedrace" ){
+ client().dropCollection( ns() );
+ _n = 0;
+ }
+ void run(){
+ string err;
+
+ writelock lk("");
+ setClient( "unittests" );
+
+ ASSERT( userCreateNS( ns() , fromjson( "{ capped : true , size : 2000 }" ) , err , false ) );
+ for ( int i=0; i<100; i++ ){
+ insertNext();
+ ASSERT( count() < 45 );
+ }
+
+ int a = count();
+
+ auto_ptr< DBClientCursor > c = client().query( ns() , QUERY( "i" << GT << 0 ).hint( BSON( "$natural" << 1 ) ), 0, 0, 0, QueryOption_CursorTailable );
+ int n=0;
+ while ( c->more() ){
+ BSONObj z = c->next();
+ n++;
+ }
+
+ ASSERT_EQUALS( a , n );
+
+ insertNext();
+ ASSERT( c->more() );
+
+ for ( int i=0; i<50; i++ ){
+ insertNext();
+ }
+
+ while ( c->more() ){ c->next(); }
+ ASSERT( c->isDead() );
+ }
+
+ void insertNext(){
+ insert( ns() , BSON( "i" << _n++ ) );
+ }
+
+ int _n;
+ };
+
+ class HelperTest : public CollectionBase {
+ public:
+
+ HelperTest() : CollectionBase( "helpertest" ){
+ }
+
+ void run(){
+ writelock lk("");
+ setClient( "unittests" );
+
+ for ( int i=0; i<50; i++ ){
+ insert( ns() , BSON( "_id" << i << "x" << i * 2 ) );
+ }
+
+ ASSERT_EQUALS( 50 , count() );
+
+ BSONObj res;
+ ASSERT( Helpers::findOne( ns() , BSON( "_id" << 20 ) , res , true ) );
+ ASSERT_EQUALS( 40 , res["x"].numberInt() );
+
+ ASSERT( Helpers::findById( cc(), ns() , BSON( "_id" << 20 ) , res ) );
+ ASSERT_EQUALS( 40 , res["x"].numberInt() );
+
+ ASSERT( ! Helpers::findById( cc(), ns() , BSON( "_id" << 200 ) , res ) );
+
+ unsigned long long slow , fast;
+
+ int n = 10000;
+ {
+ Timer t;
+ for ( int i=0; i<n; i++ ){
+ ASSERT( Helpers::findOne( ns() , BSON( "_id" << 20 ) , res , true ) );
+ }
+ slow = t.micros();
+ }
+ {
+ Timer t;
+ for ( int i=0; i<n; i++ ){
+ ASSERT( Helpers::findById( cc(), ns() , BSON( "_id" << 20 ) , res ) );
+ }
+ fast = t.micros();
+ }
+
+ cout << "HelperTest slow:" << slow << " fast:" << fast << endl;
+
+ {
+ auto_ptr<CursorIterator> i = Helpers::find( ns() );
+ int n = 0;
+ while ( i->hasNext() ){
+ BSONObj o = i->next();
+ n++;
+ }
+ ASSERT_EQUALS( 50 , n );
+
+ i = Helpers::find( ns() , BSON( "_id" << 20 ) );
+ n = 0;
+ while ( i->hasNext() ){
+ BSONObj o = i->next();
+ n++;
+ }
+ ASSERT_EQUALS( 1 , n );
+ }
+
+ }
+ };
+
+ class HelperByIdTest : public CollectionBase {
+ public:
+
+ HelperByIdTest() : CollectionBase( "helpertestbyid" ){
+ }
+
+ void run(){
+ writelock lk("");
+ setClient( "unittests" );
+
+ for ( int i=0; i<1000; i++ ){
+ insert( ns() , BSON( "_id" << i << "x" << i * 2 ) );
+ }
+ for ( int i=0; i<1000; i+=2 ){
+ client_.remove( ns() , BSON( "_id" << i ) );
+ }
+
+ BSONObj res;
+ for ( int i=0; i<1000; i++ ){
+ bool found = Helpers::findById( cc(), ns() , BSON( "_id" << i ) , res );
+ ASSERT_EQUALS( i % 2 , int(found) );
+ }
+
+ }
+ };
+
+ class ClientCursorTest : public CollectionBase{
+ ClientCursorTest() : CollectionBase( "clientcursortest" ){
+ }
+
+ void run(){
+ writelock lk("");
+ setClient( "unittests" );
+
+ for ( int i=0; i<1000; i++ ){
+ insert( ns() , BSON( "_id" << i << "x" << i * 2 ) );
+ }
+
+
+ }
+ };
+
+ class All : public Suite {
+ public:
+ All() : Suite( "query" ) {
+ }
+
+ void setupTests(){
+ add< CountBasic >();
+ add< CountQuery >();
+ add< CountFields >();
+ add< CountQueryFields >();
+ add< CountIndexedRegex >();
+ add< BoundedKey >();
+ add< GetMore >();
+ add< ReturnOneOfManyAndTail >();
+ add< TailNotAtEnd >();
+ add< EmptyTail >();
+ add< TailableDelete >();
+ add< TailableInsertDelete >();
+ add< OplogReplayMode >();
+ add< ArrayId >();
+ add< UnderscoreNs >();
+ add< EmptyFieldSpec >();
+ add< MultiNe >();
+ add< EmbeddedNe >();
+ add< AutoResetIndexCache >();
+ add< UniqueIndex >();
+ add< UniqueIndexPreexistingData >();
+ add< SubobjectInArray >();
+ add< Size >();
+ add< FullArray >();
+ add< InsideArray >();
+ add< IndexInsideArrayCorrect >();
+ add< SubobjArr >();
+ add< MinMax >();
+ add< DirectLocking >();
+ add< FastCountIn >();
+ add< EmbeddedArray >();
+ add< DifferentNumbers >();
+ add< SymbolStringSame >();
+ add< TailableCappedRaceCondition >();
+ add< HelperTest >();
+ add< HelperByIdTest >();
+ }
+ } myall;
+
+} // namespace QueryTests
+
diff --git a/dbtests/repltests.cpp b/dbtests/repltests.cpp
new file mode 100644
index 0000000..d4d97c1
--- /dev/null
+++ b/dbtests/repltests.cpp
@@ -0,0 +1,1050 @@
+// repltests.cpp : Unit tests for replication
+//
+
+/**
+ * Copyright (C) 2009 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/repl.h"
+
+#include "../db/db.h"
+#include "../db/instance.h"
+#include "../db/json.h"
+
+#include "dbtests.h"
+
+namespace mongo {
+ void createOplog();
+}
+
+namespace ReplTests {
+
+ BSONObj f( const char *s ) {
+ return fromjson( s );
+ }
+
+ class Base {
+ public:
+ Base() {
+ master = true;
+ createOplog();
+ dblock lk;
+ setClient( ns() );
+ ensureHaveIdIndex( ns() );
+ }
+ ~Base() {
+ try {
+ master = false;
+ deleteAll( ns() );
+ deleteAll( cllNS() );
+ } catch ( ... ) {
+ FAIL( "Exception while cleaning up test" );
+ }
+ }
+ protected:
+ static const char *ns() {
+ return "unittests.repltests";
+ }
+ static const char *cllNS() {
+ return "local.oplog.$main";
+ }
+ DBDirectClient *client() const { return &client_; }
+ BSONObj one( const BSONObj &query = BSONObj() ) const {
+ return client()->findOne( ns(), query );
+ }
+ void checkOne( const BSONObj &o ) const {
+ check( o, one( o ) );
+ }
+ void checkAll( const BSONObj &o ) const {
+ auto_ptr< DBClientCursor > c = client()->query( ns(), o );
+ assert( c->more() );
+ while( c->more() ) {
+ check( o, c->next() );
+ }
+ }
+ void check( const BSONObj &expected, const BSONObj &got ) const {
+ if ( expected.woCompare( got ) ) {
+ out() << "expected: " << expected.toString()
+ << ", got: " << got.toString() << endl;
+ }
+ ASSERT_EQUALS( expected , got );
+ }
+ BSONObj oneOp() const {
+ return client()->findOne( cllNS(), BSONObj() );
+ }
+ int count() const {
+ int count = 0;
+ dblock lk;
+ setClient( ns() );
+ auto_ptr< Cursor > c = theDataFileMgr.findAll( ns() );
+ for(; c->ok(); c->advance(), ++count ) {
+// cout << "obj: " << c->current().toString() << endl;
+ }
+ return count;
+ }
+ static int opCount() {
+ dblock lk;
+ setClient( cllNS() );
+ int count = 0;
+ for( auto_ptr< Cursor > c = theDataFileMgr.findAll( cllNS() ); c->ok(); c->advance() )
+ ++count;
+ return count;
+ }
+ static void applyAllOperations() {
+ class Applier : public ReplSource {
+ public:
+ static void apply( const BSONObj &op ) {
+ ReplSource::applyOperation( op );
+ }
+ };
+ dblock lk;
+ setClient( cllNS() );
+ vector< BSONObj > ops;
+ for( auto_ptr< Cursor > c = theDataFileMgr.findAll( cllNS() ); c->ok(); c->advance() )
+ ops.push_back( c->current() );
+ setClient( ns() );
+ for( vector< BSONObj >::iterator i = ops.begin(); i != ops.end(); ++i )
+ Applier::apply( *i );
+ }
+ static void printAll( const char *ns ) {
+ dblock lk;
+ setClient( ns );
+ auto_ptr< Cursor > c = theDataFileMgr.findAll( ns );
+ vector< DiskLoc > toDelete;
+ out() << "all for " << ns << endl;
+ for(; c->ok(); c->advance() ) {
+ out() << c->current().toString() << endl;
+ }
+ }
+ // These deletes don't get logged.
+ static void deleteAll( const char *ns ) {
+ dblock lk;
+ setClient( ns );
+ auto_ptr< Cursor > c = theDataFileMgr.findAll( ns );
+ vector< DiskLoc > toDelete;
+ for(; c->ok(); c->advance() ) {
+ toDelete.push_back( c->currLoc() );
+ }
+ for( vector< DiskLoc >::iterator i = toDelete.begin(); i != toDelete.end(); ++i ) {
+ theDataFileMgr.deleteRecord( ns, i->rec(), *i, true );
+ }
+ }
+ static void insert( const BSONObj &o, bool god = false ) {
+ dblock lk;
+ setClient( ns() );
+ theDataFileMgr.insert( ns(), o.objdata(), o.objsize(), god );
+ }
+ static BSONObj wid( const char *json ) {
+ class BSONObjBuilder b;
+ OID id;
+ id.init();
+ b.appendOID( "_id", &id );
+ b.appendElements( fromjson( json ) );
+ return b.obj();
+ }
+ private:
+ static DBDirectClient client_;
+ };
+ DBDirectClient Base::client_;
+
+ class LogBasic : public Base {
+ public:
+ void run() {
+ ASSERT_EQUALS( 1, opCount() );
+ client()->insert( ns(), fromjson( "{\"a\":\"b\"}" ) );
+ ASSERT_EQUALS( 2, opCount() );
+ }
+ };
+
+ namespace Idempotence {
+
+ class Base : public ReplTests::Base {
+ public:
+ virtual ~Base() {}
+ void run() {
+ reset();
+ doIt();
+ int nOps = opCount();
+ check();
+ applyAllOperations();
+ check();
+ ASSERT_EQUALS( nOps, opCount() );
+
+ reset();
+ applyAllOperations();
+ check();
+ ASSERT_EQUALS( nOps, opCount() );
+ applyAllOperations();
+ check();
+ ASSERT_EQUALS( nOps, opCount() );
+ }
+ protected:
+ virtual void doIt() const = 0;
+ virtual void check() const = 0;
+ virtual void reset() const = 0;
+ };
+
+ class InsertTimestamp : public Base {
+ public:
+ void doIt() const {
+ BSONObjBuilder b;
+ b.append( "a", 1 );
+ b.appendTimestamp( "t" );
+ client()->insert( ns(), b.done() );
+ date_ = client()->findOne( ns(), QUERY( "a" << 1 ) ).getField( "t" ).date();
+ }
+ void check() const {
+ BSONObj o = client()->findOne( ns(), QUERY( "a" << 1 ) );
+ ASSERT( 0 != o.getField( "t" ).date() );
+ ASSERT_EQUALS( date_, o.getField( "t" ).date() );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ }
+ private:
+ mutable Date_t date_;
+ };
+
+ class InsertAutoId : public Base {
+ public:
+ InsertAutoId() : o_( fromjson( "{\"a\":\"b\"}" ) ) {}
+ void doIt() const {
+ client()->insert( ns(), o_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ }
+ protected:
+ BSONObj o_;
+ };
+
+ class InsertWithId : public InsertAutoId {
+ public:
+ InsertWithId() {
+ o_ = fromjson( "{\"_id\":ObjectId(\"0f0f0f0f0f0f0f0f0f0f0f0f\"),\"a\":\"b\"}" );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ checkOne( o_ );
+ }
+ };
+
+ class InsertTwo : public Base {
+ public:
+ InsertTwo() :
+ o_( fromjson( "{'_id':1,a:'b'}" ) ),
+ t_( fromjson( "{'_id':2,c:'d'}" ) ) {}
+ void doIt() const {
+ vector< BSONObj > v;
+ v.push_back( o_ );
+ v.push_back( t_ );
+ client()->insert( ns(), v );
+ }
+ void check() const {
+ ASSERT_EQUALS( 2, count() );
+ checkOne( o_ );
+ checkOne( t_ );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ }
+ private:
+ BSONObj o_;
+ BSONObj t_;
+ };
+
+ class InsertTwoIdentical : public Base {
+ public:
+ InsertTwoIdentical() : o_( fromjson( "{\"a\":\"b\"}" ) ) {}
+ void doIt() const {
+ client()->insert( ns(), o_ );
+ client()->insert( ns(), o_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 2, count() );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ }
+ private:
+ BSONObj o_;
+ };
+
+ class UpdateTimestamp : public Base {
+ public:
+ void doIt() const {
+ BSONObjBuilder b;
+ b.append( "_id", 1 );
+ b.appendTimestamp( "t" );
+ client()->update( ns(), BSON( "_id" << 1 ), b.done() );
+ date_ = client()->findOne( ns(), QUERY( "_id" << 1 ) ).getField( "t" ).date();
+ }
+ void check() const {
+ BSONObj o = client()->findOne( ns(), QUERY( "_id" << 1 ) );
+ ASSERT( 0 != o.getField( "t" ).date() );
+ ASSERT_EQUALS( date_, o.getField( "t" ).date() );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( BSON( "_id" << 1 ) );
+ }
+ private:
+ mutable Date_t date_;
+ };
+
+ class UpdateSameField : public Base {
+ public:
+ UpdateSameField() :
+ q_( fromjson( "{a:'b'}" ) ),
+ o1_( wid( "{a:'b'}" ) ),
+ o2_( wid( "{a:'b'}" ) ),
+ u_( fromjson( "{a:'c'}" ) ){}
+ void doIt() const {
+ client()->update( ns(), q_, u_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 2, count() );
+ ASSERT( !client()->findOne( ns(), q_ ).isEmpty() );
+ ASSERT( !client()->findOne( ns(), u_ ).isEmpty() );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( o1_ );
+ insert( o2_ );
+ }
+ private:
+ BSONObj q_, o1_, o2_, u_;
+ };
+
+ class UpdateSameFieldWithId : public Base {
+ public:
+ UpdateSameFieldWithId() :
+ o_( fromjson( "{'_id':1,a:'b'}" ) ),
+ q_( fromjson( "{a:'b'}" ) ),
+ u_( fromjson( "{'_id':1,a:'c'}" ) ){}
+ void doIt() const {
+ client()->update( ns(), q_, u_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 2, count() );
+ ASSERT( !client()->findOne( ns(), q_ ).isEmpty() );
+ ASSERT( !client()->findOne( ns(), u_ ).isEmpty() );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( o_ );
+ insert( fromjson( "{'_id':2,a:'b'}" ) );
+ }
+ private:
+ BSONObj o_, q_, u_;
+ };
+
+ class UpdateSameFieldExplicitId : public Base {
+ public:
+ UpdateSameFieldExplicitId() :
+ o_( fromjson( "{'_id':1,a:'b'}" ) ),
+ u_( fromjson( "{'_id':1,a:'c'}" ) ){}
+ void doIt() const {
+ client()->update( ns(), o_, u_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ checkOne( u_ );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( o_ );
+ }
+ protected:
+ BSONObj o_, u_;
+ };
+
+ class UpdateId : public UpdateSameFieldExplicitId {
+ public:
+ UpdateId() {
+ o_ = fromjson( "{'_id':1}" );
+ u_ = fromjson( "{'_id':2}" );
+ }
+ };
+
+ class UpdateDifferentFieldExplicitId : public Base {
+ public:
+ UpdateDifferentFieldExplicitId() :
+ o_( fromjson( "{'_id':1,a:'b'}" ) ),
+ q_( fromjson( "{'_id':1}" ) ),
+ u_( fromjson( "{'_id':1,a:'c'}" ) ){}
+ void doIt() const {
+ client()->update( ns(), q_, u_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ checkOne( u_ );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( o_ );
+ }
+ protected:
+ BSONObj o_, q_, u_;
+ };
+
+ class UpsertUpdateNoMods : public UpdateDifferentFieldExplicitId {
+ void doIt() const {
+ client()->update( ns(), q_, u_, true );
+ }
+ };
+
+ class UpsertInsertNoMods : public InsertAutoId {
+ void doIt() const {
+ client()->update( ns(), fromjson( "{a:'c'}" ), o_, true );
+ }
+ };
+
+ class UpdateSet : public Base {
+ public:
+ UpdateSet() :
+ o_( fromjson( "{'_id':1,a:5}" ) ),
+ q_( fromjson( "{a:5}" ) ),
+ u_( fromjson( "{$set:{a:7}}" ) ),
+ ou_( fromjson( "{'_id':1,a:7}" ) ) {}
+ void doIt() const {
+ client()->update( ns(), q_, u_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ checkOne( ou_ );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( o_ );
+ }
+ protected:
+ BSONObj o_, q_, u_, ou_;
+ };
+
+ class UpdateInc : public Base {
+ public:
+ UpdateInc() :
+ o_( fromjson( "{'_id':1,a:5}" ) ),
+ q_( fromjson( "{a:5}" ) ),
+ u_( fromjson( "{$inc:{a:3}}" ) ),
+ ou_( fromjson( "{'_id':1,a:8}" ) ) {}
+ void doIt() const {
+ client()->update( ns(), q_, u_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ checkOne( ou_ );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( o_ );
+ }
+ protected:
+ BSONObj o_, q_, u_, ou_;
+ };
+
+ class UpsertInsertIdMod : public Base {
+ public:
+ UpsertInsertIdMod() :
+ q_( fromjson( "{'_id':5,a:4}" ) ),
+ u_( fromjson( "{$inc:{a:3}}" ) ),
+ ou_( fromjson( "{'_id':5,a:7}" ) ) {}
+ void doIt() const {
+ client()->update( ns(), q_, u_, true );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ checkOne( ou_ );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ }
+ protected:
+ BSONObj q_, u_, ou_;
+ };
+
+ class UpsertInsertSet : public Base {
+ public:
+ UpsertInsertSet() :
+ q_( fromjson( "{a:5}" ) ),
+ u_( fromjson( "{$set:{a:7}}" ) ),
+ ou_( fromjson( "{a:7}" ) ) {}
+ void doIt() const {
+ client()->update( ns(), q_, u_, true );
+ }
+ void check() const {
+ ASSERT_EQUALS( 2, count() );
+ ASSERT( !client()->findOne( ns(), ou_ ).isEmpty() );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':7,a:7}" ) );
+ }
+ protected:
+ BSONObj o_, q_, u_, ou_;
+ };
+
+ class UpsertInsertInc : public Base {
+ public:
+ UpsertInsertInc() :
+ q_( fromjson( "{a:5}" ) ),
+ u_( fromjson( "{$inc:{a:3}}" ) ),
+ ou_( fromjson( "{a:8}" ) ) {}
+ void doIt() const {
+ client()->update( ns(), q_, u_, true );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ ASSERT( !client()->findOne( ns(), ou_ ).isEmpty() );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ }
+ protected:
+ BSONObj o_, q_, u_, ou_;
+ };
+
+ class MultiInc : public Base {
+ public:
+
+ string s() const {
+ stringstream ss;
+ auto_ptr<DBClientCursor> cc = client()->query( ns() , Query().sort( BSON( "_id" << 1 ) ) );
+ bool first = true;
+ while ( cc->more() ){
+ if ( first ) first = false;
+ else ss << ",";
+
+ BSONObj o = cc->next();
+ ss << o["x"].numberInt();
+ }
+ return ss.str();
+ }
+
+ void doIt() const {
+ client()->insert( ns(), BSON( "_id" << 1 << "x" << 1 ) );
+ client()->insert( ns(), BSON( "_id" << 2 << "x" << 5 ) );
+
+ ASSERT_EQUALS( "1,5" , s() );
+
+ client()->update( ns() , BSON( "_id" << 1 ) , BSON( "$inc" << BSON( "x" << 1 ) ) );
+ ASSERT_EQUALS( "2,5" , s() );
+
+ client()->update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) );
+ ASSERT_EQUALS( "3,5" , s() );
+
+ client()->update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) , false , true );
+ check();
+ }
+
+ void check() const {
+ ASSERT_EQUALS( "4,6" , s() );
+ }
+
+ void reset() const {
+ deleteAll( ns() );
+ }
+ };
+
+ class UpdateWithoutPreexistingId : public Base {
+ public:
+ UpdateWithoutPreexistingId() :
+ o_( fromjson( "{a:5}" ) ),
+ u_( fromjson( "{a:5}" ) ),
+ ot_( fromjson( "{b:4}" ) ) {}
+ void doIt() const {
+ client()->update( ns(), o_, u_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 2, count() );
+ checkOne( u_ );
+ checkOne( ot_ );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( ot_, true );
+ insert( o_, true );
+ }
+ protected:
+ BSONObj o_, u_, ot_;
+ };
+
+ class Remove : public Base {
+ public:
+ Remove() :
+ o1_( f( "{\"_id\":\"010101010101010101010101\",\"a\":\"b\"}" ) ),
+ o2_( f( "{\"_id\":\"010101010101010101010102\",\"a\":\"b\"}" ) ),
+ q_( f( "{\"a\":\"b\"}" ) ) {}
+ void doIt() const {
+ client()->remove( ns(), q_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 0, count() );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( o1_ );
+ insert( o2_ );
+ }
+ protected:
+ BSONObj o1_, o2_, q_;
+ };
+
+ class RemoveOne : public Remove {
+ void doIt() const {
+ client()->remove( ns(), q_, true );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ }
+ };
+
+ class FailingUpdate : public Base {
+ public:
+ FailingUpdate() :
+ o_( fromjson( "{'_id':1,a:'b'}" ) ),
+ u_( fromjson( "{'_id':1,c:'d'}" ) ) {}
+ void doIt() const {
+ client()->update( ns(), o_, u_ );
+ client()->insert( ns(), o_ );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ checkOne( o_ );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ }
+ protected:
+ BSONObj o_, u_;
+ };
+
+ class SetNumToStr : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), BSON( "$set" << BSON( "a" << "bcd" ) ) );
+ }
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ checkOne( BSON( "_id" << 0 << "a" << "bcd" ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( BSON( "_id" << 0 << "a" << 4.0 ) );
+ }
+ };
+
+ class Push : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), BSON( "$push" << BSON( "a" << 5.0 ) ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[4,5]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4]}" ) );
+ }
+ };
+
+ class PushUpsert : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), BSON( "$push" << BSON( "a" << 5.0 ) ), true );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[4,5]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4]}" ) );
+ }
+ };
+
+ class MultiPush : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), BSON( "$push" << BSON( "a" << 5.0 ) << "$push" << BSON( "b.c" << 6.0 ) ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[4,5],b:{c:[6]}}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4]}" ) );
+ }
+ };
+
+ class EmptyPush : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), BSON( "$push" << BSON( "a" << 5.0 ) ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[5]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0}" ) );
+ }
+ };
+
+ class PushAll : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$pushAll:{a:[5.0,6.0]}}" ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[4,5,6]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4]}" ) );
+ }
+ };
+
+ class PushAllUpsert : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$pushAll:{a:[5.0,6.0]}}" ), true );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[4,5,6]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4]}" ) );
+ }
+ };
+
+ class EmptyPushAll : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$pushAll:{a:[5.0,6.0]}}" ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[5,6]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0}" ) );
+ }
+ };
+
+ class Pull : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), BSON( "$pull" << BSON( "a" << 4.0 ) ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[5]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4,5]}" ) );
+ }
+ };
+
+ class PullNothing : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), BSON( "$pull" << BSON( "a" << 6.0 ) ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[4,5]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4,5]}" ) );
+ }
+ };
+
+ class PullAll : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$pullAll:{a:[4,5]}}" ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[6]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4,5,6]}" ) );
+ }
+ };
+
+ class Pop : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$pop:{a:1}}" ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[4,5]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4,5,6]}" ) );
+ }
+ };
+
+ class PopReverse : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$pop:{a:-1}}" ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( fromjson( "{'_id':0,a:[5,6]}" ), one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:[4,5,6]}" ) );
+ }
+ };
+
+ class BitOp : public Base {
+ public:
+ void doIt() const {
+ client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$bit:{a:{and:2,or:8}}}" ) );
+ }
+ using ReplTests::Base::check;
+ void check() const {
+ ASSERT_EQUALS( 1, count() );
+ check( BSON( "_id" << 0 << "a" << ( ( 3 & 2 ) | 8 ) ) , one( fromjson( "{'_id':0}" ) ) );
+ }
+ void reset() const {
+ deleteAll( ns() );
+ insert( fromjson( "{'_id':0,a:3}" ) );
+ }
+ };
+
+
+
+ } // namespace Idempotence
+
+ class DeleteOpIsIdBased : public Base {
+ public:
+ void run() {
+ insert( BSON( "_id" << 0 << "a" << 10 ) );
+ insert( BSON( "_id" << 1 << "a" << 11 ) );
+ insert( BSON( "_id" << 3 << "a" << 10 ) );
+ client()->remove( ns(), BSON( "a" << 10 ) );
+ ASSERT_EQUALS( 1U, client()->count( ns(), BSONObj() ) );
+ insert( BSON( "_id" << 0 << "a" << 11 ) );
+ insert( BSON( "_id" << 2 << "a" << 10 ) );
+ insert( BSON( "_id" << 3 << "a" << 10 ) );
+
+ applyAllOperations();
+ ASSERT_EQUALS( 2U, client()->count( ns(), BSONObj() ) );
+ ASSERT( !one( BSON( "_id" << 1 ) ).isEmpty() );
+ ASSERT( !one( BSON( "_id" << 2 ) ).isEmpty() );
+ }
+ };
+
+ class DbIdsTest {
+ public:
+ void run() {
+ setClient( "unittests.repltest.DbIdsTest" );
+
+ s_.reset( new DbIds( "local.temp.DbIdsTest" ) );
+ s_->reset();
+ check( false, false, false );
+
+ s_->set( "a", BSON( "_id" << 4 ), true );
+ check( true, false, false );
+ s_->set( "a", BSON( "_id" << 4 ), false );
+ check( false, false, false );
+
+ s_->set( "b", BSON( "_id" << 4 ), true );
+ check( false, true, false );
+ s_->set( "b", BSON( "_id" << 4 ), false );
+ check( false, false, false );
+
+ s_->set( "a", BSON( "_id" << 5 ), true );
+ check( false, false, true );
+ s_->set( "a", BSON( "_id" << 5 ), false );
+ check( false, false, false );
+
+ s_->set( "a", BSON( "_id" << 4 ), true );
+ s_->set( "b", BSON( "_id" << 4 ), true );
+ s_->set( "a", BSON( "_id" << 5 ), true );
+ check( true, true, true );
+
+ s_->reset();
+ check( false, false, false );
+
+ s_->set( "a", BSON( "_id" << 4 ), true );
+ s_->set( "a", BSON( "_id" << 4 ), true );
+ check( true, false, false );
+ s_->set( "a", BSON( "_id" << 4 ), false );
+ check( false, false, false );
+ }
+ private:
+ void check( bool one, bool two, bool three ) {
+ ASSERT_EQUALS( one, s_->get( "a", BSON( "_id" << 4 ) ) );
+ ASSERT_EQUALS( two, s_->get( "b", BSON( "_id" << 4 ) ) );
+ ASSERT_EQUALS( three, s_->get( "a", BSON( "_id" << 5 ) ) );
+ }
+ dblock lk_;
+ auto_ptr< DbIds > s_;
+ };
+
+ class MemIdsTest {
+ public:
+ void run() {
+ int n = sizeof( BSONObj ) + BSON( "_id" << 4 ).objsize();
+
+ s_.reset();
+ ASSERT_EQUALS( 0, s_.roughSize() );
+ ASSERT( !s_.get( "a", BSON( "_id" << 4 ) ) );
+ ASSERT( !s_.get( "b", BSON( "_id" << 4 ) ) );
+ s_.set( "a", BSON( "_id" << 4 ), true );
+ ASSERT_EQUALS( n, s_.roughSize() );
+ ASSERT( s_.get( "a", BSON( "_id" << 4 ) ) );
+ ASSERT( !s_.get( "b", BSON( "_id" << 4 ) ) );
+ s_.set( "a", BSON( "_id" << 4 ), false );
+ ASSERT_EQUALS( 0, s_.roughSize() );
+ ASSERT( !s_.get( "a", BSON( "_id" << 4 ) ) );
+
+ s_.set( "a", BSON( "_id" << 4 ), true );
+ s_.set( "b", BSON( "_id" << 4 ), true );
+ s_.set( "b", BSON( "_id" << 100 ), true );
+ s_.set( "b", BSON( "_id" << 101 ), true );
+ ASSERT_EQUALS( n * 4, s_.roughSize() );
+ }
+ private:
+ MemIds s_;
+ };
+
+ class IdTrackerTest {
+ public:
+ void run() {
+ setClient( "unittests.repltests.IdTrackerTest" );
+
+ ASSERT( s_.inMem() );
+ s_.reset( 4 * sizeof( BSONObj ) - 1 );
+ s_.haveId( "a", BSON( "_id" << 0 ), true );
+ s_.haveId( "a", BSON( "_id" << 1 ), true );
+ s_.haveId( "b", BSON( "_id" << 0 ), true );
+ s_.haveModId( "b", BSON( "_id" << 0 ), true );
+ ASSERT( s_.inMem() );
+ check();
+ s_.mayUpgradeStorage();
+ ASSERT( !s_.inMem() );
+ check();
+
+ s_.haveId( "a", BSON( "_id" << 1 ), false );
+ ASSERT( !s_.haveId( "a", BSON( "_id" << 1 ) ) );
+ s_.haveId( "a", BSON( "_id" << 1 ), true );
+ check();
+ ASSERT( !s_.inMem() );
+
+ s_.reset();
+ ASSERT( s_.inMem() );
+ }
+ private:
+ void check() {
+ ASSERT( s_.haveId( "a", BSON( "_id" << 0 ) ) );
+ ASSERT( s_.haveId( "a", BSON( "_id" << 1 ) ) );
+ ASSERT( s_.haveId( "b", BSON( "_id" << 0 ) ) );
+ ASSERT( s_.haveModId( "b", BSON( "_id" << 0 ) ) );
+ }
+ dblock lk_;
+ IdTracker s_;
+ };
+
+ class All : public Suite {
+ public:
+ All() : Suite( "repl" ){
+ }
+
+ void setupTests(){
+ add< LogBasic >();
+ add< Idempotence::InsertTimestamp >();
+ add< Idempotence::InsertAutoId >();
+ add< Idempotence::InsertWithId >();
+ add< Idempotence::InsertTwo >();
+ add< Idempotence::InsertTwoIdentical >();
+ add< Idempotence::UpdateTimestamp >();
+ add< Idempotence::UpdateSameField >();
+ add< Idempotence::UpdateSameFieldWithId >();
+ add< Idempotence::UpdateSameFieldExplicitId >();
+ add< Idempotence::UpdateId >();
+ add< Idempotence::UpdateDifferentFieldExplicitId >();
+ add< Idempotence::UpsertUpdateNoMods >();
+ add< Idempotence::UpsertInsertNoMods >();
+ add< Idempotence::UpdateSet >();
+ add< Idempotence::UpdateInc >();
+ add< Idempotence::UpsertInsertIdMod >();
+ add< Idempotence::UpsertInsertSet >();
+ add< Idempotence::UpsertInsertInc >();
+ add< Idempotence::MultiInc >();
+ // Don't worry about this until someone wants this functionality.
+// add< Idempotence::UpdateWithoutPreexistingId >();
+ add< Idempotence::Remove >();
+ add< Idempotence::RemoveOne >();
+ add< Idempotence::FailingUpdate >();
+ add< Idempotence::SetNumToStr >();
+ add< Idempotence::Push >();
+ add< Idempotence::PushUpsert >();
+ add< Idempotence::MultiPush >();
+ add< Idempotence::EmptyPush >();
+ add< Idempotence::PushAll >();
+ add< Idempotence::PushAllUpsert >();
+ add< Idempotence::EmptyPushAll >();
+ add< Idempotence::Pull >();
+ add< Idempotence::PullNothing >();
+ add< Idempotence::PullAll >();
+ add< Idempotence::Pop >();
+ add< Idempotence::PopReverse >();
+ add< Idempotence::BitOp >();
+ add< DeleteOpIsIdBased >();
+ add< DbIdsTest >();
+ add< MemIdsTest >();
+ add< IdTrackerTest >();
+ }
+ } myall;
+
+} // namespace ReplTests
+
diff --git a/dbtests/sharding.cpp b/dbtests/sharding.cpp
new file mode 100644
index 0000000..c7c072a
--- /dev/null
+++ b/dbtests/sharding.cpp
@@ -0,0 +1,56 @@
+// sharding.cpp : some unit tests for sharding internals
+
+/**
+ * Copyright (C) 2009 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+
+#include "dbtests.h"
+
+#include "../client/parallel.h"
+
+namespace ShardingTests {
+
+ namespace serverandquerytests {
+ class test1 {
+ public:
+ void run(){
+ ServerAndQuery a( "foo:1" , BSON( "a" << GT << 0 << LTE << 100 ) );
+ ServerAndQuery b( "foo:1" , BSON( "a" << GT << 200 << LTE << 1000 ) );
+
+ ASSERT( a < b );
+ ASSERT( ! ( b < a ) );
+
+ set<ServerAndQuery> s;
+ s.insert( a );
+ s.insert( b );
+
+ ASSERT_EQUALS( (unsigned int)2 , s.size() );
+ }
+ };
+ }
+
+ class All : public Suite {
+ public:
+ All() : Suite( "sharding" ){
+ }
+
+ void setupTests(){
+ add< serverandquerytests::test1 >();
+ }
+ } myall;
+
+}
diff --git a/dbtests/socktests.cpp b/dbtests/socktests.cpp
new file mode 100644
index 0000000..c263f2e
--- /dev/null
+++ b/dbtests/socktests.cpp
@@ -0,0 +1,44 @@
+// socktests.cpp : sock.{h,cpp} unit tests.
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../util/sock.h"
+
+#include "dbtests.h"
+
+namespace SockTests {
+
+ class HostByName {
+ public:
+ void run() {
+ ASSERT_EQUALS( "127.0.0.1", hostbyname( "localhost" ) );
+ ASSERT_EQUALS( "127.0.0.1", hostbyname( "127.0.0.1" ) );
+ }
+ };
+
+ class All : public Suite {
+ public:
+ All() : Suite( "sock" ){}
+ void setupTests(){
+ add< HostByName >();
+ }
+ } myall;
+
+} // namespace SockTests
+
diff --git a/dbtests/test.vcproj b/dbtests/test.vcproj
new file mode 100644
index 0000000..4582ef2
--- /dev/null
+++ b/dbtests/test.vcproj
@@ -0,0 +1,1931 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="test"
+ ProjectGUID="{215B2D68-0A70-4D10-8E75-B33010C62A91}"
+ RootNamespace="dbtests"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ UseOfATL="0"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\js\src;&quot;..\pcre-7.4&quot;;&quot;c:\Program Files\boost\boost_1_35_0&quot;"
+ PreprocessorDefinitions="OLDJS;STATIC_JS_API;XP_WIN;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;PCRE_STATIC"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="2"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4355;4800"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib Psapi.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="&quot;c:\Program Files\boost\boost_1_35_0\lib&quot;"
+ IgnoreAllDefaultLibraries="false"
+ IgnoreDefaultLibraryNames=""
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\..\js\src;&quot;..\pcre-7.4&quot;;&quot;c:\Program Files\boost\boost_1_35_0&quot;"
+ PreprocessorDefinitions="OLDJS;STATIC_JS_API;XP_WIN;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;PCRE_STATIC"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="stdafx.h"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4355;4800"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="&quot;c:\program files\boost\boost_1_35_0\lib&quot;"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="release_nojni|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="&quot;..\pcre-7.4&quot;;&quot;c:\Program Files\boost\boost_1_35_0&quot;;&quot;c:\program files\java\jdk\include&quot;;&quot;c:\program files\java\jdk\include\win32&quot;"
+ PreprocessorDefinitions="NOJNI;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;PCRE_STATIC"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="stdafx.h"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4355;4800"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="&quot;c:\program files\boost\boost_1_35_0\lib&quot;"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Recstore|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ UseOfATL="0"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\js\src;&quot;..\pcre-7.4&quot;;&quot;c:\Program Files\boost\boost_1_35_0&quot;"
+ PreprocessorDefinitions="_RECSTORE;OLDJS;STATIC_JS_API;XP_WIN;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;PCRE_STATIC"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="2"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4355;4800"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="&quot;c:\Program Files\boost\boost_1_35_0\lib&quot;"
+ IgnoreAllDefaultLibraries="false"
+ IgnoreDefaultLibraryNames=""
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="misc and third party"
+ >
+ <File
+ RelativePath="..\..\boostw\boost_1_34_1\boost\config\auto_link.hpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\db.rc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\js\js\Debug\js.lib"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\..\js\js\Release\js.lib"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcrecpp.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcrecpp.h"
+ >
+ </File>
+ <File
+ RelativePath="..\SConstruct"
+ >
+ </File>
+ <File
+ RelativePath="..\targetver.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\boostw\boost_1_34_1\boost\version.hpp"
+ >
+ </File>
+ <Filter
+ Name="pcre"
+ >
+ <File
+ RelativePath="..\pcre-7.4\config.h"
+ >
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre.h"
+ >
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_chartables.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_compile.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_config.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_dfa_exec.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_exec.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_fullinfo.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_get.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_globals.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_info.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_maketables.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_newline.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_ord2utf8.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_refcount.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_scanner.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_stringpiece.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_study.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_tables.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_try_flipped.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_ucp_searchfuncs.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_valid_utf8.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_version.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcre_xclass.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\pcre-7.4\pcreposix.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="storage related"
+ >
+ <File
+ RelativePath="..\db\rec.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\reccache.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\reccache.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\reci.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\recstore.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\storage.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\storage.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="client"
+ >
+ <File
+ RelativePath="..\client\connpool.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\client\connpool.h"
+ >
+ </File>
+ <File
+ RelativePath="..\client\dbclient.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\client\dbclient.h"
+ >
+ </File>
+ <File
+ RelativePath="..\client\model.h"
+ >
+ </File>
+ <File
+ RelativePath="..\client\quorum.cpp"
+ >
+ </File>
+ <Filter
+ Name="btree related"
+ >
+ <File
+ RelativePath="..\db\btree.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\btree.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\btreecursor.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="db"
+ >
+ <File
+ RelativePath="..\db\clientcursor.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\cmdline.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\commands.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\concurrency.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\curop.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\cursor.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\database.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\db.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\dbhelpers.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\dbinfo.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\dbmessage.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\extsort.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\introspect.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\jsobj.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\json.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\matcher.h"
+ >
+ </File>
+ <File
+ RelativePath="..\grid\message.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\minilex.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\namespace.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\pdfile.h"
+ >
+ </File>
+ <File
+ RelativePath="..\grid\protocol.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\query.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\queryoptimizer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\queryutil.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\repl.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\replset.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\resource.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\scanandorder.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\security.h"
+ >
+ </File>
+ <File
+ RelativePath="..\stdafx.h"
+ >
+ </File>
+ <Filter
+ Name="cpp"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\db\client.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\clientcursor.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\cloner.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\commands.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\cursor.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\database.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\dbcommands.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\dbeval.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\dbhelpers.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\dbstats.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\dbwebserver.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\extsort.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\index.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\instance.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\introspect.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\jsobj.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\json.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\lasterror.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\matcher.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\mmap_win.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\namespace.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\nonce.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\pdfile.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\query.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\queryoptimizer.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\repl.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\security.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\security_commands.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\stdafx.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\db\tests.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\top.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\db\update.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="util"
+ >
+ <File
+ RelativePath="..\util\builder.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\file.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\goodies.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\hashtab.h"
+ >
+ </File>
+ <File
+ RelativePath="..\db\lasterror.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\log.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\lruishmap.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\md5.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\md5.hpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\miniwebserver.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\mmap.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\sock.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\unittest.h"
+ >
+ </File>
+ <Filter
+ Name="cpp"
+ >
+ <File
+ RelativePath="..\util\assert_util.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\background.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\base64.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\httpclient.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\md5.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ PrecompiledHeaderThrough=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="release_nojni|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\util\md5main.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\message.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\miniwebserver.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\mmap.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\ntservice.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\processinfo_win32.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\sock.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\thread_pool.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\util\util.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="shard"
+ >
+ <File
+ RelativePath="..\s\d_logic.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="scripting"
+ >
+ <File
+ RelativePath="..\scripting\engine.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripting\engine_spidermonkey.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\shell\mongo_vstudio.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Recstore|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="dbtests"
+ >
+ <File
+ RelativePath=".\basictests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\btreetests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\clienttests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\cursortests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\dbtests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\framework.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\jsobjtests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\jsontests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\jstests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\matchertests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\namespacetests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\pairingtests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\pdfiletests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\queryoptimizertests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\querytests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\repltests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\socktests.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\threadedtests.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ DisableSpecificWarnings="4180"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\updatetests.cpp"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/dbtests/test.vcxproj b/dbtests/test.vcxproj
new file mode 100644
index 0000000..efbf2d0
--- /dev/null
+++ b/dbtests/test.vcxproj
@@ -0,0 +1,586 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug Recstore|Win32">
+ <Configuration>Debug Recstore</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="release_nojni|Win32">
+ <Configuration>release_nojni</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{215B2D68-0A70-4D10-8E75-B33010C62A91}</ProjectGuid>
+ <RootNamespace>dbtests</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <UseOfAtl>false</UseOfAtl>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <UseOfAtl>false</UseOfAtl>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.21006.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">true</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\js\src;..\pcre-7.4;c:\Program Files\boost\boost_1_41_0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>OLDJS;STATIC_JS_API;XP_WIN;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;PCRE_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4355;4800;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>c:\Program Files\boost\boost_1_41_0\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..\..\js\src;..\pcre-7.4;c:\Program Files\boost\boost_1_41_0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>OLDJS;STATIC_JS_API;XP_WIN;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;PCRE_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4355;4800;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>c:\program files\boost\boost_1_41_0\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..\pcre-7.4;c:\Program Files\boost\boost_1_41_0;c:\program files\java\jdk\include;c:\program files\java\jdk\include\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NOJNI;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;PCRE_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4355;4800;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>c:\program files\boost\boost_1_41_0\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\js\src;..\pcre-7.4;c:\Program Files\boost\boost_1_41_0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_RECSTORE;OLDJS;STATIC_JS_API;XP_WIN;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;PCRE_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4355;4800;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>c:\Program Files\boost\boost_1_41_0\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="..\pcre-7.4\pcrecpp.h" />
+ <ClInclude Include="..\targetver.h" />
+ <ClInclude Include="..\pcre-7.4\config.h" />
+ <ClInclude Include="..\pcre-7.4\pcre.h" />
+ <ClInclude Include="..\db\rec.h" />
+ <ClInclude Include="..\db\reccache.h" />
+ <ClInclude Include="..\db\reci.h" />
+ <ClInclude Include="..\db\recstore.h" />
+ <ClInclude Include="..\db\storage.h" />
+ <ClInclude Include="..\client\connpool.h" />
+ <ClInclude Include="..\client\dbclient.h" />
+ <ClInclude Include="..\client\model.h" />
+ <ClInclude Include="..\db\btree.h" />
+ <ClInclude Include="..\db\clientcursor.h" />
+ <ClInclude Include="..\db\cmdline.h" />
+ <ClInclude Include="..\db\commands.h" />
+ <ClInclude Include="..\db\concurrency.h" />
+ <ClInclude Include="..\db\curop.h" />
+ <ClInclude Include="..\db\cursor.h" />
+ <ClInclude Include="..\db\database.h" />
+ <ClInclude Include="..\db\db.h" />
+ <ClInclude Include="..\db\dbhelpers.h" />
+ <ClInclude Include="..\db\dbinfo.h" />
+ <ClInclude Include="..\db\dbmessage.h" />
+ <ClInclude Include="..\db\introspect.h" />
+ <ClInclude Include="..\db\jsobj.h" />
+ <ClInclude Include="..\db\json.h" />
+ <ClInclude Include="..\db\matcher.h" />
+ <ClInclude Include="..\grid\message.h" />
+ <ClInclude Include="..\db\minilex.h" />
+ <ClInclude Include="..\db\namespace.h" />
+ <ClInclude Include="..\db\pdfile.h" />
+ <ClInclude Include="..\grid\protocol.h" />
+ <ClInclude Include="..\db\query.h" />
+ <ClInclude Include="..\db\queryoptimizer.h" />
+ <ClInclude Include="..\db\repl.h" />
+ <ClInclude Include="..\db\replset.h" />
+ <ClInclude Include="..\db\resource.h" />
+ <ClInclude Include="..\db\scanandorder.h" />
+ <ClInclude Include="..\db\security.h" />
+ <ClInclude Include="..\stdafx.h" />
+ <ClInclude Include="..\util\builder.h" />
+ <ClInclude Include="..\util\file.h" />
+ <ClInclude Include="..\util\goodies.h" />
+ <ClInclude Include="..\util\hashtab.h" />
+ <ClInclude Include="..\db\lasterror.h" />
+ <ClInclude Include="..\util\log.h" />
+ <ClInclude Include="..\util\lruishmap.h" />
+ <ClInclude Include="..\util\md5.h" />
+ <ClInclude Include="..\util\md5.hpp" />
+ <ClInclude Include="..\util\miniwebserver.h" />
+ <ClInclude Include="..\util\mmap.h" />
+ <ClInclude Include="..\util\sock.h" />
+ <ClInclude Include="..\util\unittest.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="..\db\db.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\js\js\Release\js.lib">
+ <FileType>Document</FileType>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ </CustomBuild>
+ <CustomBuild Include="..\..\js\js\Debug\js.lib">
+ <FileType>Document</FileType>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\pcre-7.4\pcrecpp.cc">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_chartables.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_compile.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_config.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_dfa_exec.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_exec.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_fullinfo.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_get.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_globals.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_info.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_maketables.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_newline.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_ord2utf8.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_refcount.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_scanner.cc">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_stringpiece.cc">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_study.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_tables.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_try_flipped.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_ucp_searchfuncs.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_valid_utf8.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_version.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcre_xclass.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\pcre-7.4\pcreposix.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\db\reccache.cpp" />
+ <ClCompile Include="..\db\storage.cpp" />
+ <ClCompile Include="..\client\connpool.cpp" />
+ <ClCompile Include="..\client\dbclient.cpp" />
+ <ClCompile Include="..\db\btree.cpp" />
+ <ClCompile Include="..\db\btreecursor.cpp" />
+ <ClCompile Include="..\db\queryutil.cpp" />
+ <ClCompile Include="..\db\client.cpp" />
+ <ClCompile Include="..\db\clientcursor.cpp" />
+ <ClCompile Include="..\db\cloner.cpp" />
+ <ClCompile Include="..\db\commands.cpp" />
+ <ClCompile Include="..\db\cursor.cpp" />
+ <ClCompile Include="..\db\dbcommands.cpp" />
+ <ClCompile Include="..\db\dbeval.cpp" />
+ <ClCompile Include="..\db\dbhelpers.cpp" />
+ <ClCompile Include="..\db\dbinfo.cpp" />
+ <ClCompile Include="..\db\dbwebserver.cpp" />
+ <ClCompile Include="..\db\extsort.cpp" />
+ <ClCompile Include="..\db\instance.cpp" />
+ <ClCompile Include="..\db\introspect.cpp" />
+ <ClCompile Include="..\db\jsobj.cpp" />
+ <ClCompile Include="..\db\json.cpp" />
+ <ClCompile Include="..\db\lasterror.cpp" />
+ <ClCompile Include="..\db\matcher.cpp" />
+ <ClCompile Include="..\util\mmap_win.cpp" />
+ <ClCompile Include="..\db\namespace.cpp" />
+ <ClCompile Include="..\db\nonce.cpp" />
+ <ClCompile Include="..\db\pdfile.cpp" />
+ <ClCompile Include="..\db\query.cpp" />
+ <ClCompile Include="..\db\queryoptimizer.cpp" />
+ <ClCompile Include="..\db\repl.cpp" />
+ <ClCompile Include="..\db\security.cpp" />
+ <ClCompile Include="..\db\security_commands.cpp" />
+ <ClCompile Include="..\stdafx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\db\tests.cpp" />
+ <ClCompile Include="..\db\update.cpp" />
+ <ClCompile Include="..\util\assert_util.cpp" />
+ <ClCompile Include="..\util\background.cpp" />
+ <ClCompile Include="..\util\base64.cpp" />
+ <ClCompile Include="..\util\httpclient.cpp" />
+ <ClCompile Include="..\util\md5.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeaderFile>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nojni|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\util\md5main.cpp" />
+ <ClCompile Include="..\util\message.cpp" />
+ <ClCompile Include="..\util\miniwebserver.cpp" />
+ <ClCompile Include="..\util\mmap.cpp" />
+ <ClCompile Include="..\util\ntservice.cpp" />
+ <ClCompile Include="..\util\processinfo_none.cpp" />
+ <ClCompile Include="..\util\sock.cpp" />
+ <ClCompile Include="..\util\util.cpp" />
+ <ClCompile Include="..\s\d_logic.cpp" />
+ <ClCompile Include="..\scripting\engine.cpp" />
+ <ClCompile Include="..\scripting\engine_spidermonkey.cpp" />
+ <ClCompile Include="..\shell\mongo_vstudio.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Recstore|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="basictests.cpp" />
+ <ClCompile Include="btreetests.cpp" />
+ <ClCompile Include="clienttests.cpp" />
+ <ClCompile Include="cursortests.cpp" />
+ <ClCompile Include="dbtests.cpp" />
+ <ClCompile Include="framework.cpp" />
+ <ClCompile Include="jsobjtests.cpp" />
+ <ClCompile Include="jsontests.cpp" />
+ <ClCompile Include="jstests.cpp" />
+ <ClCompile Include="matchertests.cpp" />
+ <ClCompile Include="namespacetests.cpp" />
+ <ClCompile Include="pairingtests.cpp" />
+ <ClCompile Include="pdfiletests.cpp" />
+ <ClCompile Include="queryoptimizertests.cpp" />
+ <ClCompile Include="querytests.cpp" />
+ <ClCompile Include="repltests.cpp" />
+ <ClCompile Include="socktests.cpp" />
+ <ClCompile Include="updatetests.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="..\SConstruct" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/dbtests/threadedtests.cpp b/dbtests/threadedtests.cpp
new file mode 100644
index 0000000..f3ebe39
--- /dev/null
+++ b/dbtests/threadedtests.cpp
@@ -0,0 +1,135 @@
+// threadedtests.cpp - Tests for threaded code
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../util/mvar.h"
+#include "../util/thread_pool.h"
+#include <boost/thread.hpp>
+#include <boost/bind.hpp>
+
+#include "dbtests.h"
+
+namespace ThreadedTests {
+
+ template <int nthreads_param=10>
+ class ThreadedTest{
+ public:
+ virtual void setup() {} //optional
+ virtual void subthread() = 0;
+ virtual void validate() = 0;
+
+ static const int nthreads = nthreads_param;
+
+ void run(){
+ setup();
+
+ launch_subthreads(nthreads);
+
+ validate();
+ }
+
+ virtual ~ThreadedTest() {}; // not necessary, but makes compilers happy
+
+ private:
+ void launch_subthreads(int remaining){
+ if (!remaining) return;
+
+ boost::thread athread(boost::bind(&ThreadedTest::subthread, this));
+
+ launch_subthreads(remaining - 1);
+
+ athread.join();
+ }
+ };
+
+ // Tested with up to 30k threads
+ class IsWrappingIntAtomic : public ThreadedTest<> {
+ static const int iterations = 1000000;
+ WrappingInt target;
+
+ void subthread(){
+ for(int i=0; i < iterations; i++){
+ //target.x++; // verified to fail with this version
+ target.atomicIncrement();
+ }
+ }
+ void validate(){
+ ASSERT_EQUALS(target.x , unsigned(nthreads * iterations));
+ }
+ };
+
+ class MVarTest : public ThreadedTest<> {
+ static const int iterations = 10000;
+ MVar<int> target;
+
+ public:
+ MVarTest() : target(0) {}
+ void subthread(){
+ for(int i=0; i < iterations; i++){
+ int val = target.take();
+#if BOOST_VERSION >= 103500
+ //increase chances of catching failure
+ boost::this_thread::yield();
+#endif
+ target.put(val+1);
+ }
+ }
+ void validate(){
+ ASSERT_EQUALS(target.take() , nthreads * iterations);
+ }
+ };
+
+ class ThreadPoolTest{
+ static const int iterations = 10000;
+ static const int nThreads = 8;
+
+ WrappingInt counter;
+ void increment(int n){
+ for (int i=0; i<n; i++){
+ counter.atomicIncrement();
+ }
+ }
+
+ public:
+ void run(){
+ ThreadPool tp(nThreads);
+
+ for (int i=0; i < iterations; i++){
+ tp.schedule(&WrappingInt::atomicIncrement, &counter);
+ tp.schedule(&ThreadPoolTest::increment, this, 2);
+ }
+
+ tp.join();
+
+ ASSERT(counter == (unsigned)(iterations * 3));
+ }
+ };
+
+ class All : public Suite {
+ public:
+ All() : Suite( "threading" ){
+ }
+
+ void setupTests(){
+ add< IsWrappingIntAtomic >();
+ add< MVarTest >();
+ add< ThreadPoolTest >();
+ }
+ } myall;
+}
diff --git a/dbtests/updatetests.cpp b/dbtests/updatetests.cpp
new file mode 100644
index 0000000..a9c4b1e
--- /dev/null
+++ b/dbtests/updatetests.cpp
@@ -0,0 +1,770 @@
+// updatetests.cpp : unit tests relating to update requests
+//
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+#include "../db/query.h"
+
+#include "../db/db.h"
+#include "../db/instance.h"
+#include "../db/json.h"
+#include "../db/lasterror.h"
+#include "../db/update.h"
+
+#include "dbtests.h"
+
+namespace UpdateTests {
+
+ class ClientBase {
+ public:
+ // NOTE: Not bothering to backup the old error record.
+ ClientBase() {
+ mongo::lastError.reset( new LastError() );
+ }
+ ~ClientBase() {
+ mongo::lastError.release();
+ }
+ protected:
+ static void insert( const char *ns, BSONObj o ) {
+ client_.insert( ns, o );
+ }
+ static void update( const char *ns, BSONObj q, BSONObj o, bool upsert = 0 ) {
+ client_.update( ns, Query( q ), o, upsert );
+ }
+ static bool error() {
+ return !client_.getPrevError().getField( "err" ).isNull();
+ }
+ DBDirectClient &client() const { return client_; }
+ private:
+ static DBDirectClient client_;
+ };
+ DBDirectClient ClientBase::client_;
+
+ class Fail : public ClientBase {
+ public:
+ virtual ~Fail() {}
+ void run() {
+ prep();
+ ASSERT( !error() );
+ doIt();
+ ASSERT( error() );
+ }
+ protected:
+ const char *ns() { return "unittests.UpdateTests_Fail"; }
+ virtual void prep() {
+ insert( ns(), fromjson( "{a:1}" ) );
+ }
+ virtual void doIt() = 0;
+ };
+
+ class ModId : public Fail {
+ void doIt() {
+ update( ns(), BSONObj(), fromjson( "{$set:{'_id':4}}" ) );
+ }
+ };
+
+ class ModNonmodMix : public Fail {
+ void doIt() {
+ update( ns(), BSONObj(), fromjson( "{$set:{a:4},z:3}" ) );
+ }
+ };
+
+ class InvalidMod : public Fail {
+ void doIt() {
+ update( ns(), BSONObj(), fromjson( "{$awk:{a:4}}" ) );
+ }
+ };
+
+ class ModNotFirst : public Fail {
+ void doIt() {
+ update( ns(), BSONObj(), fromjson( "{z:3,$set:{a:4}}" ) );
+ }
+ };
+
+ class ModDuplicateFieldSpec : public Fail {
+ void doIt() {
+ update( ns(), BSONObj(), fromjson( "{$set:{a:4},$inc:{a:1}}" ) );
+ }
+ };
+
+ class IncNonNumber : public Fail {
+ void doIt() {
+ update( ns(), BSONObj(), fromjson( "{$inc:{a:'d'}}" ) );
+ }
+ };
+
+ class PushAllNonArray : public Fail {
+ void doIt() {
+ insert( ns(), fromjson( "{a:[1]}" ) );
+ update( ns(), BSONObj(), fromjson( "{$pushAll:{a:'d'}}" ) );
+ }
+ };
+
+ class PullAllNonArray : public Fail {
+ void doIt() {
+ insert( ns(), fromjson( "{a:[1]}" ) );
+ update( ns(), BSONObj(), fromjson( "{$pullAll:{a:'d'}}" ) );
+ }
+ };
+
+ class IncTargetNonNumber : public Fail {
+ void doIt() {
+ insert( ns(), BSON( "a" << "a" ) );
+ update( ns(), BSON( "a" << "a" ), fromjson( "{$inc:{a:1}}" ) );
+ }
+ };
+
+ class SetBase : public ClientBase {
+ public:
+ ~SetBase() {
+ client().dropCollection( ns() );
+ }
+ protected:
+ const char *ns() { return "unittests.updatetests.SetBase"; }
+ };
+
+ class SetNum : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), BSON( "a" << 1 ) );
+ client().update( ns(), BSON( "a" << 1 ), BSON( "$set" << BSON( "a" << 4 ) ) );
+ ASSERT( !client().findOne( ns(), BSON( "a" << 4 ) ).isEmpty() );
+ }
+ };
+
+ class SetString : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), BSON( "a" << "b" ) );
+ client().update( ns(), BSON( "a" << "b" ), BSON( "$set" << BSON( "a" << "c" ) ) );
+ ASSERT( !client().findOne( ns(), BSON( "a" << "c" ) ).isEmpty() );
+ }
+ };
+
+ class SetStringDifferentLength : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), BSON( "a" << "b" ) );
+ client().update( ns(), BSON( "a" << "b" ), BSON( "$set" << BSON( "a" << "cd" ) ) );
+ ASSERT( !client().findOne( ns(), BSON( "a" << "cd" ) ).isEmpty() );
+ }
+ };
+
+ class SetStringToNum : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), BSON( "a" << "b" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a" << 5 ) ) );
+ ASSERT( !client().findOne( ns(), BSON( "a" << 5 ) ).isEmpty() );
+ }
+ };
+
+ class SetStringToNumInPlace : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), BSON( "a" << "bcd" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a" << 5.0 ) ) );
+ ASSERT( !client().findOne( ns(), BSON( "a" << 5.0 ) ).isEmpty() );
+ }
+ };
+
+ class ModDotted : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{a:{b:4}}" ) );
+ client().update( ns(), Query(), BSON( "$inc" << BSON( "a.b" << 10 ) ) );
+ ASSERT( !client().findOne( ns(), BSON( "a.b" << 14 ) ).isEmpty() );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a.b" << 55 ) ) );
+ ASSERT( !client().findOne( ns(), BSON( "a.b" << 55 ) ).isEmpty() );
+ }
+ };
+
+ class SetInPlaceDotted : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{a:{b:'cdef'}}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a.b" << "llll" ) ) );
+ ASSERT( !client().findOne( ns(), BSON( "a.b" << "llll" ) ).isEmpty() );
+ }
+ };
+
+ class SetRecreateDotted : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:{b:'cdef'}}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a.b" << "lllll" ) ) );
+ ASSERT( client().findOne( ns(), BSON( "a.b" << "lllll" ) ).woCompare( fromjson( "{'_id':0,a:{b:'lllll'}}" ) ) == 0 );
+ }
+ };
+
+ class SetMissingDotted : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0}" ) );
+ client().update( ns(), BSONObj(), BSON( "$set" << BSON( "a.b" << "lllll" ) ) );
+ ASSERT( client().findOne( ns(), BSON( "a.b" << "lllll" ) ).woCompare( fromjson( "{'_id':0,a:{b:'lllll'}}" ) ) == 0 );
+ }
+ };
+
+ class SetAdjacentDotted : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:{c:4}}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a.b" << "lllll" ) ) );
+ ASSERT_EQUALS( client().findOne( ns(), BSON( "a.b" << "lllll" ) ) , fromjson( "{'_id':0,a:{b:'lllll',c:4}}" ) );
+ }
+ };
+
+ class IncMissing : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0}" ) );
+ client().update( ns(), Query(), BSON( "$inc" << BSON( "f" << 3.0 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,f:3}" ) ) == 0 );
+ }
+ };
+
+ class MultiInc : public SetBase {
+ public:
+
+ string s(){
+ stringstream ss;
+ auto_ptr<DBClientCursor> cc = client().query( ns() , Query().sort( BSON( "_id" << 1 ) ) );
+ bool first = true;
+ while ( cc->more() ){
+ if ( first ) first = false;
+ else ss << ",";
+
+ BSONObj o = cc->next();
+ ss << o["x"].numberInt();
+ }
+ return ss.str();
+ }
+
+ void run(){
+ client().insert( ns(), BSON( "_id" << 1 << "x" << 1 ) );
+ client().insert( ns(), BSON( "_id" << 2 << "x" << 5 ) );
+
+ ASSERT_EQUALS( "1,5" , s() );
+
+ client().update( ns() , BSON( "_id" << 1 ) , BSON( "$inc" << BSON( "x" << 1 ) ) );
+ ASSERT_EQUALS( "2,5" , s() );
+
+ client().update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) );
+ ASSERT_EQUALS( "3,5" , s() );
+
+ client().update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) , false , true );
+ ASSERT_EQUALS( "4,6" , s() );
+
+ }
+ };
+
+ class UnorderedNewSet : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "f.g.h" << 3.0 << "f.g.a" << 2.0 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,f:{g:{a:2,h:3}}}" ) ) == 0 );
+ }
+ };
+
+ class UnorderedNewSetAdjacent : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0}" ) );
+ client().update( ns(), BSONObj(), BSON( "$set" << BSON( "f.g.h.b" << 3.0 << "f.g.a.b" << 2.0 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,f:{g:{a:{b:2},h:{b:3}}}}" ) ) == 0 );
+ }
+ };
+
+ class ArrayEmbeddedSet : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,z:[4,'b']}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "z.0" << "a" ) ) );
+ ASSERT_EQUALS( client().findOne( ns(), Query() ) , fromjson( "{'_id':0,z:['a','b']}" ) );
+ }
+ };
+
+ class AttemptEmbedInExistingNum : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:1}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a.b" << 1 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:1}" ) ) == 0 );
+ }
+ };
+
+ class AttemptEmbedConflictsWithOtherSet : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a" << 2 << "a.b" << 1 ) ) );
+ ASSERT_EQUALS( client().findOne( ns(), Query() ) , fromjson( "{'_id':0}" ) );
+ }
+ };
+
+ class ModMasksEmbeddedConflict : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:{b:2}}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a" << 2 << "a.b" << 1 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:2}}" ) ) == 0 );
+ }
+ };
+
+ class ModOverwritesExistingObject : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:{b:2}}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a" << BSON( "c" << 2 ) ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{c:2}}" ) ) == 0 );
+ }
+ };
+
+ class InvalidEmbeddedSet : public Fail {
+ public:
+ virtual void doIt() {
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a." << 1 ) ) );
+ }
+ };
+
+ class UpsertMissingEmbedded : public SetBase {
+ public:
+ void run() {
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a.b" << 1 ) ), true );
+ ASSERT( !client().findOne( ns(), QUERY( "a.b" << 1 ) ).isEmpty() );
+ }
+ };
+
+ class Push : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:[1]}" ) );
+ client().update( ns(), Query(), BSON( "$push" << BSON( "a" << 5 ) ) );
+ ASSERT_EQUALS( client().findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,5]}" ) );
+ }
+ };
+
+ class PushInvalidEltType : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:1}" ) );
+ client().update( ns(), Query(), BSON( "$push" << BSON( "a" << 5 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:1}" ) ) == 0 );
+ }
+ };
+
+ class PushConflictsWithOtherMod : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:[1]}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a" << 1 ) <<"$push" << BSON( "a" << 5 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:[1]}" ) ) == 0 );
+ }
+ };
+
+ class PushFromNothing : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0}" ) );
+ client().update( ns(), Query(), BSON( "$push" << BSON( "a" << 5 ) ) );
+ ASSERT_EQUALS( client().findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[5]}" ) );
+ }
+ };
+
+ class PushFromEmpty : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:[]}" ) );
+ client().update( ns(), Query(), BSON( "$push" << BSON( "a" << 5 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:[5]}" ) ) == 0 );
+ }
+ };
+
+ class PushInsideNothing : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0}" ) );
+ client().update( ns(), Query(), BSON( "$push" << BSON( "a.b" << 5 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:[5]}}" ) ) == 0 );
+ }
+ };
+
+ class CantPushInsideOtherMod : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a" << BSONObj() ) << "$push" << BSON( "a.b" << 5 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0}" ) ) == 0 );
+ }
+ };
+
+ class CantPushTwice : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:[]}" ) );
+ client().update( ns(), Query(), BSON( "$push" << BSON( "a" << 4 ) << "$push" << BSON( "a" << 5 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:[]}" ) ) == 0 );
+ }
+ };
+
+ class SetEncapsulationConflictsWithExistingType : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:{b:4}}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a.b.c" << 4.0 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:4}}" ) ) == 0 );
+ }
+ };
+
+ class CantPushToParent : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:{b:4}}" ) );
+ client().update( ns(), Query(), BSON( "$push" << BSON( "a" << 4.0 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:4}}" ) ) == 0 );
+ }
+ };
+
+ class CantIncParent : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:{b:4}}" ) );
+ client().update( ns(), Query(), BSON( "$inc" << BSON( "a" << 4.0 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:4}}" ) ) == 0 );
+ }
+ };
+
+ class DontDropEmpty : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:{b:{}}}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a.c" << 4.0 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:{},c:4}}" ) ) == 0 );
+ }
+ };
+
+ class InsertInEmpty : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), fromjson( "{'_id':0,a:{b:{}}}" ) );
+ client().update( ns(), Query(), BSON( "$set" << BSON( "a.b.f" << 4.0 ) ) );
+ ASSERT( client().findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:{f:4}}}" ) ) == 0 );
+ }
+ };
+
+ class IndexParentOfMod : public SetBase {
+ public:
+ void run() {
+ client().ensureIndex( ns(), BSON( "a" << 1 ) );
+ client().insert( ns(), fromjson( "{'_id':0}" ) );
+ client().update( ns(), Query(), fromjson( "{$set:{'a.b':4}}" ) );
+ ASSERT_EQUALS( fromjson( "{'_id':0,a:{b:4}}" ) , client().findOne( ns(), Query() ) );
+ ASSERT_EQUALS( fromjson( "{'_id':0,a:{b:4}}" ) , client().findOne( ns(), fromjson( "{'a.b':4}" ) ) ); // make sure the index works
+ }
+ };
+
+ class IndexModSet : public SetBase {
+ public:
+ void run() {
+ client().ensureIndex( ns(), BSON( "a.b" << 1 ) );
+ client().insert( ns(), fromjson( "{'_id':0,a:{b:3}}" ) );
+ client().update( ns(), Query(), fromjson( "{$set:{'a.b':4}}" ) );
+ ASSERT_EQUALS( fromjson( "{'_id':0,a:{b:4}}" ) , client().findOne( ns(), Query() ) );
+ ASSERT_EQUALS( fromjson( "{'_id':0,a:{b:4}}" ) , client().findOne( ns(), fromjson( "{'a.b':4}" ) ) ); // make sure the index works
+ }
+ };
+
+
+ class PreserveIdWithIndex : public SetBase { // Not using $set, but base class is still useful
+ public:
+ void run() {
+ client().insert( ns(), BSON( "_id" << 55 << "i" << 5 ) );
+ client().update( ns(), BSON( "i" << 5 ), BSON( "i" << 6 ) );
+ ASSERT( !client().findOne( ns(), Query( BSON( "_id" << 55 ) ).hint
+ ( "{\"_id\":ObjectId(\"000000000000000000000000\")}" ) ).isEmpty() );
+ }
+ };
+
+ class CheckNoMods : public SetBase {
+ public:
+ void run() {
+ client().update( ns(), BSONObj(), BSON( "i" << 5 << "$set" << BSON( "q" << 3 ) ), true );
+ ASSERT( error() );
+ }
+ };
+
+ class UpdateMissingToNull : public SetBase {
+ public:
+ void run() {
+ client().insert( ns(), BSON( "a" << 5 ) );
+ client().update( ns(), BSON( "a" << 5 ), fromjson( "{$set:{b:null}}" ) );
+ ASSERT_EQUALS( jstNULL, client().findOne( ns(), QUERY( "a" << 5 ) ).getField( "b" ).type() );
+ }
+ };
+
+ namespace ModSetTests {
+
+ class internal1 {
+ public:
+ void run(){
+ BSONObj b = BSON( "$inc" << BSON( "x" << 1 << "a.b" << 1 ) );
+ ModSet m;
+ m.getMods( b );
+
+ ASSERT( m.haveModForField( "x" ) );
+ ASSERT( m.haveModForField( "a.b" ) );
+ ASSERT( ! m.haveModForField( "y" ) );
+ ASSERT( ! m.haveModForField( "a.c" ) );
+ ASSERT( ! m.haveModForField( "a" ) );
+
+ ASSERT( m.haveConflictingMod( "x" ) );
+ ASSERT( m.haveConflictingMod( "a" ) );
+ ASSERT( m.haveConflictingMod( "a.b" ) );
+ ASSERT( ! m.haveConflictingMod( "a.bc" ) );
+ ASSERT( ! m.haveConflictingMod( "a.c" ) );
+ ASSERT( ! m.haveConflictingMod( "a.a" ) );
+ }
+ };
+
+ class Base {
+ public:
+
+ virtual ~Base(){}
+
+
+ void test( BSONObj morig , BSONObj in , BSONObj wanted ){
+ BSONObj m = morig.copy();
+ ModSet set;
+ set.getMods( m );
+
+ BSONObj out = set.createNewFromMods( in );
+ ASSERT_EQUALS( wanted , out );
+ }
+ };
+
+ class inc1 : public Base {
+ public:
+ void run(){
+ BSONObj m = BSON( "$inc" << BSON( "x" << 1 ) );
+ test( m , BSON( "x" << 5 ) , BSON( "x" << 6 ) );
+ test( m , BSON( "a" << 5 ) , BSON( "a" << 5 << "x" << 1 ) );
+ test( m , BSON( "z" << 5 ) , BSON( "x" << 1 << "z" << 5 ) );
+ }
+ };
+
+ class inc2 : public Base {
+ public:
+ void run(){
+ BSONObj m = BSON( "$inc" << BSON( "a.b" << 1 ) );
+ test( m , BSONObj() , BSON( "a" << BSON( "b" << 1 ) ) );
+ test( m , BSON( "a" << BSON( "b" << 2 ) ) , BSON( "a" << BSON( "b" << 3 ) ) );
+
+ m = BSON( "$inc" << BSON( "a.b" << 1 << "a.c" << 1 ) );
+ test( m , BSONObj() , BSON( "a" << BSON( "b" << 1 << "c" << 1 ) ) );
+
+
+ }
+ };
+
+ class set1 : public Base {
+ public:
+ void run(){
+ test( BSON( "$set" << BSON( "x" << 17 ) ) , BSONObj() , BSON( "x" << 17 ) );
+ test( BSON( "$set" << BSON( "x" << 17 ) ) , BSON( "x" << 5 ) , BSON( "x" << 17 ) );
+
+ test( BSON( "$set" << BSON( "x.a" << 17 ) ) , BSON( "z" << 5 ) , BSON( "x" << BSON( "a" << 17 )<< "z" << 5 ) );
+ }
+ };
+
+ class push1 : public Base {
+ public:
+ void run(){
+ test( BSON( "$push" << BSON( "a" << 5 ) ) , fromjson( "{a:[1]}" ) , fromjson( "{a:[1,5]}" ) );
+ }
+ };
+
+ };
+
+ namespace basic {
+ class Base : public ClientBase {
+ virtual const char * ns() = 0;
+ virtual void dotest() = 0;
+
+ protected:
+
+ void test( const char* initial , const char* mod , const char* after ){
+ test( fromjson( initial ) , fromjson( mod ) , fromjson( after ) );
+ }
+
+
+ void test( const BSONObj& initial , const BSONObj& mod , const BSONObj& after ){
+ client().dropCollection( ns() );
+ client().insert( ns() , initial );
+ client().update( ns() , BSONObj() , mod );
+ ASSERT_EQUALS( after , client().findOne( ns(), BSONObj() ));
+ client().dropCollection( ns() );
+ }
+
+ public:
+
+ Base(){}
+ virtual ~Base(){
+ }
+
+ void run(){
+ client().dropCollection( ns() );
+
+ dotest();
+
+ client().dropCollection( ns() );
+ }
+ };
+
+ class SingleTest : public Base {
+ virtual BSONObj initial() = 0;
+ virtual BSONObj mod() = 0;
+ virtual BSONObj after() = 0;
+
+ void dotest(){
+ test( initial() , mod() , after() );
+ }
+
+ };
+
+ class inc1 : public SingleTest {
+ virtual BSONObj initial(){
+ return BSON( "_id" << 1 << "x" << 1 );
+ }
+ virtual BSONObj mod(){
+ return BSON( "$inc" << BSON( "x" << 2 ) );
+ }
+ virtual BSONObj after(){
+ return BSON( "_id" << 1 << "x" << 3 );
+ }
+ virtual const char * ns(){
+ return "unittests.inc1";
+ }
+
+ };
+
+ class bit1 : public Base {
+ const char * ns(){
+ return "unittests.bit1";
+ }
+ void dotest(){
+ test( BSON( "_id" << 1 << "x" << 3 ) , BSON( "$bit" << BSON( "x" << BSON( "and" << 2 ) ) ) , BSON( "_id" << 1 << "x" << ( 3 & 2 ) ) );
+ test( BSON( "_id" << 1 << "x" << 1 ) , BSON( "$bit" << BSON( "x" << BSON( "or" << 4 ) ) ) , BSON( "_id" << 1 << "x" << ( 1 | 4 ) ) );
+ test( BSON( "_id" << 1 << "x" << 3 ) , BSON( "$bit" << BSON( "x" << BSON( "and" << 2 << "or" << 8 ) ) ) , BSON( "_id" << 1 << "x" << ( ( 3 & 2 ) | 8 ) ) );
+ test( BSON( "_id" << 1 << "x" << 3 ) , BSON( "$bit" << BSON( "x" << BSON( "or" << 2 << "and" << 8 ) ) ) , BSON( "_id" << 1 << "x" << ( ( 3 | 2 ) & 8 ) ) );
+
+ }
+ };
+
+ class unset : public Base {
+ const char * ns(){
+ return "unittests.unset";
+ }
+ void dotest(){
+ test( "{_id:1,x:1}" , "{$unset:{x:1}}" , "{_id:1}" );
+ }
+ };
+
+ class setswitchint : public Base {
+ const char * ns(){
+ return "unittests.int1";
+ }
+ void dotest(){
+ test( BSON( "_id" << 1 << "x" << 1 ) , BSON( "$set" << BSON( "x" << 5.6 ) ) , BSON( "_id" << 1 << "x" << 5.6 ) );
+ test( BSON( "_id" << 1 << "x" << 5.6 ) , BSON( "$set" << BSON( "x" << 1 ) ) , BSON( "_id" << 1 << "x" << 1 ) );
+ }
+ };
+
+
+ };
+
+ class All : public Suite {
+ public:
+ All() : Suite( "update" ) {
+ }
+ void setupTests(){
+ add< ModId >();
+ add< ModNonmodMix >();
+ add< InvalidMod >();
+ add< ModNotFirst >();
+ add< ModDuplicateFieldSpec >();
+ add< IncNonNumber >();
+ add< PushAllNonArray >();
+ add< PullAllNonArray >();
+ add< IncTargetNonNumber >();
+ add< SetNum >();
+ add< SetString >();
+ add< SetStringDifferentLength >();
+ add< SetStringToNum >();
+ add< SetStringToNumInPlace >();
+ add< ModDotted >();
+ add< SetInPlaceDotted >();
+ add< SetRecreateDotted >();
+ add< SetMissingDotted >();
+ add< SetAdjacentDotted >();
+ add< IncMissing >();
+ add< MultiInc >();
+ add< UnorderedNewSet >();
+ add< UnorderedNewSetAdjacent >();
+ add< ArrayEmbeddedSet >();
+ add< AttemptEmbedInExistingNum >();
+ add< AttemptEmbedConflictsWithOtherSet >();
+ add< ModMasksEmbeddedConflict >();
+ add< ModOverwritesExistingObject >();
+ add< InvalidEmbeddedSet >();
+ add< UpsertMissingEmbedded >();
+ add< Push >();
+ add< PushInvalidEltType >();
+ add< PushConflictsWithOtherMod >();
+ add< PushFromNothing >();
+ add< PushFromEmpty >();
+ add< PushInsideNothing >();
+ add< CantPushInsideOtherMod >();
+ add< CantPushTwice >();
+ add< SetEncapsulationConflictsWithExistingType >();
+ add< CantPushToParent >();
+ add< CantIncParent >();
+ add< DontDropEmpty >();
+ add< InsertInEmpty >();
+ add< IndexParentOfMod >();
+ add< IndexModSet >();
+ add< PreserveIdWithIndex >();
+ add< CheckNoMods >();
+ add< UpdateMissingToNull >();
+
+ add< ModSetTests::internal1 >();
+ add< ModSetTests::inc1 >();
+ add< ModSetTests::inc2 >();
+ add< ModSetTests::set1 >();
+ add< ModSetTests::push1 >();
+
+ add< basic::inc1 >();
+ add< basic::bit1 >();
+ add< basic::unset >();
+ add< basic::setswitchint >();
+ }
+ } myall;
+
+} // namespace UpdateTests
+