diff options
Diffstat (limited to 'src/mongo/dbtests/accumulatortests.cpp')
-rw-r--r-- | src/mongo/dbtests/accumulatortests.cpp | 1937 |
1 files changed, 1051 insertions, 886 deletions
diff --git a/src/mongo/dbtests/accumulatortests.cpp b/src/mongo/dbtests/accumulatortests.cpp index f57bbfcd787..031c7e8e180 100644 --- a/src/mongo/dbtests/accumulatortests.cpp +++ b/src/mongo/dbtests/accumulatortests.cpp @@ -37,889 +37,1054 @@ namespace AccumulatorTests { - using boost::intrusive_ptr; - using std::numeric_limits; - using std::string; - - class Base { - protected: - BSONObj fromDocument( const Document& document ) { - return document.toBson(); - } - BSONObj fromValue( const Value& 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 ) ); - } - private: - intrusive_ptr<ExpressionContext> _shard; - intrusive_ptr<ExpressionContext> _router; - }; - - namespace Avg { - - class Base : public AccumulatorTests::Base { - public: - virtual ~Base() { - } - protected: - void createAccumulator() { - _accumulator = AccumulatorAvg::create(); - ASSERT_EQUALS(string("$avg"), _accumulator->getOpName()); - } - Accumulator *accumulator() { return _accumulator.get(); } - private: - intrusive_ptr<Accumulator> _accumulator; - }; - - /** No documents evaluated. */ - class None : public Base { - public: - void run() { - createAccumulator(); - ASSERT_EQUALS( 0, accumulator()->getValue(false).getDouble() ); - } - }; - - /** One int value is converted to double. */ - class OneInt : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(3), false); - ASSERT_EQUALS( 3, accumulator()->getValue(false).getDouble() ); - } - }; - - /** One long value is converted to double. */ - class OneLong : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(-4LL), false); - ASSERT_EQUALS( -4, accumulator()->getValue(false).getDouble() ); - } - }; - - /** One double value. */ - class OneDouble : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(22.6), false); - ASSERT_EQUALS( 22.6, accumulator()->getValue(false).getDouble() ); - } - }; - - /** The average of two ints is an int, even if inexact. */ - class IntInt : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(10), false); - accumulator()->process(Value(11), false); - ASSERT_EQUALS( 10.5, accumulator()->getValue(false).getDouble() ); - } - }; - - /** The average of an int and a double is calculated as a double. */ - class IntDouble : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(10), false); - accumulator()->process(Value(11.0), false); - ASSERT_EQUALS( 10.5, accumulator()->getValue(false).getDouble() ); - } - }; - - /** Unlike $sum, two ints do not overflow in the 'total' portion of the average. */ - class IntIntNoOverflow : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(numeric_limits<int>::max()), false); - accumulator()->process(Value(numeric_limits<int>::max()), false); - ASSERT_EQUALS(numeric_limits<int>::max(), - accumulator()->getValue(false).getDouble()); - } - }; - - /** Two longs do overflow in the 'total' portion of the average. */ - class LongLongOverflow : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(numeric_limits<long long>::max()), false); - accumulator()->process(Value(numeric_limits<long long>::max()), false); - ASSERT_EQUALS( ( (double)numeric_limits<long long>::max() + - numeric_limits<long long>::max() ) / 2.0, - accumulator()->getValue(false).getDouble() ); - } - }; - - namespace Shard { - class SingleOperandBase : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(operand(), false); - assertBinaryEqual( expectedResult(), - fromDocument(accumulator()->getValue(true).getDocument())); - } - protected: - virtual Value operand() = 0; - virtual BSONObj expectedResult() = 0; - }; - - /** Shard result for one integer. */ - class Int : public SingleOperandBase { - Value operand() { return Value(3); } - BSONObj expectedResult() { return BSON( "subTotal" << 3.0 << "count" << 1LL ); } - }; - - /** Shard result for one long. */ - class Long : public SingleOperandBase { - Value operand() { return Value(5LL); } - BSONObj expectedResult() { return BSON( "subTotal" << 5.0 << "count" << 1LL ); } - }; - - /** Shard result for one double. */ - class Double : public SingleOperandBase { - Value operand() { return Value(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 Value operand1() = 0; - virtual Value operand2() = 0; - virtual BSONObj expectedResult() = 0; - private: - void checkAvg( const Value& a, const Value& b ) { - createAccumulator(); - accumulator()->process(a, false); - accumulator()->process(b, false); - assertBinaryEqual(expectedResult(), - fromDocument(accumulator()->getValue(true).getDocument())); - } - }; - - /** Shard two ints overflow. */ - class IntIntOverflow : public TwoOperandBase { - Value operand1() { return Value(numeric_limits<int>::max()); } - Value operand2() { return Value(3); } - BSONObj expectedResult() { - return BSON( "subTotal" << numeric_limits<int>::max() + 3.0 << "count" << 2LL ); - } - }; - - /** Shard avg an int and a long. */ - class IntLong : public TwoOperandBase { - Value operand1() { return Value(5); } - Value operand2() { return Value(3LL); } - BSONObj expectedResult() { return BSON( "subTotal" << 8.0 << "count" << 2LL ); } - }; - - /** Shard avg an int and a double. */ - class IntDouble : public TwoOperandBase { - Value operand1() { return Value(5); } - Value operand2() { return Value(6.2); } - BSONObj expectedResult() { return BSON( "subTotal" << 11.2 << "count" << 2LL ); } - }; - - /** Shard avg a long and a double. */ - class LongDouble : public TwoOperandBase { - Value operand1() { return Value(5LL); } - Value operand2() { return Value(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()->process(Value(1), false); - accumulator()->process(Value(2LL), false); - accumulator()->process(Value(4.0), false); - assertBinaryEqual(BSON( "subTotal" << 7.0 << "count" << 3LL ), - fromDocument(accumulator()->getValue(true).getDocument())); - } - }; - - } // namespace Shard - - namespace Router { - /** Router result from one shard. */ - class OneShard : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(DOC("subTotal" << 3.0 << "count" << 2LL)), true); - assertBinaryEqual( BSON( "" << 3.0 / 2 ), - fromValue( accumulator()->getValue(false) ) ); - } - }; - - /** Router result from two shards. */ - class TwoShards : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(DOC("subTotal" << 6.0 << "count" << 1LL)), true); - accumulator()->process(Value(DOC("subTotal" << 5.0 << "count" << 2LL)), true); - assertBinaryEqual( BSON( "" << 11.0 / 3 ), - fromValue( accumulator()->getValue(false) ) ); - } - }; - - } // namespace Router - - } // namespace Avg - - namespace First { - - class Base : public AccumulatorTests::Base { - protected: - void createAccumulator() { - _accumulator = AccumulatorFirst::create(); - ASSERT_EQUALS(string("$first"), _accumulator->getOpName()); - } - Accumulator *accumulator() { return _accumulator.get(); } - private: - intrusive_ptr<Accumulator> _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(false).missing() ); - } - }; - - /* The accumulator evaluates one document and retains its value. */ - class One : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - ASSERT_EQUALS( 5, accumulator()->getValue(false).getInt() ); - } - }; - - /* The accumulator evaluates one document with the field missing, returns missing value. */ - class Missing : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(), false); - ASSERT_EQUALS( EOO, accumulator()->getValue(false).getType() ); - } - }; - - /* The accumulator evaluates two documents and retains the value in the first. */ - class Two : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - accumulator()->process(Value(7), false); - ASSERT_EQUALS( 5, accumulator()->getValue(false).getInt() ); - } - }; - - /* The accumulator evaluates two documents and retains the missing value in the first. */ - class FirstMissing : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(), false); - accumulator()->process(Value(7), false); - ASSERT_EQUALS( EOO, accumulator()->getValue(false).getType() ); - } - }; - - } // namespace First - - namespace Last { - - class Base : public AccumulatorTests::Base { - protected: - void createAccumulator() { - _accumulator = AccumulatorLast::create(); - ASSERT_EQUALS(string("$last"), _accumulator->getOpName()); - } - Accumulator *accumulator() { return _accumulator.get(); } - private: - intrusive_ptr<Accumulator> _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(false).missing() ); - } - }; - - /* The accumulator evaluates one document and retains its value. */ - class One : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - ASSERT_EQUALS( 5, accumulator()->getValue(false).getInt() ); - } - }; - - /* The accumulator evaluates one document with the field missing retains undefined. */ - class Missing : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(), false); - ASSERT_EQUALS( EOO , accumulator()->getValue(false).getType() ); - } - }; - - /* The accumulator evaluates two documents and retains the value in the last. */ - class Two : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - accumulator()->process(Value(7), false); - ASSERT_EQUALS( 7, accumulator()->getValue(false).getInt() ); - } - }; - - /* The accumulator evaluates two documents and retains the undefined value in the last. */ - class LastMissing : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(7), false); - accumulator()->process(Value(), false); - ASSERT_EQUALS( EOO , accumulator()->getValue(false).getType() ); - } - }; - - } // namespace Last - - namespace Min { - - class Base : public AccumulatorTests::Base { - protected: - void createAccumulator() { - _accumulator = AccumulatorMinMax::createMin(); - ASSERT_EQUALS(string("$min"), _accumulator->getOpName()); - } - Accumulator *accumulator() { return _accumulator.get(); } - private: - intrusive_ptr<Accumulator> _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(false).missing() ); - } - }; - - /* The accumulator evaluates one document and retains its value. */ - class One : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - ASSERT_EQUALS( 5, accumulator()->getValue(false).getInt() ); - } - }; - - /* The accumulator evaluates one document with the field missing retains undefined. */ - class Missing : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(), false); - ASSERT_EQUALS( EOO , accumulator()->getValue(false).getType() ); - } - }; - - /* The accumulator evaluates two documents and retains the minimum value. */ - class Two : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - accumulator()->process(Value(7), false); - ASSERT_EQUALS( 5, accumulator()->getValue(false).getInt() ); - } - }; - - /* The accumulator evaluates two documents and retains the undefined value. */ - class LastMissing : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(7), false); - accumulator()->process(Value(), false); - ASSERT_EQUALS( 7 , accumulator()->getValue(false).getInt() ); - } - }; - - } // namespace Min - - namespace Max { - - class Base : public AccumulatorTests::Base { - protected: - void createAccumulator() { - _accumulator = AccumulatorMinMax::createMax(); - ASSERT_EQUALS(string("$max"), _accumulator->getOpName()); - } - Accumulator *accumulator() { return _accumulator.get(); } - private: - intrusive_ptr<Accumulator> _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(false).missing() ); - } - }; - - /* The accumulator evaluates one document and retains its value. */ - class One : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - ASSERT_EQUALS( 5, accumulator()->getValue(false).getInt() ); - } - }; - - /* The accumulator evaluates one document with the field missing retains undefined. */ - class Missing : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(), false); - ASSERT_EQUALS( EOO, accumulator()->getValue(false).getType() ); - } - }; - - /* The accumulator evaluates two documents and retains the maximum value. */ - class Two : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - accumulator()->process(Value(7), false); - ASSERT_EQUALS( 7, accumulator()->getValue(false).getInt() ); - } - }; - - /* The accumulator evaluates two documents and retains the defined value. */ - class LastMissing : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(7), false); - accumulator()->process(Value(), false); - ASSERT_EQUALS( 7, accumulator()->getValue(false).getInt() ); - } - }; - - } // namespace Max - - namespace Sum { - - class Base : public AccumulatorTests::Base { - protected: - void createAccumulator() { - _accumulator = AccumulatorSum::create(); - ASSERT_EQUALS(string("$sum"), _accumulator->getOpName()); - } - Accumulator *accumulator() { return _accumulator.get(); } - private: - intrusive_ptr<Accumulator> _accumulator; - }; - - /** No documents evaluated. */ - class None : public Base { - public: - void run() { - createAccumulator(); - ASSERT_EQUALS( 0, accumulator()->getValue(false).getInt() ); - } - }; - - /** An int. */ - class OneInt : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - ASSERT_EQUALS( 5, accumulator()->getValue(false).getInt() ); - } - }; - - /** A long. */ - class OneLong : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(6LL), false); - ASSERT_EQUALS( 6, accumulator()->getValue(false).getLong() ); - } - }; - - /** A long that cannot be expressed as an int. */ - class OneLageLong : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(60000000000LL), false); - ASSERT_EQUALS( 60000000000LL, accumulator()->getValue(false).getLong() ); - } - }; - - /** A double. */ - class OneDouble : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(7.0), false); - ASSERT_EQUALS( 7.0, accumulator()->getValue(false).getDouble() ); - } - }; - - /** A non integer valued double. */ - class OneFractionalDouble : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(7.5), false); - ASSERT_EQUALS( 7.5, accumulator()->getValue(false).getDouble() ); - } - }; - - /** A nan double. */ - class OneNanDouble : public Base { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(numeric_limits<double>::quiet_NaN()), false); - // NaN is unequal to itself. - ASSERT_NOT_EQUALS( accumulator()->getValue(false).getDouble(), - accumulator()->getValue(false).getDouble() ); - } - }; - - class TypeConversionBase : public Base { - public: - virtual ~TypeConversionBase() { - } - void run() { - checkPairSum( summand1(), summand2() ); - checkPairSum( summand2(), summand1() ); - } - protected: - virtual Value summand1() { verify( false ); } - virtual Value summand2() { verify( false ); } - virtual Value expectedSum() = 0; - void checkPairSum( Value first, Value second ) { - createAccumulator(); - accumulator()->process(first, false); - accumulator()->process(second, false); - checkSum(); - } - void checkSum() { - Value result = accumulator()->getValue(false); - ASSERT_EQUALS( expectedSum(), result ); - ASSERT_EQUALS( expectedSum().getType(), result.getType() ); - } - }; - - /** Two ints are summed. */ - class IntInt : public TypeConversionBase { - Value summand1() { return Value(4); } - Value summand2() { return Value(5); } - Value expectedSum() { return Value(9); } - }; - - /** Two ints overflow. */ - class IntIntOverflow : public TypeConversionBase { - Value summand1() { return Value(numeric_limits<int>::max()); } - Value summand2() { return Value(10); } - Value expectedSum() { return Value(numeric_limits<int>::max() + 10LL); } - }; - - /** Two ints negative overflow. */ - class IntIntNegativeOverflow : public TypeConversionBase { - Value summand1() { return Value(-numeric_limits<int>::max()); } - Value summand2() { return Value(-10); } - Value expectedSum() { return Value(-numeric_limits<int>::max() + -10LL); } - }; - - /** An int and a long are summed. */ - class IntLong : public TypeConversionBase { - Value summand1() { return Value(4); } - Value summand2() { return Value(5LL); } - Value expectedSum() { return Value(9LL); } - }; - - /** An int and a long do not trigger an int overflow. */ - class IntLongNoIntOverflow : public TypeConversionBase { - Value summand1() { return Value(numeric_limits<int>::max()); } - Value summand2() { return Value(1LL); } - Value expectedSum() { return Value((long long)numeric_limits<int>::max() + 1); } - }; - - /** An int and a long overflow. */ - class IntLongLongOverflow : public TypeConversionBase { - Value summand1() { return Value(1); } - Value summand2() { return Value(numeric_limits<long long>::max()); } - Value expectedSum() { return Value(numeric_limits<long long>::max() + 1); } - }; - - /** Two longs are summed. */ - class LongLong : public TypeConversionBase { - Value summand1() { return Value(4LL); } - Value summand2() { return Value(5LL); } - Value expectedSum() { return Value(9LL); } - }; - - /** Two longs overflow. */ - class LongLongOverflow : public TypeConversionBase { - Value summand1() { return Value(numeric_limits<long long>::max()); } - Value summand2() { return Value(numeric_limits<long long>::max()); } - Value expectedSum() { - return Value(numeric_limits<long long>::max() - + numeric_limits<long long>::max()); - } - }; - - /** An int and a double are summed. */ - class IntDouble : public TypeConversionBase { - Value summand1() { return Value(4); } - Value summand2() { return Value(5.5); } - Value expectedSum() { return Value(9.5); } - }; - - /** An int and a NaN double are summed. */ - class IntNanDouble : public TypeConversionBase { - Value summand1() { return Value(4); } - Value summand2() { return Value(numeric_limits<double>::quiet_NaN()); } - Value expectedSum() { - // BSON compares NaN values as equal. - return Value(numeric_limits<double>::quiet_NaN()); - } - }; - - /** An int and a NaN sum to NaN. */ - class IntDoubleNoIntOverflow : public TypeConversionBase { - Value summand1() { return Value(numeric_limits<int>::max()); } - Value summand2() { return Value(1.0); } - Value expectedSum() { - return Value((long long)numeric_limits<int>::max() + 1.0); - } - }; - - /** A long and a double are summed. */ - class LongDouble : public TypeConversionBase { - Value summand1() { return Value(4LL); } - Value summand2() { return Value(5.5); } - Value expectedSum() { return Value(9.5); } - }; - - /** A long and a double do not trigger a long overflow. */ - class LongDoubleNoLongOverflow : public TypeConversionBase { - Value summand1() { return Value(numeric_limits<long long>::max()); } - Value summand2() { return Value(1.0); } - Value expectedSum() { - return Value((long long)numeric_limits<long long>::max() + 1.0); - } - }; - - /** Two double values are summed. */ - class DoubleDouble : public TypeConversionBase { - Value summand1() { return Value(2.5); } - Value summand2() { return Value(5.5); } - Value expectedSum() { return Value(8.0); } - }; - - /** Two double values overflow. */ - class DoubleDoubleOverflow : public TypeConversionBase { - Value summand1() { return Value(numeric_limits<double>::max()); } - Value summand2() { return Value(numeric_limits<double>::max()); } - Value expectedSum() { return Value(numeric_limits<double>::infinity()); } - }; - - /** Three values, an int, a long, and a double, are summed. */ - class IntLongDouble : public TypeConversionBase { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(5), false); - accumulator()->process(Value(99), false); - accumulator()->process(Value(0.2), false); - checkSum(); - } - private: - Value expectedSum() { return Value(104.2); } - }; - - /** A negative value is summed. */ - class Negative : public TypeConversionBase { - Value summand1() { return Value(5); } - Value summand2() { return Value(-8.8); } - Value expectedSum() { return Value(5 - 8.8); } - }; - - /** A long and a negative int are is summed. */ - class LongIntNegative : public TypeConversionBase { - Value summand1() { return Value(5LL); } - Value summand2() { return Value(-6); } - Value expectedSum() { return Value(-1LL); } - }; - - /** A null value is summed as zero. */ - class IntNull : public TypeConversionBase { - Value summand1() { return Value(5); } - Value summand2() { return Value(BSONNULL); } - Value expectedSum() { return Value(5); } - }; - - /** An undefined value is summed as zero. */ - class IntUndefined : public TypeConversionBase { - Value summand1() { return Value(9); } - Value summand2() { return Value(); } - Value expectedSum() { return Value(9); } - }; - - /** Two large integers do not overflow if a double is added later. */ - class NoOverflowBeforeDouble : public TypeConversionBase { - public: - void run() { - createAccumulator(); - accumulator()->process(Value(numeric_limits<long long>::max()), false); - accumulator()->process(Value(numeric_limits<long long>::max()), false); - accumulator()->process(Value(1.0), false); - checkSum(); - } - private: - Value expectedSum() { - return Value((double)numeric_limits<long long>::max() - + (double)numeric_limits<long long>::max()); - } - }; - - } // namespace Sum - - class All : public Suite { - public: - All() : Suite( "accumulator" ) { - } - void setupTests() { - add<Avg::None>(); - add<Avg::OneInt>(); - add<Avg::OneLong>(); - add<Avg::OneDouble>(); - add<Avg::IntInt>(); - add<Avg::IntDouble>(); - add<Avg::IntIntNoOverflow>(); - add<Avg::LongLongOverflow>(); - add<Avg::Shard::Int>(); - add<Avg::Shard::Long>(); - add<Avg::Shard::Double>(); - add<Avg::Shard::IntIntOverflow>(); - add<Avg::Shard::IntLong>(); - add<Avg::Shard::IntDouble>(); - add<Avg::Shard::LongDouble>(); - add<Avg::Shard::IntLongDouble>(); - add<Avg::Router::OneShard>(); - add<Avg::Router::TwoShards>(); - - add<First::None>(); - add<First::One>(); - add<First::Missing>(); - add<First::Two>(); - add<First::FirstMissing>(); - - add<Last::None>(); - add<Last::One>(); - add<Last::Missing>(); - add<Last::Two>(); - add<Last::LastMissing>(); - - add<Min::None>(); - add<Min::One>(); - add<Min::Missing>(); - add<Min::Two>(); - add<Min::LastMissing>(); - - add<Max::None>(); - add<Max::One>(); - add<Max::Missing>(); - add<Max::Two>(); - add<Max::LastMissing>(); - - add<Sum::None>(); - add<Sum::OneInt>(); - add<Sum::OneLong>(); - add<Sum::OneLageLong>(); - add<Sum::OneDouble>(); - add<Sum::OneFractionalDouble>(); - add<Sum::OneNanDouble>(); - add<Sum::IntInt>(); - add<Sum::IntIntOverflow>(); - add<Sum::IntIntNegativeOverflow>(); - add<Sum::IntLong>(); - add<Sum::IntLongNoIntOverflow>(); - add<Sum::IntLongLongOverflow>(); - add<Sum::LongLong>(); - add<Sum::LongLongOverflow>(); - add<Sum::IntDouble>(); - add<Sum::IntNanDouble>(); - add<Sum::IntDoubleNoIntOverflow>(); - add<Sum::LongDouble>(); - add<Sum::LongDoubleNoLongOverflow>(); - add<Sum::DoubleDouble>(); - add<Sum::DoubleDoubleOverflow>(); - add<Sum::IntLongDouble>(); - add<Sum::Negative>(); - add<Sum::LongIntNegative>(); - add<Sum::IntNull>(); - add<Sum::IntUndefined>(); - add<Sum::NoOverflowBeforeDouble>(); - } - }; - - SuiteInstance<All> myall; - -} // namespace AccumulatorTests +using boost::intrusive_ptr; +using std::numeric_limits; +using std::string; + +class Base { +protected: + BSONObj fromDocument(const Document& document) { + return document.toBson(); + } + BSONObj fromValue(const Value& 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)); + } + +private: + intrusive_ptr<ExpressionContext> _shard; + intrusive_ptr<ExpressionContext> _router; +}; + +namespace Avg { + +class Base : public AccumulatorTests::Base { +public: + virtual ~Base() {} + +protected: + void createAccumulator() { + _accumulator = AccumulatorAvg::create(); + ASSERT_EQUALS(string("$avg"), _accumulator->getOpName()); + } + Accumulator* accumulator() { + return _accumulator.get(); + } + +private: + intrusive_ptr<Accumulator> _accumulator; +}; + +/** No documents evaluated. */ +class None : public Base { +public: + void run() { + createAccumulator(); + ASSERT_EQUALS(0, accumulator()->getValue(false).getDouble()); + } +}; + +/** One int value is converted to double. */ +class OneInt : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(3), false); + ASSERT_EQUALS(3, accumulator()->getValue(false).getDouble()); + } +}; + +/** One long value is converted to double. */ +class OneLong : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(-4LL), false); + ASSERT_EQUALS(-4, accumulator()->getValue(false).getDouble()); + } +}; + +/** One double value. */ +class OneDouble : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(22.6), false); + ASSERT_EQUALS(22.6, accumulator()->getValue(false).getDouble()); + } +}; + +/** The average of two ints is an int, even if inexact. */ +class IntInt : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(10), false); + accumulator()->process(Value(11), false); + ASSERT_EQUALS(10.5, accumulator()->getValue(false).getDouble()); + } +}; + +/** The average of an int and a double is calculated as a double. */ +class IntDouble : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(10), false); + accumulator()->process(Value(11.0), false); + ASSERT_EQUALS(10.5, accumulator()->getValue(false).getDouble()); + } +}; + +/** Unlike $sum, two ints do not overflow in the 'total' portion of the average. */ +class IntIntNoOverflow : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(numeric_limits<int>::max()), false); + accumulator()->process(Value(numeric_limits<int>::max()), false); + ASSERT_EQUALS(numeric_limits<int>::max(), accumulator()->getValue(false).getDouble()); + } +}; + +/** Two longs do overflow in the 'total' portion of the average. */ +class LongLongOverflow : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(numeric_limits<long long>::max()), false); + accumulator()->process(Value(numeric_limits<long long>::max()), false); + ASSERT_EQUALS( + ((double)numeric_limits<long long>::max() + numeric_limits<long long>::max()) / 2.0, + accumulator()->getValue(false).getDouble()); + } +}; + +namespace Shard { +class SingleOperandBase : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(operand(), false); + assertBinaryEqual(expectedResult(), + fromDocument(accumulator()->getValue(true).getDocument())); + } + +protected: + virtual Value operand() = 0; + virtual BSONObj expectedResult() = 0; +}; + +/** Shard result for one integer. */ +class Int : public SingleOperandBase { + Value operand() { + return Value(3); + } + BSONObj expectedResult() { + return BSON("subTotal" << 3.0 << "count" << 1LL); + } +}; + +/** Shard result for one long. */ +class Long : public SingleOperandBase { + Value operand() { + return Value(5LL); + } + BSONObj expectedResult() { + return BSON("subTotal" << 5.0 << "count" << 1LL); + } +}; + +/** Shard result for one double. */ +class Double : public SingleOperandBase { + Value operand() { + return Value(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 Value operand1() = 0; + virtual Value operand2() = 0; + virtual BSONObj expectedResult() = 0; + +private: + void checkAvg(const Value& a, const Value& b) { + createAccumulator(); + accumulator()->process(a, false); + accumulator()->process(b, false); + assertBinaryEqual(expectedResult(), + fromDocument(accumulator()->getValue(true).getDocument())); + } +}; + +/** Shard two ints overflow. */ +class IntIntOverflow : public TwoOperandBase { + Value operand1() { + return Value(numeric_limits<int>::max()); + } + Value operand2() { + return Value(3); + } + BSONObj expectedResult() { + return BSON("subTotal" << numeric_limits<int>::max() + 3.0 << "count" << 2LL); + } +}; + +/** Shard avg an int and a long. */ +class IntLong : public TwoOperandBase { + Value operand1() { + return Value(5); + } + Value operand2() { + return Value(3LL); + } + BSONObj expectedResult() { + return BSON("subTotal" << 8.0 << "count" << 2LL); + } +}; + +/** Shard avg an int and a double. */ +class IntDouble : public TwoOperandBase { + Value operand1() { + return Value(5); + } + Value operand2() { + return Value(6.2); + } + BSONObj expectedResult() { + return BSON("subTotal" << 11.2 << "count" << 2LL); + } +}; + +/** Shard avg a long and a double. */ +class LongDouble : public TwoOperandBase { + Value operand1() { + return Value(5LL); + } + Value operand2() { + return Value(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()->process(Value(1), false); + accumulator()->process(Value(2LL), false); + accumulator()->process(Value(4.0), false); + assertBinaryEqual(BSON("subTotal" << 7.0 << "count" << 3LL), + fromDocument(accumulator()->getValue(true).getDocument())); + } +}; + +} // namespace Shard + +namespace Router { +/** Router result from one shard. */ +class OneShard : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(DOC("subTotal" << 3.0 << "count" << 2LL)), true); + assertBinaryEqual(BSON("" << 3.0 / 2), fromValue(accumulator()->getValue(false))); + } +}; + +/** Router result from two shards. */ +class TwoShards : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(DOC("subTotal" << 6.0 << "count" << 1LL)), true); + accumulator()->process(Value(DOC("subTotal" << 5.0 << "count" << 2LL)), true); + assertBinaryEqual(BSON("" << 11.0 / 3), fromValue(accumulator()->getValue(false))); + } +}; + +} // namespace Router + +} // namespace Avg + +namespace First { + +class Base : public AccumulatorTests::Base { +protected: + void createAccumulator() { + _accumulator = AccumulatorFirst::create(); + ASSERT_EQUALS(string("$first"), _accumulator->getOpName()); + } + Accumulator* accumulator() { + return _accumulator.get(); + } + +private: + intrusive_ptr<Accumulator> _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(false).missing()); + } +}; + +/* The accumulator evaluates one document and retains its value. */ +class One : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + ASSERT_EQUALS(5, accumulator()->getValue(false).getInt()); + } +}; + +/* The accumulator evaluates one document with the field missing, returns missing value. */ +class Missing : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(), false); + ASSERT_EQUALS(EOO, accumulator()->getValue(false).getType()); + } +}; + +/* The accumulator evaluates two documents and retains the value in the first. */ +class Two : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + accumulator()->process(Value(7), false); + ASSERT_EQUALS(5, accumulator()->getValue(false).getInt()); + } +}; + +/* The accumulator evaluates two documents and retains the missing value in the first. */ +class FirstMissing : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(), false); + accumulator()->process(Value(7), false); + ASSERT_EQUALS(EOO, accumulator()->getValue(false).getType()); + } +}; + +} // namespace First + +namespace Last { + +class Base : public AccumulatorTests::Base { +protected: + void createAccumulator() { + _accumulator = AccumulatorLast::create(); + ASSERT_EQUALS(string("$last"), _accumulator->getOpName()); + } + Accumulator* accumulator() { + return _accumulator.get(); + } + +private: + intrusive_ptr<Accumulator> _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(false).missing()); + } +}; + +/* The accumulator evaluates one document and retains its value. */ +class One : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + ASSERT_EQUALS(5, accumulator()->getValue(false).getInt()); + } +}; + +/* The accumulator evaluates one document with the field missing retains undefined. */ +class Missing : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(), false); + ASSERT_EQUALS(EOO, accumulator()->getValue(false).getType()); + } +}; + +/* The accumulator evaluates two documents and retains the value in the last. */ +class Two : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + accumulator()->process(Value(7), false); + ASSERT_EQUALS(7, accumulator()->getValue(false).getInt()); + } +}; + +/* The accumulator evaluates two documents and retains the undefined value in the last. */ +class LastMissing : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(7), false); + accumulator()->process(Value(), false); + ASSERT_EQUALS(EOO, accumulator()->getValue(false).getType()); + } +}; + +} // namespace Last + +namespace Min { + +class Base : public AccumulatorTests::Base { +protected: + void createAccumulator() { + _accumulator = AccumulatorMinMax::createMin(); + ASSERT_EQUALS(string("$min"), _accumulator->getOpName()); + } + Accumulator* accumulator() { + return _accumulator.get(); + } + +private: + intrusive_ptr<Accumulator> _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(false).missing()); + } +}; + +/* The accumulator evaluates one document and retains its value. */ +class One : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + ASSERT_EQUALS(5, accumulator()->getValue(false).getInt()); + } +}; + +/* The accumulator evaluates one document with the field missing retains undefined. */ +class Missing : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(), false); + ASSERT_EQUALS(EOO, accumulator()->getValue(false).getType()); + } +}; + +/* The accumulator evaluates two documents and retains the minimum value. */ +class Two : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + accumulator()->process(Value(7), false); + ASSERT_EQUALS(5, accumulator()->getValue(false).getInt()); + } +}; + +/* The accumulator evaluates two documents and retains the undefined value. */ +class LastMissing : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(7), false); + accumulator()->process(Value(), false); + ASSERT_EQUALS(7, accumulator()->getValue(false).getInt()); + } +}; + +} // namespace Min + +namespace Max { + +class Base : public AccumulatorTests::Base { +protected: + void createAccumulator() { + _accumulator = AccumulatorMinMax::createMax(); + ASSERT_EQUALS(string("$max"), _accumulator->getOpName()); + } + Accumulator* accumulator() { + return _accumulator.get(); + } + +private: + intrusive_ptr<Accumulator> _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(false).missing()); + } +}; + +/* The accumulator evaluates one document and retains its value. */ +class One : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + ASSERT_EQUALS(5, accumulator()->getValue(false).getInt()); + } +}; + +/* The accumulator evaluates one document with the field missing retains undefined. */ +class Missing : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(), false); + ASSERT_EQUALS(EOO, accumulator()->getValue(false).getType()); + } +}; + +/* The accumulator evaluates two documents and retains the maximum value. */ +class Two : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + accumulator()->process(Value(7), false); + ASSERT_EQUALS(7, accumulator()->getValue(false).getInt()); + } +}; + +/* The accumulator evaluates two documents and retains the defined value. */ +class LastMissing : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(7), false); + accumulator()->process(Value(), false); + ASSERT_EQUALS(7, accumulator()->getValue(false).getInt()); + } +}; + +} // namespace Max + +namespace Sum { + +class Base : public AccumulatorTests::Base { +protected: + void createAccumulator() { + _accumulator = AccumulatorSum::create(); + ASSERT_EQUALS(string("$sum"), _accumulator->getOpName()); + } + Accumulator* accumulator() { + return _accumulator.get(); + } + +private: + intrusive_ptr<Accumulator> _accumulator; +}; + +/** No documents evaluated. */ +class None : public Base { +public: + void run() { + createAccumulator(); + ASSERT_EQUALS(0, accumulator()->getValue(false).getInt()); + } +}; + +/** An int. */ +class OneInt : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + ASSERT_EQUALS(5, accumulator()->getValue(false).getInt()); + } +}; + +/** A long. */ +class OneLong : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(6LL), false); + ASSERT_EQUALS(6, accumulator()->getValue(false).getLong()); + } +}; + +/** A long that cannot be expressed as an int. */ +class OneLageLong : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(60000000000LL), false); + ASSERT_EQUALS(60000000000LL, accumulator()->getValue(false).getLong()); + } +}; + +/** A double. */ +class OneDouble : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(7.0), false); + ASSERT_EQUALS(7.0, accumulator()->getValue(false).getDouble()); + } +}; + +/** A non integer valued double. */ +class OneFractionalDouble : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(7.5), false); + ASSERT_EQUALS(7.5, accumulator()->getValue(false).getDouble()); + } +}; + +/** A nan double. */ +class OneNanDouble : public Base { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(numeric_limits<double>::quiet_NaN()), false); + // NaN is unequal to itself. + ASSERT_NOT_EQUALS(accumulator()->getValue(false).getDouble(), + accumulator()->getValue(false).getDouble()); + } +}; + +class TypeConversionBase : public Base { +public: + virtual ~TypeConversionBase() {} + void run() { + checkPairSum(summand1(), summand2()); + checkPairSum(summand2(), summand1()); + } + +protected: + virtual Value summand1() { + verify(false); + } + virtual Value summand2() { + verify(false); + } + virtual Value expectedSum() = 0; + void checkPairSum(Value first, Value second) { + createAccumulator(); + accumulator()->process(first, false); + accumulator()->process(second, false); + checkSum(); + } + void checkSum() { + Value result = accumulator()->getValue(false); + ASSERT_EQUALS(expectedSum(), result); + ASSERT_EQUALS(expectedSum().getType(), result.getType()); + } +}; + +/** Two ints are summed. */ +class IntInt : public TypeConversionBase { + Value summand1() { + return Value(4); + } + Value summand2() { + return Value(5); + } + Value expectedSum() { + return Value(9); + } +}; + +/** Two ints overflow. */ +class IntIntOverflow : public TypeConversionBase { + Value summand1() { + return Value(numeric_limits<int>::max()); + } + Value summand2() { + return Value(10); + } + Value expectedSum() { + return Value(numeric_limits<int>::max() + 10LL); + } +}; + +/** Two ints negative overflow. */ +class IntIntNegativeOverflow : public TypeConversionBase { + Value summand1() { + return Value(-numeric_limits<int>::max()); + } + Value summand2() { + return Value(-10); + } + Value expectedSum() { + return Value(-numeric_limits<int>::max() + -10LL); + } +}; + +/** An int and a long are summed. */ +class IntLong : public TypeConversionBase { + Value summand1() { + return Value(4); + } + Value summand2() { + return Value(5LL); + } + Value expectedSum() { + return Value(9LL); + } +}; + +/** An int and a long do not trigger an int overflow. */ +class IntLongNoIntOverflow : public TypeConversionBase { + Value summand1() { + return Value(numeric_limits<int>::max()); + } + Value summand2() { + return Value(1LL); + } + Value expectedSum() { + return Value((long long)numeric_limits<int>::max() + 1); + } +}; + +/** An int and a long overflow. */ +class IntLongLongOverflow : public TypeConversionBase { + Value summand1() { + return Value(1); + } + Value summand2() { + return Value(numeric_limits<long long>::max()); + } + Value expectedSum() { + return Value(numeric_limits<long long>::max() + 1); + } +}; + +/** Two longs are summed. */ +class LongLong : public TypeConversionBase { + Value summand1() { + return Value(4LL); + } + Value summand2() { + return Value(5LL); + } + Value expectedSum() { + return Value(9LL); + } +}; + +/** Two longs overflow. */ +class LongLongOverflow : public TypeConversionBase { + Value summand1() { + return Value(numeric_limits<long long>::max()); + } + Value summand2() { + return Value(numeric_limits<long long>::max()); + } + Value expectedSum() { + return Value(numeric_limits<long long>::max() + numeric_limits<long long>::max()); + } +}; + +/** An int and a double are summed. */ +class IntDouble : public TypeConversionBase { + Value summand1() { + return Value(4); + } + Value summand2() { + return Value(5.5); + } + Value expectedSum() { + return Value(9.5); + } +}; + +/** An int and a NaN double are summed. */ +class IntNanDouble : public TypeConversionBase { + Value summand1() { + return Value(4); + } + Value summand2() { + return Value(numeric_limits<double>::quiet_NaN()); + } + Value expectedSum() { + // BSON compares NaN values as equal. + return Value(numeric_limits<double>::quiet_NaN()); + } +}; + +/** An int and a NaN sum to NaN. */ +class IntDoubleNoIntOverflow : public TypeConversionBase { + Value summand1() { + return Value(numeric_limits<int>::max()); + } + Value summand2() { + return Value(1.0); + } + Value expectedSum() { + return Value((long long)numeric_limits<int>::max() + 1.0); + } +}; + +/** A long and a double are summed. */ +class LongDouble : public TypeConversionBase { + Value summand1() { + return Value(4LL); + } + Value summand2() { + return Value(5.5); + } + Value expectedSum() { + return Value(9.5); + } +}; + +/** A long and a double do not trigger a long overflow. */ +class LongDoubleNoLongOverflow : public TypeConversionBase { + Value summand1() { + return Value(numeric_limits<long long>::max()); + } + Value summand2() { + return Value(1.0); + } + Value expectedSum() { + return Value((long long)numeric_limits<long long>::max() + 1.0); + } +}; + +/** Two double values are summed. */ +class DoubleDouble : public TypeConversionBase { + Value summand1() { + return Value(2.5); + } + Value summand2() { + return Value(5.5); + } + Value expectedSum() { + return Value(8.0); + } +}; + +/** Two double values overflow. */ +class DoubleDoubleOverflow : public TypeConversionBase { + Value summand1() { + return Value(numeric_limits<double>::max()); + } + Value summand2() { + return Value(numeric_limits<double>::max()); + } + Value expectedSum() { + return Value(numeric_limits<double>::infinity()); + } +}; + +/** Three values, an int, a long, and a double, are summed. */ +class IntLongDouble : public TypeConversionBase { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(5), false); + accumulator()->process(Value(99), false); + accumulator()->process(Value(0.2), false); + checkSum(); + } + +private: + Value expectedSum() { + return Value(104.2); + } +}; + +/** A negative value is summed. */ +class Negative : public TypeConversionBase { + Value summand1() { + return Value(5); + } + Value summand2() { + return Value(-8.8); + } + Value expectedSum() { + return Value(5 - 8.8); + } +}; + +/** A long and a negative int are is summed. */ +class LongIntNegative : public TypeConversionBase { + Value summand1() { + return Value(5LL); + } + Value summand2() { + return Value(-6); + } + Value expectedSum() { + return Value(-1LL); + } +}; + +/** A null value is summed as zero. */ +class IntNull : public TypeConversionBase { + Value summand1() { + return Value(5); + } + Value summand2() { + return Value(BSONNULL); + } + Value expectedSum() { + return Value(5); + } +}; + +/** An undefined value is summed as zero. */ +class IntUndefined : public TypeConversionBase { + Value summand1() { + return Value(9); + } + Value summand2() { + return Value(); + } + Value expectedSum() { + return Value(9); + } +}; + +/** Two large integers do not overflow if a double is added later. */ +class NoOverflowBeforeDouble : public TypeConversionBase { +public: + void run() { + createAccumulator(); + accumulator()->process(Value(numeric_limits<long long>::max()), false); + accumulator()->process(Value(numeric_limits<long long>::max()), false); + accumulator()->process(Value(1.0), false); + checkSum(); + } + +private: + Value expectedSum() { + return Value((double)numeric_limits<long long>::max() + + (double)numeric_limits<long long>::max()); + } +}; + +} // namespace Sum + +class All : public Suite { +public: + All() : Suite("accumulator") {} + void setupTests() { + add<Avg::None>(); + add<Avg::OneInt>(); + add<Avg::OneLong>(); + add<Avg::OneDouble>(); + add<Avg::IntInt>(); + add<Avg::IntDouble>(); + add<Avg::IntIntNoOverflow>(); + add<Avg::LongLongOverflow>(); + add<Avg::Shard::Int>(); + add<Avg::Shard::Long>(); + add<Avg::Shard::Double>(); + add<Avg::Shard::IntIntOverflow>(); + add<Avg::Shard::IntLong>(); + add<Avg::Shard::IntDouble>(); + add<Avg::Shard::LongDouble>(); + add<Avg::Shard::IntLongDouble>(); + add<Avg::Router::OneShard>(); + add<Avg::Router::TwoShards>(); + + add<First::None>(); + add<First::One>(); + add<First::Missing>(); + add<First::Two>(); + add<First::FirstMissing>(); + + add<Last::None>(); + add<Last::One>(); + add<Last::Missing>(); + add<Last::Two>(); + add<Last::LastMissing>(); + + add<Min::None>(); + add<Min::One>(); + add<Min::Missing>(); + add<Min::Two>(); + add<Min::LastMissing>(); + + add<Max::None>(); + add<Max::One>(); + add<Max::Missing>(); + add<Max::Two>(); + add<Max::LastMissing>(); + + add<Sum::None>(); + add<Sum::OneInt>(); + add<Sum::OneLong>(); + add<Sum::OneLageLong>(); + add<Sum::OneDouble>(); + add<Sum::OneFractionalDouble>(); + add<Sum::OneNanDouble>(); + add<Sum::IntInt>(); + add<Sum::IntIntOverflow>(); + add<Sum::IntIntNegativeOverflow>(); + add<Sum::IntLong>(); + add<Sum::IntLongNoIntOverflow>(); + add<Sum::IntLongLongOverflow>(); + add<Sum::LongLong>(); + add<Sum::LongLongOverflow>(); + add<Sum::IntDouble>(); + add<Sum::IntNanDouble>(); + add<Sum::IntDoubleNoIntOverflow>(); + add<Sum::LongDouble>(); + add<Sum::LongDoubleNoLongOverflow>(); + add<Sum::DoubleDouble>(); + add<Sum::DoubleDoubleOverflow>(); + add<Sum::IntLongDouble>(); + add<Sum::Negative>(); + add<Sum::LongIntNegative>(); + add<Sum::IntNull>(); + add<Sum::IntUndefined>(); + add<Sum::NoOverflowBeforeDouble>(); + } +}; + +SuiteInstance<All> myall; + +} // namespace AccumulatorTests |