From 9c2ed42daa8fbbef4a919c21ec564e2db55e8d60 Mon Sep 17 00:00:00 2001 From: Mark Benvenuto Date: Sat, 20 Jun 2015 00:22:50 -0400 Subject: SERVER-18579: Clang-Format - reformat code, no comment reflow --- src/mongo/db/ops/modifier_push_test.cpp | 2445 +++++++++++++++---------------- 1 file changed, 1215 insertions(+), 1230 deletions(-) (limited to 'src/mongo/db/ops/modifier_push_test.cpp') diff --git a/src/mongo/db/ops/modifier_push_test.cpp b/src/mongo/db/ops/modifier_push_test.cpp index 884566d48ac..275e1a4945e 100644 --- a/src/mongo/db/ops/modifier_push_test.cpp +++ b/src/mongo/db/ops/modifier_push_test.cpp @@ -47,1409 +47,1394 @@ namespace { - using mongo::BSONObj; - using mongo::BSONObjBuilder; - using mongo::BSONArrayBuilder; - using mongo::fromjson; - using mongo::LogBuilder; - using mongo::ModifierInterface; - using mongo::ModifierPush; - using mongo::NumberInt; - using mongo::Ordering; - using mongo::Status; - using mongo::StringData; - using mongo::mutablebson::ConstElement; - using mongo::mutablebson::countChildren; - using mongo::mutablebson::Document; - using mongo::mutablebson::Element; - using std::sort; - using std::vector; - - void combineVec(const vector& origVec, - const vector& modVec, - int32_t slice, - vector* combined) { - - using namespace std; - combined->clear(); - - // Slice 0 means the result is empty - if (slice == 0) - return; - - // Combine both vectors - *combined = origVec; - combined->insert(combined->end(), modVec.begin(), modVec.end()); - - // Remove sliced items - bool removeFromFront = (slice < 0); - - // if abs(slice) is larger than the size, nothing to do. - if (abs(slice) >= int32_t(combined->size())) - return; - - if (removeFromFront) { - // Slice is negative. - int32_t removeCount = combined->size() + slice; - combined->erase(combined->begin(), combined->begin() + removeCount); - } - else { - combined->resize(std::min(combined->size(), size_t(slice))); - } - } - - /** - * Comparator between two BSONObjects that takes in consideration only the keys and - * direction described in the sort pattern. - */ - struct ProjectKeyCmp { - BSONObj sortPattern; - bool useWholeValue; - - ProjectKeyCmp(BSONObj pattern) : sortPattern(pattern) { - useWholeValue = pattern.hasField(""); - } - - int operator()(const BSONObj& left, const BSONObj& right) const { - int ret = 0; - if (useWholeValue) { - ret = left.woCompare( right, Ordering::make(sortPattern), false ); - } else { - BSONObj lhsKey = left.extractFields(sortPattern, true); - BSONObj rhsKey = right.extractFields(sortPattern, true); - ret = lhsKey.woCompare(rhsKey, sortPattern); - } - return ret < 0; - } - }; - - void combineAndSortVec(const vector& origVec, - const vector& modVec, - int32_t slice, - BSONObj sortOrder, - vector* combined) { - - combined->clear(); +using mongo::BSONObj; +using mongo::BSONObjBuilder; +using mongo::BSONArrayBuilder; +using mongo::fromjson; +using mongo::LogBuilder; +using mongo::ModifierInterface; +using mongo::ModifierPush; +using mongo::NumberInt; +using mongo::Ordering; +using mongo::Status; +using mongo::StringData; +using mongo::mutablebson::ConstElement; +using mongo::mutablebson::countChildren; +using mongo::mutablebson::Document; +using mongo::mutablebson::Element; +using std::sort; +using std::vector; + +void combineVec(const vector& origVec, + const vector& modVec, + int32_t slice, + vector* combined) { + using namespace std; + combined->clear(); + + // Slice 0 means the result is empty + if (slice == 0) + return; + + // Combine both vectors + *combined = origVec; + combined->insert(combined->end(), modVec.begin(), modVec.end()); + + // Remove sliced items + bool removeFromFront = (slice < 0); + + // if abs(slice) is larger than the size, nothing to do. + if (abs(slice) >= int32_t(combined->size())) + return; + + if (removeFromFront) { + // Slice is negative. + int32_t removeCount = combined->size() + slice; + combined->erase(combined->begin(), combined->begin() + removeCount); + } else { + combined->resize(std::min(combined->size(), size_t(slice))); + } +} - // Slice 0 means the result is empty - if (slice == 0) - return; - - *combined = origVec; - combined->insert(combined->end(), modVec.begin(), modVec.end()); - - sort(combined->begin(), combined->end(), ProjectKeyCmp(sortOrder)); - - // Remove sliced items - bool removeFromFront = (slice < 0); - - // if abs(slice) is larger than the size, nothing to do. - if (abs(slice) >= int32_t(combined->size())) - return; - - if (removeFromFront) { - // Slice is negative. - int32_t removeCount = combined->size() + slice; - combined->erase(combined->begin(), combined->begin() + removeCount); - } - else { - combined->resize(std::min(combined->size(), size_t(slice))); +/** + * Comparator between two BSONObjects that takes in consideration only the keys and + * direction described in the sort pattern. + */ +struct ProjectKeyCmp { + BSONObj sortPattern; + bool useWholeValue; + + ProjectKeyCmp(BSONObj pattern) : sortPattern(pattern) { + useWholeValue = pattern.hasField(""); + } + + int operator()(const BSONObj& left, const BSONObj& right) const { + int ret = 0; + if (useWholeValue) { + ret = left.woCompare(right, Ordering::make(sortPattern), false); + } else { + BSONObj lhsKey = left.extractFields(sortPattern, true); + BSONObj rhsKey = right.extractFields(sortPattern, true); + ret = lhsKey.woCompare(rhsKey, sortPattern); } - } - - // - // Init testing (module field checking, which is done in 'fieldchecker' - // - - TEST(Init, SimplePush) { - BSONObj modObj = fromjson("{$push: {x: 0}}"); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + return ret < 0; + } +}; + +void combineAndSortVec(const vector& origVec, + const vector& modVec, + int32_t slice, + BSONObj sortOrder, + vector* combined) { + combined->clear(); + + // Slice 0 means the result is empty + if (slice == 0) + return; + + *combined = origVec; + combined->insert(combined->end(), modVec.begin(), modVec.end()); + + sort(combined->begin(), combined->end(), ProjectKeyCmp(sortOrder)); + + // Remove sliced items + bool removeFromFront = (slice < 0); + + // if abs(slice) is larger than the size, nothing to do. + if (abs(slice) >= int32_t(combined->size())) + return; + + if (removeFromFront) { + // Slice is negative. + int32_t removeCount = combined->size() + slice; + combined->erase(combined->begin(), combined->begin() + removeCount); + } else { + combined->resize(std::min(combined->size(), size_t(slice))); + } +} + +// +// Init testing (module field checking, which is done in 'fieldchecker' +// + +TEST(Init, SimplePush) { + BSONObj modObj = fromjson("{$push: {x: 0}}"); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +// +// If present, is the $each clause valid? +// + +TEST(Init, PushEachNormal) { + BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2]}}}"); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushEachMixed) { + BSONObj modObj = fromjson("{$push: {x: {$each: [1, {a: 2}]}}}"); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushEachObject) { + // $each must be an array + BSONObj modObj = fromjson("{$push: {x: {$each: {'0': 1}}}}"); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } +} - // - // If present, is the $each clause valid? - // - - TEST(Init, PushEachNormal) { - BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2]}}}"); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), +TEST(Init, PushEachSimpleType) { + // $each must be an array. + BSONObj modObj = fromjson("{$push: {x: {$each: 1}}}"); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachMixed) { - BSONObj modObj = fromjson("{$push: {x: {$each: [1, {a: 2}]}}}"); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), +} + +TEST(Init, PushEachEmpty) { + BSONObj modObj = fromjson("{$push: {x: {$each: []}}}"); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushEachInvalidType) { + // $each must be an array. + BSONObj modObj = fromjson("{$push: {x: {$each: {b: 1}}}}"); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachObject) { - // $each must be an array - BSONObj modObj = fromjson("{$push: {x: {$each: {'0': 1}}}}"); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachSimpleType) { - // $each must be an array. - BSONObj modObj = fromjson("{$push: {x: {$each: 1}}}"); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachEmpty) { - BSONObj modObj = fromjson("{$push: {x: {$each: []}}}"); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), +} + +// +// If present, is the $slice clause valid? +// + +TEST(Init, PushEachWithSliceBottom) { + BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: -3}}}"); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushEachWithSliceTop) { + BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: 3}}}"); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushEachWithInvalidSliceObject) { + BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: {a: 1}}}}"); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachInvalidType) { - // $each must be an array. - BSONObj modObj = fromjson("{$push: {x: {$each: {b: 1}}}}"); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +} - // - // If present, is the $slice clause valid? - // - - TEST(Init, PushEachWithSliceBottom) { - BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: -3}}}"); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), +TEST(Init, PushEachWithInvalidSliceDouble) { + BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: -2.1}}}"); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachWithSliceTop) { - BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: 3}}}"); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachWithInvalidSliceObject) { - BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: {a: 1}}}}"); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachWithInvalidSliceDouble) { - BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: -2.1}}}"); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachWithValidSliceDouble) { - BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: -2.0}}}"); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), +} + +TEST(Init, PushEachWithValidSliceDouble) { + BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: -2.0}}}"); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushEachWithUnsupportedFullSlice) { + BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: [1,2]}}}"); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } +} - TEST(Init, PushEachWithUnsupportedFullSlice) { - BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: [1,2]}}}"); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachWithWrongTypeSlice) { - BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: '-1'}}}"); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - // - // If present, is the sort $sort clause valid? - // - - TEST(Init, PushEachWithObjectSort) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {a:1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), +TEST(Init, PushEachWithWrongTypeSlice) { + BSONObj modObj = fromjson("{$push: {x: {$each: [1, 2], $slice: '-1'}}}"); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachWithNumbericSort) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort:1 }}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachWithInvalidSortType) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: [{a:1}]}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachDuplicateSortPattern) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: [{a:1,a:1}]}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - TEST(Init, PushEachWithInvalidSortValue) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {a:100}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +} + +// +// If present, is the sort $sort clause valid? +// + +TEST(Init, PushEachWithObjectSort) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {a:1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushEachWithNumbericSort) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort:1 }}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushEachWithInvalidSortType) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: [{a:1}]}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushEachWithEmptySortField) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'':1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +TEST(Init, PushEachDuplicateSortPattern) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: [{a:1,a:1}]}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushEachWithEmptyDottedSortField) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'.':1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +TEST(Init, PushEachWithInvalidSortValue) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {a:100}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushEachWithMissingSortFieldSuffix) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'a.':1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +TEST(Init, PushEachWithEmptySortField) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'':1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushEachWithMissingSortFieldPreffix) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'.b':1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +TEST(Init, PushEachWithEmptyDottedSortField) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'.':1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushEachWithMissingSortFieldMiddle) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'a..b':1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +TEST(Init, PushEachWithMissingSortFieldSuffix) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'a.':1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushEachWithEmptySort) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort:{} }}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +TEST(Init, PushEachWithMissingSortFieldPreffix) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'.b':1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - // - // If in $pushAll semantics, do we check the array and that nothing else is there? - // +TEST(Init, PushEachWithMissingSortFieldMiddle) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {'a..b':1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushAllSimple) { - BSONObj modObj = fromjson("{$pushAll: {x: [0]}}"); - ModifierPush mod(ModifierPush::PUSH_ALL); - ASSERT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), +TEST(Init, PushEachWithEmptySort) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort:{} }}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } +} + +// +// If in $pushAll semantics, do we check the array and that nothing else is there? +// + +TEST(Init, PushAllSimple) { + BSONObj modObj = fromjson("{$pushAll: {x: [0]}}"); + ModifierPush mod(ModifierPush::PUSH_ALL); + ASSERT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushAllMultiple) { + BSONObj modObj = fromjson("{$pushAll: {x: [1,2,3]}}"); + ModifierPush mod(ModifierPush::PUSH_ALL); + ASSERT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushAllObject) { + BSONObj modObj = fromjson("{$pushAll: {x: [{a:1},{a:2}]}}"); + ModifierPush mod(ModifierPush::PUSH_ALL); + ASSERT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushAllMixed) { + BSONObj modObj = fromjson("{$pushAll: {x: [1,{a:2}]}}"); + ModifierPush mod(ModifierPush::PUSH_ALL); + ASSERT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushAllWrongType) { + BSONObj modObj = fromjson("{$pushAll: {x: 1}}"); + ModifierPush mod(ModifierPush::PUSH_ALL); + ASSERT_NOT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushAllMultiple) { - BSONObj modObj = fromjson("{$pushAll: {x: [1,2,3]}}"); - ModifierPush mod(ModifierPush::PUSH_ALL); - ASSERT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), +TEST(Init, PushAllNotArray) { + BSONObj modObj = fromjson("{$pushAll: {x: {a:1}}}"); + ModifierPush mod(ModifierPush::PUSH_ALL); + ASSERT_NOT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } +} + +// +// Are all clauses present? Is anything extroneous? Is anything duplicated? +// + +TEST(Init, PushEachWithSortMissingSlice) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $sort:{a:1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, PushEachInvalidClause) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $xxx: -1, $sort:{a:1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushAllObject) { - BSONObj modObj = fromjson("{$pushAll: {x: [{a:1},{a:2}]}}"); - ModifierPush mod(ModifierPush::PUSH_ALL); - ASSERT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), +TEST(Init, PushEachExtraField) { + const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {a:1}, b: 1}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } +} - TEST(Init, PushAllMixed) { - BSONObj modObj = fromjson("{$pushAll: {x: [1,{a:2}]}}"); - ModifierPush mod(ModifierPush::PUSH_ALL); - ASSERT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), +TEST(Init, PushEachDuplicateSortClause) { + const char* c = "{$push: {x:{$each:[{a:1},{a:2}], $slice:-2.0, $sort:{a:1}, $sort:{a:1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), ModifierInterface::Options::normal())); - } +} - TEST(Init, PushAllWrongType) { - BSONObj modObj = fromjson("{$pushAll: {x: 1}}"); - ModifierPush mod(ModifierPush::PUSH_ALL); - ASSERT_NOT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +TEST(Init, PushEachDuplicateSliceClause) { + const char* c = "{$push: {x: {$each:[{a:1},{a:2}], $slice:-2.0, $slice:-2, $sort:{a:1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushAllNotArray) { - BSONObj modObj = fromjson("{$pushAll: {x: {a:1}}}"); - ModifierPush mod(ModifierPush::PUSH_ALL); - ASSERT_NOT_OK(mod.init(modObj["$pushAll"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +TEST(Init, PushEachDuplicateEachClause) { + const char* c = "{$push: {x: {$each:[{a:1}], $each:[{a:2}], $slice:-3, $sort:{a:1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - // - // Are all clauses present? Is anything extroneous? Is anything duplicated? - // +TEST(Init, PushEachWithSliceFirst) { + const char* c = "{$push: {x: {$slice: -2.0, $each: [{a:1},{a:2}], $sort: {a:1}}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushEachWithSortMissingSlice) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $sort:{a:1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +TEST(Init, PushEachWithSortFirst) { + const char* c = "{$push: {x: {$sort: {a:1}, $slice: -2.0, $each: [{a:1},{a:2}]}}}"; + BSONObj modObj = fromjson(c); + ModifierPush mod; + ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} - TEST(Init, PushEachInvalidClause) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $xxx: -1, $sort:{a:1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +// +// Simple mod +// - TEST(Init, PushEachExtraField) { - const char* c = "{$push: {x: {$each: [{a:1},{a:2}], $slice: -2.0, $sort: {a:1}, b: 1}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } +/** Helper to build and manipulate a $push or a $pushAll mod. */ +class Mod { +public: + Mod() : _mod() {} - TEST(Init, PushEachDuplicateSortClause) { - const char* c = "{$push: {x:{$each:[{a:1},{a:2}], $slice:-2.0, $sort:{a:1}, $sort:{a:1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); + explicit Mod(BSONObj modObj) + : _mod(mongoutils::str::equals(modObj.firstElement().fieldName(), "$pushAll") + ? ModifierPush::PUSH_ALL + : ModifierPush::PUSH_NORMAL) { + _modObj = modObj; + StringData modName = modObj.firstElement().fieldName(); + ASSERT_OK(_mod.init(_modObj[modName].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); } - TEST(Init, PushEachDuplicateSliceClause) { - const char* c = "{$push: {x: {$each:[{a:1},{a:2}], $slice:-2.0, $slice:-2, $sort:{a:1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); + Status prepare(Element root, StringData matchedField, ModifierInterface::ExecInfo* execInfo) { + return _mod.prepare(root, matchedField, execInfo); } - TEST(Init, PushEachDuplicateEachClause) { - const char* c = "{$push: {x: {$each:[{a:1}], $each:[{a:2}], $slice:-3, $sort:{a:1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_NOT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); + Status apply() const { + return _mod.apply(); } - TEST(Init, PushEachWithSliceFirst) { - const char* c = "{$push: {x: {$slice: -2.0, $each: [{a:1},{a:2}], $sort: {a:1}}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } - TEST(Init, PushEachWithSortFirst) { - const char* c = "{$push: {x: {$sort: {a:1}, $slice: -2.0, $each: [{a:1},{a:2}]}}}"; - BSONObj modObj = fromjson(c); - ModifierPush mod; - ASSERT_OK(mod.init(modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); + ModifierPush& mod() { + return _mod; } - // - // Simple mod - // - - /** Helper to build and manipulate a $push or a $pushAll mod. */ - class Mod { - public: - Mod() : _mod() {} - - explicit Mod(BSONObj modObj) - : _mod(mongoutils::str::equals(modObj.firstElement().fieldName(), "$pushAll") ? - ModifierPush::PUSH_ALL : ModifierPush::PUSH_NORMAL) { - _modObj = modObj; - StringData modName = modObj.firstElement().fieldName(); - ASSERT_OK(_mod.init(_modObj[modName].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - Status prepare(Element root, - StringData matchedField, - ModifierInterface::ExecInfo* execInfo) { - return _mod.prepare(root, matchedField, execInfo); - } - - Status apply() const { - return _mod.apply(); - } +private: + ModifierPush _mod; + BSONObj _modObj; +}; - Status log(LogBuilder* logBuilder) const { - return _mod.log(logBuilder); - } +TEST(SimpleMod, PrepareNonArray) { + Document doc(fromjson("{a: 1}")); + Mod pushMod(fromjson("{$push: {a: 1}}")); - ModifierPush& mod() { return _mod; } + ModifierInterface::ExecInfo dummy; + ASSERT_NOT_OK(pushMod.prepare(doc.root(), "", &dummy)); +} - private: - ModifierPush _mod; - BSONObj _modObj; - }; +TEST(SimpleMod, PrepareApplyEmpty) { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: 1}}")); - TEST(SimpleMod, PrepareNonArray) { - Document doc(fromjson("{a: 1}")); - Mod pushMod(fromjson("{$push: {a: 1}}")); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - ModifierInterface::ExecInfo dummy; - ASSERT_NOT_OK(pushMod.prepare(doc.root(), "", &dummy)); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - TEST(SimpleMod, PrepareApplyEmpty) { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: 1}}")); + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [1]}"), doc); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); +} - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); +TEST(SimpleMod, PrepareApplyInexistent) { + Document doc(fromjson("{}")); + Mod pushMod(fromjson("{$push: {a: 1}}")); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [1]}"), doc); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - TEST(SimpleMod, PrepareApplyInexistent) { - Document doc(fromjson("{}")); - Mod pushMod(fromjson("{$push: {a: 1}}")); + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [1]}"), doc); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); +} - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); +TEST(SimpleMod, PrepareApplyNormal) { + Document doc(fromjson("{a: [0]}")); + Mod pushMod(fromjson("{$push: {a: 1}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [1]}"), doc); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); - } + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [0,1]}"), doc); - TEST(SimpleMod, PrepareApplyNormal) { - Document doc(fromjson("{a: [0]}")); - Mod pushMod(fromjson("{$push: {a: 1}}")); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'a.1':1}}"), logDoc); +} + +// +// Simple object mod +// - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); +TEST(SimpleObjMod, PrepareNonArray) { + Document doc(fromjson("{a: 1}")); + Mod pushMod(fromjson("{$push: {a: {b: 1}}}")); + + ModifierInterface::ExecInfo dummy; + ASSERT_NOT_OK(pushMod.prepare(doc.root(), "", &dummy)); +} + +TEST(SimpleObjMod, PrepareApplyEmpty) { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: {b: 1}}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [{b:1}]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [{b:1}]}}"), logDoc); +} + +TEST(SimpleObjMod, PrepareApplyInexistent) { + Document doc(fromjson("{}")); + Mod pushMod(fromjson("{$push: {a: {b: 1}}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [{b:1}]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [{b:1}]}}"), logDoc); +} + +TEST(SimpleObjMod, PrepareApplyNormal) { + Document doc(fromjson("{a: [{b:0}]}")); + Mod pushMod(fromjson("{$push: {a: {b: 1}}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [{b:0},{b:1}]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'a.1':{b:1}}}"), logDoc); +} + +TEST(SimpleObjMod, PrepareApplyDotted) { + Document doc(fromjson( + "{ _id : 1 , " + " question : 'a', " + " choices : { " + " first : { choice : 'b' }, " + " second : { choice : 'c' } }" + "}")); + Mod pushMod(fromjson("{$push: {'choices.first.votes': 1}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "choices.first.votes"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson( + "{ _id : 1 , " + " question : 'a', " + " choices : { " + " first : { choice : 'b', votes: [1]}, " + " second : { choice : 'c' } }" + "}"), + doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'choices.first.votes':[1]}}"), logDoc); +} + + +// +// $pushAll Variation +// + +TEST(PushAll, PrepareApplyEmpty) { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$pushAll: {a: [1]}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(doc, fromjson("{a: [1]}")); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(logDoc, fromjson("{$set: {a: [1]}}")); +} + +TEST(PushAll, PrepareApplyInexistent) { + Document doc(fromjson("{}")); + Mod pushMod(fromjson("{$pushAll: {a: [1]}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(doc, fromjson("{a: [1]}")); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(logDoc, fromjson("{$set: {a: [1]}}")); +} + +TEST(PushAll, PrepareApplyNormal) { + Document doc(fromjson("{a: [0]}")); + Mod pushMod(fromjson("{$pushAll: {a: [1,2]}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(doc, fromjson("{a: [0,1,2]}")); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(logDoc, fromjson("{$set: {'a.1': 1, 'a.2':2}}")); +} + +// +// Simple $each mod +// + +TEST(SimpleEachMod, PrepareNonArray) { + Document doc(fromjson("{a: 1}")); + Mod pushMod(fromjson("{$push: {a: {$each: [1]}}}")); + + ModifierInterface::ExecInfo dummy; + ASSERT_NOT_OK(pushMod.prepare(doc.root(), "", &dummy)); +} + +TEST(SimpleEachMod, PrepareApplyEmpty) { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: {$each: [1]}}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [1]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); +} + +TEST(SimpleEachMod, PrepareApplyInexistent) { + Document doc(fromjson("{}")); + Mod pushMod(fromjson("{$push: {a: {$each: [1]}}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [1]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); +} + +TEST(SimpleEachMod, PrepareApplyInexistentMultiple) { + Document doc(fromjson("{}")); + Mod pushMod(fromjson("{$push: {a: {$each: [1, 2]}}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [1, 2]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [1, 2]}}"), logDoc); +} + +TEST(SimpleEachMod, PrepareApplyNormal) { + Document doc(fromjson("{a: [0]}")); + Mod pushMod(fromjson("{$push: {a: {$each: [1]}}}")); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [0,1]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'a.1': 1}}"), logDoc); +} + +TEST(SimpleEachMod, PrepareApplyNormalMultiple) { + Document doc(fromjson("{a: [0]}")); + Mod pushMod(fromjson("{$push: {a: {$each: [1,2]}}}")); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [0,1]}"), doc); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.1':1}}"), logDoc); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [0,1,2]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'a.1': 1, 'a.2':2}}"), logDoc); +} - // - // Simple object mod - // +/** + * Slice variants + */ +TEST(SlicePushEach, TopOne) { + Document doc(fromjson("{a: [3]}")); + Mod pushMod(fromjson("{$push: {a: {$each: [2, -1], $slice:1}}}")); - TEST(SimpleObjMod, PrepareNonArray) { - Document doc(fromjson("{a: 1}")); - Mod pushMod(fromjson("{$push: {a: {b: 1}}}")); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - ModifierInterface::ExecInfo dummy; - ASSERT_NOT_OK(pushMod.prepare(doc.root(), "", &dummy)); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - TEST(SimpleObjMod, PrepareApplyEmpty) { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: {b: 1}}}")); + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [3]}"), doc); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [3]}}"), logDoc); +} - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); +/** + * Sort for scalar (whole) array elements + */ +TEST(SortPushEach, NumberSort) { + Document doc(fromjson("{a: [3]}")); + Mod pushMod(fromjson("{$push: {a: {$each: [2, -1], $sort:1}}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [-1,2,3]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [-1, 2, 3]}}"), logDoc); +} + +TEST(SortPushEach, NumberSortReverse) { + Document doc(fromjson("{a: [3]}")); + Mod pushMod(fromjson("{$push: {a: {$each: [4, -1], $sort:-1}}}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [4,3,-1]}"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {a: [4,3,-1]}}"), logDoc); +} + +TEST(SortPushEach, MixedSortWhole) { + Document doc(fromjson("{a: [3, 't', {b:1}, {a:1}]}")); + Mod pushMod(fromjson("{$push: {a: {$each: [4, -1], $sort:1}}}")); + const BSONObj expectedObj = fromjson("{a: [-1,3,4,'t', {a:1}, {b:1}]}"); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(expectedObj, doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(BSON("$set" << expectedObj), logDoc); +} + +TEST(SortPushEach, MixedSortWholeReverse) { + Document doc(fromjson("{a: [3, 't', {b:1}, {a:1}]}")); + Mod pushMod(fromjson("{$push: {a: {$each: [4, -1], $sort:-1}}}")); + const BSONObj expectedObj = fromjson("{a: [{b:1}, {a:1}, 't', 4, 3, -1]}"); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(expectedObj, doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(BSON("$set" << expectedObj), logDoc); +} + +TEST(SortPushEach, MixedSortEmbeddedField) { + Document doc(fromjson("{a: [3, 't', {b:1}, {a:1}]}")); + Mod pushMod(fromjson("{$push: {a: {$each: [4, -1], $sort:{a:1}}}}")); + const BSONObj expectedObj = fromjson("{a: [3, 't', {b: 1}, 4, -1, {a: 1}]}"); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(expectedObj, doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(BSON("$set" << expectedObj), logDoc); +} - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [{b:1}]}"), doc); +/** + * This fixture supports building $push mods with parameterized $each arrays and $slices. + * It always assume that the array being operated on is called 'a'. To build a mod, one + * issues a set*Mod() call. + * + * The setSimpleMod() call will build a $each array of numbers. The setObjectMod() call + * will build a $each array with object. Both these calls take the slice as a parameter as + * well. + * + * Here's a typical test case flow: + * + Determine what the original document's 'a' array would contain + * + Ditto for the $push's $each arrray + * + Loop over slice value + * + Apply the $push with current slice value to the doc + * + Use the fixture/helpers to combine and slice the mod's and original's 'a' + * array + * + Build a document with the above and check against the one generated by the mod apply + */ +class SlicedMod : public mongo::unittest::Test { +public: + SlicedMod() : _mod() {} - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [{b:1}]}}"), logDoc); + virtual void setUp() { + // no op; set all state using the setMod() call } - TEST(SimpleObjMod, PrepareApplyInexistent) { - Document doc(fromjson("{}")); - Mod pushMod(fromjson("{$push: {a: {b: 1}}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); + /** Sets up the mod to be {$push: {a: {$each: [], $slice: }}} */ + void setSimpleMod(int32_t slice, const vector& eachArray) { + BSONArrayBuilder arrBuilder; + for (vector::const_iterator it = eachArray.begin(); it != eachArray.end(); ++it) { + arrBuilder.append(*it); + } - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [{b:1}]}"), doc); + _modObj = + BSON("$push" << BSON("a" << BSON("$each" << arrBuilder.arr() << "$slice" << slice))); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [{b:1}]}}"), logDoc); + ASSERT_OK(_mod.init(_modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); } - TEST(SimpleObjMod, PrepareApplyNormal) { - Document doc(fromjson("{a: [{b:0}]}")); - Mod pushMod(fromjson("{$push: {a: {b: 1}}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); + /** Sets up the mod to be {$push: {a: {$each:[,...], $slice:, $sort:}}} */ + void setSortMod(int32_t slice, const vector& eachArray, BSONObj sort) { + BSONArrayBuilder arrBuilder; + for (vector::const_iterator it = eachArray.begin(); it != eachArray.end(); ++it) { + arrBuilder.append(*it); + } - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [{b:0},{b:1}]}"), doc); + _modObj = BSON("$push" << BSON("a" << BSON("$each" << arrBuilder.arr() << "$slice" << slice + << "$sort" << sort))); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.1':{b:1}}}"), logDoc); + ASSERT_OK(_mod.init(_modObj["$push"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); } - TEST(SimpleObjMod, PrepareApplyDotted) { - Document doc(fromjson("{ _id : 1 , " - " question : 'a', " - " choices : { " - " first : { choice : 'b' }, " - " second : { choice : 'c' } }" - "}")); - Mod pushMod(fromjson("{$push: {'choices.first.votes': 1}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + /** Returns an object {a: [<'vec's content>]} */ + BSONObj getObjectUsing(const vector& vec) { + BSONArrayBuilder arrBuilder; + for (vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { + arrBuilder.append(*it); + } - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "choices.first.votes"); - ASSERT_FALSE(execInfo.noOp); + BSONObjBuilder builder; + builder.appendArray("a", arrBuilder.obj()); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson( "{ _id : 1 , " - " question : 'a', " - " choices : { " - " first : { choice : 'b', votes: [1]}, " - " second : { choice : 'c' } }" - "}"), - doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'choices.first.votes':[1]}}"), logDoc); + return builder.obj(); } + /** Returns an object {a: [<'vec's content>]} */ + BSONObj getObjectUsing(const vector& vec) { + BSONArrayBuilder arrBuilder; + for (vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { + arrBuilder.append(*it); + } - // - // $pushAll Variation - // - - TEST(PushAll, PrepareApplyEmpty) { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$pushAll: {a: [1]}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(doc, fromjson("{a: [1]}")); + BSONObjBuilder builder; + builder.appendArray("a", arrBuilder.obj()); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(logDoc, fromjson("{$set: {a: [1]}}")); + return builder.obj(); } - TEST(PushAll, PrepareApplyInexistent) { - Document doc(fromjson("{}")); - Mod pushMod(fromjson("{$pushAll: {a: [1]}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(doc, fromjson("{a: [1]}")); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(logDoc, fromjson("{$set: {a: [1]}}")); + ModifierPush& mod() { + return _mod; } - TEST(PushAll, PrepareApplyNormal) { - Document doc(fromjson("{a: [0]}")); - Mod pushMod(fromjson("{$pushAll: {a: [1,2]}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(doc, fromjson("{a: [0,1,2]}")); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(logDoc, fromjson("{$set: {'a.1': 1, 'a.2':2}}")); + BSONObj modObj() { + return _modObj; } - // - // Simple $each mod - // +private: + ModifierPush _mod; + BSONObj _modObj; + vector _eachArray; +}; - TEST(SimpleEachMod, PrepareNonArray) { - Document doc(fromjson("{a: 1}")); - Mod pushMod(fromjson("{$push: {a: {$each: [1]}}}")); +TEST_F(SlicedMod, SimpleArrayFromEmpty) { + // We'll simulate the original document having {a: []} and the mod being + // {$push: {a: {$each: [1], $slice: <-2..0>}}} + vector docArray; + vector eachArray; + eachArray.push_back(1); - ModifierInterface::ExecInfo dummy; - ASSERT_NOT_OK(pushMod.prepare(doc.root(), "", &dummy)); - } - - TEST(SimpleEachMod, PrepareApplyEmpty) { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: {$each: [1]}}}")); + for (int32_t slice = -2; slice <= 0; slice++) { + setSimpleMod(slice, eachArray); + Document doc(getObjectUsing(docArray /* {a: []} */)); ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + ASSERT_OK(mod().prepare(doc.root(), "", &execInfo)); ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); ASSERT_FALSE(execInfo.noOp); - ASSERT_OK(pushMod.apply()); + ASSERT_OK(mod().apply()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [1]}"), doc); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); + vector combinedVec; + combineVec(docArray, /* a: [] */ + eachArray, /* a: [1] */ + slice, + &combinedVec); + ASSERT_EQUALS(getObjectUsing(combinedVec), doc); } +} - TEST(SimpleEachMod, PrepareApplyInexistent) { - Document doc(fromjson("{}")); - Mod pushMod(fromjson("{$push: {a: {$each: [1]}}}")); +TEST_F(SlicedMod, SimpleArrayFromExisting) { + // We'll simulate the original document having {a: [2,3]} and the mod being + // {$push: {a: {$each: [1], $slice: <-4..0>}}} + vector docArray; + docArray.push_back(2); + docArray.push_back(3); + vector eachArray; + eachArray.push_back(1); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [1]}"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); - } - - TEST(SimpleEachMod, PrepareApplyInexistentMultiple) { - Document doc(fromjson("{}")); - Mod pushMod(fromjson("{$push: {a: {$each: [1, 2]}}}")); + for (int32_t slice = -4; slice <= 0; slice++) { + setSimpleMod(slice, eachArray); + Document doc(getObjectUsing(docArray /* {a: [2, 3]} */)); ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + ASSERT_OK(mod().prepare(doc.root(), "", &execInfo)); ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); ASSERT_FALSE(execInfo.noOp); - ASSERT_OK(pushMod.apply()); + ASSERT_OK(mod().apply()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [1, 2]}"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [1, 2]}}"), logDoc); - } - - TEST(SimpleEachMod, PrepareApplyNormal) { - Document doc(fromjson("{a: [0]}")); - Mod pushMod(fromjson("{$push: {a: {$each: [1]}}}")); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + vector combinedVec; + combineVec(docArray, /* a: [2, 3] */ + eachArray, /* a: [1] */ + slice, + &combinedVec); + ASSERT_EQUALS(getObjectUsing(combinedVec), doc); + } +} + +TEST_F(SlicedMod, ObjectArrayFromEmpty) { + // We'll simulate the original document having {a: []} and the mod being + // {$push: {a: {$each: [{a:2,b:1}], $slice: <-4..0>}, $sort: {a:-1/1,b:-1/1}} + vector docArray; + vector eachArray; + eachArray.push_back(fromjson("{a:2,b:1}")); + eachArray.push_back(fromjson("{a:1,b:2}")); + + for (int32_t aOrB = 0; aOrB < 2; aOrB++) { + for (int32_t sortA = 0; sortA < 2; sortA++) { + for (int32_t sortB = 0; sortB < 2; sortB++) { + for (int32_t slice = -3; slice <= 3; slice++) { + BSONObj sortOrder; + if (aOrB == 0) { + sortOrder = BSON("a" << (sortA ? 1 : -1) << "b" << (sortB ? 1 : -1)); + } else { + sortOrder = BSON("b" << (sortB ? 1 : -1) << "a" << (sortA ? 1 : -1)); + } - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); + setSortMod(slice, eachArray, sortOrder); + Document doc(getObjectUsing(docArray /* {a: []} */)); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [0,1]}"), doc); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod().prepare(doc.root(), "", &execInfo)); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.1': 1}}"), logDoc); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - TEST(SimpleEachMod, PrepareApplyNormalMultiple) { - Document doc(fromjson("{a: [0]}")); - Mod pushMod(fromjson("{$push: {a: {$each: [1,2]}}}")); + ASSERT_OK(mod().apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + vector combinedVec; + combineAndSortVec(docArray, /* a: [] */ + eachArray, /* a: [{a:2,b:1},{a:1,b:2}] */ + slice, + sortOrder, + &combinedVec); + ASSERT_EQUALS(getObjectUsing(combinedVec), doc); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [0,1,2]}"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.1': 1, 'a.2':2}}"), logDoc); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod().log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(BSON("$set" << getObjectUsing(combinedVec)), logDoc); + } + } + } } +} + +TEST_F(SlicedMod, ObjectArrayFromExisting) { + // We'll simulate the original document having {a: [{a:2,b:3},{a:3,:b1}]} and the mod being + // {$push: {a: {$each: [{a:2,b:1}], $slice: <-4..0>}, $sort: {a:-1/1,b:-1/1}} + vector docArray; + docArray.push_back(fromjson("{a:2,b:3}")); + docArray.push_back(fromjson("{a:3,b:1}")); + vector eachArray; + eachArray.push_back(fromjson("{a:2,b:1}")); + + for (int32_t aOrB = 0; aOrB < 2; aOrB++) { + for (int32_t sortA = 0; sortA < 2; sortA++) { + for (int32_t sortB = 0; sortB < 2; sortB++) { + for (int32_t slice = -4; slice <= 4; slice++) { + BSONObj sortOrder; + if (aOrB == 0) { + sortOrder = BSON("a" << (sortA ? 1 : -1) << "b" << (sortB ? 1 : -1)); + } else { + sortOrder = BSON("b" << (sortB ? 1 : -1) << "a" << (sortA ? 1 : -1)); + } - /** - * Slice variants - */ - TEST(SlicePushEach, TopOne) { - Document doc(fromjson("{a: [3]}")); - Mod pushMod(fromjson("{$push: {a: {$each: [2, -1], $slice:1}}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [3]}"), doc); + setSortMod(slice, eachArray, sortOrder); + Document doc(getObjectUsing(docArray /* {a: [{a:2,b:b},{a:3,:b1}]} */)); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [3]}}"), logDoc); - } + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod().prepare(doc.root(), "", &execInfo)); - /** - * Sort for scalar (whole) array elements - */ - TEST(SortPushEach, NumberSort) { - Document doc(fromjson("{a: [3]}")); - Mod pushMod(fromjson("{$push: {a: {$each: [2, -1], $sort:1}}}")); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + ASSERT_OK(mod().apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); + vector combinedVec; + combineAndSortVec(docArray, /* a: [{a:2,b:3},{a:3,:b1}] */ + eachArray, /* a: [{a:2,b:1}] */ + slice, + sortOrder, + &combinedVec); + ASSERT_EQUALS(getObjectUsing(combinedVec), doc); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [-1,2,3]}"), doc); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod().log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(BSON("$set" << getObjectUsing(combinedVec)), logDoc); + } + } + } + } +} + +// Push to position tests + +TEST(ToPosition, BadInputs) { + const char* const bad[] = { + "{$push: {a: { $each: [1], $position:-1}}}", + "{$push: {a: { $each: [1], $position:'s'}}}", + "{$push: {a: { $each: [1], $position:{}}}}", + "{$push: {a: { $each: [1], $position:[0]}}}", + "{$push: {a: { $each: [1], $position:1.1211212}}}", + "{$push: {a: { $each: [1], $position:3.000000000001}}}", + "{$push: {a: { $each: [1], $position:1.2}}}", + "{$push: {a: { $each: [1], $position:-1.2}}}", + "{$push: {a: { $each: [1], $position:9.0e19}}}", + "{$push: {a: { $each: [1], $position:-9.0e19}}}", + NULL, + }; - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [-1, 2, 3]}}"), logDoc); + int i = 0; + while (bad[i] != NULL) { + ModifierPush pushMod(ModifierPush::PUSH_NORMAL); + BSONObj modObj = fromjson(bad[i]); + ASSERT_NOT_OK(pushMod.init(modObj.firstElement().embeddedObject().firstElement(), + ModifierInterface::Options::normal())); + i++; } +} - TEST(SortPushEach, NumberSortReverse) { - Document doc(fromjson("{a: [3]}")); - Mod pushMod(fromjson("{$push: {a: {$each: [4, -1], $sort:-1}}}")); +TEST(ToPosition, GoodInputs) { + { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: { $each: [1], $position: NumberLong(1)}}}")); ModifierInterface::ExecInfo execInfo; ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [4,3,-1]}"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: [4,3,-1]}}"), logDoc); } - - TEST(SortPushEach, MixedSortWhole) { - Document doc(fromjson("{a: [3, 't', {b:1}, {a:1}]}")); - Mod pushMod(fromjson("{$push: {a: {$each: [4, -1], $sort:1}}}")); - const BSONObj expectedObj = fromjson("{a: [-1,3,4,'t', {a:1}, {b:1}]}"); + { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:NumberInt(100)}}}")); ModifierInterface::ExecInfo execInfo; ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(expectedObj, doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(BSON("$set" << expectedObj), logDoc); } - - TEST(SortPushEach, MixedSortWholeReverse) { - Document doc(fromjson("{a: [3, 't', {b:1}, {a:1}]}")); - Mod pushMod(fromjson("{$push: {a: {$each: [4, -1], $sort:-1}}}")); - const BSONObj expectedObj = fromjson("{a: [{b:1}, {a:1}, 't', 4, 3, -1]}"); + { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:1.0}}}")); ModifierInterface::ExecInfo execInfo; ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(expectedObj, doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(BSON("$set" << expectedObj), logDoc); } - - TEST(SortPushEach, MixedSortEmbeddedField) { - Document doc(fromjson("{a: [3, 't', {b:1}, {a:1}]}")); - Mod pushMod(fromjson("{$push: {a: {$each: [4, -1], $sort:{a:1}}}}")); - const BSONObj expectedObj = fromjson("{a: [3, 't', {b: 1}, 4, -1, {a: 1}]}"); + { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:1000000}}}")); ModifierInterface::ExecInfo execInfo; ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(expectedObj, doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(BSON("$set" << expectedObj), logDoc); - } - - /** - * This fixture supports building $push mods with parameterized $each arrays and $slices. - * It always assume that the array being operated on is called 'a'. To build a mod, one - * issues a set*Mod() call. - * - * The setSimpleMod() call will build a $each array of numbers. The setObjectMod() call - * will build a $each array with object. Both these calls take the slice as a parameter as - * well. - * - * Here's a typical test case flow: - * + Determine what the original document's 'a' array would contain - * + Ditto for the $push's $each arrray - * + Loop over slice value - * + Apply the $push with current slice value to the doc - * + Use the fixture/helpers to combine and slice the mod's and original's 'a' - * array - * + Build a document with the above and check against the one generated by the mod apply - */ - class SlicedMod : public mongo::unittest::Test { - public: - SlicedMod() : _mod() {} - - virtual void setUp() { - // no op; set all state using the setMod() call - } - - /** Sets up the mod to be {$push: {a: {$each: [], $slice: }}} */ - void setSimpleMod(int32_t slice, const vector& eachArray) { - - BSONArrayBuilder arrBuilder; - for (vector::const_iterator it = eachArray.begin(); it != eachArray.end(); ++it) { - arrBuilder.append(*it); - } - - _modObj = BSON("$push" << - BSON("a" - << BSON("$each" << arrBuilder.arr() << - "$slice" << slice))); - - ASSERT_OK(_mod.init(_modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - /** Sets up the mod to be {$push: {a: {$each:[,...], $slice:, $sort:}}} */ - void setSortMod(int32_t slice, const vector& eachArray, BSONObj sort) { - - BSONArrayBuilder arrBuilder; - for (vector::const_iterator it = eachArray.begin(); - it != eachArray.end(); - ++it) { - arrBuilder.append(*it); - } - - _modObj = BSON("$push" << - BSON("a" - << BSON("$each" << arrBuilder.arr() << - "$slice" << slice << - "$sort" << sort))); - - ASSERT_OK(_mod.init(_modObj["$push"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - /** Returns an object {a: [<'vec's content>]} */ - BSONObj getObjectUsing(const vector& vec) { - - BSONArrayBuilder arrBuilder; - for (vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { - arrBuilder.append(*it); - } - - BSONObjBuilder builder; - builder.appendArray("a", arrBuilder.obj()); - - return builder.obj(); - } - - /** Returns an object {a: [<'vec's content>]} */ - BSONObj getObjectUsing(const vector& vec) { - - BSONArrayBuilder arrBuilder; - for (vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { - arrBuilder.append(*it); - } - - BSONObjBuilder builder; - builder.appendArray("a", arrBuilder.obj()); - - return builder.obj(); - } - - ModifierPush& mod() { return _mod; } - - BSONObj modObj() { return _modObj; } - - private: - ModifierPush _mod; - BSONObj _modObj; - vector _eachArray; - }; - - TEST_F(SlicedMod, SimpleArrayFromEmpty) { - // We'll simulate the original document having {a: []} and the mod being - // {$push: {a: {$each: [1], $slice: <-2..0>}}} - vector docArray; - vector eachArray; - eachArray.push_back(1); - - for (int32_t slice = -2; slice <= 0; slice++) { - setSimpleMod(slice, eachArray); - Document doc(getObjectUsing(docArray /* {a: []} */)); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod().prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod().apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - - vector combinedVec; - combineVec(docArray, /* a: [] */ - eachArray, /* a: [1] */ - slice, - &combinedVec); - ASSERT_EQUALS(getObjectUsing(combinedVec), doc); - } - } - - TEST_F(SlicedMod, SimpleArrayFromExisting) { - // We'll simulate the original document having {a: [2,3]} and the mod being - // {$push: {a: {$each: [1], $slice: <-4..0>}}} - vector docArray; - docArray.push_back(2); - docArray.push_back(3); - vector eachArray; - eachArray.push_back(1); - - for (int32_t slice = -4; slice <= 0; slice++) { - setSimpleMod(slice, eachArray); - Document doc(getObjectUsing(docArray /* {a: [2, 3]} */)); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod().prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod().apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - - vector combinedVec; - combineVec(docArray, /* a: [2, 3] */ - eachArray, /* a: [1] */ - slice, - &combinedVec); - ASSERT_EQUALS(getObjectUsing(combinedVec), doc); - } - } - - TEST_F(SlicedMod, ObjectArrayFromEmpty) { - // We'll simulate the original document having {a: []} and the mod being - // {$push: {a: {$each: [{a:2,b:1}], $slice: <-4..0>}, $sort: {a:-1/1,b:-1/1}} - vector docArray; - vector eachArray; - eachArray.push_back(fromjson("{a:2,b:1}")); - eachArray.push_back(fromjson("{a:1,b:2}")); - - for (int32_t aOrB = 0; aOrB < 2 ; aOrB++) { - for (int32_t sortA = 0; sortA < 2; sortA++) { - for (int32_t sortB = 0; sortB < 2; sortB++) { - for (int32_t slice = -3; slice <= 3; slice++) { - - BSONObj sortOrder; - if (aOrB == 0) { - sortOrder = BSON("a" << (sortA ? 1 : -1) << "b" << (sortB ? 1 : -1)); - } - else { - sortOrder = BSON("b" << (sortB ? 1 : -1) << "a" << (sortA ? 1 : -1)); - } - - setSortMod(slice, eachArray, sortOrder); - Document doc(getObjectUsing(docArray /* {a: []} */)); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod().prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod().apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - - vector combinedVec; - combineAndSortVec(docArray, /* a: [] */ - eachArray, /* a: [{a:2,b:1},{a:1,b:2}] */ - slice, - sortOrder, - &combinedVec); - ASSERT_EQUALS(getObjectUsing(combinedVec), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod().log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(BSON("$set" << getObjectUsing(combinedVec)), logDoc); - - } - } - } - } - } - - TEST_F(SlicedMod, ObjectArrayFromExisting) { - // We'll simulate the original document having {a: [{a:2,b:3},{a:3,:b1}]} and the mod being - // {$push: {a: {$each: [{a:2,b:1}], $slice: <-4..0>}, $sort: {a:-1/1,b:-1/1}} - vector docArray; - docArray.push_back(fromjson("{a:2,b:3}")); - docArray.push_back(fromjson("{a:3,b:1}")); - vector eachArray; - eachArray.push_back(fromjson("{a:2,b:1}")); - - for (int32_t aOrB = 0; aOrB < 2 ; aOrB++) { - for (int32_t sortA = 0; sortA < 2; sortA++) { - for (int32_t sortB = 0; sortB < 2; sortB++) { - for (int32_t slice = -4; slice <= 4; slice++) { - - BSONObj sortOrder; - if (aOrB == 0) { - sortOrder = BSON("a" << (sortA ? 1 : -1) << "b" << (sortB ? 1 : -1)); - } - else { - sortOrder = BSON("b" << (sortB ? 1 : -1) << "a" << (sortA ? 1 : -1)); - } - - setSortMod(slice, eachArray, sortOrder); - Document doc(getObjectUsing(docArray /* {a: [{a:2,b:b},{a:3,:b1}]} */)); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod().prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod().apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - - vector combinedVec; - combineAndSortVec(docArray, /* a: [{a:2,b:3},{a:3,:b1}] */ - eachArray, /* a: [{a:2,b:1}] */ - slice, - sortOrder, - &combinedVec); - ASSERT_EQUALS(getObjectUsing(combinedVec), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod().log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(BSON("$set" << getObjectUsing(combinedVec)), logDoc); - - } - } - } - } - } - - // Push to position tests - - TEST(ToPosition, BadInputs) { - const char* const bad[] = { - "{$push: {a: { $each: [1], $position:-1}}}", - "{$push: {a: { $each: [1], $position:'s'}}}", - "{$push: {a: { $each: [1], $position:{}}}}", - "{$push: {a: { $each: [1], $position:[0]}}}", - "{$push: {a: { $each: [1], $position:1.1211212}}}", - "{$push: {a: { $each: [1], $position:3.000000000001}}}", - "{$push: {a: { $each: [1], $position:1.2}}}", - "{$push: {a: { $each: [1], $position:-1.2}}}", - "{$push: {a: { $each: [1], $position:9.0e19}}}", - "{$push: {a: { $each: [1], $position:-9.0e19}}}", - NULL, - }; - - int i = 0; - while(bad[i] != NULL) - { - ModifierPush pushMod(ModifierPush::PUSH_NORMAL); - BSONObj modObj = fromjson(bad[i]); - ASSERT_NOT_OK(pushMod.init(modObj.firstElement().embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - i++; - } } - - TEST(ToPosition, GoodInputs) { - { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: { $each: [1], $position: NumberLong(1)}}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - } - { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:NumberInt(100)}}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - } - { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:1.0}}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - } - { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:1000000}}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - } - { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:0}}}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - } - } - - TEST(ToPosition, EmptyArrayFront) { + { Document doc(fromjson("{a: []}")); Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:0}}}")); ModifierInterface::ExecInfo execInfo; ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + } +} - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); +TEST(ToPosition, EmptyArrayFront) { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:0}}}")); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [1]}"), doc); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a':[1]}}"), logDoc); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - TEST(ToPosition, EmptyArrayBackBigPosition) { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:1000}}}")); + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [1]}"), doc); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'a':[1]}}"), logDoc); +} - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); +TEST(ToPosition, EmptyArrayBackBigPosition) { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:1000}}}")); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [1]}"), doc); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a':[1]}}"), logDoc); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - TEST(ToPosition, EmptyArrayBack) { - Document doc(fromjson("{a: []}")); - Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:1}}}")); + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [1]}"), doc); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'a':[1]}}"), logDoc); +} - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); +TEST(ToPosition, EmptyArrayBack) { + Document doc(fromjson("{a: []}")); + Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:1}}}")); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [1]}"), doc); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a':[1]}}"), logDoc); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - TEST(ToPosition, Front) { - Document doc(fromjson("{a: [0]}")); - Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:0}}}")); + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [1]}"), doc); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'a':[1]}}"), logDoc); +} - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); +TEST(ToPosition, Front) { + Document doc(fromjson("{a: [0]}")); + Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:0}}}")); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [1, 0]}"), doc); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a':[1, 0]}}"), logDoc); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); - TEST(ToPosition, Back) { - Document doc(fromjson("{a: [0]}")); - Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:100}}}")); + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [1, 0]}"), doc); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'a':[1, 0]}}"), logDoc); +} - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); +TEST(ToPosition, Back) { + Document doc(fromjson("{a: [0]}")); + Mod pushMod(fromjson("{$push: {a: { $each: [1], $position:100}}}")); - ASSERT_OK(pushMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{a: [0,1]}"), doc); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(pushMod.prepare(doc.root(), "", &execInfo)); - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(pushMod.log(&logBuilder)); - ASSERT_EQUALS(countChildren(logDoc.root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.1':1}}"), logDoc); - } + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(pushMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{a: [0,1]}"), doc); -} // unnamed namespace + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); + ASSERT_EQUALS(countChildren(logDoc.root()), 1u); + ASSERT_EQUALS(fromjson("{$set: {'a.1':1}}"), logDoc); +} + +} // unnamed namespace -- cgit v1.2.1