diff options
Diffstat (limited to 'src/mongo/db/ops/modifier_inc_test.cpp')
-rw-r--r-- | src/mongo/db/ops/modifier_inc_test.cpp | 933 |
1 files changed, 466 insertions, 467 deletions
diff --git a/src/mongo/db/ops/modifier_inc_test.cpp b/src/mongo/db/ops/modifier_inc_test.cpp index 8a97bc490e7..52ab8d0e41c 100644 --- a/src/mongo/db/ops/modifier_inc_test.cpp +++ b/src/mongo/db/ops/modifier_inc_test.cpp @@ -42,483 +42,482 @@ namespace { - using mongo::BSONObj; - using mongo::LogBuilder; - using mongo::ModifierInc; - using mongo::ModifierInterface; - using mongo::NumberInt; - using mongo::Status; - using mongo::StringData; - using mongo::fromjson; - using mongo::mutablebson::ConstElement; - using mongo::mutablebson::Document; - using mongo::mutablebson::Element; - using mongo::mutablebson::countChildren; - - /** Helper to build and manipulate a $inc mod. */ - class Mod { - public: - - explicit Mod(BSONObj modObj) - : _modObj(modObj) - , _mod(mongoutils::str::equals(modObj.firstElement().fieldName(), "$mul") ? - ModifierInc::MODE_MUL : ModifierInc::MODE_INC) { - 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(); - } - - Status log(LogBuilder* logBuilder) const { - return _mod.log(logBuilder); - } - - ModifierInc& mod() { return _mod; } - - private: - BSONObj _modObj; - ModifierInc _mod; - }; - - TEST(Init, FailToInitWithInvalidValue) { - BSONObj modObj; - ModifierInc mod; - - // String is an invalid increment argument - modObj = fromjson("{ $inc : { a : '' } }"); - ASSERT_NOT_OK(mod.init(modObj["$inc"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - - // Object is an invalid increment argument - modObj = fromjson("{ $inc : { a : {} } }"); - ASSERT_NOT_OK(mod.init(modObj["$inc"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - - // Array is an invalid increment argument - modObj = fromjson("{ $inc : { a : [] } }"); - ASSERT_NOT_OK(mod.init(modObj["$inc"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); +using mongo::BSONObj; +using mongo::LogBuilder; +using mongo::ModifierInc; +using mongo::ModifierInterface; +using mongo::NumberInt; +using mongo::Status; +using mongo::StringData; +using mongo::fromjson; +using mongo::mutablebson::ConstElement; +using mongo::mutablebson::Document; +using mongo::mutablebson::Element; +using mongo::mutablebson::countChildren; + +/** Helper to build and manipulate a $inc mod. */ +class Mod { +public: + explicit Mod(BSONObj modObj) + : _modObj(modObj), + _mod(mongoutils::str::equals(modObj.firstElement().fieldName(), "$mul") + ? ModifierInc::MODE_MUL + : ModifierInc::MODE_INC) { + StringData modName = modObj.firstElement().fieldName(); + ASSERT_OK(_mod.init(_modObj[modName].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); } - TEST(Init, InitParsesNumberInt) { - Mod incMod(BSON("$inc" << BSON("a" << static_cast<int>(1)))); + Status prepare(Element root, StringData matchedField, ModifierInterface::ExecInfo* execInfo) { + return _mod.prepare(root, matchedField, execInfo); } - TEST(Init, InitParsesNumberLong) { - Mod incMod(BSON("$inc" << BSON("a" << static_cast<long long>(1)))); + Status apply() const { + return _mod.apply(); } - TEST(Init, InitParsesNumberDouble) { - Mod incMod(BSON("$inc" << BSON("a" << 1.0))); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } - TEST(SimpleMod, PrepareSimpleOK) { - Document doc(fromjson("{ a : 1 }")); - Mod incMod(fromjson("{ $inc: { a : 1 }}")); - - ModifierInterface::ExecInfo execInfo; - - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_FALSE(execInfo.noOp); - } - - TEST(SimpleMod, PrepareSimpleNonNumericObject) { - Document doc(fromjson("{ a : {} }")); - Mod incMod(fromjson("{ $inc: { a : 1 }}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_NOT_OK(incMod.prepare(doc.root(), "", &execInfo)); - } - - TEST(SimpleMod, PrepareSimpleNonNumericArray) { - - Document doc(fromjson("{ a : [] }")); - Mod incMod(fromjson("{ $inc: { a : 1 }}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_NOT_OK(incMod.prepare(doc.root(), "", &execInfo)); - } - - TEST(SimpleMod, PrepareSimpleNonNumericString) { - Document doc(fromjson("{ a : '' }")); - Mod incMod(fromjson("{ $inc: { a : 1 }}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_NOT_OK(incMod.prepare(doc.root(), "", &execInfo)); - } - - TEST(SimpleMod, ApplyAndLogEmptyDocument) { - Document doc(fromjson("{}")); - Mod incMod(fromjson("{ $inc: { a : 1 }}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : 1 }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); - } - - TEST(SimpleMod, LogWithoutApplyEmptyDocument) { - Document doc(fromjson("{}")); - Mod incMod(fromjson("{ $inc: { a : 1 }}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); - } - - TEST(SimpleMod, ApplyAndLogSimpleDocument) { - Document doc(fromjson("{ a : 2 }")); - Mod incMod(fromjson("{ $inc: { a : 1 }}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : 3 }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : 3 } }"), logDoc); + ModifierInc& mod() { + return _mod; } - TEST(DottedMod, ApplyAndLogSimpleDocument) { - Document doc(fromjson("{ a : { b : 2 } }")); - Mod incMod(fromjson("{ $inc: { 'a.b' : 1 } }")); +private: + BSONObj _modObj; + ModifierInc _mod; +}; + +TEST(Init, FailToInitWithInvalidValue) { + BSONObj modObj; + ModifierInc mod; + + // String is an invalid increment argument + modObj = fromjson("{ $inc : { a : '' } }"); + ASSERT_NOT_OK(mod.init(modObj["$inc"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : { b : 3 } }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { 'a.b' : 3 } }"), logDoc); - } - - TEST(InPlace, IntToInt) { - Document doc(BSON("a" << static_cast<int>(1))); - Mod incMod(BSON("$inc" << BSON("a" << static_cast<int>(1)))); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - } - - TEST(InPlace, LongToLong) { - Document doc(BSON("a" << static_cast<long long>(1))); - Mod incMod(BSON("$inc" << BSON("a" << static_cast<long long>(1)))); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - } - - TEST(InPlace, DoubleToDouble) { - Document doc(BSON("a" << 1.0)); - Mod incMod(BSON("$inc" << BSON("a" << 1.0 ))); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - } - - TEST(NoOp, Int) { - Document doc(BSON("a" << static_cast<int>(1))); - Mod incMod(BSON("$inc" << BSON("a" << static_cast<int>(0)))); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_TRUE(execInfo.noOp); - } - - TEST(NoOp, Long) { - Document doc(BSON("a" << static_cast<long long>(1))); - Mod incMod(BSON("$inc" << BSON("a" << static_cast<long long>(0)))); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_TRUE(execInfo.noOp); - } - - TEST(NoOp, Double) { - Document doc(BSON("a" << 1.0)); - Mod incMod(BSON("$inc" << BSON("a" << 0.0))); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_TRUE(execInfo.noOp); - } - - TEST(Upcasting, UpcastIntToLong) { - // Checks that $inc : NumberLong(0) turns a NumberInt into a NumberLong and logs it - // correctly. - Document doc(BSON("a" << static_cast<int>(1))); - ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); - - Mod incMod(BSON("$inc" << BSON("a" << static_cast<long long>(0)))); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : 1 }"), doc); - ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType()); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); - ASSERT_EQUALS(mongo::NumberLong, logDoc.root()["$set"]["a"].getType()); - } - - TEST(Upcasting, UpcastIntToDouble) { - // Checks that $inc : 0.0 turns a NumberInt into a NumberDouble and logs it - // correctly. - Document doc(BSON("a" << static_cast<int>(1))); - ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); - - Mod incMod(fromjson("{ $inc : { a : 0.0 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : 1.0 }"), doc); - ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : 1.0 } }"), logDoc); - ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType()); - } + // Object is an invalid increment argument + modObj = fromjson("{ $inc : { a : {} } }"); + ASSERT_NOT_OK(mod.init(modObj["$inc"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); + + // Array is an invalid increment argument + modObj = fromjson("{ $inc : { a : [] } }"); + ASSERT_NOT_OK(mod.init(modObj["$inc"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, InitParsesNumberInt) { + Mod incMod(BSON("$inc" << BSON("a" << static_cast<int>(1)))); +} + +TEST(Init, InitParsesNumberLong) { + Mod incMod(BSON("$inc" << BSON("a" << static_cast<long long>(1)))); +} + +TEST(Init, InitParsesNumberDouble) { + Mod incMod(BSON("$inc" << BSON("a" << 1.0))); +} + +TEST(SimpleMod, PrepareSimpleOK) { + Document doc(fromjson("{ a : 1 }")); + Mod incMod(fromjson("{ $inc: { a : 1 }}")); + + ModifierInterface::ExecInfo execInfo; + + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_FALSE(execInfo.noOp); +} + +TEST(SimpleMod, PrepareSimpleNonNumericObject) { + Document doc(fromjson("{ a : {} }")); + Mod incMod(fromjson("{ $inc: { a : 1 }}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_NOT_OK(incMod.prepare(doc.root(), "", &execInfo)); +} + +TEST(SimpleMod, PrepareSimpleNonNumericArray) { + Document doc(fromjson("{ a : [] }")); + Mod incMod(fromjson("{ $inc: { a : 1 }}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_NOT_OK(incMod.prepare(doc.root(), "", &execInfo)); +} + +TEST(SimpleMod, PrepareSimpleNonNumericString) { + Document doc(fromjson("{ a : '' }")); + Mod incMod(fromjson("{ $inc: { a : 1 }}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_NOT_OK(incMod.prepare(doc.root(), "", &execInfo)); +} + +TEST(SimpleMod, ApplyAndLogEmptyDocument) { + Document doc(fromjson("{}")); + Mod incMod(fromjson("{ $inc: { a : 1 }}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : 1 }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); +} + +TEST(SimpleMod, LogWithoutApplyEmptyDocument) { + Document doc(fromjson("{}")); + Mod incMod(fromjson("{ $inc: { a : 1 }}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); +} + +TEST(SimpleMod, ApplyAndLogSimpleDocument) { + Document doc(fromjson("{ a : 2 }")); + Mod incMod(fromjson("{ $inc: { a : 1 }}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : 3 }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : 3 } }"), logDoc); +} + +TEST(DottedMod, ApplyAndLogSimpleDocument) { + Document doc(fromjson("{ a : { b : 2 } }")); + Mod incMod(fromjson("{ $inc: { 'a.b' : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : { b : 3 } }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { 'a.b' : 3 } }"), logDoc); +} + +TEST(InPlace, IntToInt) { + Document doc(BSON("a" << static_cast<int>(1))); + Mod incMod(BSON("$inc" << BSON("a" << static_cast<int>(1)))); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); +} + +TEST(InPlace, LongToLong) { + Document doc(BSON("a" << static_cast<long long>(1))); + Mod incMod(BSON("$inc" << BSON("a" << static_cast<long long>(1)))); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); +} + +TEST(InPlace, DoubleToDouble) { + Document doc(BSON("a" << 1.0)); + Mod incMod(BSON("$inc" << BSON("a" << 1.0))); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); +} + +TEST(NoOp, Int) { + Document doc(BSON("a" << static_cast<int>(1))); + Mod incMod(BSON("$inc" << BSON("a" << static_cast<int>(0)))); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_TRUE(execInfo.noOp); +} + +TEST(NoOp, Long) { + Document doc(BSON("a" << static_cast<long long>(1))); + Mod incMod(BSON("$inc" << BSON("a" << static_cast<long long>(0)))); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_TRUE(execInfo.noOp); +} + +TEST(NoOp, Double) { + Document doc(BSON("a" << 1.0)); + Mod incMod(BSON("$inc" << BSON("a" << 0.0))); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_TRUE(execInfo.noOp); +} + +TEST(Upcasting, UpcastIntToLong) { + // Checks that $inc : NumberLong(0) turns a NumberInt into a NumberLong and logs it + // correctly. + Document doc(BSON("a" << static_cast<int>(1))); + ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); + + Mod incMod(BSON("$inc" << BSON("a" << static_cast<long long>(0)))); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : 1 }"), doc); + ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType()); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); + ASSERT_EQUALS(mongo::NumberLong, logDoc.root()["$set"]["a"].getType()); +} + +TEST(Upcasting, UpcastIntToDouble) { + // Checks that $inc : 0.0 turns a NumberInt into a NumberDouble and logs it + // correctly. + Document doc(BSON("a" << static_cast<int>(1))); + ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); + + Mod incMod(fromjson("{ $inc : { a : 0.0 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : 1.0 }"), doc); + ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : 1.0 } }"), logDoc); + ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType()); +} + +TEST(Upcasting, UpcastLongToDouble) { + // Checks that $inc : 0.0 turns a NumberLong into a NumberDouble and logs it + // correctly. + Document doc(BSON("a" << static_cast<long long>(1))); + ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType()); + + Mod incMod(fromjson("{ $inc : { a : 0.0 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : 1.0 }"), doc); + ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : 1.0 } }"), logDoc); + ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType()); +} + +TEST(Upcasting, DoublesStayDoubles) { + // Checks that $inc : 0 doesn't change a NumberDouble away from double + Document doc(fromjson("{ a : 1.0 }")); + ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); + + Mod incMod(fromjson("{ $inc : { a : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : 2.0 }"), doc); + ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : 2.0 } }"), logDoc); + ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType()); +} + +// The only interesting overflow cases are int->long via increment: we never overflow to +// double, and we never decrease precision on decrement. + +TEST(Spilling, OverflowIntToLong) { + const int initial_value = std::numeric_limits<int32_t>::max(); + + Document doc(BSON("a" << static_cast<int>(initial_value))); + ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); + + Mod incMod(fromjson("{ $inc : { a : 1 } }")); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + const long long target_value = static_cast<long long>(initial_value) + 1; + + ASSERT_OK(incMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(BSON("a" << target_value), doc); +} + +TEST(Spilling, UnderflowIntToLong) { + const int initial_value = std::numeric_limits<int32_t>::min(); + + Document doc(BSON("a" << static_cast<int>(initial_value))); + ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); + + Mod incMod(fromjson("{ $inc : { a : -1 } }")); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + const long long target_value = static_cast<long long>(initial_value) - 1; + + ASSERT_OK(incMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(BSON("a" << target_value), doc); +} + +TEST(Lifecycle, IncModCanBeReused) { + Document doc1(fromjson("{ a : 1 }")); + Document doc2(fromjson("{ a : 1 }")); + + Mod incMod(fromjson("{ $inc: { a : 1 }}")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc1.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_TRUE(doc1.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : 2 }"), doc1); + + ASSERT_OK(incMod.prepare(doc2.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_TRUE(doc2.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : 2 }"), doc2); +} + +// Given the current implementation of $mul, we really only need one test for +// $mul. However, in the future, we should probably write additional ones, or, perhaps find +// a way to run all the obove tests in both modes. +TEST(Multiplication, ApplyAndLogSimpleDocument) { + Document doc(fromjson("{ a : { b : 2 } }")); + Mod incMod(fromjson("{ $mul: { 'a.b' : 3 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : { b : 6 } }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { 'a.b' : 6 } }"), logDoc); +} - TEST(Upcasting, UpcastLongToDouble) { - // Checks that $inc : 0.0 turns a NumberLong into a NumberDouble and logs it - // correctly. - Document doc(BSON("a" << static_cast<long long>(1))); - ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType()); - - Mod incMod(fromjson("{ $inc : { a : 0.0 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : 1.0 }"), doc); - ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : 1.0 } }"), logDoc); - ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType()); - } - - TEST(Upcasting, DoublesStayDoubles) { - // Checks that $inc : 0 doesn't change a NumberDouble away from double - Document doc(fromjson("{ a : 1.0 }")); - ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); - - Mod incMod(fromjson("{ $inc : { a : 1 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : 2.0 }"), doc); - ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : 2.0 } }"), logDoc); - ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType()); - } - - // The only interesting overflow cases are int->long via increment: we never overflow to - // double, and we never decrease precision on decrement. - - TEST(Spilling, OverflowIntToLong) { - const int initial_value = std::numeric_limits<int32_t>::max(); - - Document doc(BSON("a" << static_cast<int>(initial_value))); - ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); - - Mod incMod(fromjson("{ $inc : { a : 1 } }")); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - const long long target_value = static_cast<long long>(initial_value) + 1; - - ASSERT_OK(incMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("a" << target_value), doc); - } - - TEST(Spilling, UnderflowIntToLong) { - const int initial_value = std::numeric_limits<int32_t>::min(); - - Document doc(BSON("a" << static_cast<int>(initial_value))); - ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); - - Mod incMod(fromjson("{ $inc : { a : -1 } }")); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - const long long target_value = static_cast<long long>(initial_value) - 1; - - ASSERT_OK(incMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("a" << target_value), doc); - } - - TEST(Lifecycle, IncModCanBeReused) { - Document doc1(fromjson("{ a : 1 }")); - Document doc2(fromjson("{ a : 1 }")); - - Mod incMod(fromjson("{ $inc: { a : 1 }}")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc1.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_TRUE(doc1.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : 2 }"), doc1); - - ASSERT_OK(incMod.prepare(doc2.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_TRUE(doc2.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : 2 }"), doc2); - } - - // Given the current implementation of $mul, we really only need one test for - // $mul. However, in the future, we should probably write additional ones, or, perhaps find - // a way to run all the obove tests in both modes. - TEST(Multiplication, ApplyAndLogSimpleDocument) { - Document doc(fromjson("{ a : { b : 2 } }")); - Mod incMod(fromjson("{ $mul: { 'a.b' : 3 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : { b : 6 } }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { 'a.b' : 6 } }"), logDoc); - } - - TEST(Multiplication, ApplyAndLogMissingElement) { - Document doc(fromjson("{ a : 0 }")); - Mod incMod(fromjson("{ $mul : { b : 3 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : 0, b : 0 }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(incMod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { b : 0 } }"), logDoc); - } - - TEST(Multiplication, ApplyMissingElementInt) { - const int int_zero = 0; - const int int_three = 3; - - Document doc(BSON("a" << int_zero)); - Mod incMod(BSON("$mul" << BSON("b" << int_three))); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("a" << int_zero << "b" << int_zero), doc); - ASSERT_EQUALS(mongo::NumberInt, doc.root().rightChild().getType()); - } - - TEST(Multiplication, ApplyMissingElementLongLong) { - const long long ll_zero = 0; - const long long ll_three = 3; - - Document doc(BSON("a" << ll_zero)); - Mod incMod(BSON("$mul" << BSON("b" << ll_three))); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("a" << ll_zero << "b" << ll_zero), doc); - ASSERT_EQUALS(mongo::NumberLong, doc.root().rightChild().getType()); - } - - TEST(Multiplication, ApplyMissingElementDouble) { - const double double_zero = 0; - const double double_three = 3; - - Document doc(BSON("a" << double_zero)); - Mod incMod(BSON("$mul" << BSON("b" << double_three))); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(incMod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("a" << double_zero << "b" << 0), doc); - ASSERT_EQUALS(mongo::NumberDouble, doc.root().rightChild().getType()); - } +TEST(Multiplication, ApplyAndLogMissingElement) { + Document doc(fromjson("{ a : 0 }")); + Mod incMod(fromjson("{ $mul : { b : 3 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : 0, b : 0 }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { b : 0 } }"), logDoc); +} -} // namespace +TEST(Multiplication, ApplyMissingElementInt) { + const int int_zero = 0; + const int int_three = 3; + + Document doc(BSON("a" << int_zero)); + Mod incMod(BSON("$mul" << BSON("b" << int_three))); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(BSON("a" << int_zero << "b" << int_zero), doc); + ASSERT_EQUALS(mongo::NumberInt, doc.root().rightChild().getType()); +} + +TEST(Multiplication, ApplyMissingElementLongLong) { + const long long ll_zero = 0; + const long long ll_three = 3; + + Document doc(BSON("a" << ll_zero)); + Mod incMod(BSON("$mul" << BSON("b" << ll_three))); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(BSON("a" << ll_zero << "b" << ll_zero), doc); + ASSERT_EQUALS(mongo::NumberLong, doc.root().rightChild().getType()); +} + +TEST(Multiplication, ApplyMissingElementDouble) { + const double double_zero = 0; + const double double_three = 3; + + Document doc(BSON("a" << double_zero)); + Mod incMod(BSON("$mul" << BSON("b" << double_three))); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo)); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(incMod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(BSON("a" << double_zero << "b" << 0), doc); + ASSERT_EQUALS(mongo::NumberDouble, doc.root().rightChild().getType()); +} + +} // namespace |