summaryrefslogtreecommitdiff
path: root/src/mongo/bson/mutable/mutable_bson_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/bson/mutable/mutable_bson_test.cpp')
-rw-r--r--src/mongo/bson/mutable/mutable_bson_test.cpp2993
1 files changed, 2694 insertions, 299 deletions
diff --git a/src/mongo/bson/mutable/mutable_bson_test.cpp b/src/mongo/bson/mutable/mutable_bson_test.cpp
index f04a441c32e..3f8b2f40c74 100644
--- a/src/mongo/bson/mutable/mutable_bson_test.cpp
+++ b/src/mongo/bson/mutable/mutable_bson_test.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2010 10gen Inc.
+/* Copyright 2012 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,19 +15,20 @@
#include "mongo/platform/basic.h"
-#include "mongo/bson/mutable/mutable_bson.h"
+#include "mongo/bson/mutable/document.h"
#include "mongo/base/status.h"
#include "mongo/bson/bsonobj.h"
-#include "mongo/bson/mutable/mutable_bson_algo.h"
-#include "mongo/bson/mutable/mutable_bson_builder.h"
-#include "mongo/bson/mutable/mutable_bson_heap.h"
-#include "mongo/bson/mutable/mutable_bson_internal.h"
+#include "mongo/bson/mutable/algorithm.h"
+#include "mongo/bson/mutable/mutable_bson_test_utils.h"
+#include "mongo/bson/mutable/damage_vector.h"
#include "mongo/db/json.h"
#include "mongo/unittest/unittest.h"
namespace {
+ namespace mmb = mongo::mutablebson;
+
TEST(TopologyBuilding, TopDownFromScratch) {
/*
[ e0 ]
@@ -42,21 +43,20 @@ namespace {
[ e4 ]..[ e5 ]
*/
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element e0 = doc.makeObjElement("e0");
- mongo::mutablebson::Element e1 = doc.makeObjElement("e1");
- mongo::mutablebson::Element e2 = doc.makeObjElement("e2");
- mongo::mutablebson::Element e3 = doc.makeObjElement("e3");
- mongo::mutablebson::Element e4 = doc.makeObjElement("e4");
- mongo::mutablebson::Element e5 = doc.makeObjElement("e5");
+ mmb::Element e0 = doc.makeElementObject("e0");
+ mmb::Element e1 = doc.makeElementObject("e1");
+ mmb::Element e2 = doc.makeElementObject("e2");
+ mmb::Element e3 = doc.makeElementObject("e3");
+ mmb::Element e4 = doc.makeElementObject("e4");
+ mmb::Element e5 = doc.makeElementObject("e5");
- ASSERT_EQUALS(e0.addChild(e1), mongo::Status::OK());
- ASSERT_EQUALS(e0.addChild(e2), mongo::Status::OK());
- ASSERT_EQUALS(e2.addChild(e3), mongo::Status::OK());
- ASSERT_EQUALS(e3.addChild(e4), mongo::Status::OK());
- ASSERT_EQUALS(e3.addChild(e5), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e1), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e2), mongo::Status::OK());
+ ASSERT_EQUALS(e2.pushBack(e3), mongo::Status::OK());
+ ASSERT_EQUALS(e3.pushBack(e4), mongo::Status::OK());
+ ASSERT_EQUALS(e3.pushBack(e5), mongo::Status::OK());
ASSERT_EQUALS("e0", e0.getFieldName());
ASSERT_EQUALS("e1", e0.leftChild().getFieldName());
@@ -88,19 +88,18 @@ namespace {
[ e3 ]..[ e4 ]
*/
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element e0 = doc.makeObjElement("e0");
- mongo::mutablebson::Element e1 = doc.makeObjElement("e1");
- mongo::mutablebson::Element e2 = doc.makeObjElement("e2");
- mongo::mutablebson::Element e3 = doc.makeObjElement("e3");
- mongo::mutablebson::Element e4 = doc.makeObjElement("e4");
+ mmb::Element e0 = doc.makeElementObject("e0");
+ mmb::Element e1 = doc.makeElementObject("e1");
+ mmb::Element e2 = doc.makeElementObject("e2");
+ mmb::Element e3 = doc.makeElementObject("e3");
+ mmb::Element e4 = doc.makeElementObject("e4");
- ASSERT_EQUALS(e0.addChild(e1), mongo::Status::OK());
- ASSERT_EQUALS(e0.addChild(e2), mongo::Status::OK());
- ASSERT_EQUALS(e2.addChild(e3), mongo::Status::OK());
- ASSERT_EQUALS(e3.addSiblingAfter(e4), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e1), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e2), mongo::Status::OK());
+ ASSERT_EQUALS(e2.pushBack(e3), mongo::Status::OK());
+ ASSERT_EQUALS(e3.addSiblingRight(e4), mongo::Status::OK());
ASSERT_EQUALS("e4", e3.rightSibling().getFieldName());
ASSERT_EQUALS("e3", e4.leftSibling().getFieldName());
@@ -120,21 +119,20 @@ namespace {
[ e7 ]..[ e5 ]..[ e6 ]
*/
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element e2 = doc.makeObjElement("e2");
- mongo::mutablebson::Element e3 = doc.makeObjElement("e3");
- mongo::mutablebson::Element e4 = doc.makeObjElement("e4");
- mongo::mutablebson::Element e5 = doc.makeObjElement("e5");
- mongo::mutablebson::Element e6 = doc.makeObjElement("e6");
- mongo::mutablebson::Element e7 = doc.makeObjElement("e7");
+ mmb::Element e2 = doc.makeElementObject("e2");
+ mmb::Element e3 = doc.makeElementObject("e3");
+ mmb::Element e4 = doc.makeElementObject("e4");
+ mmb::Element e5 = doc.makeElementObject("e5");
+ mmb::Element e6 = doc.makeElementObject("e6");
+ mmb::Element e7 = doc.makeElementObject("e7");
- ASSERT_EQUALS(e2.addChild(e3), mongo::Status::OK());
- ASSERT_EQUALS(e2.addChild(e4), mongo::Status::OK());
- ASSERT_EQUALS(e3.addChild(e5), mongo::Status::OK());
- ASSERT_EQUALS(e5.addSiblingAfter(e6), mongo::Status::OK());
- ASSERT_EQUALS(e5.addSiblingBefore(e7), mongo::Status::OK());
+ ASSERT_EQUALS(e2.pushBack(e3), mongo::Status::OK());
+ ASSERT_EQUALS(e2.pushBack(e4), mongo::Status::OK());
+ ASSERT_EQUALS(e3.pushBack(e5), mongo::Status::OK());
+ ASSERT_EQUALS(e5.addSiblingRight(e6), mongo::Status::OK());
+ ASSERT_EQUALS(e5.addSiblingLeft(e7), mongo::Status::OK());
ASSERT_EQUALS("e5", e7.rightSibling().getFieldName());
ASSERT_EQUALS("e7", e5.leftSibling().getFieldName());
@@ -161,23 +159,22 @@ namespace {
/ \
[ e9 ]..[ e10]
*/
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
-
- mongo::mutablebson::Element e3 = doc.makeObjElement("e3");
- mongo::mutablebson::Element e4 = doc.makeObjElement("e4");
- mongo::mutablebson::Element e5 = doc.makeObjElement("e5");
- mongo::mutablebson::Element e6 = doc.makeObjElement("e6");
- mongo::mutablebson::Element e8 = doc.makeObjElement("e8");
- mongo::mutablebson::Element e9 = doc.makeObjElement("e9");
- mongo::mutablebson::Element e10 = doc.makeObjElement("e10");
-
- ASSERT_EQUALS(e3.addChild(e4), mongo::Status::OK());
- ASSERT_EQUALS(e3.addChild(e5), mongo::Status::OK());
- ASSERT_EQUALS(e3.addChild(e6), mongo::Status::OK());
- ASSERT_EQUALS(e8.addChild(e9), mongo::Status::OK());
- ASSERT_EQUALS(e8.addChild(e10), mongo::Status::OK());
- ASSERT_EQUALS(e5.addChild(e8), mongo::Status::OK());
+ mmb::Document doc;
+
+ mmb::Element e3 = doc.makeElementObject("e3");
+ mmb::Element e4 = doc.makeElementObject("e4");
+ mmb::Element e5 = doc.makeElementObject("e5");
+ mmb::Element e6 = doc.makeElementObject("e6");
+ mmb::Element e8 = doc.makeElementObject("e8");
+ mmb::Element e9 = doc.makeElementObject("e9");
+ mmb::Element e10 = doc.makeElementObject("e10");
+
+ ASSERT_EQUALS(e3.pushBack(e4), mongo::Status::OK());
+ ASSERT_EQUALS(e3.pushBack(e5), mongo::Status::OK());
+ ASSERT_EQUALS(e3.pushBack(e6), mongo::Status::OK());
+ ASSERT_EQUALS(e8.pushBack(e9), mongo::Status::OK());
+ ASSERT_EQUALS(e8.pushBack(e10), mongo::Status::OK());
+ ASSERT_EQUALS(e5.pushBack(e8), mongo::Status::OK());
ASSERT_EQUALS("e8", e9.parent().getFieldName());
ASSERT_EQUALS("e8", e10.parent().getFieldName());
@@ -197,15 +194,14 @@ namespace {
/ \ \
[ e1 ] [ e2 ] [ e2 ]
*/
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element e0 = doc.makeObjElement("e0");
- mongo::mutablebson::Element e1 = doc.makeObjElement("e1");
- mongo::mutablebson::Element e2 = doc.makeObjElement("e2");
+ mmb::Element e0 = doc.makeElementObject("e0");
+ mmb::Element e1 = doc.makeElementObject("e1");
+ mmb::Element e2 = doc.makeElementObject("e2");
- ASSERT_EQUALS(e0.addChild(e1), mongo::Status::OK());
- ASSERT_EQUALS(e0.addChild(e2), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e1), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e2), mongo::Status::OK());
ASSERT_EQUALS(e1.remove(), mongo::Status::OK());
ASSERT_EQUALS("e2", e0.leftChild().getFieldName());
@@ -225,24 +221,23 @@ namespace {
/ \
[ e9 ]..[ e10]
*/
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
-
- mongo::mutablebson::Element e3 = doc.makeObjElement("e3");
- mongo::mutablebson::Element e4 = doc.makeObjElement("e4");
- mongo::mutablebson::Element e5 = doc.makeObjElement("e5");
- mongo::mutablebson::Element e6 = doc.makeObjElement("e6");
- mongo::mutablebson::Element e8 = doc.makeObjElement("e8");
- mongo::mutablebson::Element e9 = doc.makeObjElement("e9");
- mongo::mutablebson::Element e10 = doc.makeObjElement("e10");
-
- ASSERT_EQUALS(e3.addChild(e4), mongo::Status::OK());
- ASSERT_EQUALS(e3.addChild(e5), mongo::Status::OK());
- ASSERT_EQUALS(e5.addSiblingAfter(e6), mongo::Status::OK());
-
- ASSERT_EQUALS(e8.addChild(e9), mongo::Status::OK());
- ASSERT_EQUALS(e8.addChild(e10), mongo::Status::OK());
- ASSERT_EQUALS(e5.addChild(e8), mongo::Status::OK());
+ mmb::Document doc;
+
+ mmb::Element e3 = doc.makeElementObject("e3");
+ mmb::Element e4 = doc.makeElementObject("e4");
+ mmb::Element e5 = doc.makeElementObject("e5");
+ mmb::Element e6 = doc.makeElementObject("e6");
+ mmb::Element e8 = doc.makeElementObject("e8");
+ mmb::Element e9 = doc.makeElementObject("e9");
+ mmb::Element e10 = doc.makeElementObject("e10");
+
+ ASSERT_EQUALS(e3.pushBack(e4), mongo::Status::OK());
+ ASSERT_EQUALS(e3.pushBack(e5), mongo::Status::OK());
+ ASSERT_EQUALS(e5.addSiblingRight(e6), mongo::Status::OK());
+
+ ASSERT_EQUALS(e8.pushBack(e9), mongo::Status::OK());
+ ASSERT_EQUALS(e8.pushBack(e10), mongo::Status::OK());
+ ASSERT_EQUALS(e5.pushBack(e8), mongo::Status::OK());
ASSERT_EQUALS(e5.remove(), mongo::Status::OK());
ASSERT_EQUALS("e3", e4.parent().getFieldName());
@@ -260,17 +255,15 @@ namespace {
[ e1 ]..[ e2 ] [ e1 ]..[ e2 ]
*/
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element e0 = doc.makeObjElement("e0");
- mongo::mutablebson::Element e1 = doc.makeObjElement("e1");
- mongo::mutablebson::Element e2 = doc.makeObjElement("e2");
+ mmb::Element e0 = doc.makeElementObject("e0");
+ mmb::Element e1 = doc.makeElementObject("e1");
+ mmb::Element e2 = doc.makeElementObject("e2");
- ASSERT_EQUALS(e0.addChild(e1), mongo::Status::OK());
- ASSERT_EQUALS(e0.addChild(e2), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e1), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e2), mongo::Status::OK());
ASSERT_EQUALS(e0.rename("f0"), mongo::Status::OK());
-
ASSERT_EQUALS("f0", e0.getFieldName());
}
@@ -288,21 +281,20 @@ namespace {
[ e4 ]..[ e5 ]
*/
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element e0 = doc.makeObjElement("e0");
- mongo::mutablebson::Element e1 = doc.makeObjElement("e1");
- mongo::mutablebson::Element e2 = doc.makeObjElement("e2");
- mongo::mutablebson::Element e3 = doc.makeObjElement("e3");
- mongo::mutablebson::Element e4 = doc.makeObjElement("e4");
- mongo::mutablebson::Element e5 = doc.makeObjElement("e5");
+ mmb::Element e0 = doc.makeElementObject("e0");
+ mmb::Element e1 = doc.makeElementObject("e1");
+ mmb::Element e2 = doc.makeElementObject("e2");
+ mmb::Element e3 = doc.makeElementObject("e3");
+ mmb::Element e4 = doc.makeElementObject("e4");
+ mmb::Element e5 = doc.makeElementObject("e5");
- ASSERT_EQUALS(e0.addChild(e1), mongo::Status::OK());
- ASSERT_EQUALS(e0.addChild(e2), mongo::Status::OK());
- ASSERT_EQUALS(e2.addChild(e3), mongo::Status::OK());
- ASSERT_EQUALS(e3.addChild(e4), mongo::Status::OK());
- ASSERT_EQUALS(e3.addChild(e5), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e1), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e2), mongo::Status::OK());
+ ASSERT_EQUALS(e2.pushBack(e3), mongo::Status::OK());
+ ASSERT_EQUALS(e3.pushBack(e4), mongo::Status::OK());
+ ASSERT_EQUALS(e3.pushBack(e5), mongo::Status::OK());
ASSERT_EQUALS("e0", e0.getFieldName());
ASSERT_EQUALS("e1", e0.leftChild().getFieldName());
@@ -317,7 +309,9 @@ namespace {
ASSERT_EQUALS("e5", e3.rightChild().getFieldName());
ASSERT_EQUALS("e5", e4.rightSibling().getFieldName());
ASSERT_EQUALS("e4", e5.leftSibling().getFieldName());
- ASSERT_EQUALS(e3.move(e0), mongo::Status::OK());
+
+ ASSERT_EQUALS(e3.remove(), mongo::Status::OK());
+ ASSERT_EQUALS(e0.pushBack(e3), mongo::Status::OK());
ASSERT_EQUALS("e0", e3.parent().getFieldName());
ASSERT_EQUALS("e1", e0.leftChild().getFieldName());
@@ -328,8 +322,116 @@ namespace {
ASSERT_EQUALS("e5", e3.rightChild().getFieldName());
}
+ TEST(TopologyBuilding, CantAddAttachedAsLeftSibling) {
+ mmb::Document doc;
+ ASSERT_OK(doc.root().appendString("foo", "foo"));
+ mmb::Element foo = doc.root().rightChild();
+ ASSERT_TRUE(foo.ok());
+ ASSERT_OK(doc.root().appendString("bar", "bar"));
+ mmb::Element bar = doc.root().rightChild();
+ ASSERT_TRUE(bar.ok());
+ ASSERT_NOT_OK(foo.addSiblingLeft(bar));
+ }
+
+ TEST(TopologyBuilding, CantAddAttachedAsRightSibling) {
+ mmb::Document doc;
+ ASSERT_OK(doc.root().appendString("foo", "foo"));
+ mmb::Element foo = doc.root().rightChild();
+ ASSERT_TRUE(foo.ok());
+ ASSERT_OK(doc.root().appendString("bar", "bar"));
+ mmb::Element bar = doc.root().rightChild();
+ ASSERT_TRUE(bar.ok());
+ ASSERT_NOT_OK(foo.addSiblingRight(bar));
+ }
+
+ TEST(TopologyBuilding, CantAddAttachedAsChild) {
+ mmb::Document doc;
+ mmb::Element foo = doc.makeElementObject("foo");
+ ASSERT_TRUE(foo.ok());
+ ASSERT_OK(doc.root().pushBack(foo));
+ ASSERT_OK(doc.root().appendString("bar", "bar"));
+ mmb::Element bar = doc.root().rightChild();
+ ASSERT_TRUE(bar.ok());
+ ASSERT_NOT_OK(foo.pushFront(bar));
+ ASSERT_NOT_OK(foo.pushBack(bar));
+ }
+
+ TEST(TopologyBuilding, CantAddChildrenToNonObject) {
+ mmb::Document doc;
+ ASSERT_OK(doc.root().appendString("foo", "bar"));
+ mmb::Element foo = doc.root().rightChild();
+ ASSERT_TRUE(foo.ok());
+ mmb::Element bar = doc.makeElementString("bar", "bar");
+ ASSERT_TRUE(bar.ok());
+ ASSERT_NOT_OK(foo.pushFront(bar));
+ ASSERT_NOT_OK(foo.pushBack(bar));
+ }
+
+ TEST(TopologyBuilding, CantAddLeftSiblingToDetached) {
+ mmb::Document doc;
+ ASSERT_OK(doc.root().appendString("foo", "foo"));
+ mmb::Element foo = doc.root().rightChild();
+ ASSERT_TRUE(foo.ok());
+ ASSERT_OK(foo.remove());
+ ASSERT_FALSE(foo.parent().ok());
+ mmb::Element bar = doc.makeElementString("bar", "bar");
+ ASSERT_TRUE(bar.ok());
+ ASSERT_NOT_OK(foo.addSiblingLeft(bar));
+ }
+
+ TEST(TopologyBuilding, CantAddRightSiblingToDetached) {
+ mmb::Document doc;
+ ASSERT_OK(doc.root().appendString("foo", "foo"));
+ mmb::Element foo = doc.root().rightChild();
+ ASSERT_TRUE(foo.ok());
+ ASSERT_OK(foo.remove());
+ ASSERT_FALSE(foo.parent().ok());
+ mmb::Element bar = doc.makeElementString("bar", "bar");
+ ASSERT_TRUE(bar.ok());
+ ASSERT_NOT_OK(foo.addSiblingRight(bar));
+ }
+
+ TEST(TopologyBuilding, AddSiblingLeftIntrusion) {
+ mmb::Document doc;
+ ASSERT_OK(doc.root().appendString("first", "first"));
+ mmb::Element first = doc.root().rightChild();
+ ASSERT_TRUE(first.ok());
+ ASSERT_OK(doc.root().appendString("last", "last"));
+ mmb::Element last = doc.root().rightChild();
+ ASSERT_TRUE(last.ok());
+
+ ASSERT_EQUALS(first, last.leftSibling());
+ ASSERT_EQUALS(last, first.rightSibling());
+
+ mmb::Element middle = doc.makeElementString("middle", "middle");
+ ASSERT_TRUE(middle.ok());
+ ASSERT_OK(last.addSiblingLeft(middle));
+
+ ASSERT_EQUALS(middle, first.rightSibling());
+ ASSERT_EQUALS(middle, last.leftSibling());
+
+ ASSERT_EQUALS(first, middle.leftSibling());
+ ASSERT_EQUALS(last, middle.rightSibling());
+ }
+
+ TEST(TopologyBuilding, AddSiblingRightIntrusion) {
+ mmb::Document doc;
+ ASSERT_OK(doc.root().appendString("first", "first"));
+ mmb::Element first = doc.root().rightChild();
+ ASSERT_TRUE(first.ok());
+ ASSERT_OK(doc.root().appendString("last", "last"));
+ mmb::Element last = doc.root().rightChild();
+ ASSERT_TRUE(last.ok());
+ mmb::Element middle = doc.makeElementString("middle", "middle");
+ ASSERT_TRUE(middle.ok());
+ ASSERT_OK(first.addSiblingRight(middle));
+ ASSERT_EQUALS(first, middle.leftSibling());
+ ASSERT_EQUALS(last, middle.rightSibling());
+ }
+
TEST(ArrayAPI, SimpleNumericArray) {
/*
+ { a : [] } create
{ a : [10] } pushBack
{ a : [10, 20] } pushBack
{ a : [10, 20, 30] } pushBack
@@ -341,25 +443,29 @@ namespace {
{ a : [10, 20, 30] } popFront
{ a : [10, 20] } popBack
{ a : [20] } popFront
+ { a : [100] } set
+ { a : [] } popFront
*/
-
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
-
- mongo::mutablebson::Element e1 = doc.makeArrayElement("a");
- mongo::mutablebson::Element e2 = doc.makeIntElement("", 10);
- ASSERT_EQUALS(10, e2.getIntValue());
- mongo::mutablebson::Element e3 = doc.makeIntElement("", 20);
- ASSERT_EQUALS(20, e3.getIntValue());
- mongo::mutablebson::Element e4 = doc.makeIntElement("", 30);
- ASSERT_EQUALS(30, e4.getIntValue());
- mongo::mutablebson::Element e5 = doc.makeIntElement("", 40);
- ASSERT_EQUALS(40, e5.getIntValue());
- mongo::mutablebson::Element e6 = doc.makeIntElement("", 5);
- ASSERT_EQUALS(5, e6.getIntValue());
- mongo::mutablebson::Element e7 = doc.makeIntElement("", 0);
- ASSERT_EQUALS(0, e7.getIntValue());
-
+
+ mmb::Document doc;
+
+ mmb::Element e1 = doc.makeElementArray("a");
+ ASSERT_FALSE(e1[0].ok());
+ ASSERT_FALSE(e1[1].ok());
+
+ mmb::Element e2 = doc.makeElementInt("", 10);
+ ASSERT_EQUALS(10, e2.getValueInt());
+ mmb::Element e3 = doc.makeElementInt("", 20);
+ ASSERT_EQUALS(20, e3.getValueInt());
+ mmb::Element e4 = doc.makeElementInt("", 30);
+ ASSERT_EQUALS(30, e4.getValueInt());
+ mmb::Element e5 = doc.makeElementInt("", 40);
+ ASSERT_EQUALS(40, e5.getValueInt());
+ mmb::Element e6 = doc.makeElementInt("", 5);
+ ASSERT_EQUALS(5, e6.getValueInt());
+ mmb::Element e7 = doc.makeElementInt("", 0);
+ ASSERT_EQUALS(0, e7.getValueInt());
+
ASSERT_EQUALS(e1.pushBack(e2), mongo::Status::OK());
ASSERT_EQUALS(e1.pushBack(e3), mongo::Status::OK());
ASSERT_EQUALS(e1.pushBack(e4), mongo::Status::OK());
@@ -367,252 +473,2541 @@ namespace {
ASSERT_EQUALS(e1.pushFront(e6), mongo::Status::OK());
ASSERT_EQUALS(e1.pushFront(e7), mongo::Status::OK());
- uint32_t n;
- ASSERT_EQUALS(e1.arraySize(&n), mongo::Status::OK());
- ASSERT_EQUALS(6, (int)n);
-
- mongo::mutablebson::Element e(&doc, mongo::mutablebson::EMPTY_REP);
- ASSERT_EQUALS(e1.get(0, &e), mongo::Status::OK());
- ASSERT_EQUALS(0, e.getIntValue());
- ASSERT_EQUALS(e1.get(1, &e), mongo::Status::OK());
- ASSERT_EQUALS(5, e.getIntValue());
- ASSERT_EQUALS(e1.get(2, &e), mongo::Status::OK());
- ASSERT_EQUALS(10, e.getIntValue());
- ASSERT_EQUALS(e1.get(3, &e), mongo::Status::OK());
- ASSERT_EQUALS(20, e.getIntValue());
- ASSERT_EQUALS(e1.get(4, &e), mongo::Status::OK());
- ASSERT_EQUALS(30, e.getIntValue());
- ASSERT_EQUALS(e1.get(5, &e), mongo::Status::OK());
- ASSERT_EQUALS(40, e.getIntValue());
-
- ASSERT_EQUALS(e1.peekBack(&e), mongo::Status::OK());
- ASSERT_EQUALS(40, e.getIntValue());
+ ASSERT_EQUALS(size_t(6), mmb::countChildren(e1));
+ ASSERT_EQUALS(0, e1[0].getValueInt());
+ ASSERT_EQUALS(5, e1[1].getValueInt());
+ ASSERT_EQUALS(10, e1[2].getValueInt());
+ ASSERT_EQUALS(20, e1[3].getValueInt());
+ ASSERT_EQUALS(30, e1[4].getValueInt());
+ ASSERT_EQUALS(40, e1[5].getValueInt());
+ ASSERT_EQUALS(40, e1.rightChild().getValueInt());
ASSERT_EQUALS(e1.popBack(), mongo::Status::OK());
- ASSERT_EQUALS(e1.arraySize(&n), mongo::Status::OK());
- ASSERT_EQUALS(5, (int)n);
- ASSERT_EQUALS(e1.get(0, &e), mongo::Status::OK());
- ASSERT_EQUALS(0, e.getIntValue());
- ASSERT_EQUALS(e1.get(1, &e), mongo::Status::OK());
- ASSERT_EQUALS(5, e.getIntValue());
- ASSERT_EQUALS(e1.get(2, &e), mongo::Status::OK());
- ASSERT_EQUALS(10, e.getIntValue());
- ASSERT_EQUALS(e1.get(3, &e), mongo::Status::OK());
- ASSERT_EQUALS(20, e.getIntValue());
- ASSERT_EQUALS(e1.get(4, &e), mongo::Status::OK());
- ASSERT_EQUALS(30, e.getIntValue());
-
- ASSERT_EQUALS(e1.peekFront(&e), mongo::Status::OK());
- ASSERT_EQUALS(0, e.getIntValue());
+
+ ASSERT_EQUALS(size_t(5), mmb::countChildren(e1));
+ ASSERT_EQUALS(0, e1[0].getValueInt());
+ ASSERT_EQUALS(5, e1[1].getValueInt());
+ ASSERT_EQUALS(10, e1[2].getValueInt());
+ ASSERT_EQUALS(20, e1[3].getValueInt());
+ ASSERT_EQUALS(30, e1[4].getValueInt());
+ ASSERT_EQUALS(0, e1.leftChild().getValueInt());
ASSERT_EQUALS(e1.popFront(), mongo::Status::OK());
- ASSERT_EQUALS(e1.arraySize(&n), mongo::Status::OK());
- ASSERT_EQUALS(4, (int)n);
- ASSERT_EQUALS(e1.get(0, &e), mongo::Status::OK());
- ASSERT_EQUALS(5, e.getIntValue());
- ASSERT_EQUALS(e1.get(1, &e), mongo::Status::OK());
- ASSERT_EQUALS(10, e.getIntValue());
- ASSERT_EQUALS(e1.get(2, &e), mongo::Status::OK());
- ASSERT_EQUALS(20, e.getIntValue());
- ASSERT_EQUALS(e1.get(3, &e), mongo::Status::OK());
- ASSERT_EQUALS(30, e.getIntValue());
-
- ASSERT_EQUALS(e1.peekFront(&e), mongo::Status::OK());
- ASSERT_EQUALS(5, e.getIntValue());
+
+ ASSERT_EQUALS(size_t(4), mmb::countChildren(e1));
+ ASSERT_EQUALS(5, e1[0].getValueInt());
+ ASSERT_EQUALS(10, e1[1].getValueInt());
+ ASSERT_EQUALS(20, e1[2].getValueInt());
+ ASSERT_EQUALS(30, e1[3].getValueInt());
+ ASSERT_EQUALS(5, e1.leftChild().getValueInt());
ASSERT_EQUALS(e1.popFront(), mongo::Status::OK());
- ASSERT_EQUALS(e1.arraySize(&n), mongo::Status::OK());
- ASSERT_EQUALS(3, (int)n);
- ASSERT_EQUALS(e1.get(0, &e), mongo::Status::OK());
- ASSERT_EQUALS(10, e.getIntValue());
- ASSERT_EQUALS(e1.get(1, &e), mongo::Status::OK());
- ASSERT_EQUALS(20, e.getIntValue());
- ASSERT_EQUALS(e1.get(2, &e), mongo::Status::OK());
- ASSERT_EQUALS(30, e.getIntValue());
-
- ASSERT_EQUALS(e1.peekBack(&e), mongo::Status::OK());
- ASSERT_EQUALS(30, e.getIntValue());
+
+ ASSERT_EQUALS(size_t(3), mmb::countChildren(e1));
+ ASSERT_EQUALS(10, e1[0].getValueInt());
+ ASSERT_EQUALS(20, e1[1].getValueInt());
+ ASSERT_EQUALS(30, e1[2].getValueInt());
+ ASSERT_EQUALS(30, e1.rightChild().getValueInt());
ASSERT_EQUALS(e1.popBack(), mongo::Status::OK());
- ASSERT_EQUALS(e1.arraySize(&n), mongo::Status::OK());
- ASSERT_EQUALS(2, (int)n);
- ASSERT_EQUALS(e1.get(0, &e), mongo::Status::OK());
- ASSERT_EQUALS(10, e.getIntValue());
- ASSERT_EQUALS(e1.get(1, &e), mongo::Status::OK());
- ASSERT_EQUALS(20, e.getIntValue());
-
- ASSERT_EQUALS(e1.peekFront(&e), mongo::Status::OK());
- ASSERT_EQUALS(10, e.getIntValue());
+
+ ASSERT_EQUALS(size_t(2), mmb::countChildren(e1));
+ ASSERT_EQUALS(10, e1[0].getValueInt());
+ ASSERT_EQUALS(20, e1[1].getValueInt());
+ ASSERT_EQUALS(10, e1.leftChild().getValueInt());
+ ASSERT_EQUALS(e1.popFront(), mongo::Status::OK());
+
+ ASSERT_EQUALS(size_t(1), mmb::countChildren(e1));
+ ASSERT_EQUALS(20, e1[0].getValueInt());
+
+ ASSERT_EQUALS(e1[0].setValueInt(100), mongo::Status::OK());
+ ASSERT_EQUALS(100, e1[0].getValueInt());
+ ASSERT_EQUALS(100, e1.leftChild().getValueInt());
+ ASSERT_EQUALS(size_t(1), mmb::countChildren(e1));
ASSERT_EQUALS(e1.popFront(), mongo::Status::OK());
- ASSERT_EQUALS(e1.arraySize(&n), mongo::Status::OK());
- ASSERT_EQUALS(1, (int)n);
- ASSERT_EQUALS(e1.get(0, &e), mongo::Status::OK());
- ASSERT_EQUALS(20, e.getIntValue());
-
- mongo::mutablebson::Element e8 = doc.makeIntElement("", 100);
- ASSERT_EQUALS(100, e8.getIntValue());
- ASSERT_EQUALS(e1.set(0, e8), mongo::Status::OK());
- ASSERT_EQUALS(e1.peekFront(&e), mongo::Status::OK());
- ASSERT_EQUALS(100, e.getIntValue());
+
+ ASSERT_EQUALS(size_t(0), mmb::countChildren(e1));
+ ASSERT_FALSE(e1[0].ok());
+ ASSERT_FALSE(e1[1].ok());
}
TEST(Element, setters) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element t0 = doc.makeNullElement("t0");
+ mmb::Element t0 = doc.makeElementNull("t0");
- t0.setBoolValue(true);
- ASSERT_EQUALS(mongo::Bool, t0.type());
+ t0.setValueBool(true);
+ ASSERT_EQUALS(mongo::Bool, t0.getType());
- t0.setIntValue(12345);
- ASSERT_EQUALS(mongo::NumberInt, t0.type());
+ t0.setValueInt(12345);
+ ASSERT_EQUALS(mongo::NumberInt, t0.getType());
- t0.setLongValue(12345LL);
- ASSERT_EQUALS(mongo::NumberLong, t0.type());
+ t0.setValueLong(12345LL);
+ ASSERT_EQUALS(mongo::NumberLong, t0.getType());
- t0.setTSValue(mongo::OpTime());
- ASSERT_EQUALS(mongo::Timestamp, t0.type());
+ t0.setValueTimestamp(mongo::OpTime());
+ ASSERT_EQUALS(mongo::Timestamp, t0.getType());
- t0.setDateValue(12345LL);
- ASSERT_EQUALS(mongo::Date, t0.type());
+ t0.setValueDate(12345LL);
+ ASSERT_EQUALS(mongo::Date, t0.getType());
- t0.setDoubleValue(123.45);
- ASSERT_EQUALS(mongo::NumberDouble, t0.type());
+ t0.setValueDouble(123.45);
+ ASSERT_EQUALS(mongo::NumberDouble, t0.getType());
- t0.setOIDValue(mongo::OID("47cc67093475061e3d95369d"));
- ASSERT_EQUALS(mongo::jstOID, t0.type());
+ t0.setValueOID(mongo::OID("47cc67093475061e3d95369d"));
+ ASSERT_EQUALS(mongo::jstOID, t0.getType());
- t0.setRegexValue("[a-zA-Z]?");
- ASSERT_EQUALS(mongo::RegEx, t0.type());
+ t0.setValueRegex("[a-zA-Z]?", "");
+ ASSERT_EQUALS(mongo::RegEx, t0.getType());
+
+ t0.setValueString("foo bar baz");
+ ASSERT_EQUALS(mongo::String, t0.getType());
+ }
- t0.setStringValue("foo bar baz");
- ASSERT_EQUALS(mongo::String, t0.type());
+ TEST(Element, toString) {
+ mongo::BSONObj obj = mongo::fromjson("{ a : 1, b : [1, 2, 3], c : { x : 'x' } }");
+ mmb::Document doc(obj);
+
+ // Deserialize the 'c' but keep its value the same.
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ ASSERT_OK(c.appendString("y", "y"));
+ ASSERT_OK(c.popBack());
+
+ // 'a'
+ mongo::BSONObjIterator iter(obj);
+ mmb::Element docChild = doc.root().leftChild();
+ ASSERT_TRUE(docChild.ok());
+ ASSERT_EQUALS(iter.next().toString(), docChild.toString());
+
+ // 'b'
+ docChild = docChild.rightSibling();
+ ASSERT_TRUE(docChild.ok());
+ ASSERT_TRUE(iter.more());
+ ASSERT_EQUALS(iter.next().toString(), mmb::ConstElement(docChild).toString());
+
+ // 'c'
+ docChild = docChild.rightSibling();
+ ASSERT_TRUE(docChild.ok());
+ ASSERT_TRUE(iter.more());
+ ASSERT_EQUALS(iter.next().toString(), docChild.toString());
+
+ // eoo
+ docChild = docChild.rightSibling();
+ ASSERT_FALSE(iter.more());
+ ASSERT_FALSE(docChild.ok());
}
TEST(TimestampType, createElement) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element t0 = doc.makeTSElement("t0", mongo::OpTime());
- ASSERT(mongo::OpTime() == t0.getTSValue());
+ mmb::Element t0 = doc.makeElementTimestamp("t0", mongo::OpTime());
+ ASSERT(mongo::OpTime() == t0.getValueTimestamp());
- mongo::mutablebson::Element t1 = doc.makeTSElement("t1", mongo::OpTime(123, 456));
- ASSERT(mongo::OpTime(123, 456) == t1.getTSValue());
+ mmb::Element t1 = doc.makeElementTimestamp("t1", mongo::OpTime(123, 456));
+ ASSERT(mongo::OpTime(123, 456) == t1.getValueTimestamp());
}
TEST(TimestampType, setElement) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element t0 = doc.makeTSElement("t0", mongo::OpTime());
- t0.setTSValue(mongo::OpTime(123, 456));
- ASSERT(mongo::OpTime(123, 456) == t0.getTSValue());
+ mmb::Element t0 = doc.makeElementTimestamp("t0", mongo::OpTime());
+ t0.setValueTimestamp(mongo::OpTime(123, 456));
+ ASSERT(mongo::OpTime(123, 456) == t0.getValueTimestamp());
// Try setting to other types and back to OpTime
- t0.setLongValue(1234567890);
- ASSERT_EQUALS(1234567890LL, t0.getLongValue());
- t0.setTSValue(mongo::OpTime(789, 321));
- ASSERT(mongo::OpTime(789, 321) == t0.getTSValue());
-
- t0.setStringValue("foo bar baz");
- ASSERT_EQUALS(0, strcmp("foo bar baz", t0.getStringValue()));
- t0.setTSValue(mongo::OpTime(9876, 5432));
- ASSERT(mongo::OpTime(9876, 5432) == t0.getTSValue());
+ t0.setValueLong(1234567890);
+ ASSERT_EQUALS(1234567890LL, t0.getValueLong());
+ t0.setValueTimestamp(mongo::OpTime(789, 321));
+ ASSERT(mongo::OpTime(789, 321) == t0.getValueTimestamp());
+
+ t0.setValueString("foo bar baz");
+ ASSERT_EQUALS("foo bar baz", t0.getValueString());
+ t0.setValueTimestamp(mongo::OpTime(9876, 5432));
+ ASSERT(mongo::OpTime(9876, 5432) == t0.getValueTimestamp());
}
TEST(TimestampType, appendElement) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element t0 = doc.makeObjElement("e0");
- t0.appendTS("a timestamp field", mongo::OpTime(1352151971, 471));
+ mmb::Element t0 = doc.makeElementObject("e0");
+ t0.appendTimestamp("a timestamp field", mongo::OpTime(1352151971, 471));
- mongo::mutablebson::SiblingIterator it =
- mongo::mutablebson::findFirstChildNamed(t0, "a timestamp field");
- ASSERT_EQUALS(it.done(), false);
- ASSERT(mongo::OpTime(1352151971, 471) ==
- mongo::mutablebson::Element(&doc, it.getRep()).getTSValue());
+ mmb::Element it =
+ mmb::findFirstChildNamed(t0, "a timestamp field");
+ ASSERT_TRUE(it.ok());
+ ASSERT(mongo::OpTime(1352151971, 471) == it.getValueTimestamp());
}
TEST(SafeNumType, createElement) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element t0 = doc.makeSafeNumElement("t0", mongo::SafeNum(123.456));
- ASSERT_EQUALS(mongo::SafeNum(123.456), t0.getSafeNumValue());
+ mmb::Element t0 = doc.makeElementSafeNum("t0", mongo::SafeNum(123.456));
+ ASSERT_EQUALS(mongo::SafeNum(123.456), t0.getValueSafeNum());
}
// Try getting SafeNums from different types.
TEST(SafeNumType, getSafeNum) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element t0 = doc.makeIntElement("t0", 1234567890);
- ASSERT_EQUALS(1234567890, t0.getIntValue());
- mongo::SafeNum num = t0.getSafeNumValue();
+ mmb::Element t0 = doc.makeElementInt("t0", 1234567890);
+ ASSERT_EQUALS(1234567890, t0.getValueInt());
+ mongo::SafeNum num = t0.getValueSafeNum();
ASSERT_EQUALS(num, 1234567890);
- t0.setLongValue(1234567890LL);
- ASSERT_EQUALS(1234567890LL, t0.getLongValue());
- num = t0.getSafeNumValue();
+ t0.setValueLong(1234567890LL);
+ ASSERT_EQUALS(1234567890LL, t0.getValueLong());
+ num = t0.getValueSafeNum();
ASSERT_EQUALS(num, 1234567890LL);
- t0.setDoubleValue(123.456789);
- ASSERT_EQUALS(123.456789, t0.getDoubleValue());
- num = t0.getSafeNumValue();
+ t0.setValueDouble(123.456789);
+ ASSERT_EQUALS(123.456789, t0.getValueDouble());
+ num = t0.getValueSafeNum();
ASSERT_EQUALS(num, 123.456789);
}
TEST(SafeNumType, setSafeNum) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element t0 = doc.makeSafeNumElement("t0", mongo::SafeNum(123456));
- t0.setSafeNumValue(mongo::SafeNum(654321));
- ASSERT_EQUALS(mongo::SafeNum(654321), t0.getSafeNumValue());
+ mmb::Element t0 = doc.makeElementSafeNum("t0", mongo::SafeNum(123456));
+ t0.setValueSafeNum(mongo::SafeNum(654321));
+ ASSERT_EQUALS(mongo::SafeNum(654321), t0.getValueSafeNum());
// Try setting to other types and back to SafeNum
- t0.setLongValue(1234567890);
- ASSERT_EQUALS(1234567890LL, t0.getLongValue());
- t0.setSafeNumValue(mongo::SafeNum(1234567890));
- ASSERT_EQUALS(mongo::SafeNum(1234567890), t0.getSafeNumValue());
+ t0.setValueLong(1234567890);
+ ASSERT_EQUALS(1234567890LL, t0.getValueLong());
+ t0.setValueSafeNum(mongo::SafeNum(1234567890));
+ ASSERT_EQUALS(mongo::SafeNum(1234567890), t0.getValueSafeNum());
+
+ t0.setValueString("foo bar baz");
+
+ mongo::StringData left = "foo bar baz";
+ mongo::StringData right = t0.getValueString();
+ ASSERT_EQUALS(left, right);
- t0.setStringValue("foo bar baz");
- ASSERT_EQUALS("foo bar baz", std::string(t0.getStringValue()));
- t0.setSafeNumValue(mongo::SafeNum(12345));
- ASSERT_EQUALS(mongo::SafeNum(12345), t0.getSafeNumValue());
+ ASSERT_EQUALS(mongo::StringData("foo bar baz"), t0.getValueString());
+ t0.setValueSafeNum(mongo::SafeNum(12345));
+ ASSERT_EQUALS(mongo::SafeNum(12345), t0.getValueSafeNum());
}
TEST(SafeNumType, appendElement) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
+ mmb::Document doc;
- mongo::mutablebson::Element t0 = doc.makeObjElement("e0");
+ mmb::Element t0 = doc.makeElementObject("e0");
t0.appendSafeNum("a timestamp field", mongo::SafeNum(1352151971LL));
- mongo::mutablebson::SiblingIterator it = findFirstChildNamed(t0, "a timestamp field");
- ASSERT_EQUALS(it.done(), false);
- ASSERT_EQUALS(mongo::SafeNum(1352151971LL),
- mongo::mutablebson::Element(&doc, it.getRep()).getSafeNumValue());
+ mmb::Element it = findFirstChildNamed(t0, "a timestamp field");
+ ASSERT_TRUE(it.ok());
+ ASSERT_EQUALS(mongo::SafeNum(1352151971LL), it.getValueSafeNum());
}
TEST(OIDType, getOidValue) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
- mongo::mutablebson::Element t0 = doc.makeObjElement("e0");
+ mmb::Document doc;
+ mmb::Element t0 = doc.makeElementObject("e0");
const mongo::OID generated = mongo::OID::gen();
t0.appendOID("myOid", generated);
- mongo::mutablebson::SiblingIterator it = findFirstChildNamed(t0, "myOid");
- const mongo::OID recovered = mongo::OID((*it).getOIDValue());
+ mmb::Element it = findFirstChildNamed(t0, "myOid");
+ const mongo::OID recovered = mongo::OID(it.getValueOID());
ASSERT_EQUALS(generated, recovered);
}
TEST(OIDType, nullOID) {
- mongo::mutablebson::BasicHeap myHeap;
- mongo::mutablebson::Document doc(&myHeap);
- mongo::mutablebson::Element t0 = doc.makeObjElement("e0");
+ mmb::Document doc;
+ mmb::Element t0 = doc.makeElementObject("e0");
const mongo::OID withNull("50a9c82263e413ad0028faad");
t0.appendOID("myOid", withNull);
- mongo::mutablebson::SiblingIterator it = findFirstChildNamed(t0, "myOid");
- const mongo::OID recovered = mongo::OID((*it).getOIDValue());
+ mmb::Element it = findFirstChildNamed(t0, "myOid");
+ const mongo::OID recovered = mongo::OID(it.getValueOID());
ASSERT_EQUALS(withNull, recovered);
}
-} // unnamed namespace
+ static const char jsonSample[] =
+ "{_id:ObjectId(\"47cc67093475061e3d95369d\"),"
+ "query:\"kate hudson\","
+ "owner:1234567887654321,"
+ "date:\"2011-05-13T14:22:46.777Z\","
+ "score:123.456,"
+ "field1:Infinity,"
+ "\"field2\":-Infinity,"
+ "\"field3\":NaN,"
+ "users:["
+ "{uname:\"@aaaa\",editid:\"123\",date:1303959350,yes_votes:0,no_votes:0},"
+ "{uname:\"@bbbb\",editid:\"456\",date:1303959350,yes_votes:0,no_votes:0},"
+ "{uname:\"@cccc\",editid:\"789\",date:1303959350,yes_votes:0,no_votes:0}],"
+ "pattern:/match.*this/,"
+ "lastfield:\"last\"}";
+
+ TEST(Serialization, RoundTrip) {
+ mongo::BSONObj obj = mongo::fromjson(jsonSample);
+ mmb::Document doc(obj.copy());
+ mongo::BSONObj built = doc.getObject();
+ ASSERT_EQUALS(obj, built);
+ }
+
+ TEST(Documentation, Example1) {
+
+ // Create a new document
+ mmb::Document doc;
+ ASSERT_EQUALS(mongo::fromjson("{}"),
+ doc.getObject());
+
+ // Get the root of the document.
+ mmb::Element root = doc.root();
+
+ // Create a new mongo::NumberInt typed Element to represent life, the universe, and
+ // everything, then push that Element into the root object, making it a child of root.
+ mmb::Element e0 = doc.makeElementInt("ltuae", 42);
+ ASSERT_OK(root.pushBack(e0));
+ ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42 }"),
+ doc.getObject());
+
+ // Create a new empty mongo::Object-typed Element named 'magic', and push it back as a
+ // child of the root, making it a sibling of e0.
+ mmb::Element e1 = doc.makeElementObject("magic");
+ ASSERT_OK(root.pushBack(e1));
+ ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42, magic : {} }"),
+ doc.getObject());
+
+ // Create a new mongo::NumberDouble typed Element to represent Pi, and insert it as child
+ // of the new object we just created.
+ mmb::Element e3 = doc.makeElementDouble("pi", 3.14);
+ ASSERT_OK(e1.pushBack(e3));
+ ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42, magic : { pi : 3.14 } }"),
+ doc.getObject());
+
+ // Create a new mongo::NumberDouble to represent Plancks constant in electrovolt
+ // micrometers, and add it as a child of the 'magic' object.
+ mmb::Element e4 = doc.makeElementDouble("hbar", 1.239);
+ ASSERT_OK(e1.pushBack(e4));
+ ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42, magic : { pi : 3.14, hbar : 1.239 } }"),
+ doc.getObject());
+
+ // Rename the parent element of 'hbar' to be 'constants'.
+ ASSERT_OK(e4.parent().rename("constants"));
+ ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42, constants : { pi : 3.14, hbar : 1.239 } }"),
+ doc.getObject());
+
+ // Rename 'ltuae' to 'answer' by accessing it as the root objects left child.
+ ASSERT_OK(doc.root().leftChild().rename("answer"));
+ ASSERT_EQUALS(mongo::fromjson("{ answer : 42, constants : { pi : 3.14, hbar : 1.239 } }"),
+ doc.getObject());
+
+ // Sort the constants by name.
+ mmb::sortChildren(doc.root().rightChild(), mmb::FieldNameLessThan());
+ ASSERT_EQUALS(mongo::fromjson("{ answer : 42, constants : { hbar : 1.239, pi : 3.14 } }"),
+ doc.getObject());
+ }
+
+ TEST(Documentation, Example2) {
+
+ static const char inJson[] =
+ "{"
+ " 'whale': { 'alive': true, 'dv': -9.8, 'height': 50.0, attrs : [ 'big' ] },"
+ " 'petunias': { 'alive': true, 'dv': -9.8, 'height': 50.0 } "
+ "}";
+ mongo::BSONObj obj = mongo::fromjson(inJson);
+
+ // Create a new document representing BSONObj with the above contents.
+ mmb::Document doc(obj);
+
+ // The whale hits the planet and dies.
+ mmb::Element whale = mmb::findFirstChildNamed(doc.root(), "whale");
+ ASSERT_TRUE(whale.ok());
+ // Find the 'dv' field in the whale.
+ mmb::Element whale_deltav = mmb::findFirstChildNamed(whale, "dv");
+ ASSERT_TRUE(whale_deltav.ok());
+ // Set the dv field to zero.
+ ASSERT_OK(whale_deltav.setValueDouble(0.0));
+ // Find the 'height' field in the whale.
+ mmb::Element whale_height = mmb::findFirstChildNamed(whale, "height");
+ ASSERT_TRUE(whale_height.ok());
+ // Set the height field to zero.
+ ASSERT_OK(whale_height.setValueDouble(0));
+ // Find the 'alive' field, and set it to false.
+ mmb::Element whale_alive = mmb::findFirstChildNamed(whale, "alive");
+ ASSERT_TRUE(whale_alive.ok());
+ ASSERT_OK(whale_alive.setValueBool(false));
+
+ // The petunias survive, update its fields much like we did above.
+ mmb::Element petunias = mmb::findFirstChildNamed(doc.root(), "petunias");
+ ASSERT_TRUE(petunias.ok());
+ mmb::Element petunias_deltav = mmb::findFirstChildNamed(petunias, "dv");
+ ASSERT_TRUE(petunias_deltav.ok());
+ ASSERT_OK(petunias_deltav.setValueDouble(0.0));
+ mmb::Element petunias_height = mmb::findFirstChildNamed(petunias, "height");
+ ASSERT_TRUE(petunias_height.ok());
+ ASSERT_OK(petunias_height.setValueDouble(0));
+
+ // Replace the whale by its wreckage, saving only its attributes:
+ // Construct a new mongo::Object element for the ex-whale.
+ mmb::Element ex_whale = doc.makeElementObject("ex-whale");
+ ASSERT_OK(doc.root().pushBack(ex_whale));
+ // Find the attributes of the old 'whale' element.
+ mmb::Element whale_attrs = mmb::findFirstChildNamed(whale, "attrs");
+ // Remove the attributes from the whale (they remain valid, but detached).
+ ASSERT_OK(whale_attrs.remove());
+ // Insert the attributes into the ex-whale.
+ ASSERT_OK(ex_whale.pushBack(whale_attrs));
+ // Remove the whale object.
+ ASSERT_OK(whale.remove());
+
+ static const char outJson[] =
+ "{"
+ " 'petunias': { 'alive': true, 'dv': 0.0, 'height': 0 },"
+ " 'ex-whale': { 'attrs': [ 'big' ] } })"
+ "}";
+
+ mongo::BSONObjBuilder builder;
+ doc.writeTo(&builder);
+ ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject());
+ }
+
+ namespace {
+ void apply(mongo::BSONObj* obj, const mmb::DamageVector& damages, const char* source) {
+ const mmb::DamageVector::const_iterator end = damages.end();
+ mmb::DamageVector::const_iterator where = damages.begin();
+ char* const target = const_cast<char *>(obj->objdata());
+ for ( ; where != end; ++where ) {
+ std::memcpy(
+ target + where->targetOffset,
+ source + where->sourceOffset,
+ where->size);
+ }
+ }
+ } // namespace
+
+ TEST(Documentation, Example2InPlaceWithDamageVector) {
+
+ static const char inJson[] =
+ "{"
+ " 'whale': { 'alive': true, 'dv': -9.8, 'height': 50.0, attrs : [ 'big' ] },"
+ " 'petunias': { 'alive': true, 'dv': -9.8, 'height': 50.0 } "
+ "}";
+
+ // Make the object, and make a copy for reference.
+ mongo::BSONObj obj = mongo::fromjson(inJson);
+ const mongo::BSONObj copyOfObj = obj.getOwned();
+ ASSERT_EQUALS(obj, copyOfObj);
+
+ // Create a new document representing BSONObj with the above contents.
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_EQUALS(obj, doc);
+ ASSERT_EQUALS(copyOfObj, doc);
+
+ // Enable in-place mutation for this document
+ ASSERT_EQUALS(mmb::Document::kInPlaceEnabled, doc.getCurrentInPlaceMode());
+
+ // The whale hits the planet and dies.
+ mmb::Element whale = mmb::findFirstChildNamed(doc.root(), "whale");
+ ASSERT_TRUE(whale.ok());
+ // Find the 'dv' field in the whale.
+ mmb::Element whale_deltav = mmb::findFirstChildNamed(whale, "dv");
+ ASSERT_TRUE(whale_deltav.ok());
+ // Set the dv field to zero.
+ ASSERT_OK(whale_deltav.setValueDouble(0.0));
+ // Find the 'height' field in the whale.
+ mmb::Element whale_height = mmb::findFirstChildNamed(whale, "height");
+ ASSERT_TRUE(whale_height.ok());
+ // Set the height field to zero.
+ ASSERT_OK(whale_height.setValueDouble(0));
+ // Find the 'alive' field, and set it to false.
+ mmb::Element whale_alive = mmb::findFirstChildNamed(whale, "alive");
+ ASSERT_TRUE(whale_alive.ok());
+ ASSERT_OK(whale_alive.setValueBool(false));
+
+ // The petunias survive, update its fields much like we did above.
+ mmb::Element petunias = mmb::findFirstChildNamed(doc.root(), "petunias");
+ ASSERT_TRUE(petunias.ok());
+ mmb::Element petunias_deltav = mmb::findFirstChildNamed(petunias, "dv");
+ ASSERT_TRUE(petunias_deltav.ok());
+ ASSERT_OK(petunias_deltav.setValueDouble(0.0));
+ mmb::Element petunias_height = mmb::findFirstChildNamed(petunias, "height");
+ ASSERT_TRUE(petunias_height.ok());
+ ASSERT_OK(petunias_height.setValueDouble(0));
+
+ // Demonstrate that while the document has changed, the underlying BSONObj has not yet
+ // changed.
+ ASSERT_FALSE(obj == doc);
+ ASSERT_EQUALS(copyOfObj, obj);
+
+ // Ensure that in-place updates are still enabled.
+ ASSERT_EQUALS(mmb::Document::kInPlaceEnabled, doc.getCurrentInPlaceMode());
+
+ // Extract the damage events
+ mmb::DamageVector damages;
+ const char* source = NULL;
+ size_t size = 0;
+ ASSERT_EQUALS(true, doc.getInPlaceUpdates(&damages, &source, &size));
+ ASSERT_NOT_EQUALS(0U, damages.size());
+ ASSERT_NOT_EQUALS(static_cast<const char*>(NULL), source);
+ ASSERT_NOT_EQUALS(0U, size);
+
+ apply(&obj, damages, source);
+
+ static const char outJson[] =
+ "{"
+ " 'whale': { 'alive': false, 'dv': 0, 'height': 0, attrs : [ 'big' ] },"
+ " 'petunias': { 'alive': true, 'dv': 0, 'height': 0 } "
+ "}";
+ mongo::BSONObj outObj = mongo::fromjson(outJson);
+
+ ASSERT_EQUALS(outObj, doc);
+
+ mongo::BSONObjBuilder builder;
+ doc.writeTo(&builder);
+ ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject());
+ }
+
+ TEST(Documentation, Example3) {
+ static const char inJson[] =
+ "{"
+ " 'xs': { 'x' : 'x', 'X' : 'X' },"
+ " 'ys': { 'y' : 'y' }"
+ "}";
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+
+ mmb::Document doc(inObj);
+ mmb::Element xs = doc.root().leftChild();
+ ASSERT_TRUE(xs.ok());
+ mmb::Element ys = xs.rightSibling();
+ ASSERT_TRUE(ys.ok());
+ mmb::Element dne = ys.rightSibling();
+ ASSERT_FALSE(dne.ok());
+ mmb::Element ycaps = doc.makeElementString("Y", "Y");
+ ASSERT_OK(ys.pushBack(ycaps));
+ mmb::Element pun = doc.makeElementArray("why");
+ ASSERT_OK(ys.pushBack(pun));
+ pun.appendString("na", "not");
+ mongo::BSONObj outObj = doc.getObject();
+
+ static const char outJson[] =
+ "{"
+ " 'xs': { 'x' : 'x', 'X' : 'X' },"
+ " 'ys': { 'y' : 'y', 'Y' : 'Y', 'why' : ['not'] }"
+ "}";
+ ASSERT_EQUALS(mongo::fromjson(outJson), outObj);
+ }
+
+ TEST(Document, LifecycleConstructDefault) {
+ // Verify the state of a newly created empty Document.
+ mmb::Document doc;
+ ASSERT_TRUE(doc.root().ok());
+ ASSERT_TRUE(const_cast<const mmb::Document&>(doc).root().ok());
+ ASSERT_TRUE(doc.root().isType(mongo::Object));
+ ASSERT_FALSE(doc.root().leftSibling().ok());
+ ASSERT_FALSE(doc.root().rightSibling().ok());
+ ASSERT_FALSE(doc.root().leftChild().ok());
+ ASSERT_FALSE(doc.root().rightChild().ok());
+ ASSERT_FALSE(doc.root().parent().ok());
+ ASSERT_FALSE(doc.root().hasValue());
+ }
+
+ TEST(Document, LifecycleConstructEmptyBSONObj) {
+ // Verify the state of a newly created empty Document where the construction argument
+ // is an empty BSONObj.
+ mongo::BSONObj obj;
+ mmb::Document doc(obj);
+ ASSERT_TRUE(doc.root().ok());
+ ASSERT_TRUE(const_cast<const mmb::Document&>(doc).root().ok());
+ ASSERT_TRUE(doc.root().isType(mongo::Object));
+ ASSERT_FALSE(doc.root().leftSibling().ok());
+ ASSERT_FALSE(doc.root().rightSibling().ok());
+ ASSERT_FALSE(doc.root().leftChild().ok());
+ ASSERT_FALSE(doc.root().rightChild().ok());
+ ASSERT_FALSE(doc.root().parent().ok());
+ ASSERT_FALSE(doc.root().hasValue());
+ }
+
+ TEST(Document, LifecycleConstructSimpleBSONObj) {
+ // Verify the state of a newly created Document where the construction argument is a
+ // simple (flat) BSONObj.
+ mongo::BSONObj obj = mongo::fromjson("{ e1: 1, e2: 'hello', e3: false }");
+ mmb::Document doc(obj);
+
+ // Check the state of the root.
+ ASSERT_TRUE(doc.root().ok());
+ ASSERT_TRUE(const_cast<const mmb::Document&>(doc).root().ok());
+ ASSERT_TRUE(doc.root().isType(mongo::Object));
+ ASSERT_FALSE(doc.root().parent().ok());
+ ASSERT_FALSE(doc.root().leftSibling().ok());
+ ASSERT_FALSE(doc.root().rightSibling().ok());
+ ASSERT_FALSE(doc.root().hasValue());
+
+ mmb::ConstElement e1Child = doc.root().leftChild();
+ // Check the connectivity of 'e1'.
+ ASSERT_TRUE(e1Child.ok());
+ ASSERT_EQUALS(doc.root(), e1Child.parent());
+ ASSERT_FALSE(e1Child.leftSibling().ok());
+ ASSERT_TRUE(e1Child.rightSibling().ok());
+ ASSERT_FALSE(e1Child.leftChild().ok());
+ ASSERT_FALSE(e1Child.rightChild().ok());
+
+ // Check the type, name, and value of 'e1'.
+ ASSERT_TRUE(e1Child.isType(mongo::NumberInt));
+ ASSERT_EQUALS("e1", e1Child.getFieldName());
+ ASSERT_TRUE(e1Child.hasValue());
+ ASSERT_EQUALS(int32_t(1), e1Child.getValueInt());
+
+ mmb::ConstElement e2Child = e1Child.rightSibling();
+ // Check the connectivity of 'e2'.
+ ASSERT_TRUE(e2Child.ok());
+ ASSERT_EQUALS(doc.root(), e2Child.parent());
+ ASSERT_TRUE(e2Child.leftSibling().ok());
+ ASSERT_TRUE(e2Child.rightSibling().ok());
+ ASSERT_EQUALS(e1Child, e2Child.leftSibling());
+ ASSERT_FALSE(e2Child.leftChild().ok());
+ ASSERT_FALSE(e2Child.rightChild().ok());
+
+ // Check the type, name and value of 'e2'.
+ ASSERT_TRUE(e2Child.isType(mongo::String));
+ ASSERT_EQUALS("e2", e2Child.getFieldName());
+ ASSERT_TRUE(e2Child.hasValue());
+ ASSERT_EQUALS("hello", e2Child.getValueString());
+
+ mmb::ConstElement e3Child = e2Child.rightSibling();
+ // Check the connectivity of 'e3'.
+ ASSERT_TRUE(e3Child.ok());
+ ASSERT_EQUALS(doc.root(), e3Child.parent());
+ ASSERT_TRUE(e3Child.leftSibling().ok());
+ ASSERT_FALSE(e3Child.rightSibling().ok());
+ ASSERT_EQUALS(e2Child, e3Child.leftSibling());
+ ASSERT_FALSE(e2Child.leftChild().ok());
+ ASSERT_FALSE(e2Child.rightChild().ok());
+
+ // Check the type, name and value of 'e3'.
+ ASSERT_TRUE(e3Child.isType(mongo::Bool));
+ ASSERT_EQUALS("e3", e3Child.getFieldName());
+ ASSERT_TRUE(e3Child.hasValue());
+ ASSERT_EQUALS(false, e3Child.getValueBool());
+ }
+
+ TEST(Document, RenameDeserialization) {
+ // Regression test for a bug where certain rename operations failed to deserialize up
+ // the tree correctly, resulting in a lost rename
+ static const char inJson[] =
+ "{"
+ " 'a' : { 'b' : { 'c' : { 'd' : 4 } } }"
+ "}";
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+
+ mmb::Document doc(inObj);
+ mmb::Element a = doc.root().leftChild();
+ ASSERT_TRUE(a.ok());
+ mmb::Element b = a.leftChild();
+ ASSERT_TRUE(b.ok());
+ mmb::Element c = b.leftChild();
+ ASSERT_TRUE(c.ok());
+ c.rename("C");
+ mongo::BSONObj outObj = doc.getObject();
+ static const char outJson[] =
+ "{"
+ " 'a' : { 'b' : { 'C' : { 'd' : 4 } } }"
+ "}";
+ ASSERT_EQUALS(mongo::fromjson(outJson), outObj);
+ }
+
+ TEST(Document, CantRenameRootElement) {
+ mmb::Document doc;
+ ASSERT_NOT_OK(doc.root().rename("foo"));
+ }
+
+ TEST(Document, RemoveElementWithOpaqueRightSibling) {
+ // Regression test for a bug where removing an element with an opaque right sibling
+ // would access an invalidated rep. Note that this test may or may not fail depending
+ // on the details of memory allocation: failures would be clearly visible with
+ // valgrind, however.
+ static const char inJson[] =
+ "{"
+ " 'a' : 1, 'b' : 2, 'c' : 3"
+ "}";
+
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+ mmb::Document doc(inObj);
+
+ mmb::Element a = doc.root().leftChild();
+ ASSERT_TRUE(a.ok());
+ a.remove();
+
+ static const char outJson[] =
+ "{"
+ " 'b' : 2, 'c' : 3"
+ "}";
+ mongo::BSONObj outObj = doc.getObject();
+ ASSERT_EQUALS(mongo::fromjson(outJson), outObj);
+ }
+
+ TEST(Document, AddRightSiblingToElementWithOpaqueRightSibling) {
+ // Regression test for a bug where adding a right sibling to a node with an opaque
+ // right sibling would potentially access an invalidated rep. Like the 'remove' test
+ // above, this may or may not crash, but would be visible under a memory checking tool.
+ static const char inJson[] =
+ "{"
+ " 'a' : 1, 'b' : 2, 'c' : 3"
+ "}";
+
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+ mmb::Document doc(inObj);
+
+ mmb::Element a = doc.root().leftChild();
+ ASSERT_TRUE(a.ok());
+ mmb::Element newElt = doc.makeElementString("X", "X");
+ ASSERT_OK(a.addSiblingRight(newElt));
+
+ static const char outJson[] =
+ "{"
+ " 'a' : 1, 'X' : 'X', 'b' : 2, 'c' : 3"
+ "}";
+ mongo::BSONObj outObj = doc.getObject();
+ ASSERT_EQUALS(mongo::fromjson(outJson), outObj);
+ }
+
+ TEST(Document, ArrayIndexedAccessFromJson) {
+ static const char inJson[] =
+ "{"
+ " a : 1, b : [{ c : 1 }]"
+ "}";
+
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+ mmb::Document doc(inObj);
+
+ mmb::Element a = doc.root().leftChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS("a", a.getFieldName());
+ ASSERT_EQUALS(mongo::NumberInt, a.getType());
+
+ mmb::Element b = a.rightSibling();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS("b", b.getFieldName());
+ ASSERT_EQUALS(mongo::Array, b.getType());
+
+ mmb::Element b0 = b[0];
+ ASSERT_TRUE(b0.ok());
+ ASSERT_EQUALS("0", b0.getFieldName());
+ ASSERT_EQUALS(mongo::Object, b0.getType());
+ }
+
+ TEST(Document, ArrayIndexedAccessFromManuallyBuilt) {
+ mmb::Document doc;
+ mmb::Element root = doc.root();
+ ASSERT_TRUE(root.ok());
+ {
+ ASSERT_OK(root.appendInt("a", 1));
+ mmb::Element b = doc.makeElementArray("b");
+ ASSERT_TRUE(b.ok());
+ ASSERT_OK(root.pushBack(b));
+ mmb::Element b0 = doc.makeElementObject("ignored");
+ ASSERT_TRUE(b0.ok());
+ ASSERT_OK(b.pushBack(b0));
+ ASSERT_OK(b0.appendInt("c", 1));
+ }
+
+ mmb::Element a = doc.root().leftChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS("a", a.getFieldName());
+ ASSERT_EQUALS(mongo::NumberInt, a.getType());
+
+ mmb::Element b = a.rightSibling();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS("b", b.getFieldName());
+ ASSERT_EQUALS(mongo::Array, b.getType());
+
+ mmb::Element b0 = b[0];
+ ASSERT_TRUE(b0.ok());
+ ASSERT_EQUALS("ignored", b0.getFieldName());
+ ASSERT_EQUALS(mongo::Object, b0.getType());
+ }
+
+ TEST(Document, EndElement) {
+ mmb::Document doc;
+ mmb::Element end = doc.end();
+ ASSERT_FALSE(end.ok());
+ mmb::Element missing = doc.root().leftChild();
+ ASSERT_EQUALS(end, missing);
+ missing = doc.root().rightChild();
+ ASSERT_EQUALS(end, missing);
+ missing = doc.root().leftSibling();
+ ASSERT_EQUALS(end, missing);
+ missing = doc.root().rightSibling();
+ ASSERT_EQUALS(end, missing);
+ }
+
+ TEST(Document, ConstEndElement) {
+ const mmb::Document doc;
+ mmb::ConstElement end = doc.end();
+ ASSERT_FALSE(end.ok());
+ mmb::ConstElement missing = doc.root().leftChild();
+ ASSERT_EQUALS(end, missing);
+ missing = doc.root().rightChild();
+ ASSERT_EQUALS(end, missing);
+ missing = doc.root().leftSibling();
+ ASSERT_EQUALS(end, missing);
+ missing = doc.root().rightSibling();
+ ASSERT_EQUALS(end, missing);
+ }
+
+ TEST(Element, EmptyDocHasNoChildren) {
+ mmb::Document doc;
+ ASSERT_FALSE(doc.root().hasChildren());
+ }
+
+ TEST(Element, PopulatedDocHasChildren) {
+ mmb::Document doc;
+ ASSERT_OK(doc.root().appendInt("a", 1));
+ ASSERT_TRUE(doc.root().hasChildren());
+ mmb::Element lc = doc.root().leftChild();
+ ASSERT_FALSE(lc.hasChildren());
+ }
+
+ TEST(Element, LazyEmptyDocHasNoChildren) {
+ static const char inJson[] = "{}";
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+ mmb::Document doc(inObj);
+ ASSERT_FALSE(doc.root().hasChildren());
+ }
+
+ TEST(Element, LazySingletonDocHasChildren) {
+ static const char inJson[] = "{ a : 1 }";
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+ mmb::Document doc(inObj);
+ ASSERT_TRUE(doc.root().hasChildren());
+ ASSERT_FALSE(doc.root().leftChild().hasChildren());
+ }
+
+ TEST(Element, LazyConstDoubletonDocHasChildren) {
+ static const char inJson[] = "{ a : 1, b : 2 }";
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+ const mmb::Document doc(inObj);
+ ASSERT_TRUE(doc.root().hasChildren());
+ ASSERT_FALSE(doc.root().leftChild().hasChildren());
+ ASSERT_FALSE(doc.root().rightChild().hasChildren());
+ ASSERT_FALSE(doc.root().leftChild() == doc.root().rightChild());
+ }
+
+ TEST(Document, AddChildToEmptyOpaqueSubobject) {
+ mongo::BSONObj inObj = mongo::fromjson("{a: {}}");
+ mmb::Document doc(inObj);
+
+ mmb::Element elem = doc.root()["a"];
+ ASSERT_TRUE(elem.ok());
+
+ mmb::Element newElem = doc.makeElementInt("0", 1);
+ ASSERT_TRUE(newElem.ok());
+
+ ASSERT_OK(elem.pushBack(newElem));
+ }
+
+ TEST(Element, IsNumeric) {
+ mmb::Document doc;
+
+ mmb::Element elt = doc.makeElementNull("dummy");
+ ASSERT_FALSE(elt.isNumeric());
+
+ elt = doc.makeElementInt("dummy", 42);
+ ASSERT_TRUE(elt.isNumeric());
+
+ elt = doc.makeElementString("dummy", "dummy");
+ ASSERT_FALSE(elt.isNumeric());
+
+ elt = doc.makeElementLong("dummy", 42);
+ ASSERT_TRUE(elt.isNumeric());
+
+ elt = doc.makeElementBool("dummy", false);
+ ASSERT_FALSE(elt.isNumeric());
+
+ elt = doc.makeElementDouble("dummy", 42.0);
+ ASSERT_TRUE(elt.isNumeric());
+ }
+
+ TEST(Element, IsIntegral) {
+ mmb::Document doc;
+
+ mmb::Element elt = doc.makeElementNull("dummy");
+ ASSERT_FALSE(elt.isIntegral());
+
+ elt = doc.makeElementInt("dummy", 42);
+ ASSERT_TRUE(elt.isIntegral());
+
+ elt = doc.makeElementString("dummy", "dummy");
+ ASSERT_FALSE(elt.isIntegral());
+
+ elt = doc.makeElementLong("dummy", 42);
+ ASSERT_TRUE(elt.isIntegral());
+
+ elt = doc.makeElementDouble("dummy", 42.0);
+ ASSERT_FALSE(elt.isIntegral());
+ }
+
+ TEST(Document, ArraySerialization) {
+
+ static const char inJson[] =
+ "{ "
+ " 'a' : { 'b' : [ 'c', 'd' ] } "
+ "}";
+
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+ mmb::Document doc(inObj);
+
+ mmb::Element root = doc.root();
+ mmb::Element a = root.leftChild();
+ mmb::Element b = a.leftChild();
+ mmb::Element new_array = doc.makeElementArray("XXX");
+ mmb::Element e = doc.makeElementString("e", "e");
+ new_array.pushBack(e);
+ b.pushBack(new_array);
+
+ static const char outJson[] =
+ "{ "
+ " 'a' : { 'b' : [ 'c', 'd', [ 'e' ] ] } "
+ "}";
+
+ const mongo::BSONObj outObj = doc.getObject();
+ ASSERT_EQUALS(mongo::fromjson(outJson), outObj);
+ }
+
+ TEST(Document, SetValueBSONElementFieldNameHandling) {
+ static const char inJson[] = "{ a : 4 }";
+ mongo::BSONObj inObj = mongo::fromjson(inJson);
+ mmb::Document doc(inObj);
+
+ static const char inJson2[] = "{ b : 5 }";
+ mongo::BSONObj inObj2 = mongo::fromjson(inJson2);
+ mongo::BSONObjIterator iterator = inObj2.begin();
+
+ ASSERT_TRUE(iterator.more());
+ const mongo::BSONElement b = iterator.next();
+
+ mmb::Element a = doc.root().leftChild();
+ a.setValueBSONElement(b);
+
+ static const char outJson[] = "{ a : 5 }";
+ ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject());
+ }
+
+ TEST(Document, CreateElementWithEmptyFieldName) {
+ mmb::Document doc;
+ mmb::Element noname = doc.makeElementObject(mongo::StringData());
+ ASSERT_TRUE(noname.ok());
+ ASSERT_EQUALS(mongo::StringData(), noname.getFieldName());
+ }
+
+ TEST(Document, CreateElementFromBSONElement) {
+ mongo::BSONObj obj = mongo::fromjson("{a:1}}");
+ mmb::Document doc;
+ ASSERT_OK(doc.root().appendElement(obj["a"]));
+
+ mmb::Element newElem = doc.root()["a"];
+ ASSERT_TRUE(newElem.ok());
+ ASSERT_EQUALS(newElem.getType(), mongo::NumberInt);
+ ASSERT_EQUALS(newElem.getValueInt(), 1);
+ }
+
+ TEST(Document, toStringEmpty) {
+ mongo::BSONObj obj;
+ mmb::Document doc;
+ ASSERT_EQUALS(obj.toString(), doc.toString());
+ }
+
+ TEST(Document, toStringComplex) {
+ mongo::BSONObj obj = mongo::fromjson("{a : 1, b : [1, 2, 3], c : 'c'}");
+ mmb::Document doc(obj);
+ ASSERT_EQUALS(obj.toString(), doc.toString());
+ }
+
+ TEST(Document, toStringEphemeralObject) {
+ mmb::Document doc;
+ mmb::Element e = doc.makeElementObject("foo");
+ ASSERT_OK(doc.root().pushBack(e));
+ ASSERT_OK(e.appendDouble("d", 1.0));
+ ASSERT_OK(e.appendString("s", "str"));
+ ASSERT_EQUALS(
+ mongo::fromjson("{ foo: { d : 1.0, s : 'str' } }").firstElement().toString(),
+ e.toString());
+ }
+
+ TEST(Document, toStringEphemeralArray) {
+ mmb::Document doc;
+ mmb::Element e = doc.makeElementArray("foo");
+ ASSERT_OK(doc.root().pushBack(e));
+ ASSERT_OK(e.appendDouble(mongo::StringData(), 1.0));
+ ASSERT_OK(e.appendString(mongo::StringData(), "str"));
+ ASSERT_EQUALS(
+ mongo::fromjson("{ foo: [ 1.0, 'str' ] }").firstElement().toString(),
+ e.toString());
+ }
+
+ TEST(Document, ElementCloningToDifferentDocument) {
+
+ const char initial[] = "{ a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6 ] }";
+
+ mmb::Document source(mongo::fromjson(initial));
+
+ // Dirty the 'd' node and parents.
+ source.root()["d"].pushBack(source.makeElementInt(mongo::StringData(), 7));
+
+ mmb::Document target;
+
+ mmb::Element newElement = target.makeElement(source.root()["d"]);
+ ASSERT_TRUE(newElement.ok());
+ mongo::Status status = target.root().pushBack(newElement);
+ ASSERT_OK(status);
+ const char* expected =
+ "{ d : [ 4, 5, 6, 7 ] }";
+ ASSERT_EQUALS(mongo::fromjson(expected), target);
+
+ newElement = target.makeElement(source.root()["b"]);
+ ASSERT_TRUE(newElement.ok());
+ status = target.root().pushBack(newElement);
+ ASSERT_OK(status);
+ expected =
+ "{ d : [ 4, 5, 6, 7 ], b : [ 1, 2, 3 ] }";
+ ASSERT_EQUALS(mongo::fromjson(expected), target);
+
+ newElement = target.makeElementWithNewFieldName("C", source.root()["c"]);
+ ASSERT_TRUE(newElement.ok());
+ status = target.root().pushBack(newElement);
+ ASSERT_OK(status);
+ expected =
+ "{ d : [ 4, 5, 6, 7 ], b : [ 1, 2, 3 ], C : { 'c' : 'c' } }";
+ ASSERT_EQUALS(mongo::fromjson(expected), target);
+ }
+
+ TEST(Document, ElementCloningToSameDocument) {
+
+ const char initial[] = "{ a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6 ] }";
+
+ mmb::Document doc(mongo::fromjson(initial));
+
+ // Dirty the 'd' node and parents.
+ doc.root()["d"].pushBack(doc.makeElementInt(mongo::StringData(), 7));
+
+ mmb::Element newElement = doc.makeElement(doc.root()["d"]);
+ ASSERT_TRUE(newElement.ok());
+ mongo::Status status = doc.root().pushBack(newElement);
+ ASSERT_OK(status);
+ const char* expected =
+ "{ "
+ " a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6, 7 ], "
+ " d : [ 4, 5, 6, 7 ] "
+ "}";
+ ASSERT_EQUALS(mongo::fromjson(expected), doc);
+
+ newElement = doc.makeElement(doc.root()["b"]);
+ ASSERT_TRUE(newElement.ok());
+ status = doc.root().pushBack(newElement);
+ ASSERT_OK(status);
+ expected =
+ "{ "
+ " a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6, 7 ], "
+ " d : [ 4, 5, 6, 7 ], "
+ " b : [ 1, 2, 3 ] "
+ "}";
+ ASSERT_EQUALS(mongo::fromjson(expected), doc);
+
+ newElement = doc.makeElementWithNewFieldName("C", doc.root()["c"]);
+ ASSERT_TRUE(newElement.ok());
+ status = doc.root().pushBack(newElement);
+ ASSERT_OK(status);
+ expected =
+ "{ "
+ " a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6, 7 ], "
+ " d : [ 4, 5, 6, 7 ], "
+ " b : [ 1, 2, 3 ], "
+ " C : { 'c' : 'c' } "
+ "}";
+ ASSERT_EQUALS(mongo::fromjson(expected), doc);
+ }
+
+ TEST(Document, RootCloningToDifferentDocument) {
+
+ const char initial[] = "{ a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6 ] }";
+
+ mmb::Document source(mongo::fromjson(initial));
+
+ // Dirty the 'd' node and parents.
+ source.root()["d"].pushBack(source.makeElementInt(mongo::StringData(), 7));
+
+ mmb::Document target;
+
+ mmb::Element newElement = target.makeElementWithNewFieldName("X", source.root());
+ mongo::Status status = target.root().pushBack(newElement);
+ ASSERT_OK(status);
+ const char expected[] =
+ "{ X : { a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6, 7 ] } }";
+
+ ASSERT_EQUALS(mongo::fromjson(expected), target);
+ }
+
+ TEST(Document, RootCloningToSameDocument) {
+
+ const char initial[] = "{ a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6 ] }";
+
+ mmb::Document doc(mongo::fromjson(initial));
+
+ // Dirty the 'd' node and parents.
+ doc.root()["d"].pushBack(doc.makeElementInt(mongo::StringData(), 7));
+
+ mmb::Element newElement = doc.makeElementWithNewFieldName("X", doc.root());
+ mongo::Status status = doc.root().pushBack(newElement);
+ ASSERT_OK(status);
+ const char expected[] =
+ "{ "
+ " a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6, 7 ], "
+ "X : { a : 1, b : [ 1, 2, 3 ], c : { 'c' : 'c' }, d : [ 4, 5, 6, 7 ] }"
+ "}";
+
+ ASSERT_EQUALS(mongo::fromjson(expected), doc);
+ }
+
+ TEST(Element, PopOpsOnEmpty) {
+ mmb::Document doc;
+ mmb::Element root = doc.root();
+ ASSERT_NOT_OK(root.popFront());
+ ASSERT_NOT_OK(root.popBack());
+ }
+
+ TEST(Document, NameOfRootElementIsEmpty) {
+ mmb::Document doc;
+ // NOTE: You really shouldn't rely on this behavior; this test is mostly for coverage.
+ ASSERT_EQUALS(mongo::StringData(), doc.root().getFieldName());
+ }
+
+ TEST(Document, SetValueOnRootFails) {
+ mmb::Document doc;
+ ASSERT_NOT_OK(doc.root().setValueInt(5));
+ }
+
+ TEST(Document, ValueOfEphemeralObjectElementIsEmpty) {
+ mmb::Document doc;
+ mmb::Element root = doc.root();
+ mmb::Element ephemeralObject = doc.makeElementObject("foo");
+ ASSERT_OK(root.pushBack(ephemeralObject));
+ ASSERT_FALSE(ephemeralObject.hasValue());
+ // NOTE: You really shouldn't rely on this behavior; this test is mostly for coverage.
+ ASSERT_EQUALS(mongo::BSONElement(), ephemeralObject.getValue());
+ }
+
+ TEST(Element, RemovingRemovedElementFails) {
+ // Once an Element is removed, you can't remove it again until you re-attach it
+ // somewhere. However, its children are still manipulable.
+ mmb::Document doc(mongo::fromjson("{ a : { b : 'c' } }"));
+ mmb::Element a = doc.root().leftChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_OK(a.remove());
+ ASSERT_NOT_OK(a.remove());
+ mmb::Element b = a.leftChild();
+ ASSERT_OK(b.remove());
+ ASSERT_NOT_OK(b.remove());
+ ASSERT_OK(a.pushBack(b));
+ ASSERT_OK(b.remove());
+ }
+
+ namespace {
+ // Checks that two BSONElements are byte-for-byte identical.
+ bool identical(const mongo::BSONElement& lhs, const mongo::BSONElement& rhs) {
+ if (lhs.size() != rhs.size())
+ return false;
+ return std::memcmp(lhs.rawdata(), rhs.rawdata(), lhs.size()) == 0;
+ }
+ } // namespace
+
+ TEST(TypeSupport, EncodingEquivalenceDouble) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const double value1 = 3.1415926;
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::NumberDouble);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendDouble(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::NumberDouble);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueDouble());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::NumberDouble);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueDouble(value1);
+ ASSERT_EQUALS(c.getType(), mongo::NumberDouble);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceString) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const std::string value1 = "value1";
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::String);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendString(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::String);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueString());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::String);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueString(value1);
+ ASSERT_EQUALS(c.getType(), mongo::String);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceObject) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const mongo::BSONObj value1 = mongo::fromjson("{ a : 1, b : 2.0, c : 'hello' }");
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::Object);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendObject(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::Object);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueObject());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::Object);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueObject(value1);
+ ASSERT_EQUALS(c.getType(), mongo::Object);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceArray) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const mongo::BSONObj dummy = (mongo::fromjson("{ x : [ 1, 2.0, 'hello' ] } "));
+ const mongo::BSONArray value1(dummy.firstElement().embeddedObject());
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::Array);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendArray(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::Array);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueArray());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::Array);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueArray(value1);
+ ASSERT_EQUALS(c.getType(), mongo::Array);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceBinary) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const mongo::BinDataType value1 = mongo::newUUID;
+ const unsigned char value2[] = {
+ 0x00, 0x9D, 0x15, 0xA3,
+ 0x3B, 0xCC, 0x46, 0x60,
+ 0x90, 0x45, 0xEF, 0x54,
+ 0x77, 0x8A, 0x87, 0x0C
+ };
+ builder.appendBinData(name, sizeof(value2), value1, &value2[0]);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::BinData);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendBinary(name, sizeof(value2), value1, &value2[0]));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::BinData);
+ ASSERT_TRUE(a.hasValue());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::BinData);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueBinary(sizeof(value2), value1, &value2[0]);
+ ASSERT_EQUALS(c.getType(), mongo::BinData);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceUndefined) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ builder.appendUndefined(name);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::Undefined);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendUndefined(name));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::Undefined);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_TRUE(mmb::ConstElement(a).isValueUndefined());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::Undefined);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueUndefined();
+ ASSERT_EQUALS(c.getType(), mongo::Undefined);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceOID) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const mongo::OID value1 = mongo::OID::gen();
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::jstOID);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendOID(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::jstOID);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueOID());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::jstOID);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueOID(value1);
+ ASSERT_EQUALS(c.getType(), mongo::jstOID);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceBoolean) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const bool value1 = true;
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::Bool);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendBool(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::Bool);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueBool());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::Bool);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueBool(value1);
+ ASSERT_EQUALS(c.getType(), mongo::Bool);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceDate) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const mongo::Date_t value1 = mongo::jsTime();
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::Date);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendDate(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::Date);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueDate());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::Date);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueDate(value1);
+ ASSERT_EQUALS(c.getType(), mongo::Date);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceNull) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ builder.appendNull(name);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::jstNULL);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::jstNULL);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_TRUE(mmb::ConstElement(a).isValueNull());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::jstNULL);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendUndefined(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueNull();
+ ASSERT_EQUALS(c.getType(), mongo::jstNULL);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceRegex) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const std::string value1 = "some_regex_data";
+ const std::string value2 = "flags";
+ builder.appendRegex(name, value1, value2);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::RegEx);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendRegex(name, value1, value2));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::RegEx);
+ ASSERT_TRUE(a.hasValue());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::RegEx);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueRegex(value1, value2);
+ ASSERT_EQUALS(c.getType(), mongo::RegEx);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceDBRef) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const std::string value1 = "some_ns";
+ const mongo::OID value2 = mongo::OID::gen();
+ builder.appendDBRef(name, value1, value2);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::DBRef);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendDBRef(name, value1, value2));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::DBRef);
+ ASSERT_TRUE(a.hasValue());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::DBRef);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueDBRef(value1, value2);
+ ASSERT_EQUALS(c.getType(), mongo::DBRef);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceCode) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const std::string value1 = "{ print 4; }";
+ builder.appendCode(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::Code);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendCode(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::Code);
+ ASSERT_TRUE(a.hasValue());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::Code);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueCode(value1);
+ ASSERT_EQUALS(c.getType(), mongo::Code);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceSymbol) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const std::string value1 = "#symbol";
+ builder.appendSymbol(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::Symbol);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendSymbol(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::Symbol);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueSymbol());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::Symbol);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueSymbol(value1);
+ ASSERT_EQUALS(c.getType(), mongo::Symbol);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceCodeWithScope) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const std::string value1 = "print x;";
+ const mongo::BSONObj value2 = mongo::fromjson("{ x : 4 }");
+ builder.appendCodeWScope(name, value1, value2);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::CodeWScope);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendCodeWithScope(name, value1, value2));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::CodeWScope);
+ ASSERT_TRUE(a.hasValue());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::CodeWScope);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueCodeWithScope(value1, value2);
+ ASSERT_EQUALS(c.getType(), mongo::CodeWScope);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceInt) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const int value1 = true;
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::NumberInt);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendInt(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::NumberInt);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueInt());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::NumberInt);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueInt(value1);
+ ASSERT_EQUALS(c.getType(), mongo::NumberInt);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceTimestamp) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const mongo::OpTime value1 = mongo::OpTime(mongo::jsTime());
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::Timestamp);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendTimestamp(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::Timestamp);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_TRUE(value1 == mmb::ConstElement(a).getValueTimestamp());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::Timestamp);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueTimestamp(value1);
+ ASSERT_EQUALS(c.getType(), mongo::Timestamp);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceLong) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const long long value1 = 420000000000000LL;
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::NumberLong);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendLong(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::NumberLong);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueLong());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::NumberLong);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueLong(value1);
+ ASSERT_EQUALS(c.getType(), mongo::NumberLong);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceMinKey) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ builder.appendMinKey(name);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::MinKey);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendMinKey(name));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::MinKey);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_TRUE(mmb::ConstElement(a).isValueMinKey());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::MinKey);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueMinKey();
+ ASSERT_EQUALS(c.getType(), mongo::MinKey);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(TypeSupport, EncodingEquivalenceMaxKey) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ builder.appendMaxKey(name);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::MaxKey);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendMaxKey(name));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::MaxKey);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_TRUE(mmb::ConstElement(a).isValueMaxKey());
+
+ // Construct via call passing BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::MaxKey);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call.
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueMaxKey();
+ ASSERT_EQUALS(c.getType(), mongo::MaxKey);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+
+ TEST(Document, ManipulateComplexObjInLeafHeap) {
+ // Test that an object with complex substructure that lives in the leaf builder can be
+ // manipulated in the same way as an object with complex substructure that lives
+ // freely.
+ mmb::Document doc;
+ static const char inJson[] = "{ a: 1, b: 2, d : ['w', 'x', 'y', 'z'] }";
+ mmb::Element embedded = doc.makeElementObject("embedded", mongo::fromjson(inJson));
+ ASSERT_OK(doc.root().pushBack(embedded));
+ mmb::Element free = doc.makeElementObject("free");
+ ASSERT_OK(doc.root().pushBack(free));
+
+ mmb::Element e_a = embedded.leftChild();
+ ASSERT_TRUE(e_a.ok());
+ ASSERT_EQUALS("a", e_a.getFieldName());
+ mmb::Element e_b = e_a.rightSibling();
+ ASSERT_TRUE(e_b.ok());
+ ASSERT_EQUALS("b", e_b.getFieldName());
+
+ mmb::Element new_c = doc.makeElementDouble("c", 2.0);
+ ASSERT_TRUE(new_c.ok());
+ ASSERT_OK(e_b.addSiblingRight(new_c));
+
+ mmb::Element e_d = new_c.rightSibling();
+ ASSERT_TRUE(e_d.ok());
+ ASSERT_EQUALS("d", e_d.getFieldName());
+
+ mmb::Element e_d_0 = e_d.leftChild();
+ ASSERT_TRUE(e_d_0.ok());
+
+ mmb::Element e_d_1 = e_d_0.rightSibling();
+ ASSERT_TRUE(e_d_1.ok());
+
+ mmb::Element e_d_2 = e_d_1.rightSibling();
+ ASSERT_TRUE(e_d_2.ok());
+
+ ASSERT_OK(e_d_1.remove());
+
+ static const char outJson[] =
+ "{ embedded: { a: 1, b: 2, c: 2.0, d : ['w', 'y', 'z'] }, free: {} }";
+ ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject());
+ }
+
+ TEST(DocumentInPlace, EphemeralDocumentsDoNotUseInPlaceMode) {
+ mmb::Document doc;
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeIsHonored1) {
+ mongo::BSONObj obj;
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ doc.disableInPlaceUpdates();
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeIsHonored2) {
+ mongo::BSONObj obj;
+ mmb::Document doc(obj, mmb::Document::kInPlaceDisabled);
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ doc.disableInPlaceUpdates();
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeWorksWithNoMutations) {
+ mongo::BSONObj obj;
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ const char* source = NULL;
+ mmb::DamageVector damages;
+ ASSERT_TRUE(damages.empty());
+ doc.getInPlaceUpdates(&damages, &source);
+ ASSERT_TRUE(damages.empty());
+ ASSERT_NOT_EQUALS(static_cast<const char*>(NULL), source);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeIsDisabledByAddSiblingLeft) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ mmb::Element newElt = doc.makeElementInt("bar", 42);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_OK(doc.root().leftChild().addSiblingLeft(newElt));
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeIsDisabledByAddSiblingRight) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ mmb::Element newElt = doc.makeElementInt("bar", 42);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_OK(doc.root().leftChild().addSiblingRight(newElt));
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeIsDisabledByRemove) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_OK(doc.root().leftChild().remove());
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ // NOTE: Someday, we may do in-place renames, but renaming 'foo' to 'foobar' will never
+ // work because the sizes don't match. Validate that this disables in-place updates.
+ TEST(DocumentInPlace, InPlaceModeIsDisabledByRename) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_OK(doc.root().leftChild().rename("foobar"));
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeIsDisabledByPushFront) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ mmb::Element newElt = doc.makeElementInt("bar", 42);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_OK(doc.root().pushFront(newElt));
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeIsDisabledByPushBack) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ mmb::Element newElt = doc.makeElementInt("bar", 42);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_OK(doc.root().pushBack(newElt));
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeIsDisabledByPopFront) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_OK(doc.root().popFront());
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, InPlaceModeIsDisabledByPopBack) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_OK(doc.root().popBack());
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, ReserveDamageEventsIsAlwaysSafeToCall) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ doc.reserveDamageEvents(10);
+ doc.disableInPlaceUpdates();
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ doc.reserveDamageEvents(10);
+ }
+
+ TEST(DocumentInPlace, GettingInPlaceUpdatesWhenDisabledClearsArguments) {
+ mongo::BSONObj obj = mongo::fromjson("{ foo : 'foo' }");
+ mmb::Document doc(obj, mmb::Document::kInPlaceDisabled);
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+
+ mmb::DamageVector damages;
+ const mmb::DamageEvent event = { 0 };
+ damages.push_back(event);
+ const char* source = "foo";
+ ASSERT_FALSE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_TRUE(damages.empty());
+ ASSERT_EQUALS(static_cast<const char*>(NULL), source);
+
+ damages.push_back(event);
+ source = "bar";
+ size_t size = 1;
+ ASSERT_FALSE(doc.getInPlaceUpdates(&damages, &source, &size));
+ ASSERT_TRUE(damages.empty());
+ ASSERT_EQUALS(static_cast<const char*>(NULL), source);
+ ASSERT_EQUALS(0U, size);
+ }
+
+ // This isn't a great test since we aren't testing all possible combinations of compatible
+ // and incompatible sets, but since all setValueX calls decay to the internal setValue, we
+ // can be pretty sure that this will at least check the logic somewhat.
+ TEST(DocumentInPlace, InPlaceModeIsDisabledByIncompatibleSetValue) {
+ mongo::BSONObj obj(mongo::fromjson("{ foo : false }"));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ mmb::Element foo = doc.root().leftChild();
+ ASSERT_TRUE(foo.ok());
+ ASSERT_OK(foo.setValueString("foo"));
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ }
+
+ TEST(DocumentInPlace, DisablingInPlaceDoesNotDiscardUpdates) {
+ mongo::BSONObj obj(mongo::fromjson("{ foo : false, bar : true }"));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+
+ mmb::Element foo = doc.root().leftChild();
+ ASSERT_TRUE(foo.ok());
+ ASSERT_OK(foo.setValueBool(true));
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+
+ mmb::Element bar = doc.root().rightChild();
+ ASSERT_TRUE(bar.ok());
+ ASSERT_OK(bar.setValueBool(false));
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+
+ ASSERT_OK(doc.root().appendString("baz", "baz"));
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+
+ static const char outJson[] =
+ "{ foo : true, bar : false, baz : 'baz' }";
+ ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject());
+ }
+
+ TEST(DocumentInPlace, StringLifecycle) {
+ mongo::BSONObj obj(mongo::fromjson("{ x : 'foo' }"));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueString("bar");
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::String));
+ ASSERT_EQUALS("bar", x.getValueString());
+
+ // TODO: When in-place updates for leaf elements is implemented, add tests here.
+ }
+
+ TEST(DocumentInPlace, BinDataLifecycle) {
+ const char kData1[] = "\x01\x02\x03\x04\x05\x06";
+ const char kData2[] = "\x10\x20\x30\x40\x50\x60";
+
+ const mongo::BSONBinData binData1(kData1, sizeof(kData1) - 1, mongo::BinDataGeneral);
+ const mongo::BSONBinData binData2(kData2, sizeof(kData2) - 1, mongo::bdtCustom);
+
+ mongo::BSONObj obj(BSON("x" << binData1));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueBinary(binData2.length, binData2.type, binData2.data);
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::BinData));
+
+ mongo::BSONElement value = x.getValue();
+ ASSERT_EQUALS(binData2.type, value.binDataType());
+ int len = 0;
+ const char* const data = value.binDataClean(len);
+ ASSERT_EQUALS(binData2.length, len);
+ ASSERT_EQUALS(0, std::memcmp(data, kData2, len));
+
+ // TODO: When in-place updates for leaf elements is implemented, add tests here.
+ }
+
+ TEST(DocumentInPlace, OIDLifecycle) {
+ const mongo::OID oid1 = mongo::OID::gen();
+ const mongo::OID oid2 = mongo::OID::gen();
+ ASSERT_NOT_EQUALS(oid1, oid2);
+
+ mongo::BSONObj obj(BSON("x" << oid1));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueOID(oid2);
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::jstOID));
+ ASSERT_EQUALS(oid2, x.getValueOID());
+
+ // TODO: When in-place updates for leaf elements is implemented, add tests here.
+ }
+
+ TEST(DocumentInPlace, BooleanLifecycle) {
+ mongo::BSONObj obj(mongo::fromjson("{ x : false }"));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueBool(false);
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::Bool));
+ ASSERT_EQUALS(false, x.getValueBool());
+
+ // TODO: Re-enable when in-place updates to leaf elements is supported
+ // x.setValueBool(true);
+ // ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ // apply(&obj, damages, source);
+ // ASSERT_TRUE(x.hasValue());
+ // ASSERT_TRUE(x.isType(mongo::Bool));
+ // ASSERT_EQUALS(true, x.getValueBool());
+ }
+
+ TEST(DocumentInPlace, DateLifecycle) {
+ mongo::BSONObj obj(BSON("x" << mongo::Date_t(1000)));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueDate(mongo::Date_t(20000));
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::Date));
+ ASSERT_EQUALS(mongo::Date_t(20000), x.getValueDate());
+
+ // TODO: When in-place updates for leaf elements is implemented, add tests here.
+ }
+
+ TEST(DocumentInPlace, NumberIntLifecycle) {
+ const int value1 = 42;
+ const int value2 = 3;
+ mongo::BSONObj obj(BSON("x" << value1));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueInt(value2);
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::NumberInt));
+ ASSERT_EQUALS(value2, x.getValueInt());
+
+ // TODO: Re-enable when in-place updates to leaf elements is supported
+ // x.setValueInt(value1);
+ // ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ // apply(&obj, damages, source);
+ // ASSERT_TRUE(x.hasValue());
+ // ASSERT_TRUE(x.isType(mongo::NumberInt));
+ // ASSERT_EQUALS(value1, x.getValueInt());
+ }
+
+ TEST(DocumentInPlace, TimestampLifecycle) {
+ mongo::BSONObj obj(BSON("x" << mongo::OpTime(mongo::Date_t(1000))));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueTimestamp(mongo::OpTime(mongo::Date_t(20000)));
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::Timestamp));
+ ASSERT_TRUE(mongo::OpTime(mongo::Date_t(20000)) == x.getValueTimestamp());
+
+ // TODO: When in-place updates for leaf elements is implemented, add tests here.
+ }
+
+ TEST(DocumentInPlace, NumberLongLifecycle) {
+ const long long value1 = 42;
+ const long long value2 = 3;
+
+ mongo::BSONObj obj(BSON("x" << value1));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueLong(value2);
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::NumberLong));
+ ASSERT_EQUALS(value2, x.getValueLong());
+
+ // TODO: Re-enable when in-place updates to leaf elements is supported
+ // x.setValueLong(value1);
+ // ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ // apply(&obj, damages, source);
+ // ASSERT_TRUE(x.hasValue());
+ // ASSERT_TRUE(x.isType(mongo::NumberLong));
+ // ASSERT_EQUALS(value1, x.getValueLong());
+ }
+
+ TEST(DocumentInPlace, NumberDoubleLifecycle) {
+ const double value1 = 32.0;
+ const double value2 = 2.0;
+
+ mongo::BSONObj obj(BSON("x" << value1));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueDouble(value2);
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::NumberDouble));
+ ASSERT_EQUALS(value2, x.getValueDouble());
+
+ // TODO: Re-enable when in-place updates to leaf elements is supported
+ // x.setValueDouble(value1);
+ // ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ // apply(&obj, damages, source);
+ // ASSERT_TRUE(x.hasValue());
+ // ASSERT_TRUE(x.isType(mongo::NumberDouble));
+ // ASSERT_EQUALS(value1, x.getValueDouble());
+ }
+
+ // Doubles and longs are the same size, 8 bytes, so we should be able to do in-place
+ // updates between them.
+ TEST(DocumentInPlace, DoubleToLongAndBack) {
+ const double value1 = 32.0;
+ const long long value2 = 42;
+
+ mongo::BSONObj obj(BSON("x" << value1));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueLong(value2);
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ // We changed the type, so we get an extra damage event.
+ ASSERT_EQUALS(2U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::NumberLong));
+ ASSERT_EQUALS(value2, x.getValueLong());
+
+ // TODO: Re-enable when in-place updates to leaf elements is supported
+ // x.setValueDouble(value1);
+ // ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ // apply(&obj, damages, source);
+ // ASSERT_TRUE(x.hasValue());
+ // ASSERT_TRUE(x.isType(mongo::NumberDouble));
+ // ASSERT_EQUALS(value1, x.getValueDouble());
+ }
+
+ TEST(DocumentComparison, SimpleComparison) {
+ const mongo::BSONObj obj =
+ mongo::fromjson("{ a : 'a', b : ['b', 'b', 'b'], c : { one : 1.0 } }");
+
+ const mmb::Document doc1(obj.getOwned());
+ ASSERT_EQUALS(0, doc1.compareWithBSONObj(obj));
+ const mmb::Document doc2(obj.getOwned());
+ ASSERT_EQUALS(0, doc1.compareWith(doc2));
+ ASSERT_EQUALS(0, doc2.compareWith(doc1));
+ }
+
+ TEST(DocumentComparison, SimpleComparisonWithDeserializedElements) {
+ const mongo::BSONObj obj =
+ mongo::fromjson("{ a : 'a', b : ['b', 'b', 'b'], c : { one : 1.0 } }");
+
+ // Perform an operation on 'b' that doesn't change the serialized value, but
+ // deserializes the node.
+ mmb::Document doc1(obj.getOwned());
+ const mmb::Document doc1Copy(obj.getOwned());
+ mmb::Element b = doc1.root()["b"];
+ ASSERT_TRUE(b.ok());
+ mmb::Element b0 = b[0];
+ ASSERT_TRUE(b0.ok());
+ ASSERT_OK(b0.remove());
+ ASSERT_OK(b.pushBack(b0));
+ // Ensure that it compares correctly against the source object.
+ ASSERT_EQUALS(0, doc1.compareWithBSONObj(obj));
+ // Ensure that it compares correctly against a pristine document.
+ ASSERT_EQUALS(0, doc1.compareWith(doc1Copy));
+ ASSERT_EQUALS(0, doc1Copy.compareWith(doc1));
+
+ // Perform an operation on 'c' that doesn't change the serialized value, but
+ // deserializeds the node.
+ mmb::Document doc2(obj.getOwned());
+ const mmb::Document doc2Copy(obj.getOwned());
+ mmb::Element c = doc2.root()["c"];
+ ASSERT_TRUE(c.ok());
+ mmb::Element c1 = c.leftChild();
+ ASSERT_TRUE(c1.ok());
+ ASSERT_OK(c1.remove());
+ ASSERT_OK(c.pushBack(c1));
+ // Ensure that it compares correctly against the source object
+ ASSERT_EQUALS(0, doc2.compareWithBSONObj(obj));
+ // Ensure that it compares correctly against a pristine document.
+ ASSERT_EQUALS(0, doc2.compareWith(doc2Copy));
+ ASSERT_EQUALS(0, doc2Copy.compareWith(doc2));
+
+ // Ensure that the two deserialized documents compare with each other correctly.
+ ASSERT_EQUALS(0, doc1.compareWith(doc2));
+ ASSERT_EQUALS(0, doc2.compareWith(doc1));
+ }
+
+ TEST(UnorderedEqualityChecker, Identical) {
+ const mongo::BSONObj b1 = mongo::fromjson(
+ "{ a : [1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : 2, z : 3 } }");
+
+ const mongo::BSONObj b2 = b1.getOwned();
+
+ ASSERT_EQUALS(mmb::unordered(b1), mmb::unordered(b2));
+ }
+
+ TEST(UnorderedEqualityChecker, DifferentValuesAreNotEqual) {
+ const mongo::BSONObj b1 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : 2, z : 3 } }");
+ const mongo::BSONObj b2 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : 2, z : 4 } }");
+
+ ASSERT_NOT_EQUALS(mmb::unordered(b1), mmb::unordered(b2));
+ }
+
+ TEST(UnorderedEqualityChecker, DifferentTypesAreNotEqual) {
+ const mongo::BSONObj b1 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : 2, z : 3 } }");
+ const mongo::BSONObj b2 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : '2', z : 3 } }");
+
+ ASSERT_NOT_EQUALS(mmb::unordered(b1), mmb::unordered(b2));
+ }
+
+ TEST(UnorderedEqualityChecker, DifferentFieldNamesAreNotEqual) {
+ const mongo::BSONObj b1 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : 2, z : 3 } }");
+ const mongo::BSONObj b2 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, Y : 2, z : 3 } }");
+
+ ASSERT_NOT_EQUALS(mmb::unordered(b1), mmb::unordered(b2));
+ }
+
+ TEST(UnorderedEqualityChecker, MissingFieldsInObjectAreNotEqual) {
+ const mongo::BSONObj b1 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : 2, z : 3 } }");
+ const mongo::BSONObj b2 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, z : 3 } }");
+
+ ASSERT_NOT_EQUALS(mmb::unordered(b1), mmb::unordered(b2));
+ }
+
+ TEST(UnorderedEqualityChecker, ObjectOrderingIsNotConsidered) {
+ const mongo::BSONObj b1 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : 2, z : 3 } }");
+ const mongo::BSONObj b2 = mongo::fromjson(
+ "{ b : { y : 2, z : 3 , x : 1 }, a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ] }");
+
+ ASSERT_EQUALS(mmb::unordered(b1), mmb::unordered(b2));
+ }
+
+ TEST(UnorderedEqualityChecker, ArrayOrderingIsConsidered) {
+ const mongo::BSONObj b1 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : 2, z : 3 } }");
+
+ const mongo::BSONObj b2 = mongo::fromjson(
+ "{ a : [ 1, { 'a' : 'b', 'x' : 'y' }, 2 ], b : { x : 1, y : 2, z : 3 } }");
+
+ ASSERT_NOT_EQUALS(mmb::unordered(b1), mmb::unordered(b2));
+ }
+
+ TEST(UnorderedEqualityChecker, MissingItemsInArrayAreNotEqual) {
+ const mongo::BSONObj b1 = mongo::fromjson(
+ "{ a : [ 1, 2, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, y : 2, z : 3 } }");
+ const mongo::BSONObj b2 = mongo::fromjson(
+ "{ a : [ 1, { 'a' : 'b', 'x' : 'y' } ], b : { x : 1, z : 3 } }");
+
+ ASSERT_NOT_EQUALS(mmb::unordered(b1), mmb::unordered(b2));
+ }
+
+} // namespace