diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-06-20 00:22:50 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-06-20 10:56:02 -0400 |
commit | 9c2ed42daa8fbbef4a919c21ec564e2db55e8d60 (patch) | |
tree | 3814f79c10d7b490948d8cb7b112ac1dd41ceff1 /src/mongo/dbtests/updatetests.cpp | |
parent | 01965cf52bce6976637ecb8f4a622aeb05ab256a (diff) | |
download | mongo-9c2ed42daa8fbbef4a919c21ec564e2db55e8d60.tar.gz |
SERVER-18579: Clang-Format - reformat code, no comment reflow
Diffstat (limited to 'src/mongo/dbtests/updatetests.cpp')
-rw-r--r-- | src/mongo/dbtests/updatetests.cpp | 3889 |
1 files changed, 1978 insertions, 1911 deletions
diff --git a/src/mongo/dbtests/updatetests.cpp b/src/mongo/dbtests/updatetests.cpp index e5159780a6e..efd90ce8820 100644 --- a/src/mongo/dbtests/updatetests.cpp +++ b/src/mongo/dbtests/updatetests.cpp @@ -45,1997 +45,2064 @@ namespace UpdateTests { - using std::unique_ptr; - using std::numeric_limits; - using std::string; - using std::stringstream; - using std::vector; - - class ClientBase { - public: - ClientBase() : _client(&_txn) { - mongo::LastError::get(_txn.getClient()).reset(); - } - virtual ~ClientBase() { - mongo::LastError::get(_txn.getClient()).reset(); - } - - protected: - void insert( const char *ns, BSONObj o ) { - _client.insert( ns, o ); - } - void update( const char *ns, BSONObj q, BSONObj o, bool upsert = 0 ) { - _client.update( ns, Query( q ), o, upsert ); - } - bool error() { - return !_client.getPrevError().getField( "err" ).isNull(); - } - - OperationContextImpl _txn; - DBDirectClient _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 SetOnInsertFromEmpty : public SetBase { - public: - void run() { - // Try with upsert false first. - _client.insert( ns(), BSONObj() /* empty document */); - _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 1 ) ), false ); - ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); - - // Then with upsert true. - _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 1 ) ), true ); - ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); - - } - }; - - class SetOnInsertFromNonExistent : public SetBase { - public: - void run() { - // Try with upsert false first. - _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 1 ) ), false ); - ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); - - // Then with upsert true. - _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 1 ) ), true ); - ASSERT( !_client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); - - } - }; - - class SetOnInsertFromNonExistentWithQuery : public SetBase { - public: - void run() { - Query q("{a:1}"); - - // Try with upsert false first. - _client.update( ns(), q, BSON( "$setOnInsert" << BSON( "b" << 1 ) ), false ); - ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); - - // Then with upsert true. - _client.update( ns(), q, BSON( "$setOnInsert" << BSON( "b" << 1 ) ), true ); - ASSERT( !_client.findOne( ns(), BSON( "a" << 1 << "b" << 1) ).isEmpty() ); - - } - }; - - class SetOnInsertFromNonExistentWithQueryOverField : public SetBase { - public: - void run() { - Query q("{a:1}"); // same field that we'll setOnInsert on - - // Try with upsert false first. - _client.update( ns(), q, BSON( "$setOnInsert" << BSON( "a" << 2 ) ), false ); - ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); - - // Then with upsert true. - _client.update( ns(), q, BSON( "$setOnInsert" << BSON( "a" << 2 ) ), true ); - ASSERT( !_client.findOne( ns(), BSON( "a" << 2 ) ).isEmpty() ); - - } - }; - - class SetOnInsertMissingField : public SetBase { - public: - void run() { - BSONObj res = fromjson("{'_id':0, a:1}"); - _client.insert( ns(), res ); - _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "b" << 1 ) ) ); - ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).woCompare( res ) == 0 ); - } - }; - - class SetOnInsertExisting : public SetBase { - public: - void run() { - _client.insert( ns(), BSON( "a" << 1 ) ); - _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 2 ) ) ); - ASSERT( !_client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); - } - }; - - class SetOnInsertMixed : public SetBase { - public: - void run() { - // Try with upsert false first. - _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << 1 ) << - "$setOnInsert" << BSON( "b" << 2 ) ), false ); - ASSERT( _client.findOne( ns(), BSON( "a" << 1 << "b" << 2 ) ).isEmpty() ); - - // Then with upsert true. - _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << 1 ) << - "$setOnInsert" << BSON( "b" << 2 ) ), true ); - ASSERT( !_client.findOne( ns(), BSON( "a" << 1 << "b" << 2 ) ).isEmpty() ); - } - }; - - class SetOnInsertMissingParent : public SetBase { - public: - void run() { - // In a mod that uses dontApply, we should be careful not to create a - // parent unneccesarily. - BSONObj initial = fromjson( "{'_id':0}" ); - BSONObj final = fromjson( "{'_id':0, d:1}" ); - _client.insert( ns(), initial ); - _client.update( ns(), initial, BSON( "$setOnInsert" << BSON( "a.b" << 1 ) << - "$set" << BSON( "d" << 1 ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), initial ), final ); - } - }; - - 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( - mutablebson::unordered( _client.findOne( ns(), BSON( "a.b" << "lllll" ) ) ), - mutablebson::unordered( 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; - unique_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_EQUALS( - mutablebson::unordered( _client.findOne( ns(), Query() ) ), - mutablebson::unordered( fromjson( "{'_id':0,f:{g:{a:2,h:3}}}" ) ) ); - } - }; - - 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_EQUALS( - mutablebson::unordered( _client.findOne( ns(), Query() ) ), - mutablebson::unordered( fromjson( "{'_id':0,f:{g:{a:{b:2},h:{b:3}}}}" ) ) ); - } - }; - - 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 PushEachSimple : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); - // { $push : { a : { $each : [ 2, 3 ] } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 2 << 3 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2,3]}" ) ); - } - - }; - - class PushEachFromEmpty : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[]}" ) ); - // { $push : { a : { $each : [ 1, 2, 3 ] } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 << 2 << 3 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2,3]}" ) ); - } - - }; - - class PushSliceBelowFull : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); - // { $push : { a : { $each : [ 2 ] , $slice : -3 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 2 ) << "$slice" << -3 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2]}" ) ); - } - }; - - class PushSliceReachedFullExact : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); - // { $push : { a : { $each : [ 2 ] , $slice : -2 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 2 ) << "$slice" << -2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2]}" ) ); - } - }; - - class PushSliceReachedFullWithEach : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); - // { $push : { a : { $each : [ 2 , 3 ] , $slice : -2 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 2 << 3 ) << "$slice" << -2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[2,3]}" ) ); - } - }; - - class PushSliceReachedFullWithBoth : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); - // { $push : { a : { $each : [ 3 ] , $slice : -2 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 3 ) << "$slice" << -2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[2,3]}" ) ); - } - }; - - class PushSliceToZero : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); - // { $push : { a : { $each : [ 3 ] , $slice : 0 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 3 ) << "$slice" << 0 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[]}" ) ); - } - }; - - class PushSliceToZeroFromNothing : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0}" ) ); - // { $push : { a : { $each : [ 3 ] , $slice : 0 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 3 ) << "$slice" << 0 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[]}" ) ); - } - }; - - class PushSliceFromNothing : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0}" ) ); - // { $push : { a : { $each : [ 1 , 2 ] , $slice : -3 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 << 2 ) << "$slice" << -3 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2]}" ) ); - } - }; - - class PushSliceLongerThanSliceFromNothing : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0}" ) ); - // { $push : { a : { $each : [ 1 , 2 , 3 ] , $slice : -2 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 << 2 << 3 ) << "$slice" << -2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[2,3]}" ) ); - } - }; - - class PushSliceFromEmpty : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[]}" ) ); - // { $push : { a : { $each : [ 1 ] , $slice : -3 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 ) << "$slice" << -3 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1]}" ) ); - } - }; - - class PushSliceLongerThanSliceFromEmpty : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[]}" ) ); - // { $push : { a : { $each : [ 1 , 2 , 3 ] , $slice : -2 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 << 2 << 3 ) << "$slice" << -2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[2,3]}" ) ); - } - }; - - class PushSliceTwoFields : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2],b:[3,4]}" ) ); - // { $push: { a: { $each: [ 5 ] , $slice : -2 }, { b: $each: [ 6 ] , $slice: -1 } } } - BSONObj objA = BSON( "$each" << BSON_ARRAY( 5 ) << "$slice" << -2 ); - BSONObj objB = BSON( "$each" << BSON_ARRAY( 6 ) << "$slice" << -1 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << objA << "b" << objB ) ) ); - ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson("{'_id':0,a:[2,5],b:[6]}")); - } - }; - - class PushSliceAndNormal : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2],b:[3]}" ) ); - // { $push : { a : { $each : [ 5 ] , $slice : -2 } , { b : 4 } } - BSONObj objA = BSON( "$each" << BSON_ARRAY( 5 ) << "$slice" << -2 ); - _client.update( ns(), Query(), BSON("$push" << BSON("a" << objA << "b" << 4))); - ASSERT_EQUALS(_client.findOne(ns(), Query()) , fromjson("{'_id':0,a:[2,5],b:[3,4]}")); - } - }; - - class PushSliceTwoFieldsConflict : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1],b:[3]}" ) ); - // { $push: { a: { $each: [ 5 ] , $slice: -2 } , { a: $each: [ 6 ] , $slice: -1 } } } - BSONObj objA = BSON( "$each" << BSON_ARRAY( 5 ) << "$slice" << -2 ); - BSONObj other = BSON( "$each" << BSON_ARRAY( 6 ) << "$slice" << -1 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << objA << "a" << other ) ) ); - ASSERT(_client.findOne( ns(), Query()).woCompare(fromjson("{'_id':0,a:[1],b:[3]}"))==0); - } - }; - - class PushSliceAndNormalConflict : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1],b:[3]}" ) ); - // { $push : { a : { $each : [ 5 ] , $slice : -2 } , { a : 4 } } } - BSONObj objA = BSON( "$each" << BSON_ARRAY( 5 ) << "$slice" << -2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << objA << "a" << 4 ) ) ); - ASSERT(_client.findOne( ns(), Query()).woCompare(fromjson("{'_id':0,a:[1],b:[3]}"))==0); - } - }; - - class PushSliceInvalidEachType : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); - // { $push : { a : { $each : 3 , $slice : -2 } } } - BSONObj pushObj = BSON( "$each" << 3 << "$slice" << -2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT( _client.findOne(ns(), Query()).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); - } - }; - - class PushSliceInvalidSliceType : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); - // { $push : { a : { $each : [ 3 ], $slice : [ -2 ] } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$slice" << BSON_ARRAY(-2) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT( _client.findOne( ns(), Query() ).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); - } - }; - - class PushSliceInvalidSliceValue : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); - // { $push : { a : { $each : [ 3 ], $slice : 2 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$slice" << 2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT( _client.findOne( ns(), Query() ).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); - } - }; - - - class PushSliceInvalidSliceDouble : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); - // { $push : { a : { $each : [ 3 ], $slice : -2.1 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$slice" << -2.1 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT( _client.findOne( ns(), Query() ).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); - } - }; - - class PushSliceValidSliceDouble : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); - // { $push : { a : { $each : [ 3 ], $slice : -2.0 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$slice" << -2.0 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT_EQUALS(_client.findOne(ns(), Query()) , fromjson("{'_id':0,a:[2,3]}")); - } - }; - - class PushSliceInvalidSlice : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); - // { $push : { a : { $each : [ 3 ], $xxxx : 2 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$xxxx" << 2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); - ASSERT( _client.findOne( ns(), Query() ).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); - } - }; - - // - // We'd like to test the ability of $push with $sort in the following sequence of tests. We - // try to enumerate all the possibilities of where the final element would come from: the - // document, the $push itself, or both. - // - - class PushSortBase : public ClientBase { - public: - ~PushSortBase() { - _client.dropCollection( ns() ); - } +using std::unique_ptr; +using std::numeric_limits; +using std::string; +using std::stringstream; +using std::vector; + +class ClientBase { +public: + ClientBase() : _client(&_txn) { + mongo::LastError::get(_txn.getClient()).reset(); + } + virtual ~ClientBase() { + mongo::LastError::get(_txn.getClient()).reset(); + } + +protected: + void insert(const char* ns, BSONObj o) { + _client.insert(ns, o); + } + void update(const char* ns, BSONObj q, BSONObj o, bool upsert = 0) { + _client.update(ns, Query(q), o, upsert); + } + bool error() { + return !_client.getPrevError().getField("err").isNull(); + } + + OperationContextImpl _txn; + DBDirectClient _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 SetOnInsertFromEmpty : public SetBase { +public: + void run() { + // Try with upsert false first. + _client.insert(ns(), BSONObj() /* empty document */); + _client.update(ns(), Query(), BSON("$setOnInsert" << BSON("a" << 1)), false); + ASSERT(_client.findOne(ns(), BSON("a" << 1)).isEmpty()); + + // Then with upsert true. + _client.update(ns(), Query(), BSON("$setOnInsert" << BSON("a" << 1)), true); + ASSERT(_client.findOne(ns(), BSON("a" << 1)).isEmpty()); + } +}; + +class SetOnInsertFromNonExistent : public SetBase { +public: + void run() { + // Try with upsert false first. + _client.update(ns(), Query(), BSON("$setOnInsert" << BSON("a" << 1)), false); + ASSERT(_client.findOne(ns(), BSON("a" << 1)).isEmpty()); + + // Then with upsert true. + _client.update(ns(), Query(), BSON("$setOnInsert" << BSON("a" << 1)), true); + ASSERT(!_client.findOne(ns(), BSON("a" << 1)).isEmpty()); + } +}; + +class SetOnInsertFromNonExistentWithQuery : public SetBase { +public: + void run() { + Query q("{a:1}"); + + // Try with upsert false first. + _client.update(ns(), q, BSON("$setOnInsert" << BSON("b" << 1)), false); + ASSERT(_client.findOne(ns(), BSON("a" << 1)).isEmpty()); + + // Then with upsert true. + _client.update(ns(), q, BSON("$setOnInsert" << BSON("b" << 1)), true); + ASSERT(!_client.findOne(ns(), BSON("a" << 1 << "b" << 1)).isEmpty()); + } +}; + +class SetOnInsertFromNonExistentWithQueryOverField : public SetBase { +public: + void run() { + Query q("{a:1}"); // same field that we'll setOnInsert on + + // Try with upsert false first. + _client.update(ns(), q, BSON("$setOnInsert" << BSON("a" << 2)), false); + ASSERT(_client.findOne(ns(), BSON("a" << 1)).isEmpty()); + + // Then with upsert true. + _client.update(ns(), q, BSON("$setOnInsert" << BSON("a" << 2)), true); + ASSERT(!_client.findOne(ns(), BSON("a" << 2)).isEmpty()); + } +}; + +class SetOnInsertMissingField : public SetBase { +public: + void run() { + BSONObj res = fromjson("{'_id':0, a:1}"); + _client.insert(ns(), res); + _client.update(ns(), Query(), BSON("$setOnInsert" << BSON("b" << 1))); + ASSERT(_client.findOne(ns(), BSON("a" << 1)).woCompare(res) == 0); + } +}; + +class SetOnInsertExisting : public SetBase { +public: + void run() { + _client.insert(ns(), BSON("a" << 1)); + _client.update(ns(), Query(), BSON("$setOnInsert" << BSON("a" << 2))); + ASSERT(!_client.findOne(ns(), BSON("a" << 1)).isEmpty()); + } +}; + +class SetOnInsertMixed : public SetBase { +public: + void run() { + // Try with upsert false first. + _client.update(ns(), + Query(), + BSON("$set" << BSON("a" << 1) << "$setOnInsert" << BSON("b" << 2)), + false); + ASSERT(_client.findOne(ns(), BSON("a" << 1 << "b" << 2)).isEmpty()); + + // Then with upsert true. + _client.update(ns(), + Query(), + BSON("$set" << BSON("a" << 1) << "$setOnInsert" << BSON("b" << 2)), + true); + ASSERT(!_client.findOne(ns(), BSON("a" << 1 << "b" << 2)).isEmpty()); + } +}; + +class SetOnInsertMissingParent : public SetBase { +public: + void run() { + // In a mod that uses dontApply, we should be careful not to create a + // parent unneccesarily. + BSONObj initial = fromjson("{'_id':0}"); + BSONObj final = fromjson("{'_id':0, d:1}"); + _client.insert(ns(), initial); + _client.update( + ns(), initial, BSON("$setOnInsert" << BSON("a.b" << 1) << "$set" << BSON("d" << 1))); + ASSERT_EQUALS(_client.findOne(ns(), initial), final); + } +}; + +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(mutablebson::unordered(_client.findOne(ns(), + BSON("a.b" + << "lllll"))), + mutablebson::unordered(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; + unique_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_EQUALS(mutablebson::unordered(_client.findOne(ns(), Query())), + mutablebson::unordered(fromjson("{'_id':0,f:{g:{a:2,h:3}}}"))); + } +}; + +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_EQUALS(mutablebson::unordered(_client.findOne(ns(), Query())), + mutablebson::unordered(fromjson("{'_id':0,f:{g:{a:{b:2},h:{b:3}}}}"))); + } +}; + +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 PushEachSimple : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1]}")); + // { $push : { a : { $each : [ 2, 3 ] } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(2 << 3)); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[1,2,3]}")); + } +}; + +class PushEachFromEmpty : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[]}")); + // { $push : { a : { $each : [ 1, 2, 3 ] } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(1 << 2 << 3)); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[1,2,3]}")); + } +}; + +class PushSliceBelowFull : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1]}")); + // { $push : { a : { $each : [ 2 ] , $slice : -3 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(2) << "$slice" << -3); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[1,2]}")); + } +}; + +class PushSliceReachedFullExact : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1]}")); + // { $push : { a : { $each : [ 2 ] , $slice : -2 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(2) << "$slice" << -2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[1,2]}")); + } +}; + +class PushSliceReachedFullWithEach : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1]}")); + // { $push : { a : { $each : [ 2 , 3 ] , $slice : -2 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(2 << 3) << "$slice" << -2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[2,3]}")); + } +}; + +class PushSliceReachedFullWithBoth : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2]}")); + // { $push : { a : { $each : [ 3 ] , $slice : -2 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(3) << "$slice" << -2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[2,3]}")); + } +}; + +class PushSliceToZero : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2]}")); + // { $push : { a : { $each : [ 3 ] , $slice : 0 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(3) << "$slice" << 0); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[]}")); + } +}; + +class PushSliceToZeroFromNothing : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0}")); + // { $push : { a : { $each : [ 3 ] , $slice : 0 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(3) << "$slice" << 0); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[]}")); + } +}; + +class PushSliceFromNothing : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0}")); + // { $push : { a : { $each : [ 1 , 2 ] , $slice : -3 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(1 << 2) << "$slice" << -3); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[1,2]}")); + } +}; + +class PushSliceLongerThanSliceFromNothing : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0}")); + // { $push : { a : { $each : [ 1 , 2 , 3 ] , $slice : -2 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(1 << 2 << 3) << "$slice" << -2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[2,3]}")); + } +}; + +class PushSliceFromEmpty : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[]}")); + // { $push : { a : { $each : [ 1 ] , $slice : -3 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(1) << "$slice" << -3); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[1]}")); + } +}; + +class PushSliceLongerThanSliceFromEmpty : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[]}")); + // { $push : { a : { $each : [ 1 , 2 , 3 ] , $slice : -2 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(1 << 2 << 3) << "$slice" << -2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[2,3]}")); + } +}; + +class PushSliceTwoFields : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2],b:[3,4]}")); + // { $push: { a: { $each: [ 5 ] , $slice : -2 }, { b: $each: [ 6 ] , $slice: -1 } } } + BSONObj objA = BSON("$each" << BSON_ARRAY(5) << "$slice" << -2); + BSONObj objB = BSON("$each" << BSON_ARRAY(6) << "$slice" << -1); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << objA << "b" << objB))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[2,5],b:[6]}")); + } +}; + +class PushSliceAndNormal : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2],b:[3]}")); + // { $push : { a : { $each : [ 5 ] , $slice : -2 } , { b : 4 } } + BSONObj objA = BSON("$each" << BSON_ARRAY(5) << "$slice" << -2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << objA << "b" << 4))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[2,5],b:[3,4]}")); + } +}; + +class PushSliceTwoFieldsConflict : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1],b:[3]}")); + // { $push: { a: { $each: [ 5 ] , $slice: -2 } , { a: $each: [ 6 ] , $slice: -1 } } } + BSONObj objA = BSON("$each" << BSON_ARRAY(5) << "$slice" << -2); + BSONObj other = BSON("$each" << BSON_ARRAY(6) << "$slice" << -1); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << objA << "a" << other))); + ASSERT(_client.findOne(ns(), Query()).woCompare(fromjson("{'_id':0,a:[1],b:[3]}")) == 0); + } +}; + +class PushSliceAndNormalConflict : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1],b:[3]}")); + // { $push : { a : { $each : [ 5 ] , $slice : -2 } , { a : 4 } } } + BSONObj objA = BSON("$each" << BSON_ARRAY(5) << "$slice" << -2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << objA << "a" << 4))); + ASSERT(_client.findOne(ns(), Query()).woCompare(fromjson("{'_id':0,a:[1],b:[3]}")) == 0); + } +}; + +class PushSliceInvalidEachType : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2]}")); + // { $push : { a : { $each : 3 , $slice : -2 } } } + BSONObj pushObj = BSON("$each" << 3 << "$slice" << -2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT(_client.findOne(ns(), Query()).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); + } +}; + +class PushSliceInvalidSliceType : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2]}")); + // { $push : { a : { $each : [ 3 ], $slice : [ -2 ] } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(3) << "$slice" << BSON_ARRAY(-2)); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT(_client.findOne(ns(), Query()).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); + } +}; + +class PushSliceInvalidSliceValue : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2]}")); + // { $push : { a : { $each : [ 3 ], $slice : 2 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(3) << "$slice" << 2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT(_client.findOne(ns(), Query()).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); + } +}; + + +class PushSliceInvalidSliceDouble : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2]}")); + // { $push : { a : { $each : [ 3 ], $slice : -2.1 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(3) << "$slice" << -2.1); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT(_client.findOne(ns(), Query()).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); + } +}; + +class PushSliceValidSliceDouble : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2]}")); + // { $push : { a : { $each : [ 3 ], $slice : -2.0 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(3) << "$slice" << -2.0); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT_EQUALS(_client.findOne(ns(), Query()), fromjson("{'_id':0,a:[2,3]}")); + } +}; + +class PushSliceInvalidSlice : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,a:[1,2]}")); + // { $push : { a : { $each : [ 3 ], $xxxx : 2 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(3) << "$xxxx" << 2); + _client.update(ns(), Query(), BSON("$push" << BSON("a" << pushObj))); + ASSERT(_client.findOne(ns(), Query()).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); + } +}; - protected: - enum UpdateType { - // Sorts ascending and slices the back of the array. - TOPK_ASC = 0, - - // Sorts descending and slices the front of the array. - TOPK_DESC = 1, - - // Sorts ascending and slices the back of the array. - BOTTOMK_ASC = 2, - - // Sorts descending and slices the front of the array. - BOTTOMK_DESC = 3 - }; - - const char* ns() { - return "unittest.updatetests.PushSortBase"; - } - - void setParams( const BSONArray& fields, - const BSONArray& values, - const BSONArray& sort, - int size ) { - _fieldNames = fields; - _fieldValues = values; - _sortFields = sort; - _sliceSize = size; - } +// +// We'd like to test the ability of $push with $sort in the following sequence of tests. We +// try to enumerate all the possibilities of where the final element would come from: the +// document, the $push itself, or both. +// - /** - * Generates the update expression portion of an update command given one of the - * possible types of update. - */ - BSONObj getUpdate( int updateType ) { - BSONObjBuilder updateBuilder; - BSONObjBuilder pushBuilder( updateBuilder.subobjStart( "$push" ) ); - BSONObjBuilder fieldBuilder( pushBuilder.subobjStart( "x" ) ); - - // Builds $each: [ {a:1,b:1,...}, {a:2,b:2,...}, ... ] - BSONArrayBuilder eachBuilder( fieldBuilder.subarrayStart( "$each" ) ); - BSONObjIterator itVal( _fieldValues ); - while ( itVal.more() ) { - BSONObjBuilder eachObjBuilder; - BSONElement val = itVal.next(); - BSONObjIterator itName( _fieldNames ); - while ( itName.more() ) { - BSONElement name = itName.next(); - eachObjBuilder.append( name.String(), val.Int() ); - } - eachBuilder.append( eachObjBuilder.done() ); - } - eachBuilder.done(); - - // Builds $slice portion. - fieldBuilder.append( "$slice", - updateType < 2 ? -_sliceSize : _sliceSize); - - // Builds $sort: <sort pattern> portion - BSONObjBuilder patternBuilder( fieldBuilder.subobjStart( "$sort" ) ); - BSONObjIterator itSort( _sortFields ); - while ( itSort.more() ) { - BSONElement sortField = itSort.next(); - patternBuilder.append( sortField.String(), - updateType%2 ? -1 : 1 ); +class PushSortBase : public ClientBase { +public: + ~PushSortBase() { + _client.dropCollection(ns()); + } + +protected: + enum UpdateType { + // Sorts ascending and slices the back of the array. + TOPK_ASC = 0, + + // Sorts descending and slices the front of the array. + TOPK_DESC = 1, + + // Sorts ascending and slices the back of the array. + BOTTOMK_ASC = 2, + + // Sorts descending and slices the front of the array. + BOTTOMK_DESC = 3 + }; + + const char* ns() { + return "unittest.updatetests.PushSortBase"; + } + + void setParams(const BSONArray& fields, + const BSONArray& values, + const BSONArray& sort, + int size) { + _fieldNames = fields; + _fieldValues = values; + _sortFields = sort; + _sliceSize = size; + } + + /** + * Generates the update expression portion of an update command given one of the + * possible types of update. + */ + BSONObj getUpdate(int updateType) { + BSONObjBuilder updateBuilder; + BSONObjBuilder pushBuilder(updateBuilder.subobjStart("$push")); + BSONObjBuilder fieldBuilder(pushBuilder.subobjStart("x")); + + // Builds $each: [ {a:1,b:1,...}, {a:2,b:2,...}, ... ] + BSONArrayBuilder eachBuilder(fieldBuilder.subarrayStart("$each")); + BSONObjIterator itVal(_fieldValues); + while (itVal.more()) { + BSONObjBuilder eachObjBuilder; + BSONElement val = itVal.next(); + BSONObjIterator itName(_fieldNames); + while (itName.more()) { + BSONElement name = itName.next(); + eachObjBuilder.append(name.String(), val.Int()); } - patternBuilder.done(); - - fieldBuilder.done(); - pushBuilder.done(); - - return updateBuilder.obj(); - } - - void check( BSONObj expected ) { - std::cout << expected.toString() << std::endl; - std::cout << _client.findOne( ns(), Query() ) << std::endl; - ASSERT( _client.findOne( ns(), Query() ).woCompare( expected ) == 0 ); - } - - private: - BSONArray _fieldNames; - BSONArray _fieldValues; - BSONArray _sortFields; - int _sliceSize; - }; - - class PushSortBelowFull : public PushSortBase { - public: - void run() { - // With the following parameters - // fields in values in - // the each array each array field to sort size - setParams( BSON_ARRAY( "a" << "b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 3 ); - - // Generates the four variations below (but for now we're only using negative slice). - // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-3, $sort: { b:1 } } } - // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-3, $sort: { b:-1 } } } - // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:3, $sort: { b:1 } } } - // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:3, $sort: { b:-1 } } } - - for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice - _client.dropCollection( ns() ); - _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1,b:1}]}" ) ); - - BSONObj result; - BSONObj expected; - switch ( i ) { + eachBuilder.append(eachObjBuilder.done()); + } + eachBuilder.done(); + + // Builds $slice portion. + fieldBuilder.append("$slice", updateType < 2 ? -_sliceSize : _sliceSize); + + // Builds $sort: <sort pattern> portion + BSONObjBuilder patternBuilder(fieldBuilder.subobjStart("$sort")); + BSONObjIterator itSort(_sortFields); + while (itSort.more()) { + BSONElement sortField = itSort.next(); + patternBuilder.append(sortField.String(), updateType % 2 ? -1 : 1); + } + patternBuilder.done(); + + fieldBuilder.done(); + pushBuilder.done(); + + return updateBuilder.obj(); + } + + void check(BSONObj expected) { + std::cout << expected.toString() << std::endl; + std::cout << _client.findOne(ns(), Query()) << std::endl; + ASSERT(_client.findOne(ns(), Query()).woCompare(expected) == 0); + } + +private: + BSONArray _fieldNames; + BSONArray _fieldValues; + BSONArray _sortFields; + int _sliceSize; +}; + +class PushSortBelowFull : public PushSortBase { +public: + void run() { + // With the following parameters + // fields in values in + // the each array each array field to sort size + setParams(BSON_ARRAY("a" + << "b"), + BSON_ARRAY(2), + BSON_ARRAY("b"), + 3); + + // Generates the four variations below (but for now we're only using negative slice). + // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-3, $sort: { b:1 } } } + // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-3, $sort: { b:-1 } } } + // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:3, $sort: { b:1 } } } + // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:3, $sort: { b:-1 } } } + + for (int i = 0; i < 2; i++) { // i < 4 when we have positive $slice + _client.dropCollection(ns()); + _client.insert(ns(), fromjson("{'_id':0,x:[{a:1,b:1}]}")); + + BSONObj result; + BSONObj expected; + switch (i) { case TOPK_ASC: case BOTTOMK_ASC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}"); + ASSERT_EQUALS(result, expected); break; case TOPK_DESC: case BOTTOMK_DESC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ) ; - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}"); + ASSERT_EQUALS(result, expected); break; - } } } - }; - - class PushSortReachedFullExact : public PushSortBase { - public: - void run() { - // With the following parameters - // fields in values in - // the each array each array field to sort size - setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 2 ); - - // Generates the four variations below (but for now we're only using negative slice). - // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:1 } } } - // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:-1 } } } - // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:1 } } } - // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:-1 } } } - - for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice - _client.dropCollection( ns() ); - _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1,b:1}]}" ) ); - - BSONObj result; - BSONObj expected; - switch (i) { + } +}; + +class PushSortReachedFullExact : public PushSortBase { +public: + void run() { + // With the following parameters + // fields in values in + // the each array each array field to sort size + setParams(BSON_ARRAY("a" + << "b"), + BSON_ARRAY(2), + BSON_ARRAY("b"), + 2); + + // Generates the four variations below (but for now we're only using negative slice). + // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:1 } } } + // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:-1 } } } + // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:1 } } } + // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:-1 } } } + + for (int i = 0; i < 2; i++) { // i < 4 when we have positive $slice + _client.dropCollection(ns()); + _client.insert(ns(), fromjson("{'_id':0,x:[{a:1,b:1}]}")); + + BSONObj result; + BSONObj expected; + switch (i) { case TOPK_ASC: case BOTTOMK_ASC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}"); + ASSERT_EQUALS(result, expected); break; case TOPK_DESC: case BOTTOMK_DESC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}"); + ASSERT_EQUALS(result, expected); break; - } } } - }; - - class PushSortReachedFullWithBoth : public PushSortBase { - public: - void run() { - // With the following parameters - // fields in values in - // the each array each array field to sort size - setParams( BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 2 ); - - // Generates the four variations below (but for now we're only using negative slice). - // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:1 } } } - // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:-1 } } } - // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:1 } } } - // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:-1 } } } - - for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice - _client.dropCollection( ns() ); - _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1,b:1},{a:3,b:3}]}" ) ); - - BSONObj result; - BSONObj expected; - switch ( i ) { + } +}; + +class PushSortReachedFullWithBoth : public PushSortBase { +public: + void run() { + // With the following parameters + // fields in values in + // the each array each array field to sort size + setParams(BSON_ARRAY("a" + << "b"), + BSON_ARRAY(2), + BSON_ARRAY("b"), + 2); + + // Generates the four variations below (but for now we're only using negative slice). + // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:1 } } } + // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:-1 } } } + // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:1 } } } + // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:-1 } } } + + for (int i = 0; i < 2; i++) { // i < 4 when we have positive $slice + _client.dropCollection(ns()); + _client.insert(ns(), fromjson("{'_id':0,x:[{a:1,b:1},{a:3,b:3}]}")); + + BSONObj result; + BSONObj expected; + switch (i) { case TOPK_ASC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:3,b:3}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:3,b:3}]}"); + ASSERT_EQUALS(result, expected); break; case TOPK_DESC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}"); + ASSERT_EQUALS(result, expected); break; case BOTTOMK_ASC: case BOTTOMK_DESC: // Implement me. break; - } - } - } - }; - - class PushSortToZero : public PushSortBase { - public: - void run() { - // With the following parameters - // fields in values in - // the each array each array field to sort size - setParams( BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 0 ); - - // Generates the four variations below (but for now we're only using negative slice). - // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } - // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } - // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } - // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } - - for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice - _client.dropCollection( ns() ); - _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1,b:1},{a:3,b:3}]}" ) ); - - BSONObj result; - BSONObj expected; - - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[]}" ); - ASSERT_EQUALS( result, expected ); - } - } - }; - - class PushSortToZeroFromNothing : public PushSortBase { - public: - void run() { - // With the following parameters - // fields in values in - // the each array each array field to sort size - setParams( BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 0 ); - - // Generates the four variations below (but for now we're only using negative slice). - // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } - // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } - // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } - // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } - - for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice - _client.dropCollection( ns() ); - _client.insert( ns(), fromjson( "{'_id':0}" ) ); - - BSONObj result; - BSONObj expected; - - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[]}" ); - ASSERT_EQUALS( result, expected ); } } - }; - - class PushSortFromNothing : public PushSortBase { - public: - void run() { - // With the following parameters - // fields in values in - // the each array each array field to sort size - setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 << 1 ), BSON_ARRAY( "b" ), 2 ); - - // Generates the four variations below (but for now we're only using negative slice). - // <genarr> = [ {a:2,b:2}, {a:1,b:1} ] - // Generates the four variations below - // TOPK_ASC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:1 } } } - // TOPK_DESC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:-1 } } } - // BOTTOMK_ASC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:1 } } } - // BOTTOMK_DESC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:-1 } } } - - for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice - _client.dropCollection( ns() ); - _client.insert( ns(), fromjson( "{'_id':0}" ) ); - - BSONObj result; - BSONObj expected; - switch (i) { + } +}; + +class PushSortToZero : public PushSortBase { +public: + void run() { + // With the following parameters + // fields in values in + // the each array each array field to sort size + setParams(BSON_ARRAY("a" + << "b"), + BSON_ARRAY(2), + BSON_ARRAY("b"), + 0); + + // Generates the four variations below (but for now we're only using negative slice). + // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } + // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } + // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } + // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } + + for (int i = 0; i < 2; i++) { // i < 4 when we have positive $slice + _client.dropCollection(ns()); + _client.insert(ns(), fromjson("{'_id':0,x:[{a:1,b:1},{a:3,b:3}]}")); + + BSONObj result; + BSONObj expected; + + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[]}"); + ASSERT_EQUALS(result, expected); + } + } +}; + +class PushSortToZeroFromNothing : public PushSortBase { +public: + void run() { + // With the following parameters + // fields in values in + // the each array each array field to sort size + setParams(BSON_ARRAY("a" + << "b"), + BSON_ARRAY(2), + BSON_ARRAY("b"), + 0); + + // Generates the four variations below (but for now we're only using negative slice). + // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } + // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } + // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } + // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } + + for (int i = 0; i < 2; i++) { // i < 4 when we have positive $slice + _client.dropCollection(ns()); + _client.insert(ns(), fromjson("{'_id':0}")); + + BSONObj result; + BSONObj expected; + + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[]}"); + ASSERT_EQUALS(result, expected); + } + } +}; + +class PushSortFromNothing : public PushSortBase { +public: + void run() { + // With the following parameters + // fields in values in + // the each array each array field to sort size + setParams(BSON_ARRAY("a" + << "b"), + BSON_ARRAY(2 << 1), + BSON_ARRAY("b"), + 2); + + // Generates the four variations below (but for now we're only using negative slice). + // <genarr> = [ {a:2,b:2}, {a:1,b:1} ] + // Generates the four variations below + // TOPK_ASC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:1 } } } + // TOPK_DESC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:-1 } } } + // BOTTOMK_ASC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:1 } } } + // BOTTOMK_DESC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:-1 } } } + + for (int i = 0; i < 2; i++) { // i < 4 when we have positive $slice + _client.dropCollection(ns()); + _client.insert(ns(), fromjson("{'_id':0}")); + + BSONObj result; + BSONObj expected; + switch (i) { case TOPK_ASC: case BOTTOMK_ASC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}"); + ASSERT_EQUALS(result, expected); break; case TOPK_DESC: case BOTTOMK_DESC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}"); + ASSERT_EQUALS(result, expected); break; - } } } - }; - - class PushSortLongerThanSliceFromNothing : public PushSortBase { - public: - void run() { - // With the following parameters - // fields in values in - // the each array each array field to sort size - setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 << 1 << 3), BSON_ARRAY( "b" ), 2 ); - - // Generates the four variations below (but for now we're only using negative slice). - // <genarr> = [ {a:2,b:2}, {a:1,b:1}, {a:3,b:3} ] - // TOPK_ASC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:1 } } } - // TOPK_DESC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:-1 } } } - // BOTTOMK_ASC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:1 } } } - // BOTTOMK_DESC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:-1 } } } - - for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice - _client.dropCollection( ns() ); - _client.insert( ns(), fromjson( "{'_id':0}" ) ); - - BSONObj result; - BSONObj expected; - switch (i) { + } +}; + +class PushSortLongerThanSliceFromNothing : public PushSortBase { +public: + void run() { + // With the following parameters + // fields in values in + // the each array each array field to sort size + setParams(BSON_ARRAY("a" + << "b"), + BSON_ARRAY(2 << 1 << 3), + BSON_ARRAY("b"), + 2); + + // Generates the four variations below (but for now we're only using negative slice). + // <genarr> = [ {a:2,b:2}, {a:1,b:1}, {a:3,b:3} ] + // TOPK_ASC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:1 } } } + // TOPK_DESC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:-1 } } } + // BOTTOMK_ASC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:1 } } } + // BOTTOMK_DESC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:-1 } } } + + for (int i = 0; i < 2; i++) { // i < 4 when we have positive $slice + _client.dropCollection(ns()); + _client.insert(ns(), fromjson("{'_id':0}")); + + BSONObj result; + BSONObj expected; + switch (i) { case TOPK_ASC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:3,b:3}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:3,b:3}]}"); + ASSERT_EQUALS(result, expected); break; case TOPK_DESC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}"); + ASSERT_EQUALS(result, expected); break; case BOTTOMK_ASC: case BOTTOMK_DESC: // Implement me. break; - } } } - }; - - class PushSortFromEmpty : public PushSortBase { - public: - void run() { - // With the following parameters - // fields in values in - // the each array each array field to sort size - setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 << 1 ), BSON_ARRAY( "b" ), 2 ); - - // Generates the four variations below (but for now we're only using negative slice). - // <genarr> = [ {a:2,b:2}, {a:1,b:1} ] - // TOPK_ASC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:1 } } } - // TOPK_DESC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:-1 } } } - // BOTTOMK_ASC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:1 } } } - // BOTTOMK_DESC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:-1 } } } - - for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice - _client.dropCollection( ns() ); - _client.insert( ns(), fromjson( "{'_id':0,x:[]}" ) ); - - BSONObj result; - BSONObj expected; - switch (i) { + } +}; + +class PushSortFromEmpty : public PushSortBase { +public: + void run() { + // With the following parameters + // fields in values in + // the each array each array field to sort size + setParams(BSON_ARRAY("a" + << "b"), + BSON_ARRAY(2 << 1), + BSON_ARRAY("b"), + 2); + + // Generates the four variations below (but for now we're only using negative slice). + // <genarr> = [ {a:2,b:2}, {a:1,b:1} ] + // TOPK_ASC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:1 } } } + // TOPK_DESC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:-1 } } } + // BOTTOMK_ASC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:1 } } } + // BOTTOMK_DESC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:-1 } } } + + for (int i = 0; i < 2; i++) { // i < 4 when we have positive $slice + _client.dropCollection(ns()); + _client.insert(ns(), fromjson("{'_id':0,x:[]}")); + + BSONObj result; + BSONObj expected; + switch (i) { case TOPK_ASC: case BOTTOMK_ASC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}"); + ASSERT_EQUALS(result, expected); break; case TOPK_DESC: case BOTTOMK_DESC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}"); + ASSERT_EQUALS(result, expected); break; - } } } - }; - - class PushSortLongerThanSliceFromEmpty : public PushSortBase { - public: - void run() { - // With the following parameters - // fields in values in - // the each array each array field to sort size - setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 << 1 << 3), BSON_ARRAY( "b" ), 2 ); - - // Generates the four variations below (but for now we're only using negative slice). - // <genarr> = [ {a:2,b:2}, {a:1,b:1}, {a:3,b:3} ] - // TOPK_ASC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:1 } } } - // TOPK_DESC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:-1 } } } - // BOTTOMK_ASC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:1 } } } - // BOTTOMK_DESC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:-1 } } } - - for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice - _client.dropCollection( ns() ); - _client.insert( ns(), fromjson( "{'_id':0,x:[]}" ) ); - - BSONObj result; - BSONObj expected; - switch (i) { + } +}; + +class PushSortLongerThanSliceFromEmpty : public PushSortBase { +public: + void run() { + // With the following parameters + // fields in values in + // the each array each array field to sort size + setParams(BSON_ARRAY("a" + << "b"), + BSON_ARRAY(2 << 1 << 3), + BSON_ARRAY("b"), + 2); + + // Generates the four variations below (but for now we're only using negative slice). + // <genarr> = [ {a:2,b:2}, {a:1,b:1}, {a:3,b:3} ] + // TOPK_ASC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:1 } } } + // TOPK_DESC: $push: { x: { $each: [ <genarray> ], $slice:-2, $sort: { b:-1 } } } + // BOTTOMK_ASC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:1 } } } + // BOTTOMK_DESC: $push: { x: { $each: [ <genarray> ], $slice:2, $sort: { b:-1 } } } + + for (int i = 0; i < 2; i++) { // i < 4 when we have positive $slice + _client.dropCollection(ns()); + _client.insert(ns(), fromjson("{'_id':0,x:[]}")); + + BSONObj result; + BSONObj expected; + switch (i) { case TOPK_ASC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:3,b:3}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:3,b:3}]}"); + ASSERT_EQUALS(result, expected); break; case TOPK_DESC: - _client.update( ns(), Query(), getUpdate(i) ); - result = _client.findOne( ns(), Query() ); - expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); - ASSERT_EQUALS( result, expected ); + _client.update(ns(), Query(), getUpdate(i)); + result = _client.findOne(ns(), Query()); + expected = fromjson("{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}"); + ASSERT_EQUALS(result, expected); break; case BOTTOMK_ASC: case BOTTOMK_DESC: // Implement me. break; - } - } - } - }; - - namespace { - - /** - * Comparator between two BSONObjects that takes in consideration only the keys and - * direction described in the sort pattern. - * - * TODO: This was pulled from update_internal.h, we should verify that these tests work - * with the new update framework $push sorter. - */ - struct ProjectKeyCmp { - BSONObj sortPattern; - - ProjectKeyCmp( BSONObj pattern ) : sortPattern( pattern) {} - - int operator()( const BSONObj& left, const BSONObj& right ) const { - BSONObj keyLeft = left.extractFields( sortPattern, true ); - BSONObj keyRight = right.extractFields( sortPattern, true ); - return keyLeft.woCompare( keyRight, sortPattern ) < 0; - } - }; - - } // namespace - - class PushSortSortMixed { - public: - void run() { - BSONObj objs[3]; - objs[0] = fromjson( "{a:1, b:1}" ); - objs[1] = fromjson( "{a:3, b:1}" ); - objs[2] = fromjson( "{a:2, b:3}" ); - - vector<BSONObj> workArea; - for ( int i = 0; i < 3; i++ ) { - workArea.push_back( objs[i] ); - } - - sort( workArea.begin(), workArea.end(), ProjectKeyCmp( BSON("b" << 1 << "a" << -1) ) ); - - ASSERT_EQUALS( workArea[0], objs[1] ); - ASSERT_EQUALS( workArea[1], objs[0] ); - ASSERT_EQUALS( workArea[2], objs[2] ); - } - }; - - class PushSortSortOutOfOrderFields { - public: - void run() { - BSONObj objs[3]; - objs[0] = fromjson( "{b:1, a:1}" ); - objs[1] = fromjson( "{a:3, b:2}" ); - objs[2] = fromjson( "{b:3, a:2}" ); - - vector<BSONObj> workArea; - for ( int i = 0; i < 3; i++ ) { - workArea.push_back( objs[i] ); - } - - sort( workArea.begin(), workArea.end(), ProjectKeyCmp( BSON("a" << 1 << "b" << 1) ) ); - - ASSERT_EQUALS( workArea[0], objs[0] ); - ASSERT_EQUALS( workArea[1], objs[2] ); - ASSERT_EQUALS( workArea[2], objs[1] ); - } - }; - - class PushSortSortExtraFields { - public: - void run() { - BSONObj objs[3]; - objs[0] = fromjson( "{b:1, c:2, a:1}" ); - objs[1] = fromjson( "{c:1, a:3, b:2}" ); - objs[2] = fromjson( "{b:3, a:2}" ); - - vector<BSONObj> workArea; - for ( int i = 0; i < 3; i++ ) { - workArea.push_back( objs[i] ); - } - - sort( workArea.begin(), workArea.end(), ProjectKeyCmp( BSON("a" << 1 << "b" << 1) ) ); - - ASSERT_EQUALS( workArea[0], objs[0] ); - ASSERT_EQUALS( workArea[1], objs[2] ); - ASSERT_EQUALS( workArea[2], objs[1] ); - } - }; - - class PushSortSortMissingFields { - public: - void run() { - BSONObj objs[3]; - objs[0] = fromjson( "{a:2, b:2}" ); - objs[1] = fromjson( "{a:1}" ); - objs[2] = fromjson( "{a:3, b:3, c:3}" ); - - vector<BSONObj> workArea; - for ( int i = 0; i < 3; i++ ) { - workArea.push_back( objs[i] ); } - - sort( workArea.begin(), workArea.end(), ProjectKeyCmp( BSON("b" << 1 << "c" << 1) ) ); - - ASSERT_EQUALS( workArea[0], objs[1] ); - ASSERT_EQUALS( workArea[1], objs[0] ); - ASSERT_EQUALS( workArea[2], objs[2] ); - } - }; - - class PushSortSortNestedFields { - public: - void run() { - BSONObj objs[3]; - objs[0] = fromjson( "{a:{b:{c:2, d:0}}}" ); - objs[1] = fromjson( "{a:{b:{c:1, d:2}}}" ); - objs[2] = fromjson( "{a:{b:{c:3, d:1}}}" ); - - vector<BSONObj> workArea; - for ( int i = 0; i < 3; i++ ) { - workArea.push_back( objs[i] ); - } - - sort( workArea.begin(), workArea.end(), ProjectKeyCmp( fromjson( "{'a.b.d':-1}" ) ) ); - - ASSERT_EQUALS( workArea[0], objs[1] ); - ASSERT_EQUALS( workArea[1], objs[2] ); - ASSERT_EQUALS( workArea[2], objs[0] ); - - sort( workArea.begin(), workArea.end(), ProjectKeyCmp( fromjson( "{'a.b':1}" ) ) ); - - ASSERT_EQUALS( workArea[0], objs[1] ); - ASSERT_EQUALS( workArea[1], objs[0] ); - ASSERT_EQUALS( workArea[2], objs[2] ); - - } - }; - - class PushSortInvalidSortPattern : public SetBase { - public: - void run() { - // Sort pattern validation is made during update command checking. Therefore, to - // catch bad patterns, we have to write updated that use them. - - BSONObj expected = fromjson( "{'_id':0,x:[{a:1}, {a:2}]}" ); - _client.insert( ns(), expected ); - - // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a..d:1} } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2 << - "$sort" << BSON( "a..d" << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - - - // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a.:1} } } } - pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2 << - "$sort" << BSON( "a." << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - - // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {.b:1} } } } - pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2 << - "$sort" << BSON( ".b" << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - - // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {.:1} } } } - pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2 << - "$sort" << BSON( "." << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - - // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {'':1} } } } - pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2 << - "$sort" << BSON( "" << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - } - }; - - class PushSortInvalidEachType : public SetBase { - public: - void run() { - BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); - _client.insert( ns(), expected ); - // { $push : { x : { $each : [ 3 ], $slice:-2, $sort : {a:1} } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 3 ) << - "$slice" << -2 << - "$sort" << BSON( "a" << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - } - }; - - class PushSortInvalidSortType : public SetBase { - public: - void run() { - BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); - _client.insert( ns(), expected ); - // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : 2} } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2 << - "$sort" << 2 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - } - }; - - class PushSortInvalidSortValue : public SetBase { - public: - void run() { - BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); - _client.insert( ns(), expected ); - // { $push : { x : { $each : [ {a:3} ], $slice:2, $sort : {a:1} } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << 2 << - "$sort" << BSON( "a" << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - } - }; - - class PushSortInvalidSortDouble : public SetBase { - public: - void run() { - BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); - _client.insert( ns(), expected ); - // { $push : { x : { $each : [ {a:3} ], $slice:-2.1, $sort : {a:1} } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2.1 << - "$sort" << BSON( "a" << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - } - }; - - class PushSortValidSortDouble : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ) ); - // { $push : { x : { $each : [ {a:3} ], $slice:-2.0, $sort : {a:1} } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2.0 << - "$sort" << BSON( "a" << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj expected = fromjson( "{'_id':0,x:[{a:2},{a:3}]}" ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - } - }; - - class PushSortInvalidSortSort : public SetBase { - public: - void run() { - BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); - _client.insert( ns(), expected ); - // { $push : { x : { $each : [ {a:3} ], $slice:-2.0, $sort : [2, 1] } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2.0 << - "$sort" << BSON_ARRAY( 2 << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - } - }; - - class PushSortInvalidSortSortOrder : public SetBase { - public: - void run() { - BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); - _client.insert( ns(), expected ); - // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a:10} } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << - "$slice" << -2 << - "$sort" << BSON( "a" << 10 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - } - }; - - class PushSortInvertedSortAndSlice : public SetBase { - public: - void run() { - _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1},{a:3}]}" ) ); - // { $push : { x : { $each : [ {a:2} ], $sort: {a:1}, $slice:-2 } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 2 ) ) << - "$sort" << BSON( "a" << 1 ) << - "$slice" << -2.0 ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj expected = fromjson( "{'_id':0,x:[{a:2},{a:3}]}" ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - - } - }; - - class PushSortInvalidDuplicatedSort : public SetBase { - public: - void run() { - BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:3}]}" ); - _client.insert( ns(), expected ); - // { $push : { x : { $each : [ {a:2} ], $sort : {a:1}, $sort: {a:1} } } } - BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 2 ) ) << - "$sort" << BSON( "a" << 1 ) << - "$sort" << BSON( "a" << 1 ) ); - _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); - BSONObj result = _client.findOne( ns(), Query() ); - ASSERT_EQUALS( result, expected ); - - } - }; - - 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() { - ASSERT_OK(dbtests::createIndex( &_txn, 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 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\":1}" ) ).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() ); - } - }; - - /** SERVER-4777 */ - class TwoModsWithinDuplicatedField : public SetBase { - public: - void run() { - _client.insert( ns(), BSON( "_id" << 0 << "a" << 1 - << "x" << BSONObj() << "x" << BSONObj() - << "z" << 5 ) ); - _client.update( ns(), BSONObj(), BSON( "$set" << BSON( "x.b" << 1 << "x.c" << 1 ) ) ); - ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1 - << "x" << BSON( "b" << 1 << "c" << 1 ) << "x" << BSONObj() - << "z" << 5 ), - _client.findOne( ns(), BSONObj() ) ); - } - }; - - /** SERVER-4777 */ - class ThreeModsWithinDuplicatedField : public SetBase { - public: - void run() { - _client.insert( ns(), - BSON( "_id" << 0 - << "x" << BSONObj() << "x" << BSONObj() << "x" << BSONObj() ) ); - _client.update( ns(), BSONObj(), - BSON( "$set" << BSON( "x.b" << 1 << "x.c" << 1 << "x.d" << 1 ) ) ); - ASSERT_EQUALS( BSON( "_id" << 0 - << "x" << BSON( "b" << 1 << "c" << 1 << "d" << 1 ) - << "x" << BSONObj() << "x" << BSONObj() ), - _client.findOne( ns(), BSONObj() ) ); - } - }; - - class TwoModsBeforeExistingField : public SetBase { - public: - void run() { - _client.insert( ns(), BSON( "_id" << 0 << "x" << 5 ) ); - _client.update( ns(), BSONObj(), - BSON( "$set" << BSON( "a" << 1 << "b" << 1 << "x" << 10 ) ) ); - ASSERT_EQUALS( - mutablebson::unordered( BSON( "_id" << 0 << "a" << 1 << "b" << 1 << "x" << 10 ) ), - mutablebson::unordered( _client.findOne( ns(), BSONObj() ) ) ); - } - }; - - namespace basic { - class Base : public ClientBase { - protected: - - virtual const char * ns() = 0; - virtual void dotest() = 0; - - void insert( const BSONObj& o ) { - _client.insert( ns() , o ); - } - - void update( const BSONObj& m ) { - _client.update( ns() , BSONObj() , m ); - } - - BSONObj findOne() { - return _client.findOne( ns() , BSONObj() ); - } - - 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() ); - insert( initial ); - update( mod ); - ASSERT_EQUALS( after , findOne() ); - _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 inc2 : public SingleTest { - virtual BSONObj initial() { - return BSON( "_id" << 1 << "x" << 1 ); - } - virtual BSONObj mod() { - return BSON( "$inc" << BSON( "x" << 2.5 ) ); - } - virtual BSONObj after() { - return BSON( "_id" << 1 << "x" << 3.5 ); - } - virtual const char * ns() { - return "unittests.inc2"; - } - - }; - - class inc3 : public SingleTest { - virtual BSONObj initial() { - return BSON( "_id" << 1 << "x" << 537142123123LL ); - } - virtual BSONObj mod() { - return BSON( "$inc" << BSON( "x" << 2 ) ); - } - virtual BSONObj after() { - return BSON( "_id" << 1 << "x" << 537142123125LL ); - } - virtual const char * ns() { - return "unittests.inc3"; - } +namespace { - }; - - class inc4 : public SingleTest { - virtual BSONObj initial() { - return BSON( "_id" << 1 << "x" << 537142123123LL ); - } - virtual BSONObj mod() { - return BSON( "$inc" << BSON( "x" << 2LL ) ); - } - virtual BSONObj after() { - return BSON( "_id" << 1 << "x" << 537142123125LL ); - } - virtual const char * ns() { - return "unittests.inc4"; - } - - }; - - class inc5 : public SingleTest { - virtual BSONObj initial() { - return BSON( "_id" << 1 << "x" << 537142123123LL ); - } - virtual BSONObj mod() { - return BSON( "$inc" << BSON( "x" << 2.0 ) ); - } - virtual BSONObj after() { - return BSON( "_id" << 1 << "x" << 537142123125LL ); - } - virtual const char * ns() { - return "unittests.inc5"; - } - - }; - - class inc6 : public Base { - - virtual const char * ns() { - return "unittests.inc6"; - } - - - virtual BSONObj initial() { return BSONObj(); } - virtual BSONObj mod() { return BSONObj(); } - virtual BSONObj after() { return BSONObj(); } - - void dotest() { - long long start = numeric_limits<int>::max() - 5; - long long max = numeric_limits<int>::max() + 5ll; - - _client.insert( ns() , BSON( "x" << (int)start ) ); - ASSERT( findOne()["x"].type() == NumberInt ); - - while ( start < max ) { - update( BSON( "$inc" << BSON( "x" << 1 ) ) ); - start += 1; - ASSERT_EQUALS( start , findOne()["x"].numberLong() ); // SERVER-2005 - } - - ASSERT( findOne()["x"].type() == NumberLong ); - } - }; - - 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 ) ); - } - }; - - - }; +/** + * Comparator between two BSONObjects that takes in consideration only the keys and + * direction described in the sort pattern. + * + * TODO: This was pulled from update_internal.h, we should verify that these tests work + * with the new update framework $push sorter. + */ +struct ProjectKeyCmp { + BSONObj sortPattern; + + ProjectKeyCmp(BSONObj pattern) : sortPattern(pattern) {} + + int operator()(const BSONObj& left, const BSONObj& right) const { + BSONObj keyLeft = left.extractFields(sortPattern, true); + BSONObj keyRight = right.extractFields(sortPattern, true); + return keyLeft.woCompare(keyRight, sortPattern) < 0; + } +}; + +} // namespace +class PushSortSortMixed { +public: + void run() { + BSONObj objs[3]; + objs[0] = fromjson("{a:1, b:1}"); + objs[1] = fromjson("{a:3, b:1}"); + objs[2] = fromjson("{a:2, b:3}"); - 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< SetOnInsertFromEmpty >(); - add< SetOnInsertFromNonExistent >(); - add< SetOnInsertFromNonExistentWithQuery >(); - add< SetOnInsertFromNonExistentWithQueryOverField >(); - add< SetOnInsertMissingField >(); - add< SetOnInsertExisting >(); - add< SetOnInsertMixed >(); - add< SetOnInsertMissingParent >(); - 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< PushEachSimple >(); - add< PushEachFromEmpty >(); - add< PushSliceBelowFull >(); - add< PushSliceReachedFullExact >(); - add< PushSliceReachedFullWithEach >(); - add< PushSliceReachedFullWithBoth >(); - add< PushSliceToZero >(); - add< PushSliceToZeroFromNothing >(); - add< PushSliceFromNothing >(); - add< PushSliceLongerThanSliceFromNothing >(); - add< PushSliceFromEmpty >(); - add< PushSliceLongerThanSliceFromEmpty >(); - add< PushSliceTwoFields >(); - add< PushSliceAndNormal >(); - add< PushSliceTwoFieldsConflict >(); - add< PushSliceAndNormalConflict >(); - add< PushSliceInvalidEachType >(); - add< PushSliceInvalidSliceType >(); - add< PushSliceInvalidSliceValue >(); - add< PushSliceInvalidSliceDouble >(); - add< PushSliceValidSliceDouble >(); - add< PushSliceInvalidSlice >(); - add< PushSortBelowFull >(); - add< PushSortReachedFullExact >(); - add< PushSortReachedFullWithBoth >(); - add< PushSortToZero >(); - add< PushSortToZeroFromNothing >(); - add< PushSortFromNothing >(); - add< PushSortLongerThanSliceFromNothing >(); - add< PushSortFromEmpty >(); - add< PushSortLongerThanSliceFromEmpty >(); - add< PushSortSortMixed >(); - add< PushSortSortOutOfOrderFields >(); - add< PushSortSortExtraFields >(); - add< PushSortSortMissingFields >(); - add< PushSortSortNestedFields >(); - add< PushSortInvalidSortPattern >(); - add< PushSortInvalidEachType >(); - add< PushSortInvalidSortType >(); - add< PushSortInvalidSortValue >(); - add< PushSortInvalidSortDouble >(); - add< PushSortValidSortDouble >(); - add< PushSortInvalidSortSort >(); - add< PushSortInvalidSortSortOrder >(); - add< PushSortInvertedSortAndSlice >(); - add< PushSortInvalidDuplicatedSort >(); - add< CantIncParent >(); - add< DontDropEmpty >(); - add< InsertInEmpty >(); - add< IndexParentOfMod >(); - add< PreserveIdWithIndex >(); - add< CheckNoMods >(); - add< UpdateMissingToNull >(); - add< TwoModsWithinDuplicatedField >(); - add< ThreeModsWithinDuplicatedField >(); - add< TwoModsBeforeExistingField >(); - add< basic::inc1 >(); - add< basic::inc2 >(); - add< basic::inc3 >(); - add< basic::inc4 >(); - add< basic::inc5 >(); - add< basic::inc6 >(); - add< basic::bit1 >(); - add< basic::unset >(); - add< basic::setswitchint >(); + vector<BSONObj> workArea; + for (int i = 0; i < 3; i++) { + workArea.push_back(objs[i]); } - }; - SuiteInstance<All> myall; + sort(workArea.begin(), workArea.end(), ProjectKeyCmp(BSON("b" << 1 << "a" << -1))); + + ASSERT_EQUALS(workArea[0], objs[1]); + ASSERT_EQUALS(workArea[1], objs[0]); + ASSERT_EQUALS(workArea[2], objs[2]); + } +}; -} // namespace UpdateTests +class PushSortSortOutOfOrderFields { +public: + void run() { + BSONObj objs[3]; + objs[0] = fromjson("{b:1, a:1}"); + objs[1] = fromjson("{a:3, b:2}"); + objs[2] = fromjson("{b:3, a:2}"); + vector<BSONObj> workArea; + for (int i = 0; i < 3; i++) { + workArea.push_back(objs[i]); + } + + sort(workArea.begin(), workArea.end(), ProjectKeyCmp(BSON("a" << 1 << "b" << 1))); + + ASSERT_EQUALS(workArea[0], objs[0]); + ASSERT_EQUALS(workArea[1], objs[2]); + ASSERT_EQUALS(workArea[2], objs[1]); + } +}; + +class PushSortSortExtraFields { +public: + void run() { + BSONObj objs[3]; + objs[0] = fromjson("{b:1, c:2, a:1}"); + objs[1] = fromjson("{c:1, a:3, b:2}"); + objs[2] = fromjson("{b:3, a:2}"); + + vector<BSONObj> workArea; + for (int i = 0; i < 3; i++) { + workArea.push_back(objs[i]); + } + + sort(workArea.begin(), workArea.end(), ProjectKeyCmp(BSON("a" << 1 << "b" << 1))); + + ASSERT_EQUALS(workArea[0], objs[0]); + ASSERT_EQUALS(workArea[1], objs[2]); + ASSERT_EQUALS(workArea[2], objs[1]); + } +}; + +class PushSortSortMissingFields { +public: + void run() { + BSONObj objs[3]; + objs[0] = fromjson("{a:2, b:2}"); + objs[1] = fromjson("{a:1}"); + objs[2] = fromjson("{a:3, b:3, c:3}"); + + vector<BSONObj> workArea; + for (int i = 0; i < 3; i++) { + workArea.push_back(objs[i]); + } + + sort(workArea.begin(), workArea.end(), ProjectKeyCmp(BSON("b" << 1 << "c" << 1))); + + ASSERT_EQUALS(workArea[0], objs[1]); + ASSERT_EQUALS(workArea[1], objs[0]); + ASSERT_EQUALS(workArea[2], objs[2]); + } +}; + +class PushSortSortNestedFields { +public: + void run() { + BSONObj objs[3]; + objs[0] = fromjson("{a:{b:{c:2, d:0}}}"); + objs[1] = fromjson("{a:{b:{c:1, d:2}}}"); + objs[2] = fromjson("{a:{b:{c:3, d:1}}}"); + + vector<BSONObj> workArea; + for (int i = 0; i < 3; i++) { + workArea.push_back(objs[i]); + } + + sort(workArea.begin(), workArea.end(), ProjectKeyCmp(fromjson("{'a.b.d':-1}"))); + + ASSERT_EQUALS(workArea[0], objs[1]); + ASSERT_EQUALS(workArea[1], objs[2]); + ASSERT_EQUALS(workArea[2], objs[0]); + + sort(workArea.begin(), workArea.end(), ProjectKeyCmp(fromjson("{'a.b':1}"))); + + ASSERT_EQUALS(workArea[0], objs[1]); + ASSERT_EQUALS(workArea[1], objs[0]); + ASSERT_EQUALS(workArea[2], objs[2]); + } +}; + +class PushSortInvalidSortPattern : public SetBase { +public: + void run() { + // Sort pattern validation is made during update command checking. Therefore, to + // catch bad patterns, we have to write updated that use them. + + BSONObj expected = fromjson("{'_id':0,x:[{a:1}, {a:2}]}"); + _client.insert(ns(), expected); + + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a..d:1} } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2 << "$sort" + << BSON("a..d" << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + + + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a.:1} } } } + pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2 << "$sort" + << BSON("a." << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {.b:1} } } } + pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2 << "$sort" + << BSON(".b" << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {.:1} } } } + pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2 << "$sort" + << BSON("." << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {'':1} } } } + pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2 << "$sort" + << BSON("" << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +class PushSortInvalidEachType : public SetBase { +public: + void run() { + BSONObj expected = fromjson("{'_id':0,x:[{a:1},{a:2}]}"); + _client.insert(ns(), expected); + // { $push : { x : { $each : [ 3 ], $slice:-2, $sort : {a:1} } } } + BSONObj pushObj = + BSON("$each" << BSON_ARRAY(3) << "$slice" << -2 << "$sort" << BSON("a" << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +class PushSortInvalidSortType : public SetBase { +public: + void run() { + BSONObj expected = fromjson("{'_id':0,x:[{a:1},{a:2}]}"); + _client.insert(ns(), expected); + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : 2} } } + BSONObj pushObj = + BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2 << "$sort" << 2); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +class PushSortInvalidSortValue : public SetBase { +public: + void run() { + BSONObj expected = fromjson("{'_id':0,x:[{a:1},{a:2}]}"); + _client.insert(ns(), expected); + // { $push : { x : { $each : [ {a:3} ], $slice:2, $sort : {a:1} } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << 2 << "$sort" + << BSON("a" << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +class PushSortInvalidSortDouble : public SetBase { +public: + void run() { + BSONObj expected = fromjson("{'_id':0,x:[{a:1},{a:2}]}"); + _client.insert(ns(), expected); + // { $push : { x : { $each : [ {a:3} ], $slice:-2.1, $sort : {a:1} } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2.1 << "$sort" + << BSON("a" << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +class PushSortValidSortDouble : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,x:[{a:1},{a:2}]}")); + // { $push : { x : { $each : [ {a:3} ], $slice:-2.0, $sort : {a:1} } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2.0 << "$sort" + << BSON("a" << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj expected = fromjson("{'_id':0,x:[{a:2},{a:3}]}"); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +class PushSortInvalidSortSort : public SetBase { +public: + void run() { + BSONObj expected = fromjson("{'_id':0,x:[{a:1},{a:2}]}"); + _client.insert(ns(), expected); + // { $push : { x : { $each : [ {a:3} ], $slice:-2.0, $sort : [2, 1] } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2.0 << "$sort" + << BSON_ARRAY(2 << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +class PushSortInvalidSortSortOrder : public SetBase { +public: + void run() { + BSONObj expected = fromjson("{'_id':0,x:[{a:1},{a:2}]}"); + _client.insert(ns(), expected); + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a:10} } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 3)) << "$slice" << -2 << "$sort" + << BSON("a" << 10)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +class PushSortInvertedSortAndSlice : public SetBase { +public: + void run() { + _client.insert(ns(), fromjson("{'_id':0,x:[{a:1},{a:3}]}")); + // { $push : { x : { $each : [ {a:2} ], $sort: {a:1}, $slice:-2 } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 2)) << "$sort" << BSON("a" << 1) + << "$slice" << -2.0); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj expected = fromjson("{'_id':0,x:[{a:2},{a:3}]}"); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +class PushSortInvalidDuplicatedSort : public SetBase { +public: + void run() { + BSONObj expected = fromjson("{'_id':0,x:[{a:1},{a:3}]}"); + _client.insert(ns(), expected); + // { $push : { x : { $each : [ {a:2} ], $sort : {a:1}, $sort: {a:1} } } } + BSONObj pushObj = BSON("$each" << BSON_ARRAY(BSON("a" << 2)) << "$sort" << BSON("a" << 1) + << "$sort" << BSON("a" << 1)); + _client.update(ns(), Query(), BSON("$push" << BSON("x" << pushObj))); + BSONObj result = _client.findOne(ns(), Query()); + ASSERT_EQUALS(result, expected); + } +}; + +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() { + ASSERT_OK(dbtests::createIndex(&_txn, 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 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\":1}")).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()); + } +}; + +/** SERVER-4777 */ +class TwoModsWithinDuplicatedField : public SetBase { +public: + void run() { + _client.insert( + ns(), BSON("_id" << 0 << "a" << 1 << "x" << BSONObj() << "x" << BSONObj() << "z" << 5)); + _client.update(ns(), BSONObj(), BSON("$set" << BSON("x.b" << 1 << "x.c" << 1))); + ASSERT_EQUALS(BSON("_id" << 0 << "a" << 1 << "x" << BSON("b" << 1 << "c" << 1) << "x" + << BSONObj() << "z" << 5), + _client.findOne(ns(), BSONObj())); + } +}; + +/** SERVER-4777 */ +class ThreeModsWithinDuplicatedField : public SetBase { +public: + void run() { + _client.insert( + ns(), BSON("_id" << 0 << "x" << BSONObj() << "x" << BSONObj() << "x" << BSONObj())); + _client.update( + ns(), BSONObj(), BSON("$set" << BSON("x.b" << 1 << "x.c" << 1 << "x.d" << 1))); + ASSERT_EQUALS(BSON("_id" << 0 << "x" << BSON("b" << 1 << "c" << 1 << "d" << 1) << "x" + << BSONObj() << "x" << BSONObj()), + _client.findOne(ns(), BSONObj())); + } +}; + +class TwoModsBeforeExistingField : public SetBase { +public: + void run() { + _client.insert(ns(), BSON("_id" << 0 << "x" << 5)); + _client.update(ns(), BSONObj(), BSON("$set" << BSON("a" << 1 << "b" << 1 << "x" << 10))); + ASSERT_EQUALS(mutablebson::unordered(BSON("_id" << 0 << "a" << 1 << "b" << 1 << "x" << 10)), + mutablebson::unordered(_client.findOne(ns(), BSONObj()))); + } +}; + +namespace basic { +class Base : public ClientBase { +protected: + virtual const char* ns() = 0; + virtual void dotest() = 0; + + void insert(const BSONObj& o) { + _client.insert(ns(), o); + } + + void update(const BSONObj& m) { + _client.update(ns(), BSONObj(), m); + } + + BSONObj findOne() { + return _client.findOne(ns(), BSONObj()); + } + + 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()); + insert(initial); + update(mod); + ASSERT_EQUALS(after, findOne()); + _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 inc2 : public SingleTest { + virtual BSONObj initial() { + return BSON("_id" << 1 << "x" << 1); + } + virtual BSONObj mod() { + return BSON("$inc" << BSON("x" << 2.5)); + } + virtual BSONObj after() { + return BSON("_id" << 1 << "x" << 3.5); + } + virtual const char* ns() { + return "unittests.inc2"; + } +}; + +class inc3 : public SingleTest { + virtual BSONObj initial() { + return BSON("_id" << 1 << "x" << 537142123123LL); + } + virtual BSONObj mod() { + return BSON("$inc" << BSON("x" << 2)); + } + virtual BSONObj after() { + return BSON("_id" << 1 << "x" << 537142123125LL); + } + virtual const char* ns() { + return "unittests.inc3"; + } +}; + +class inc4 : public SingleTest { + virtual BSONObj initial() { + return BSON("_id" << 1 << "x" << 537142123123LL); + } + virtual BSONObj mod() { + return BSON("$inc" << BSON("x" << 2LL)); + } + virtual BSONObj after() { + return BSON("_id" << 1 << "x" << 537142123125LL); + } + virtual const char* ns() { + return "unittests.inc4"; + } +}; + +class inc5 : public SingleTest { + virtual BSONObj initial() { + return BSON("_id" << 1 << "x" << 537142123123LL); + } + virtual BSONObj mod() { + return BSON("$inc" << BSON("x" << 2.0)); + } + virtual BSONObj after() { + return BSON("_id" << 1 << "x" << 537142123125LL); + } + virtual const char* ns() { + return "unittests.inc5"; + } +}; + +class inc6 : public Base { + virtual const char* ns() { + return "unittests.inc6"; + } + + + virtual BSONObj initial() { + return BSONObj(); + } + virtual BSONObj mod() { + return BSONObj(); + } + virtual BSONObj after() { + return BSONObj(); + } + + void dotest() { + long long start = numeric_limits<int>::max() - 5; + long long max = numeric_limits<int>::max() + 5ll; + + _client.insert(ns(), BSON("x" << (int)start)); + ASSERT(findOne()["x"].type() == NumberInt); + + while (start < max) { + update(BSON("$inc" << BSON("x" << 1))); + start += 1; + ASSERT_EQUALS(start, findOne()["x"].numberLong()); // SERVER-2005 + } + + ASSERT(findOne()["x"].type() == NumberLong); + } +}; + +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<SetOnInsertFromEmpty>(); + add<SetOnInsertFromNonExistent>(); + add<SetOnInsertFromNonExistentWithQuery>(); + add<SetOnInsertFromNonExistentWithQueryOverField>(); + add<SetOnInsertMissingField>(); + add<SetOnInsertExisting>(); + add<SetOnInsertMixed>(); + add<SetOnInsertMissingParent>(); + 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<PushEachSimple>(); + add<PushEachFromEmpty>(); + add<PushSliceBelowFull>(); + add<PushSliceReachedFullExact>(); + add<PushSliceReachedFullWithEach>(); + add<PushSliceReachedFullWithBoth>(); + add<PushSliceToZero>(); + add<PushSliceToZeroFromNothing>(); + add<PushSliceFromNothing>(); + add<PushSliceLongerThanSliceFromNothing>(); + add<PushSliceFromEmpty>(); + add<PushSliceLongerThanSliceFromEmpty>(); + add<PushSliceTwoFields>(); + add<PushSliceAndNormal>(); + add<PushSliceTwoFieldsConflict>(); + add<PushSliceAndNormalConflict>(); + add<PushSliceInvalidEachType>(); + add<PushSliceInvalidSliceType>(); + add<PushSliceInvalidSliceValue>(); + add<PushSliceInvalidSliceDouble>(); + add<PushSliceValidSliceDouble>(); + add<PushSliceInvalidSlice>(); + add<PushSortBelowFull>(); + add<PushSortReachedFullExact>(); + add<PushSortReachedFullWithBoth>(); + add<PushSortToZero>(); + add<PushSortToZeroFromNothing>(); + add<PushSortFromNothing>(); + add<PushSortLongerThanSliceFromNothing>(); + add<PushSortFromEmpty>(); + add<PushSortLongerThanSliceFromEmpty>(); + add<PushSortSortMixed>(); + add<PushSortSortOutOfOrderFields>(); + add<PushSortSortExtraFields>(); + add<PushSortSortMissingFields>(); + add<PushSortSortNestedFields>(); + add<PushSortInvalidSortPattern>(); + add<PushSortInvalidEachType>(); + add<PushSortInvalidSortType>(); + add<PushSortInvalidSortValue>(); + add<PushSortInvalidSortDouble>(); + add<PushSortValidSortDouble>(); + add<PushSortInvalidSortSort>(); + add<PushSortInvalidSortSortOrder>(); + add<PushSortInvertedSortAndSlice>(); + add<PushSortInvalidDuplicatedSort>(); + add<CantIncParent>(); + add<DontDropEmpty>(); + add<InsertInEmpty>(); + add<IndexParentOfMod>(); + add<PreserveIdWithIndex>(); + add<CheckNoMods>(); + add<UpdateMissingToNull>(); + add<TwoModsWithinDuplicatedField>(); + add<ThreeModsWithinDuplicatedField>(); + add<TwoModsBeforeExistingField>(); + add<basic::inc1>(); + add<basic::inc2>(); + add<basic::inc3>(); + add<basic::inc4>(); + add<basic::inc5>(); + add<basic::inc6>(); + add<basic::bit1>(); + add<basic::unset>(); + add<basic::setswitchint>(); + } +}; + +SuiteInstance<All> myall; + +} // namespace UpdateTests |