// accumulatortests.cpp : Unit tests for Accumulator 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 . */ #include "pch.h" #include "mongo/db/pipeline/accumulator.h" #include "mongo/db/interrupt_status_mongod.h" #include "mongo/db/pipeline/document.h" #include "mongo/db/pipeline/expression_context.h" #include "dbtests.h" namespace AccumulatorTests { class Base { public: Base() : _standalone( ExpressionContext::create( &InterruptStatusMongod::status ) ), _shard( ExpressionContext::create( &InterruptStatusMongod::status ) ), _router( ExpressionContext::create( &InterruptStatusMongod::status ) ) { _standalone->setInShard( false ); _standalone->setDoingMerge( false ); _shard->setInShard( true ); _shard->setDoingMerge( false ); _router->setInShard( false ); _router->setDoingMerge( true ); } protected: intrusive_ptr fromjson( const string& json ) { return frombson( mongo::fromjson( json ) ); } intrusive_ptr frombson( const BSONObj& bson ) { BSONObj myBson = bson; return Document::createFromBsonObj( &myBson ); } BSONObj fromDocument( const intrusive_ptr& document ) { BSONObjBuilder bob; document->toBson( &bob ); return bob.obj(); } BSONObj fromValue( const intrusive_ptr& value ) { BSONObjBuilder bob; value->addToBsonObj( &bob, "" ); return bob.obj(); } /** Check binary equality, ensuring use of the same numeric types. */ void assertBinaryEqual( const BSONObj& expected, const BSONObj& actual ) const { ASSERT_EQUALS( expected, actual ); ASSERT( expected.binaryEqual( actual ) ); } void assertBsonRepresentation( const BSONObj& expected, const intrusive_ptr& accumulator ) const { BSONObjBuilder bob; accumulator->addToBsonObj( &bob, "", true ); assertBinaryEqual( expected, bob.obj().firstElement().Obj().getOwned() ); } intrusive_ptr standalone() const { return _standalone; } intrusive_ptr shard() const { return _shard; } intrusive_ptr router() const { return _router; } private: intrusive_ptr _standalone; intrusive_ptr _shard; intrusive_ptr _router; }; namespace Avg { class Base : public AccumulatorTests::Base { public: virtual ~Base() { } protected: virtual intrusive_ptr context() { return standalone(); } void createAccumulator() { _accumulator = AccumulatorAvg::create( context() ); _accumulator->addOperand( ExpressionFieldPath::create( "d" ) ); assertBsonRepresentation( BSON( "$avg" << "$d" ), _accumulator ); } Accumulator *accumulator() { return _accumulator.get(); } private: intrusive_ptr _accumulator; }; /** No documents evaluated. */ class None : public Base { public: void run() { createAccumulator(); ASSERT_EQUALS( 0, accumulator()->getValue()->getDouble() ); } }; /** One int value is converted to double. */ class OneInt : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 3 ) ) ); ASSERT_EQUALS( 3, accumulator()->getValue()->getDouble() ); } }; /** One long value is converted to double. */ class OneLong : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << -4LL ) ) ); ASSERT_EQUALS( -4, accumulator()->getValue()->getDouble() ); } }; /** One double value. */ class OneDouble : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 22.6 ) ) ); ASSERT_EQUALS( 22.6, accumulator()->getValue()->getDouble() ); } }; /** The average of two ints is an int, even if inexact. */ class IntInt : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 10 ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 11 ) ) ); ASSERT_EQUALS( 10.5, accumulator()->getValue()->getDouble() ); } }; /** The average of an int and a double is calculated as a double. */ class IntDouble : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 10 ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 11.0 ) ) ); ASSERT_EQUALS( 10.5, accumulator()->getValue()->getDouble() ); } }; /** Unlike $sum, two ints do not overflow in the 'total' portion of the average. */ class IntIntNoOverflow : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << numeric_limits::max() ) ) ); accumulator()->evaluate( frombson( BSON( "d" << numeric_limits::max() ) ) ); ASSERT_EQUALS( numeric_limits::max(), accumulator()->getValue()->getDouble() ); } }; /** Two longs do overflow in the 'total' portion of the average. */ class LongLongOverflow : public Base { public: void run() { createAccumulator(); accumulator()->evaluate ( frombson( BSON( "d" << numeric_limits::max() ) ) ); accumulator()->evaluate ( frombson( BSON( "d" << numeric_limits::max() ) ) ); ASSERT_EQUALS( ( (double)numeric_limits::max() + numeric_limits::max() ) / 2.0, accumulator()->getValue()->getDouble() ); } }; namespace Shard { class Base : public Avg::Base { virtual intrusive_ptr context() { return shard(); } }; class SingleOperandBase : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( operand() ) ); assertBinaryEqual( expectedResult(), fromDocument( accumulator()->getValue()->getDocument() ) ); } protected: virtual BSONObj operand() = 0; virtual BSONObj expectedResult() = 0; }; /** Shard result for one integer. */ class Int : public SingleOperandBase { BSONObj operand() { return BSON( "d" << 3 ); } BSONObj expectedResult() { return BSON( "subTotal" << 3.0 << "count" << 1LL ); } }; /** Shard result for one long. */ class Long : public SingleOperandBase { BSONObj operand() { return BSON( "d" << 5LL ); } BSONObj expectedResult() { return BSON( "subTotal" << 5.0 << "count" << 1LL ); } }; /** Shard result for one double. */ class Double : public SingleOperandBase { BSONObj operand() { return BSON( "d" << 116.0 ); } BSONObj expectedResult() { return BSON( "subTotal" << 116.0 << "count" << 1LL ); } }; class TwoOperandBase : public Base { public: void run() { checkAvg( operand1(), operand2() ); checkAvg( operand2(), operand1() ); } protected: virtual BSONObj operand1() = 0; virtual BSONObj operand2() = 0; virtual BSONObj expectedResult() = 0; private: void checkAvg( const BSONObj& a, const BSONObj& b ) { createAccumulator(); accumulator()->evaluate( frombson( a ) ); accumulator()->evaluate( frombson( b ) ); assertBinaryEqual( expectedResult(), fromDocument( accumulator()->getValue()->getDocument() ) ); } }; /** Shard two ints overflow. */ class IntIntOverflow : public TwoOperandBase { BSONObj operand1() { return BSON( "d" << numeric_limits::max() ); } BSONObj operand2() { return BSON( "d" << 3 ); } BSONObj expectedResult() { return BSON( "subTotal" << numeric_limits::max() + 3.0 << "count" << 2LL ); } }; /** Shard avg an int and a long. */ class IntLong : public TwoOperandBase { BSONObj operand1() { return BSON( "d" << 5 ); } BSONObj operand2() { return BSON( "d" << 3LL ); } BSONObj expectedResult() { return BSON( "subTotal" << 8.0 << "count" << 2LL ); } }; /** Shard avg an int and a double. */ class IntDouble : public TwoOperandBase { BSONObj operand1() { return BSON( "d" << 5 ); } BSONObj operand2() { return BSON( "d" << 6.2 ); } BSONObj expectedResult() { return BSON( "subTotal" << 11.2 << "count" << 2LL ); } }; /** Shard avg a long and a double. */ class LongDouble : public TwoOperandBase { BSONObj operand1() { return BSON( "d" << 5LL ); } BSONObj operand2() { return BSON( "d" << 1.0 ); } BSONObj expectedResult() { return BSON( "subTotal" << 6.0 << "count" << 2LL ); } }; /** Shard avg an int, long, and double. */ class IntLongDouble : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 1 ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 2LL ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 4.0 ) ) ); assertBinaryEqual( BSON( "subTotal" << 7.0 << "count" << 3LL ), fromDocument( accumulator()->getValue()->getDocument() ) ); } }; } // namespace Shard namespace Router { class Base : public Avg::Base { virtual intrusive_ptr context() { return router(); } }; /** Router result from one shard. */ class OneShard : public Base { public: void run() { createAccumulator(); accumulator()->evaluate ( frombson ( BSON( "d" << BSON( "subTotal" << 3.0 << "count" << 2LL ) ) ) ); assertBinaryEqual( BSON( "" << 3.0 / 2 ), fromValue( accumulator()->getValue() ) ); } }; /** Router result from two shards. */ class TwoShards : public Base { public: void run() { createAccumulator(); accumulator()->evaluate ( frombson ( BSON( "d" << BSON( "subTotal" << 6.0 << "count" << 1LL ) ) ) ); accumulator()->evaluate ( frombson ( BSON( "d" << BSON( "subTotal" << 5.0 << "count" << 2LL ) ) ) ); assertBinaryEqual( BSON( "" << 11.0 / 3 ), fromValue( accumulator()->getValue() ) ); } }; } // namespace Router } // namespace Avg namespace First { class Base : public AccumulatorTests::Base { protected: void createAccumulator() { _accumulator = AccumulatorFirst::create( standalone() ); _accumulator->addOperand( ExpressionFieldPath::create( "a" ) ); assertBsonRepresentation( BSON( "$first" << "$a" ), _accumulator ); } Accumulator *accumulator() { return _accumulator.get(); } private: intrusive_ptr _accumulator; }; /** The accumulator evaluates no documents. */ class None : public Base { public: void run() { createAccumulator(); // The accumulator returns no value in this case. ASSERT( !accumulator()->getValue() ); } }; /* The accumulator evaluates one document and retains its value. */ class One : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{a:5}" ) ); ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); } }; /* The accumulator evaluates one document with the field missing retains undefined. */ class Missing : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); } }; /* The accumulator evaluates two documents and retains the value in the first. */ class Two : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{a:5}" ) ); accumulator()->evaluate( fromjson( "{a:7}" ) ); ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); } }; /* The accumulator evaluates two documents and retains the undefined value in the first. */ class FirstMissing : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); accumulator()->evaluate( fromjson( "{a:7}" ) ); ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); } }; } // namespace First namespace Last { class Base : public AccumulatorTests::Base { protected: void createAccumulator() { _accumulator = AccumulatorLast::create( standalone() ); _accumulator->addOperand( ExpressionFieldPath::create( "b" ) ); assertBsonRepresentation( BSON( "$last" << "$b" ), _accumulator ); } Accumulator *accumulator() { return _accumulator.get(); } private: intrusive_ptr _accumulator; }; /** The accumulator evaluates no documents. */ class None : public Base { public: void run() { createAccumulator(); // The accumulator returns no value in this case. ASSERT( !accumulator()->getValue() ); } }; /* The accumulator evaluates one document and retains its value. */ class One : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{b:5}" ) ); ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); } }; /* The accumulator evaluates one document with the field missing retains undefined. */ class Missing : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); } }; /* The accumulator evaluates two documents and retains the value in the last. */ class Two : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{b:5}" ) ); accumulator()->evaluate( fromjson( "{b:7}" ) ); ASSERT_EQUALS( 7, accumulator()->getValue()->getInt() ); } }; /* The accumulator evaluates two documents and retains the undefined value in the last. */ class LastMissing : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{b:7}" ) ); accumulator()->evaluate( fromjson( "{}" ) ); ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); } }; } // namespace Last namespace Min { class Base : public AccumulatorTests::Base { protected: void createAccumulator() { _accumulator = AccumulatorMinMax::createMin( standalone() ); _accumulator->addOperand( ExpressionFieldPath::create( "c" ) ); assertBsonRepresentation( BSON( "$min" << "$c" ), _accumulator ); } Accumulator *accumulator() { return _accumulator.get(); } private: intrusive_ptr _accumulator; }; /** The accumulator evaluates no documents. */ class None : public Base { public: void run() { createAccumulator(); // The accumulator returns no value in this case. ASSERT( !accumulator()->getValue() ); } }; /* The accumulator evaluates one document and retains its value. */ class One : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{c:5}" ) ); ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); } }; /* The accumulator evaluates one document with the field missing retains undefined. */ class Missing : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); } }; /* The accumulator evaluates two documents and retains the minimum value. */ class Two : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{c:5}" ) ); accumulator()->evaluate( fromjson( "{c:7}" ) ); ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); } }; /* The accumulator evaluates two documents and retains the undefined value. */ class LastMissing : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{c:7}" ) ); accumulator()->evaluate( fromjson( "{}" ) ); ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); } }; } // namespace Min namespace Max { class Base : public AccumulatorTests::Base { protected: void createAccumulator() { _accumulator = AccumulatorMinMax::createMax( standalone() ); _accumulator->addOperand( ExpressionFieldPath::create( "d" ) ); assertBsonRepresentation( BSON( "$max" << "$d" ), _accumulator ); } Accumulator *accumulator() { return _accumulator.get(); } private: intrusive_ptr _accumulator; }; /** The accumulator evaluates no documents. */ class None : public Base { public: void run() { createAccumulator(); // The accumulator returns no value in this case. ASSERT( !accumulator()->getValue() ); } }; /* The accumulator evaluates one document and retains its value. */ class One : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{d:5}" ) ); ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); } }; /* The accumulator evaluates one document with the field missing retains undefined. */ class Missing : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); } }; /* The accumulator evaluates two documents and retains the maximum value. */ class Two : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{d:5}" ) ); accumulator()->evaluate( fromjson( "{d:7}" ) ); ASSERT_EQUALS( 7, accumulator()->getValue()->getInt() ); } }; /* The accumulator evaluates two documents and retains the defined value. */ class LastMissing : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{d:7}" ) ); accumulator()->evaluate( fromjson( "{}" ) ); ASSERT_EQUALS( 7, accumulator()->getValue()->getInt() ); } }; } // namespace Max namespace Sum { class Base : public AccumulatorTests::Base { protected: void createAccumulator() { _accumulator = AccumulatorSum::create( standalone() ); _accumulator->addOperand( ExpressionFieldPath::create( "d" ) ); assertBsonRepresentation( BSON( "$sum" << "$d" ), _accumulator ); } Accumulator *accumulator() { return _accumulator.get(); } private: intrusive_ptr _accumulator; }; /** No documents evaluated. */ class None : public Base { public: void run() { createAccumulator(); ASSERT_EQUALS( 0, accumulator()->getValue()->getInt() ); } }; /** An int. */ class OneInt : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 5 ) ) ); ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); } }; /** A long. */ class OneLong : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 6LL ) ) ); ASSERT_EQUALS( 6, accumulator()->getValue()->getLong() ); } }; /** A long that cannot be expressed as an int. */ class OneLageLong : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 60000000000LL ) ) ); ASSERT_EQUALS( 60000000000LL, accumulator()->getValue()->getLong() ); } }; /** A double. */ class OneDouble : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 7.0 ) ) ); ASSERT_EQUALS( 7.0, accumulator()->getValue()->getDouble() ); } }; /** A non integer valued double. */ class OneFractionalDouble : public Base { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 7.5 ) ) ); ASSERT_EQUALS( 7.5, accumulator()->getValue()->getDouble() ); } }; /** A nan double. */ class OneNanDouble : public Base { public: void run() { createAccumulator(); accumulator()->evaluate ( frombson( BSON( "d" << numeric_limits::quiet_NaN() ) ) ); // NaN is unequal to itself. ASSERT_NOT_EQUALS( accumulator()->getValue()->getDouble(), accumulator()->getValue()->getDouble() ); } }; class TypeConversionBase : public Base { public: virtual ~TypeConversionBase() { } void run() { checkPairSum( summand1(), summand2() ); checkPairSum( summand2(), summand1() ); } protected: virtual BSONObj summand1() { verify( false ); } virtual BSONObj summand2() { verify( false ); } virtual BSONObj expectedSum() = 0; void checkPairSum( BSONObj first, BSONObj second ) { intrusive_ptr firstDocument = Document::createFromBsonObj( &first ); intrusive_ptr secondDocument = Document::createFromBsonObj( &second ); createAccumulator(); accumulator()->evaluate( firstDocument ); accumulator()->evaluate( secondDocument ); checkSum(); } void checkSum() { BSONObjBuilder resultBuilder; accumulator()->getValue()->addToBsonObj( &resultBuilder, "" ); BSONObj result = resultBuilder.obj(); ASSERT_EQUALS( expectedSum().firstElement(), result.firstElement() ); ASSERT_EQUALS( expectedSum().firstElement().type(), result.firstElement().type() ); } }; /** Two ints are summed. */ class IntInt : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 4 ); } BSONObj summand2() { return BSON( "d" << 5 ); } BSONObj expectedSum() { return BSON( "" << 9 ); } }; /** Two ints overflow. */ class IntIntOverflow : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << numeric_limits::max() ); } BSONObj summand2() { return BSON( "d" << 10 ); } BSONObj expectedSum() { return BSON( "" << numeric_limits::max() + 10LL ); } }; /** Two ints negative overflow. */ class IntIntNegativeOverflow : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << -numeric_limits::max() ); } BSONObj summand2() { return BSON( "d" << -10 ); } BSONObj expectedSum() { return BSON( "" << -numeric_limits::max() + -10LL ); } }; /** An int and a long are summed. */ class IntLong : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 4 ); } BSONObj summand2() { return BSON( "d" << 5LL ); } BSONObj expectedSum() { return BSON( "" << 9LL ); } }; /** An int and a long do not trigger an int overflow. */ class IntLongNoIntOverflow : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << numeric_limits::max() ); } BSONObj summand2() { return BSON( "d" << 1LL ); } BSONObj expectedSum() { return BSON( "" << (long long)numeric_limits::max() + 1 ); } }; /** An int and a long overflow. */ class IntLongLongOverflow : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 1 ); } BSONObj summand2() { return BSON( "d" << numeric_limits::max() ); } BSONObj expectedSum() { return BSON( "" << numeric_limits::max() + 1 ); } }; /** Two longs are summed. */ class LongLong : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 4LL ); } BSONObj summand2() { return BSON( "d" << 5LL ); } BSONObj expectedSum() { return BSON( "" << 9LL ); } }; /** Two longs overflow. */ class LongLongOverflow : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << numeric_limits::max() ); } BSONObj summand2() { return BSON( "d" << numeric_limits::max() ); } BSONObj expectedSum() { return BSON( "" << numeric_limits::max() + numeric_limits::max() ); } }; /** An int and a double are summed. */ class IntDouble : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 4 ); } BSONObj summand2() { return BSON( "d" << 5.5 ); } BSONObj expectedSum() { return BSON( "" << 9.5 ); } }; /** An int and a NaN double are summed. */ class IntNanDouble : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 4 ); } BSONObj summand2() { return BSON( "d" << numeric_limits::quiet_NaN() ); } BSONObj expectedSum() { // BSON compares NaN values as equal. return BSON( "" << numeric_limits::quiet_NaN() ); } }; /** An int and a NaN sum to NaN. */ class IntDoubleNoIntOverflow : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << numeric_limits::max() ); } BSONObj summand2() { return BSON( "d" << 1.0 ); } BSONObj expectedSum() { return BSON( "" << (long long)numeric_limits::max() + 1.0 ); } }; /** A long and a double are summed. */ class LongDouble : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 4LL ); } BSONObj summand2() { return BSON( "d" << 5.5 ); } BSONObj expectedSum() { return BSON( "" << 9.5 ); } }; /** A long and a double do not trigger a long overflow. */ class LongDoubleNoLongOverflow : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << numeric_limits::max() ); } BSONObj summand2() { return BSON( "d" << 1.0 ); } BSONObj expectedSum() { return BSON( "" << (long long)numeric_limits::max() + 1.0 ); } }; /** Two double values are summed. */ class DoubleDouble : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 2.5 ); } BSONObj summand2() { return BSON( "d" << 5.5 ); } BSONObj expectedSum() { return BSON( "" << 8.0 ); } }; /** Two double values overflow. */ class DoubleDoubleOverflow : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << numeric_limits::max() ); } BSONObj summand2() { return BSON( "d" << numeric_limits::max() ); } BSONObj expectedSum() { return BSON( "" << numeric_limits::infinity() ); } }; /** Three values, an int, a long, and a double, are summed. */ class IntLongDouble : public TypeConversionBase { public: void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 5 ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 99 ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 0.2 ) ) ); checkSum(); } private: BSONObj expectedSum() { return BSON( "" << 104.2 ); } }; /** A negative value is summed. */ class Negative : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 5 ); } BSONObj summand2() { return BSON( "d" << -8.8 ); } BSONObj expectedSum() { return BSON( "" << 5 - 8.8 ); } }; /** A long and a negative int are is summed. */ class LongIntNegative : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 5LL ); } BSONObj summand2() { return BSON( "d" << -6 ); } BSONObj expectedSum() { return BSON( "" << -1LL ); } }; /** A null value is summed as zero. */ class IntNull : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 5 ); } BSONObj summand2() { return BSON( "d" << BSONNULL ); } BSONObj expectedSum() { return BSON( "" << 5 ); } }; /** An undefined value is summed as zero. */ class IntUndefined : public TypeConversionBase { BSONObj summand1() { return BSON( "d" << 9 ); } BSONObj summand2() { return BSONObj(); } BSONObj expectedSum() { return BSON( "" << 9 ); } }; /** Two large integers do not overflow if a double is added later. */ class NoOverflowBeforeDouble : public TypeConversionBase { public: void run() { createAccumulator(); accumulator()->evaluate ( frombson( BSON( "d" << numeric_limits::max() ) ) ); accumulator()->evaluate ( frombson( BSON( "d" << numeric_limits::max() ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 1.0 ) ) ); checkSum(); } private: BSONObj expectedSum() { return BSON( "" << (double)numeric_limits::max() + (double)numeric_limits::max() ); } }; } // namespace Sum class All : public Suite { public: All() : Suite( "accumulator" ) { } void setupTests() { add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); } } myall; } // namespace AccumulatorTests