// documenttests.cpp : Unit tests for Document, Value, and related classes.

/**
 *    Copyright (C) 2012 10gen Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the GNU Affero General Public License in all respects
 *    for all of the code used other than as permitted herein. If you modify
 *    file(s) with this exception, you may extend this exception to your
 *    version of the file(s), but you are not obligated to do so. If you do not
 *    wish to do so, delete this exception statement from your version. If you
 *    delete this exception statement from all source files in the program,
 *    then also delete it in the license file.
 */

#include "mongo/pch.h"

#include "mongo/db/pipeline/document.h"
#include "mongo/db/pipeline/field_path.h"
#include "mongo/db/pipeline/value.h"
#include "mongo/dbtests/dbtests.h"

namespace DocumentTests {

    mongo::Document::FieldPair getNthField(mongo::Document doc, size_t index) {
        mongo::FieldIterator it (doc);
        while (index--) // advance index times
            it.next();
        return it.next();
    }

    namespace Document {

        using mongo::Document;

        BSONObj toBson( const Document& document ) {
            return document.toBson();
        }

        Document fromBson( BSONObj obj ) {
            return Document(obj);
        }

        void assertRoundTrips( const Document& document1 ) {
            BSONObj obj1 = toBson( document1 );
            Document document2 = fromBson( obj1 );
            BSONObj obj2 = toBson( document2 );
            ASSERT_EQUALS( obj1, obj2 );
            ASSERT_EQUALS( document1, document2 );
        }

        /** Create a Document. */
        class Create {
        public:
            void run() {
                Document document;
                ASSERT_EQUALS( 0U, document.size() );
                assertRoundTrips( document );
            }
        };

        /** Create a Document from a BSONObj. */
        class CreateFromBsonObj {
        public:
            void run() {
                Document document = fromBson( BSONObj() );
                ASSERT_EQUALS( 0U, document.size() );
                document = fromBson( BSON( "a" << 1 << "b" << "q" ) );
                ASSERT_EQUALS( 2U, document.size() );
                ASSERT_EQUALS( "a", getNthField(document, 0).first.toString() );
                ASSERT_EQUALS( 1,   getNthField(document, 0).second.getInt() );
                ASSERT_EQUALS( "b", getNthField(document, 1).first.toString() );
                ASSERT_EQUALS( "q", getNthField(document, 1).second.getString() );
                assertRoundTrips( document );
            }            
        };

        /** Add Document fields. */
        class AddField {
        public:
            void run() {
                MutableDocument md;
                md.addField( "foo", Value( 1 ) );
                ASSERT_EQUALS( 1U, md.peek().size() );
                ASSERT_EQUALS( 1, md.peek()["foo"].getInt() );
                md.addField( "bar", Value( 99 ) );
                ASSERT_EQUALS( 2U, md.peek().size() );
                ASSERT_EQUALS( 99, md.peek()["bar"].getInt() );
                // No assertion is triggered by a duplicate field name.
                md.addField( "a", Value( 5 ) );

                Document final = md.freeze();
                ASSERT_EQUALS( 3U, final.size() );
                assertRoundTrips( final );
            }
        };

        /** Get Document values. */
        class GetValue {
        public:
            void run() {
                Document document = fromBson( BSON( "a" << 1 << "b" << 2.2 ) );
                ASSERT_EQUALS( 1, document["a"].getInt() );
                ASSERT_EQUALS( 1, document["a"].getInt() );
                ASSERT_EQUALS( 2.2, document["b"].getDouble() );
                ASSERT_EQUALS( 2.2, document["b"].getDouble() );
                // Missing field.
                ASSERT( document["c"].missing() );
                ASSERT( document["c"].missing() );
                assertRoundTrips( document );
            }
        };

        /** Get Document fields. */
        class SetField {
        public:
            void run() {
                Document original = fromBson(BSON("a" << 1 << "b" << 2.2 << "c" << 99));

                // Initial positions. Used at end of function to make sure nothing moved
                const Position apos = original.positionOf("a");
                const Position bpos = original.positionOf("c");
                const Position cpos = original.positionOf("c");

                MutableDocument md (original);

                // Set the first field.
                md.setField( "a" , Value( "foo" ) );
                ASSERT_EQUALS( 3U, md.peek().size() );
                ASSERT_EQUALS( "foo", md.peek()["a"].getString() );
                ASSERT_EQUALS( "foo", getNthField(md.peek(), 0).second.getString() );
                assertRoundTrips( md.peek() );
                // Set the second field.
                md["b"] = Value("bar");
                ASSERT_EQUALS( 3U, md.peek().size() );
                ASSERT_EQUALS( "bar", md.peek()["b"].getString() );
                ASSERT_EQUALS( "bar", getNthField(md.peek(), 1).second.getString() );
                assertRoundTrips( md.peek() );

                // Remove the second field.
                md.setField("b", Value());
                PRINT(md.peek().toString());
                ASSERT_EQUALS( 2U, md.peek().size() );
                ASSERT( md.peek()["b"].missing() );
                ASSERT_EQUALS( "a", getNthField(md.peek(), 0 ).first.toString() );
                ASSERT_EQUALS( "c", getNthField(md.peek(), 1 ).first.toString() );
                ASSERT_EQUALS( 99, md.peek()["c"].getInt() );
                assertRoundTrips( md.peek() );

                // Remove the first field.
                md["a"] = Value();
                ASSERT_EQUALS( 1U, md.peek().size() );
                ASSERT( md.peek()["a"].missing() );
                ASSERT_EQUALS( "c", getNthField(md.peek(), 0 ).first.toString() );
                ASSERT_EQUALS( 99, md.peek()["c"].getInt() );
                assertRoundTrips( md.peek() );

                // Remove the final field. Verify document is empty.
                md.remove("c");
                ASSERT( md.peek().empty() );
                ASSERT_EQUALS( 0U, md.peek().size() );
                ASSERT_EQUALS( md.peek(), Document() );
                ASSERT( !FieldIterator(md.peek()).more() );
                ASSERT( md.peek()["c"].missing() );
                assertRoundTrips( md.peek() );

                // Set a nested field using []
                md["x"]["y"]["z"] = Value("nested");
                ASSERT_EQUALS(md.peek()["x"]["y"]["z"], Value("nested"));

                // Set a nested field using setNestedField
                FieldPath xxyyzz = string("xx.yy.zz");
                md.setNestedField(xxyyzz, Value("nested"));
                ASSERT_EQUALS(md.peek().getNestedField(xxyyzz), Value("nested") );

                // Set a nested fields through an existing empty document
                md["xxx"] = Value(Document());
                md["xxx"]["yyy"] = Value(Document());
                FieldPath xxxyyyzzz = string("xxx.yyy.zzz");
                md.setNestedField(xxxyyyzzz, Value("nested"));
                ASSERT_EQUALS(md.peek().getNestedField(xxxyyyzzz), Value("nested") );

                // Make sure nothing moved
                ASSERT_EQUALS(apos, md.peek().positionOf("a"));
                ASSERT_EQUALS(bpos, md.peek().positionOf("c"));
                ASSERT_EQUALS(cpos, md.peek().positionOf("c"));
                ASSERT_EQUALS(Position(), md.peek().positionOf("d"));
            }
        };

        /** Document comparator. */
        class Compare {
        public:
            void run() {
                assertComparison( 0, BSONObj(), BSONObj() );
                assertComparison( 0, BSON( "a" << 1 ), BSON( "a" << 1 ) );
                assertComparison( -1, BSONObj(), BSON( "a" << 1 ) );
                assertComparison( -1, BSON( "a" << 1 ), BSON( "c" << 1 ) );
                assertComparison( 0, BSON( "a" << 1 << "r" << 2 ), BSON( "a" << 1 << "r" << 2 ) );
                assertComparison( -1, BSON( "a" << 1 ), BSON( "a" << 1 << "r" << 2 ) );
                assertComparison( 0, BSON( "a" << 2 ), BSON( "a" << 2 ) );
                assertComparison( -1, BSON( "a" << 1 ), BSON( "a" << 2 ) );
                assertComparison( -1, BSON( "a" << 1 << "b" << 1 ), BSON( "a" << 1 << "b" << 2 ) );
                // numbers sort before strings
                assertComparison( -1, BSON( "a" << 1 ), BSON( "a" << "foo" ) );
            }
        public:
            int cmp( const BSONObj& a, const BSONObj& b ) {
                int result = Document::compare( fromBson( a ), fromBson( b ) );
                return // sign
                    result < 0 ? -1 :
                    result > 0 ? 1 :
                    0;
            }
            void assertComparison( int expectedResult, const BSONObj& a, const BSONObj& b ) {
                ASSERT_EQUALS( expectedResult, cmp( a, b ) );
                ASSERT_EQUALS( -expectedResult, cmp( b, a ) );
                if ( expectedResult == 0 ) {
                    ASSERT_EQUALS( hash( a ), hash( b ) );
                }
            }
            size_t hash( const BSONObj& obj ) {
                size_t seed = 0x106e1e1;
                Document(obj).hash_combine(seed);
                return seed;
            }
        };

        /** Comparison based on a null field's name.  Differs from BSONObj comparison behavior. */
        class CompareNamedNull {
        public:
            void run() {
                BSONObj obj1 = BSON( "z" << BSONNULL );
                BSONObj obj2 = BSON( "a" << 1 );
                // Comparsion with type precedence.
                ASSERT( obj1.woCompare( obj2 ) < 0 );
                // Comparison with field name precedence.
                ASSERT( Document::compare( fromBson( obj1 ), fromBson( obj2 ) ) > 0 );
            }
        };

        /** Shallow copy clone of a single field Document. */
        class Clone {
        public:
            void run() {
                const Document document = fromBson( BSON( "a" << BSON( "b" << 1 ) ) );
                MutableDocument cloneOnDemand (document);

                // Check equality.
                ASSERT_EQUALS(document, cloneOnDemand.peek());
                // Check pointer equality of sub document.
                ASSERT_EQUALS( document["a"].getDocument().getPtr(),
                               cloneOnDemand.peek()["a"].getDocument().getPtr() );


                // Change field in clone and ensure the original document's field is unchanged.
                cloneOnDemand.setField( StringData("a"), Value(2) );
                ASSERT_EQUALS( Value(1), document.getNestedField(FieldPath("a.b")) );


                // setNestedField and ensure the original document is unchanged.

                cloneOnDemand.reset(document);
                vector<Position> path;
                ASSERT_EQUALS( Value(1), document.getNestedField(FieldPath("a.b"), &path) );

                cloneOnDemand.setNestedField(path, Value(2));

                ASSERT_EQUALS( Value(1), document.getNestedField(FieldPath("a.b")) );
                ASSERT_EQUALS( Value(2), cloneOnDemand.peek().getNestedField(FieldPath("a.b")) );
                ASSERT_EQUALS( DOC( "a" << DOC( "b" << 1 ) ), document );
                ASSERT_EQUALS( DOC( "a" << DOC( "b" << 2 ) ), cloneOnDemand.freeze() );
            }
        };

        /** Shallow copy clone of a multi field Document. */
        class CloneMultipleFields {
        public:
            void run() {
                Document document =
                        fromBson( fromjson( "{a:1,b:['ra',4],c:{z:1},d:'lal'}" ) );
                Document clonedDocument = document.clone();
                ASSERT_EQUALS(document, clonedDocument);
            }
        };

        /** FieldIterator for an empty Document. */
        class FieldIteratorEmpty {
        public:
            void run() {
                FieldIterator iterator ( (Document()) );
                ASSERT( !iterator.more() );
            }
        };

        /** FieldIterator for a single field Document. */
        class FieldIteratorSingle {
        public:
            void run() {
                FieldIterator iterator (fromBson( BSON( "a" << 1 ) ));
                ASSERT( iterator.more() );
                Document::FieldPair field = iterator.next();
                ASSERT_EQUALS( "a", field.first.toString() );
                ASSERT_EQUALS( 1, field.second.getInt() );
                ASSERT( !iterator.more() );
            }
        };
        
        /** FieldIterator for a multiple field Document. */
        class FieldIteratorMultiple {
        public:
            void run() {
                FieldIterator iterator (fromBson( BSON( "a" << 1 << "b" << 5.6 << "c" << "z" )));
                ASSERT( iterator.more() );
                Document::FieldPair field = iterator.next();
                ASSERT_EQUALS( "a", field.first.toString() );
                ASSERT_EQUALS( 1, field.second.getInt() );
                ASSERT( iterator.more() );

                Document::FieldPair field2 = iterator.next();
                ASSERT_EQUALS( "b", field2.first.toString() );
                ASSERT_EQUALS( 5.6, field2.second.getDouble() );
                ASSERT( iterator.more() );

                Document::FieldPair field3 = iterator.next();
                ASSERT_EQUALS( "c", field3.first.toString() );
                ASSERT_EQUALS( "z", field3.second.getString() );
                ASSERT( !iterator.more() );
            }
        };

        class AllTypesDoc {
        public:
            void run() {
                // These are listed in order of BSONType with some duplicates
                append("minkey", MINKEY);
                // EOO not valid in middle of BSONObj
                append("double", 1.0);
                append("c-string", "string\0after NUL"); // after NULL is ignored
                append("c++", StringData("string\0after NUL", StringData::LiteralTag()).toString());
                append("StringData", StringData("string\0after NUL", StringData::LiteralTag()));
                append("emptyObj", BSONObj());
                append("filledObj", BSON("a" << 1));
                append("emptyArray", BSON("" << BSONArray()).firstElement());
                append("filledArray", BSON("" << BSON_ARRAY(1 << "a")).firstElement());
                append("binData", BSONBinData("a\0b", 3, BinDataGeneral));
                append("binDataCustom", BSONBinData("a\0b", 3, bdtCustom));
                append("binDataUUID", BSONBinData("123456789\0abcdef", 16, bdtUUID));
                append("undefined", BSONUndefined);
                append("oid", OID());
                append("true", true);
                append("false", false);
                append("date", jsTime());
                append("null", BSONNULL);
                append("regex", BSONRegEx(".*"));
                append("regexFlags", BSONRegEx(".*", "i"));
                append("regexEmpty", BSONRegEx("", ""));
                append("dbref", BSONDBRef("foo", OID()));
                append("code", BSONCode("function() {}"));
                append("codeNul", BSONCode(StringData("var nul = '\0'", StringData::LiteralTag())));
                append("symbol", BSONSymbol("foo"));
                append("symbolNul", BSONSymbol(StringData("f\0o", StringData::LiteralTag())));
                append("codeWScope", BSONCodeWScope("asdf", BSONObj()));
                append("codeWScopeWScope", BSONCodeWScope("asdf", BSON("one" << 1)));
                append("int", 1);
                append("timestamp", OpTime());
                append("long", 1LL);
                append("very long", 1LL << 40);
                append("maxkey", MAXKEY);

                const BSONArray arr = arrBuilder.arr();

                // can't use append any more since arrBuilder is done
                objBuilder << "mega array" << arr;
                docBuilder["mega array"] = mongo::Value(values);

                const BSONObj obj = objBuilder.obj();
                const Document doc = docBuilder.freeze();

                const BSONObj obj2 = toBson(doc);
                const Document doc2 = fromBson(obj);

                // logical equality
                ASSERT_EQUALS(obj, obj2);
                ASSERT_EQUALS(doc, doc2);

                // binary equality
                ASSERT_EQUALS(obj.objsize(), obj2.objsize());
                ASSERT_EQUALS(memcmp(obj.objdata(), obj2.objdata(), obj.objsize()), 0);

                // ensure sorter serialization round-trips correctly
                BufBuilder bb;
                doc.serializeForSorter(bb);
                BufReader reader(bb.buf(), bb.len());
                const Document doc3 = Document::deserializeForSorter(
                                                    reader, Document::SorterDeserializeSettings());
                BSONObj obj3 = toBson(doc3);
                ASSERT_EQUALS(obj.objsize(), obj3.objsize());
                ASSERT_EQUALS(memcmp(obj.objdata(), obj3.objdata(), obj.objsize()), 0);
            }

            template <typename T>
            void append(const char* name, const T& thing) {
                objBuilder << name << thing;
                arrBuilder         << thing;
                docBuilder[name] = mongo::Value(thing);
                values.push_back(mongo::Value(thing));
            }

            vector<mongo::Value> values;
            MutableDocument docBuilder;
            BSONObjBuilder objBuilder;
            BSONArrayBuilder arrBuilder;
        };
    } // namespace Document

    namespace Value {

        using mongo::Value;

        BSONObj toBson( const Value& value ) {
            if (value.missing())
                return BSONObj(); // EOO

            BSONObjBuilder bob;
            value.addToBsonObj( &bob, "" );
            return bob.obj();
        }

        Value fromBson( const BSONObj& obj ) {
            BSONElement element = obj.firstElement();
            return Value( element );
        }

        void assertRoundTrips( const Value& value1 ) {
            BSONObj obj1 = toBson( value1 );
            Value value2 = fromBson( obj1 );
            BSONObj obj2 = toBson( value2 );
            ASSERT_EQUALS( obj1, obj2 );
            ASSERT_EQUALS(value1, value2);
            ASSERT_EQUALS(value1.getType(), value2.getType());
        }

        class BSONArrayTest {
        public:
            void run() {
                ASSERT_EQUALS(Value(BSON_ARRAY(1 << 2 << 3)), DOC_ARRAY(1 << 2 << 3));
                ASSERT_EQUALS(Value(BSONArray()), Value(vector<Value>()));
            }
        };

        /** Int type. */
        class Int {
        public:
            void run() {
                Value value = Value( 5 );
                ASSERT_EQUALS( 5, value.getInt() );
                ASSERT_EQUALS( 5, value.getLong() );
                ASSERT_EQUALS( 5, value.getDouble() );
                ASSERT_EQUALS( NumberInt, value.getType() );
                assertRoundTrips( value );
            }
        };

        /** Long type. */
        class Long {
        public:
            void run() {
                Value value = Value( 99LL );
                ASSERT_EQUALS( 99, value.getLong() );
                ASSERT_EQUALS( 99, value.getDouble() );
                ASSERT_EQUALS( NumberLong, value.getType() );
                assertRoundTrips( value );
            }
        };
        
        /** Double type. */
        class Double {
        public:
            void run() {
                Value value = Value( 5.5 );
                ASSERT_EQUALS( 5.5, value.getDouble() );
                ASSERT_EQUALS( NumberDouble, value.getType() );
                assertRoundTrips( value );
            }
        };

        /** String type. */
        class String {
        public:
            void run() {
                Value value = Value( "foo" );
                ASSERT_EQUALS( "foo", value.getString() );
                ASSERT_EQUALS( mongo::String, value.getType() );
                assertRoundTrips( value );
            }
        };

        /** String with a null character. */
        class StringWithNull {
        public:
            void run() {
                string withNull( "a\0b", 3 );
                BSONObj objWithNull = BSON( "" << withNull );
                ASSERT_EQUALS( withNull, objWithNull[ "" ].str() );
                Value value = fromBson( objWithNull );
                ASSERT_EQUALS( withNull, value.getString() );
                assertRoundTrips( value );                
            }
        };

        /** Date type. */
        class Date {
        public:
            void run() {
                Value value = Value(Date_t(999));
                ASSERT_EQUALS( 999, value.getDate() );
                ASSERT_EQUALS( mongo::Date, value.getType() );
                assertRoundTrips( value );
            }
        };
        
        /** Timestamp type. */
        class Timestamp {
        public:
            void run() {
                Value value = Value( OpTime( 777 ) );
                ASSERT( OpTime( 777 ) == value.getTimestamp() );
                ASSERT_EQUALS( mongo::Timestamp, value.getType() );
                assertRoundTrips( value );
            }
        };

        /** Document with no fields. */
        class EmptyDocument {
        public:
            void run() {
                mongo::Document document = mongo::Document();
                Value value = Value( document );
                ASSERT_EQUALS( document.getPtr(), value.getDocument().getPtr() );
                ASSERT_EQUALS( Object, value.getType() );                
                assertRoundTrips( value );
            }
        };

        /** Document type. */
        class Document {
        public:
            void run() {
                mongo::MutableDocument md;
                md.addField( "a", Value( 5 ) );
                md.addField( "apple", Value( "rrr" ) );
                md.addField( "banana", Value( -.3 ) );
                mongo::Document document = md.freeze();

                Value value = Value( document );
                // Check document pointers are equal.
                ASSERT_EQUALS( document.getPtr(), value.getDocument().getPtr() );
                // Check document contents.
                ASSERT_EQUALS( 5, document["a"].getInt() );
                ASSERT_EQUALS( "rrr", document["apple"].getString() );
                ASSERT_EQUALS( -.3, document["banana"].getDouble() );
                ASSERT_EQUALS( Object, value.getType() );                
                assertRoundTrips( value );
            }
        };
        
        /** Array with no elements. */
        class EmptyArray {
        public:
            void run() {
                vector<Value> array;
                Value value (array);
                const vector<Value>& array2 =  value.getArray();

                ASSERT( array2.empty() );
                ASSERT_EQUALS( Array, value.getType() );
                ASSERT_EQUALS( 0U, value.getArrayLength() );
                assertRoundTrips( value );
            }
        };

        /** Array type. */
        class Array {
        public:
            void run() {
                vector<Value> array;
                array.push_back( Value( 5 ) );
                array.push_back( Value( "lala" ) );
                array.push_back( Value( 3.14 ) );
                Value value = Value( array );
                const vector<Value>& array2 = value.getArray();

                ASSERT( !array2.empty() );
                ASSERT_EQUALS( array2.size(), 3U);
                ASSERT_EQUALS( 5, array2[0].getInt() );
                ASSERT_EQUALS( "lala", array2[1].getString() );
                ASSERT_EQUALS( 3.14, array2[2].getDouble() );
                ASSERT_EQUALS( mongo::Array, value.getType() );
                ASSERT_EQUALS( 3U, value.getArrayLength() );
                assertRoundTrips( value );
            }
        };

        /** Oid type. */
        class Oid {
        public:
            void run() {
                Value value =
                        fromBson( BSON( "" << OID( "abcdefabcdefabcdefabcdef" ) ) );
                ASSERT_EQUALS( OID( "abcdefabcdefabcdefabcdef" ), value.getOid() );
                ASSERT_EQUALS( jstOID, value.getType() );
                assertRoundTrips( value );
            }
        };

        /** Bool type. */
        class Bool {
        public:
            void run() {
                Value value = fromBson( BSON( "" << true ) );
                ASSERT_EQUALS( true, value.getBool() );
                ASSERT_EQUALS( mongo::Bool, value.getType() );
                assertRoundTrips( value );                
            }
        };

        /** Regex type. */
        class Regex {
        public:
            void run() {
                Value value = fromBson( fromjson( "{'':/abc/}" ) );
                ASSERT_EQUALS( string("abc"), value.getRegex() );
                ASSERT_EQUALS( RegEx, value.getType() );
                assertRoundTrips( value );
            }
        };

        /** Symbol type (currently unsupported). */
        class Symbol {
        public:
            void run() {
                Value value (BSONSymbol("FOOBAR"));
                ASSERT_EQUALS( "FOOBAR", value.getSymbol() );
                ASSERT_EQUALS( mongo::Symbol, value.getType() );
                assertRoundTrips( value );
            }
        };

        /** Undefined type. */
        class Undefined {
        public:
            void run() {
                Value value = Value(BSONUndefined);
                ASSERT_EQUALS( mongo::Undefined, value.getType() );
                assertRoundTrips( value );
            }
        };

        /** Null type. */
        class Null {
        public:
            void run() {
                Value value = Value(BSONNULL);
                ASSERT_EQUALS( jstNULL, value.getType() );
                assertRoundTrips( value );
            }
        };

        /** True value. */
        class True {
        public:
            void run() {
                Value value = Value(true);
                ASSERT_EQUALS( true, value.getBool() );
                ASSERT_EQUALS( mongo::Bool, value.getType() );
                assertRoundTrips( value );
            }            
        };

        /** False value. */
        class False {
        public:
            void run() {
                Value value = Value(false);
                ASSERT_EQUALS( false, value.getBool() );
                ASSERT_EQUALS( mongo::Bool, value.getType() );
                assertRoundTrips( value );
            }            
        };
        
        /** -1 value. */
        class MinusOne {
        public:
            void run() {
                Value value = Value(-1);
                ASSERT_EQUALS( -1, value.getInt() );
                ASSERT_EQUALS( NumberInt, value.getType() );
                assertRoundTrips( value );
            }
        };
        
        /** 0 value. */
        class Zero {
        public:
            void run() {
                Value value = Value(0);
                ASSERT_EQUALS( 0, value.getInt() );
                ASSERT_EQUALS( NumberInt, value.getType() );
                assertRoundTrips( value );
            }
        };
        
        /** 1 value. */
        class One {
        public:
            void run() {
                Value value = Value(1);
                ASSERT_EQUALS( 1, value.getInt() );
                ASSERT_EQUALS( NumberInt, value.getType() );
                assertRoundTrips( value );
            }
        };

        namespace Coerce {

            class ToBoolBase {
            public:
                virtual ~ToBoolBase() {
                }
                void run() {
                    ASSERT_EQUALS( expected(), value().coerceToBool() );
                }
            protected:
                virtual Value value() = 0;
                virtual bool expected() = 0;
            };

            class ToBoolTrue : public ToBoolBase {
                bool expected() { return true; }
            };
            
            class ToBoolFalse : public ToBoolBase {
                bool expected() { return false; }
            };

            /** Coerce 0 to bool. */
            class ZeroIntToBool : public ToBoolFalse {
                Value value() { return Value( 0 ); }
            };
            
            /** Coerce -1 to bool. */
            class NonZeroIntToBool : public ToBoolTrue {
                Value value() { return Value( -1 ); }
            };
            
            /** Coerce 0LL to bool. */
            class ZeroLongToBool : public ToBoolFalse {
                Value value() { return Value( 0LL ); }
            };
            
            /** Coerce 5LL to bool. */
            class NonZeroLongToBool : public ToBoolTrue {
                Value value() { return Value( 5LL ); }
            };
            
            /** Coerce 0.0 to bool. */
            class ZeroDoubleToBool : public ToBoolFalse {
                Value value() { return Value( 0 ); }
            };
            
            /** Coerce -1.3 to bool. */
            class NonZeroDoubleToBool : public ToBoolTrue {
                Value value() { return Value( -1.3 ); }
            };

            /** Coerce "" to bool. */
            class StringToBool : public ToBoolTrue {
                Value value() { return Value( "" ); }
            };
            
            /** Coerce {} to bool. */
            class ObjectToBool : public ToBoolTrue {
                Value value() {
                    return Value( mongo::Document() );
                }
            };
            
            /** Coerce [] to bool. */
            class ArrayToBool : public ToBoolTrue {
                Value value() {
                    return Value( vector<Value>() );
                }
            };

            /** Coerce Date(0) to bool. */
            class DateToBool : public ToBoolTrue {
                Value value() { return Value(Date_t(0)); }
            };
            
            /** Coerce js literal regex to bool. */
            class RegexToBool : public ToBoolTrue {
                Value value() { return fromBson( fromjson( "{''://}" ) ); }
            };
            
            /** Coerce true to bool. */
            class TrueToBool : public ToBoolTrue {
                Value value() { return fromBson( BSON( "" << true ) ); }
            };
            
            /** Coerce false to bool. */
            class FalseToBool : public ToBoolFalse {
                Value value() { return fromBson( BSON( "" << false ) ); }
            };
            
            /** Coerce null to bool. */
            class NullToBool : public ToBoolFalse {
                Value value() { return Value(BSONNULL); }
            };
            
            /** Coerce undefined to bool. */
            class UndefinedToBool : public ToBoolFalse {
                Value value() { return Value(BSONUndefined); }
            };

            class ToIntBase {
            public:
                virtual ~ToIntBase() {
                }
                void run() {
                    if (asserts())
                        ASSERT_THROWS( value().coerceToInt(), UserException );
                    else
                        ASSERT_EQUALS( expected(), value().coerceToInt() );
                }
            protected:
                virtual Value value() = 0;
                virtual int expected() { return 0; }
                virtual bool asserts() { return false; }
            };

            /** Coerce -5 to int. */
            class IntToInt : public ToIntBase {
                Value value() { return Value( -5 ); }
                int expected() { return -5; }
            };
            
            /** Coerce long to int. */
            class LongToInt : public ToIntBase {
                Value value() { return Value( 0xff00000007LL ); }
                int expected() { return 7; }
            };
            
            /** Coerce 9.8 to int. */
            class DoubleToInt : public ToIntBase {
                Value value() { return Value( 9.8 ); }
                int expected() { return 9; }
            };
            
            /** Coerce null to int. */
            class NullToInt : public ToIntBase {
                Value value() { return Value(BSONNULL); }
                bool asserts() { return true; }
            };
            
            /** Coerce undefined to int. */
            class UndefinedToInt : public ToIntBase {
                Value value() { return Value(BSONUndefined); }
                bool asserts() { return true; }
            };
            
            /** Coerce "" to int unsupported. */
            class StringToInt {
            public:
                void run() {
                    ASSERT_THROWS( Value( "" ).coerceToInt(), UserException );
                }
            };
            
            class ToLongBase {
            public:
                virtual ~ToLongBase() {
                }
                void run() {
                    if (asserts())
                        ASSERT_THROWS( value().coerceToLong(), UserException );
                    else
                        ASSERT_EQUALS( expected(), value().coerceToLong() );
                }
            protected:
                virtual Value value() = 0;
                virtual long long expected() { return 0; }
                virtual bool asserts() { return false; }
            };
            
            /** Coerce -5 to long. */
            class IntToLong : public ToLongBase {
                Value value() { return Value( -5 ); }
                long long expected() { return -5; }
            };
            
            /** Coerce long to long. */
            class LongToLong : public ToLongBase {
                Value value() { return Value( 0xff00000007LL ); }
                long long expected() { return 0xff00000007LL; }
            };
            
            /** Coerce 9.8 to long. */
            class DoubleToLong : public ToLongBase {
                Value value() { return Value( 9.8 ); }
                long long expected() { return 9; }
            };
            
            /** Coerce null to long. */
            class NullToLong : public ToLongBase {
                Value value() { return Value(BSONNULL); }
                bool asserts() { return true; }
            };
            
            /** Coerce undefined to long. */
            class UndefinedToLong : public ToLongBase {
                Value value() { return Value(BSONUndefined); }
                bool asserts() { return true; }
            };
            
            /** Coerce string to long unsupported. */
            class StringToLong {
            public:
                void run() {
                    ASSERT_THROWS( Value( "" ).coerceToLong(), UserException );
                }
            };
            
            class ToDoubleBase {
            public:
                virtual ~ToDoubleBase() {
                }
                void run() {
                    if (asserts())
                        ASSERT_THROWS( value().coerceToDouble(), UserException );
                    else
                        ASSERT_EQUALS( expected(), value().coerceToDouble() );
                }
            protected:
                virtual Value value() = 0;
                virtual double expected() { return 0; }
                virtual bool asserts() { return false; }
            };
            
            /** Coerce -5 to double. */
            class IntToDouble : public ToDoubleBase {
                Value value() { return Value( -5 ); }
                double expected() { return -5; }
            };
            
            /** Coerce long to double. */
            class LongToDouble : public ToDoubleBase {
                Value value() {
                    // A long that cannot be exactly represented as a double.
                    return Value( static_cast<double>( 0x8fffffffffffffffLL ) );
                }
                double expected() { return static_cast<double>( 0x8fffffffffffffffLL ); }
            };
            
            /** Coerce double to double. */
            class DoubleToDouble : public ToDoubleBase {
                Value value() { return Value( 9.8 ); }
                double expected() { return 9.8; }
            };
            
            /** Coerce null to double. */
            class NullToDouble : public ToDoubleBase {
                Value value() { return Value(BSONNULL); }
                bool asserts() { return true; }
            };
            
            /** Coerce undefined to double. */
            class UndefinedToDouble : public ToDoubleBase {
                Value value() { return Value(BSONUndefined); }
                bool asserts() { return true; }
            };
            
            /** Coerce string to double unsupported. */
            class StringToDouble {
            public:
                void run() {
                    ASSERT_THROWS( Value( "" ).coerceToDouble(), UserException );
                }
            };

            class ToDateBase {
            public:
                virtual ~ToDateBase() {
                }
                void run() {
                    ASSERT_EQUALS( expected(), value().coerceToDate() );
                }
            protected:
                virtual Value value() = 0;
                virtual long long expected() = 0;
            };

            /** Coerce date to date. */
            class DateToDate : public ToDateBase {
                Value value() { return Value(Date_t(888)); }
                long long expected() { return 888; }
            };

            /**
             * Convert timestamp to date.  This extracts the time portion of the timestamp, which
             * is different from BSON behavior of interpreting all bytes as a date.
             */
            class TimestampToDate : public ToDateBase {
                Value value() {
                    return Value( OpTime( 777, 666 ) );
                }
                long long expected() { return 777 * 1000; }
            };
            
            /** Coerce string to date unsupported. */
            class StringToDate {
            public:
                void run() {
                    ASSERT_THROWS( Value( "" ).coerceToDate(), UserException );
                }
            };
            
            class ToStringBase {
            public:
                virtual ~ToStringBase() {
                }
                void run() {
                    ASSERT_EQUALS( expected(), value().coerceToString() );
                }
            protected:
                virtual Value value() = 0;
                virtual string expected() { return ""; }
            };

            /** Coerce -0.2 to string. */
            class DoubleToString : public ToStringBase {
                Value value() { return Value( -0.2 ); }
                string expected() { return "-0.2"; }
            };
            
            /** Coerce -4 to string. */
            class IntToString : public ToStringBase {
                Value value() { return Value( -4 ); }
                string expected() { return "-4"; }
            };
            
            /** Coerce 10000LL to string. */
            class LongToString : public ToStringBase {
                Value value() { return Value( 10000LL ); }
                string expected() { return "10000"; }
            };
            
            /** Coerce string to string. */
            class StringToString : public ToStringBase {
                Value value() { return Value( "fO_o" ); }
                string expected() { return "fO_o"; }
            };
            
            /** Coerce timestamp to string. */
            class TimestampToString : public ToStringBase {
                Value value() {
                    return Value( OpTime( 1, 2 ) );
                }
                string expected() { return OpTime( 1, 2 ).toStringPretty(); }
            };
            
            /** Coerce date to string. */
            class DateToString : public ToStringBase {
                Value value() { return Value(Date_t(1234567890LL*1000)); }
                string expected() { return "2009-02-13T23:31:30"; } // from js
            };

            /** Coerce null to string. */
            class NullToString : public ToStringBase {
                Value value() { return Value(BSONNULL); }
            };

            /** Coerce undefined to string. */
            class UndefinedToString : public ToStringBase {
                Value value() { return Value(BSONUndefined); }
            };

            /** Coerce document to string unsupported. */
            class DocumentToString {
            public:
                void run() {
                    ASSERT_THROWS( Value
                                        ( mongo::Document() ).coerceToString(),
                                   UserException );
                }
            };

            /** Coerce timestamp to timestamp. */
            class TimestampToTimestamp {
            public:
                void run() {
                    Value value = Value( OpTime( 1010 ) );
                    ASSERT( OpTime( 1010 ) == value.coerceToTimestamp() );
                }
            };

            /** Coerce date to timestamp unsupported. */
            class DateToTimestamp {
            public:
                void run() {
                    ASSERT_THROWS( Value(Date_t(1010)).coerceToTimestamp(),
                                   UserException );
                }
            };

        } // namespace Coerce

        /** Get the "widest" of two numeric types. */
        class GetWidestNumeric {
        public:
            void run() {
                using mongo::Undefined;
                
                // Numeric types.
                assertWidest( NumberInt, NumberInt, NumberInt );
                assertWidest( NumberLong, NumberInt, NumberLong );
                assertWidest( NumberDouble, NumberInt, NumberDouble );
                assertWidest( NumberLong, NumberLong, NumberLong );
                assertWidest( NumberDouble, NumberLong, NumberDouble );
                assertWidest( NumberDouble, NumberDouble, NumberDouble );
                
                // Missing value and numeric types (result Undefined).
                assertWidest( Undefined, NumberInt, Undefined );
                assertWidest( Undefined, NumberInt, Undefined );
                assertWidest( Undefined, NumberLong, jstNULL );
                assertWidest( Undefined, NumberLong, Undefined );
                assertWidest( Undefined, NumberDouble, jstNULL );
                assertWidest( Undefined, NumberDouble, Undefined );

                // Missing value types (result Undefined).
                assertWidest( Undefined, jstNULL, jstNULL );
                assertWidest( Undefined, jstNULL, Undefined );
                assertWidest( Undefined, Undefined, Undefined );

                // Other types (result Undefined).
                assertWidest( Undefined, NumberInt, mongo::Bool );
                assertWidest( Undefined, mongo::String, NumberDouble );
            }
        private:
            void assertWidest( BSONType expectedWidest, BSONType a, BSONType b ) {
                ASSERT_EQUALS( expectedWidest, Value::getWidestNumeric( a, b ) );
                ASSERT_EQUALS( expectedWidest, Value::getWidestNumeric( b, a ) );
            }
        };

        /** Add a Value to a BSONObj. */
        class AddToBsonObj {
        public:
            void run() {
                BSONObjBuilder bob;
                Value( 4.4 ).addToBsonObj( &bob, "a" );
                Value( 22 ).addToBsonObj( &bob, "b" );
                Value( "astring" ).addToBsonObj( &bob, "c" );
                ASSERT_EQUALS( BSON( "a" << 4.4 << "b" << 22 << "c" << "astring" ), bob.obj() );
            }
        };
        
        /** Add a Value to a BSONArray. */
        class AddToBsonArray {
        public:
            void run() {
                BSONArrayBuilder bab;
                Value( 4.4 ).addToBsonArray( &bab );
                Value( 22 ).addToBsonArray( &bab );
                Value( "astring" ).addToBsonArray( &bab );
                ASSERT_EQUALS( BSON_ARRAY( 4.4 << 22 << "astring" ), bab.arr() );
            }
        };

        /** Value comparator. */
        class Compare {
        public:
            void run() {
                BSONObjBuilder undefinedBuilder;
                undefinedBuilder.appendUndefined( "" );
                BSONObj undefined = undefinedBuilder.obj();

                // Undefined / null.
                assertComparison( 0, undefined, undefined );
                assertComparison( -1, undefined, BSON( "" << BSONNULL ) );
                assertComparison( 0, BSON( "" << BSONNULL ), BSON( "" << BSONNULL ) );

                // Undefined / null with other types.
                assertComparison( -1, undefined, BSON( "" << 1 ) );
                assertComparison( -1, undefined, BSON( "" << "bar" ) );
                assertComparison( -1, BSON( "" << BSONNULL ), BSON( "" << -1 ) );
                assertComparison( -1, BSON( "" << BSONNULL ), BSON( "" << "bar" ) );

                // Numeric types.
                assertComparison( 0, 5, 5LL );
                assertComparison( 0, -2, -2.0 );
                assertComparison( 0, 90LL, 90.0 );
                assertComparison( -1, 5, 6LL );
                assertComparison( -1, -2, 2.1 );
                assertComparison( 1, 90LL, 89.999 );
                assertComparison( -1, 90, 90.1 );
                assertComparison( 0, numeric_limits<double>::quiet_NaN(),
                                  numeric_limits<double>::signaling_NaN() );
                assertComparison( -1, numeric_limits<double>::quiet_NaN(), 5 );

                // strings compare between numbers and objects
                assertComparison( 1, "abc", 90 );
                assertComparison( -1, "abc", BSON( "a" << "b" ) );

                // String comparison.
                assertComparison( -1, "", "a" );
                assertComparison( 0, "a", "a" );
                assertComparison( -1, "a", "b" );
                assertComparison( -1, "aa", "b" );
                assertComparison( 1, "bb", "b" );
                assertComparison( 1, "bb", "b" );
                assertComparison( 1, "b-", "b" );
                assertComparison( -1, "b-", "ba" );
                // With a null character.
                assertComparison( 1, string( "a\0", 2 ), "a" );

                // Object.
                assertComparison( 0, fromjson( "{'':{}}" ), fromjson( "{'':{}}" ) );
                assertComparison( 0, fromjson( "{'':{x:1}}" ), fromjson( "{'':{x:1}}" ) );
                assertComparison( -1, fromjson( "{'':{}}" ), fromjson( "{'':{x:1}}" ) );

                // Array.
                assertComparison( 0, fromjson( "{'':[]}" ), fromjson( "{'':[]}" ) );
                assertComparison( -1, fromjson( "{'':[0]}" ), fromjson( "{'':[1]}" ) );
                assertComparison( -1, fromjson( "{'':[0,0]}" ), fromjson( "{'':[1]}" ) );
                assertComparison( -1, fromjson( "{'':[0]}" ), fromjson( "{'':[0,0]}" ) );
                assertComparison( -1, fromjson( "{'':[0]}" ), fromjson( "{'':['']}" ) );

                // OID.
                assertComparison( 0, OID( "abcdefabcdefabcdefabcdef" ),
                                  OID( "abcdefabcdefabcdefabcdef" ) );
                assertComparison( 1, OID( "abcdefabcdefabcdefabcdef" ),
                                  OID( "010101010101010101010101" ) );

                // Bool.
                assertComparison( 0, true, true );
                assertComparison( 0, false, false );
                assertComparison( 1, true, false );

                // Date.
                assertComparison( 0, Date_t( 555 ), Date_t( 555 ) );
                assertComparison( 1, Date_t( 555 ), Date_t( 554 ) );
                // Negative date.
                assertComparison( 1, Date_t( 0 ), Date_t( -1 ) );

                // Regex.
                assertComparison( 0, fromjson( "{'':/a/}" ), fromjson( "{'':/a/}" ) );
                assertComparison( -1, fromjson( "{'':/a/}" ), fromjson( "{'':/a/i}" ) );
                assertComparison( -1, fromjson( "{'':/a/}" ), fromjson( "{'':/aa/}" ) );

                // Timestamp.
                assertComparison( 0, OpTime( 1234 ), OpTime( 1234 ) );
                assertComparison( -1, OpTime( 4 ), OpTime( 1234 ) );

                // Cross-type comparisons. Listed in order of canonical types.
                assertComparison(-1, Value(mongo::MINKEY), Value());
                assertComparison(0,  Value(), Value());
                assertComparison(0,  Value(), Value(BSONUndefined));
                assertComparison(-1, Value(BSONUndefined), Value(BSONNULL));
                assertComparison(-1, Value(BSONNULL), Value(1));
                assertComparison(0,  Value(1), Value(1LL));
                assertComparison(0,  Value(1), Value(1.0));
                assertComparison(-1, Value(1), Value("string"));
                assertComparison(0,  Value("string"), Value(BSONSymbol("string")));
                assertComparison(-1, Value("string"), Value(mongo::Document()));
                assertComparison(-1, Value(mongo::Document()), Value(vector<Value>()));
                assertComparison(-1, Value(vector<Value>()), Value(BSONBinData("", 0, MD5Type)));
                assertComparison(-1, Value(BSONBinData("", 0, MD5Type)), Value(mongo::OID()));
                assertComparison(-1, Value(mongo::OID()), Value(false));
                assertComparison(-1, Value(false), Value(OpTime()));
                assertComparison(0,  Value(OpTime()), Value(Date_t(0)));
                assertComparison(-1, Value(Date_t(0)), Value(BSONRegEx("")));
                assertComparison(-1, Value(BSONRegEx("")), Value(BSONDBRef("", mongo::OID())));
                assertComparison(-1, Value(BSONDBRef("", mongo::OID())), Value(BSONCode("")));
                assertComparison(-1, Value(BSONCode("")), Value(BSONCodeWScope("", BSONObj())));
                assertComparison(-1, Value(BSONCodeWScope("", BSONObj())), Value(mongo::MAXKEY));
            }
        private:
            template<class T,class U>
            void assertComparison( int expectedResult, const T& a, const U& b ) {
                assertComparison( expectedResult, BSON( "" << a ), BSON( "" << b ) );
            }
            void assertComparison( int expectedResult, const OpTime& a, const OpTime& b ) {
                BSONObjBuilder first;
                first.appendTimestamp( "", a.asDate() );
                BSONObjBuilder second;
                second.appendTimestamp( "", b.asDate() );
                assertComparison( expectedResult, first.obj(), second.obj() );
            }
            int sign(int cmp) {
                if (cmp == 0) return 0;
                else if (cmp < 0) return -1;
                else return 1;
            }
            int cmp( const Value& a, const Value& b ) {
                return sign(Value::compare(a, b));
            }
            void assertComparison( int expectedResult, const BSONObj& a, const BSONObj& b ) {
                assertComparison(expectedResult, fromBson(a), fromBson(b));
            }
            void assertComparison(int expectedResult, const Value& a, const Value& b) {
                mongo::unittest::log() <<
                    "testing " << a.toString() << " and " << b.toString() << endl;
                // reflexivity
                ASSERT_EQUALS(0, cmp(a, a));
                ASSERT_EQUALS(0, cmp(b, b));

                // symmetry
                ASSERT_EQUALS( expectedResult, cmp( a, b ) );
                ASSERT_EQUALS( -expectedResult, cmp( b, a ) );

                if ( expectedResult == 0 ) {
                    // equal values must hash equally.
                    ASSERT_EQUALS( hash( a ), hash( b ) );
                }
                else {
                    // unequal values must hash unequally.
                    // (not true in general but we should error if it fails in any of these cases)
                    ASSERT_NOT_EQUALS( hash( a ), hash( b ) );
                }
                
                // same as BSON
                ASSERT_EQUALS(expectedResult, sign(toBson(a).firstElement().woCompare(
                                                   toBson(b).firstElement())));
            }
            size_t hash(const Value& v) {
                size_t seed = 0xf00ba6;
                v.hash_combine( seed );
                return seed;
            }
        };

        class SubFields {
        public:
            void run() {
                const Value val = fromBson(fromjson(
                            "{'': {a: [{x:1, b:[1, {y:1, c:1234, z:1}, 1]}]}}"));
                           // ^ this outer object is removed by fromBson

                ASSERT(val.getType() == mongo::Object);

                ASSERT(val[999].missing());
                ASSERT(val["missing"].missing());
                ASSERT(val["a"].getType() == mongo::Array);

                ASSERT(val["a"][999].missing());
                ASSERT(val["a"]["missing"].missing());
                ASSERT(val["a"][0].getType() == mongo::Object);

                ASSERT(val["a"][0][999].missing());
                ASSERT(val["a"][0]["missing"].missing());
                ASSERT(val["a"][0]["b"].getType() == mongo::Array);

                ASSERT(val["a"][0]["b"][999].missing());
                ASSERT(val["a"][0]["b"]["missing"].missing());
                ASSERT(val["a"][0]["b"][1].getType() == mongo::Object);

                ASSERT(val["a"][0]["b"][1][999].missing());
                ASSERT(val["a"][0]["b"][1]["missing"].missing());
                ASSERT(val["a"][0]["b"][1]["c"].getType() == mongo::NumberInt);
                ASSERT_EQUALS(val["a"][0]["b"][1]["c"].getInt(), 1234);
            }
        };
        

        class SerializationOfMissingForSorter {
            // Can't be tested in AllTypesDoc since missing values are omitted when adding to BSON.
        public:
            void run() {
                const Value missing;
                const Value arrayOfMissing = Value(vector<Value>(10));

                BufBuilder bb;
                missing.serializeForSorter(bb);
                arrayOfMissing.serializeForSorter(bb);

                BufReader reader(bb.buf(), bb.len());
                ASSERT_EQUALS(
                        missing,
                        Value::deserializeForSorter(reader, Value::SorterDeserializeSettings()));
                ASSERT_EQUALS(
                        arrayOfMissing,
                        Value::deserializeForSorter(reader, Value::SorterDeserializeSettings()));
            }
        };
    } // namespace Value

    class All : public Suite {
    public:
        All() : Suite( "document" ) {
        }
        void setupTests() {
            add<Document::Create>();
            add<Document::CreateFromBsonObj>();
            add<Document::AddField>();
            add<Document::GetValue>();
            add<Document::SetField>();
            add<Document::Compare>();
            add<Document::CompareNamedNull>();
            add<Document::Clone>();
            add<Document::CloneMultipleFields>();
            add<Document::FieldIteratorEmpty>();
            add<Document::FieldIteratorSingle>();
            add<Document::FieldIteratorMultiple>();
            add<Document::AllTypesDoc>();

            add<Value::BSONArrayTest>();
            add<Value::Int>();
            add<Value::Long>();
            add<Value::Double>();
            add<Value::String>();
            add<Value::StringWithNull>();
            add<Value::Date>();
            add<Value::Timestamp>();
            add<Value::EmptyDocument>();
            add<Value::EmptyArray>();
            add<Value::Array>();
            add<Value::Oid>();
            add<Value::Bool>();
            add<Value::Regex>();
            add<Value::Symbol>();
            add<Value::Undefined>();
            add<Value::Null>();
            add<Value::True>();
            add<Value::False>();
            add<Value::MinusOne>();
            add<Value::Zero>();
            add<Value::One>();

            add<Value::Coerce::ZeroIntToBool>();
            add<Value::Coerce::NonZeroIntToBool>();
            add<Value::Coerce::ZeroLongToBool>();
            add<Value::Coerce::NonZeroLongToBool>();
            add<Value::Coerce::ZeroDoubleToBool>();
            add<Value::Coerce::NonZeroDoubleToBool>();
            add<Value::Coerce::StringToBool>();
            add<Value::Coerce::ObjectToBool>();
            add<Value::Coerce::ArrayToBool>();
            add<Value::Coerce::DateToBool>();
            add<Value::Coerce::RegexToBool>();
            add<Value::Coerce::TrueToBool>();
            add<Value::Coerce::FalseToBool>();
            add<Value::Coerce::NullToBool>();
            add<Value::Coerce::UndefinedToBool>();
            add<Value::Coerce::IntToInt>();
            add<Value::Coerce::LongToInt>();
            add<Value::Coerce::DoubleToInt>();
            add<Value::Coerce::NullToInt>();
            add<Value::Coerce::UndefinedToInt>();
            add<Value::Coerce::StringToInt>();
            add<Value::Coerce::IntToLong>();
            add<Value::Coerce::LongToLong>();
            add<Value::Coerce::DoubleToLong>();
            add<Value::Coerce::NullToLong>();
            add<Value::Coerce::UndefinedToLong>();
            add<Value::Coerce::StringToLong>();
            add<Value::Coerce::IntToDouble>();
            add<Value::Coerce::LongToDouble>();
            add<Value::Coerce::DoubleToDouble>();
            add<Value::Coerce::NullToDouble>();
            add<Value::Coerce::UndefinedToDouble>();
            add<Value::Coerce::StringToDouble>();
            add<Value::Coerce::DateToDate>();
            add<Value::Coerce::TimestampToDate>();
            add<Value::Coerce::StringToDate>();
            add<Value::Coerce::DoubleToString>();
            add<Value::Coerce::IntToString>();
            add<Value::Coerce::LongToString>();
            add<Value::Coerce::StringToString>();
            add<Value::Coerce::TimestampToString>();
            add<Value::Coerce::DateToString>();
            add<Value::Coerce::NullToString>();
            add<Value::Coerce::UndefinedToString>();
            add<Value::Coerce::DocumentToString>();
            add<Value::Coerce::TimestampToTimestamp>();
            add<Value::Coerce::DateToTimestamp>();

            add<Value::GetWidestNumeric>();
            add<Value::AddToBsonObj>();
            add<Value::AddToBsonArray>();
            add<Value::Compare>();
            add<Value::SubFields>();
            add<Value::SerializationOfMissingForSorter>();
        }
    } myall;
    
} // namespace DocumentTests
