diff options
author | Antonin Kral <a.kral@bobek.cz> | 2010-01-31 08:32:52 +0100 |
---|---|---|
committer | Antonin Kral <a.kral@bobek.cz> | 2010-01-31 08:32:52 +0100 |
commit | 4eefaf421bfeddf040d96a3dafb12e09673423d7 (patch) | |
tree | cb2e5ccc7f98158894f977ff131949da36673591 /dbtests | |
download | mongodb-4eefaf421bfeddf040d96a3dafb12e09673423d7.tar.gz |
Imported Upstream version 1.3.1
Diffstat (limited to 'dbtests')
-rw-r--r-- | dbtests/basictests.cpp | 251 | ||||
-rw-r--r-- | dbtests/btreetests.cpp | 238 | ||||
-rw-r--r-- | dbtests/clienttests.cpp | 98 | ||||
-rw-r--r-- | dbtests/cursortests.cpp | 127 | ||||
-rw-r--r-- | dbtests/dbtests.cpp | 27 | ||||
-rw-r--r-- | dbtests/dbtests.h | 24 | ||||
-rw-r--r-- | dbtests/framework.cpp | 361 | ||||
-rw-r--r-- | dbtests/framework.h | 184 | ||||
-rw-r--r-- | dbtests/jsobjtests.cpp | 1340 | ||||
-rw-r--r-- | dbtests/jsontests.cpp | 1117 | ||||
-rw-r--r-- | dbtests/jstests.cpp | 768 | ||||
-rw-r--r-- | dbtests/matchertests.cpp | 128 | ||||
-rw-r--r-- | dbtests/mockdbclient.h | 91 | ||||
-rw-r--r-- | dbtests/namespacetests.cpp | 798 | ||||
-rw-r--r-- | dbtests/pairingtests.cpp | 344 | ||||
-rw-r--r-- | dbtests/pdfiletests.cpp | 328 | ||||
-rw-r--r-- | dbtests/perf/perftest.cpp | 695 | ||||
-rw-r--r-- | dbtests/queryoptimizertests.cpp | 1191 | ||||
-rw-r--r-- | dbtests/querytests.cpp | 919 | ||||
-rw-r--r-- | dbtests/repltests.cpp | 1050 | ||||
-rw-r--r-- | dbtests/sharding.cpp | 56 | ||||
-rw-r--r-- | dbtests/socktests.cpp | 44 | ||||
-rw-r--r-- | dbtests/test.vcproj | 1931 | ||||
-rw-r--r-- | dbtests/test.vcxproj | 586 | ||||
-rw-r--r-- | dbtests/threadedtests.cpp | 135 | ||||
-rw-r--r-- | dbtests/updatetests.cpp | 770 |
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;"..\pcre-7.4";"c:\Program Files\boost\boost_1_35_0""
+ 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=""c:\Program Files\boost\boost_1_35_0\lib""
+ 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;"..\pcre-7.4";"c:\Program Files\boost\boost_1_35_0""
+ 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=""c:\program files\boost\boost_1_35_0\lib""
+ 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=""..\pcre-7.4";"c:\Program Files\boost\boost_1_35_0";"c:\program files\java\jdk\include";"c:\program files\java\jdk\include\win32""
+ 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=""c:\program files\boost\boost_1_35_0\lib""
+ 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;"..\pcre-7.4";"c:\Program Files\boost\boost_1_35_0""
+ 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=""c:\Program Files\boost\boost_1_35_0\lib""
+ 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 + |