/* Copyright 2012 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "mongo/pch.h" // for malloc/realloc pulled from bson #include "mongo/bson/bsontypes.h" #include "mongo/util/safe_num.h" #include "mongo/unittest/unittest.h" namespace { using mongo::SafeNum; TEST(Basics, Initialization) { const SafeNum numInt(0); ASSERT_EQUALS(numInt.type(), mongo::NumberInt); const SafeNum numLong(0LL); ASSERT_EQUALS(numLong.type(), mongo::NumberLong); const SafeNum numDouble(0.0); ASSERT_EQUALS(numDouble.type(), mongo::NumberDouble); } TEST(Comparison, EOO) { const SafeNum safeNumA; const SafeNum safeNumB; ASSERT_FALSE(safeNumA.isValid()); ASSERT_FALSE(safeNumB.isValid()); ASSERT_EQUALS(safeNumA.type(), mongo::EOO); ASSERT_EQUALS(safeNumB.type(), mongo::EOO); ASSERT_TRUE(safeNumA.isEquivalent(safeNumB)); ASSERT_FALSE(safeNumA.isIdentical(safeNumB)); const SafeNum one(1); ASSERT_NOT_EQUALS(one, safeNumA); } TEST(Comparison, StrictTypeComparison) { const SafeNum one(1); const SafeNum oneLong(1LL); const SafeNum oneDouble(1.0); ASSERT_FALSE(one.isIdentical(oneLong)); ASSERT_FALSE(oneLong.isIdentical(oneDouble)); ASSERT_FALSE(oneDouble.isIdentical(one)); } TEST(Comparison, EquivalenceComparisonNormal) { const SafeNum one(1); const SafeNum oneLong(1LL); const SafeNum oneDouble(1.0); ASSERT_EQUALS(one, oneLong); ASSERT_EQUALS(oneLong, oneDouble); ASSERT_EQUALS(oneDouble, one); } TEST(Comparison, MaxIntInDouble) { const SafeNum okToConvert(SafeNum::maxIntInDouble-1); ASSERT_EQUALS(okToConvert, SafeNum(SafeNum::maxIntInDouble-1.0)); const SafeNum unsafeToConvert(SafeNum::maxIntInDouble + 100); ASSERT_NOT_EQUALS(unsafeToConvert, SafeNum(SafeNum::maxIntInDouble+100.0)); } TEST(Addition, Zero) { const SafeNum zero(0); ASSERT_EQUALS(zero + 0, zero); ASSERT_EQUALS(zero + zero, zero); const SafeNum minusOne(-1); const SafeNum plusOne(1); ASSERT_EQUALS(minusOne + 1, zero); ASSERT_EQUALS(zero + -1, minusOne); ASSERT_EQUALS(plusOne + -1, zero); ASSERT_EQUALS(zero + 1, plusOne); } TEST(Addition, UpConvertion) { const SafeNum zeroInt32(0); const SafeNum zeroInt64(0LL); const SafeNum zeroDouble(0.0); ASSERT_EQUALS((zeroInt32 + zeroInt64).type(), mongo::NumberLong); ASSERT_EQUALS((zeroInt64 + zeroInt32).type(), mongo::NumberLong); ASSERT_EQUALS((zeroInt32 + zeroDouble).type(), mongo::NumberDouble); ASSERT_EQUALS((zeroInt64 + zeroDouble).type(), mongo::NumberDouble); const SafeNum stillInt32(zeroInt32 + zeroInt32); const SafeNum stillInt64(zeroInt64 + zeroInt64); const SafeNum stillDouble(zeroDouble + zeroDouble); ASSERT_EQUALS(stillInt32.type(), mongo::NumberInt); ASSERT_EQUALS(stillInt64.type(), mongo::NumberLong); ASSERT_EQUALS(stillDouble.type(), mongo::NumberDouble); } TEST(Addition, Overflow32to64) { const SafeNum maxInt32(std::numeric_limits::max()); ASSERT_EQUALS(maxInt32.type(), mongo::NumberInt); const SafeNum int32PlusOne(maxInt32 + 1); ASSERT_EQUALS(int32PlusOne.type(), mongo::NumberLong); const SafeNum int32MinusOne(maxInt32 + -1); ASSERT_EQUALS(int32MinusOne.type(), mongo::NumberInt); const SafeNum longResult(std::numeric_limits::max() + static_cast(1)); ASSERT_EQUALS(int32PlusOne, longResult); } TEST(Addition, Overflow64toDouble) { const SafeNum maxInt64(std::numeric_limits::max()); ASSERT_EQUALS(maxInt64.type(), mongo::NumberLong); // We don't overflow int64 to double. const SafeNum int64PlusOne(maxInt64 + 1); ASSERT_EQUALS(int64PlusOne.type(), mongo::EOO); const SafeNum int64MinusOne(maxInt64 + -1); ASSERT_EQUALS(int64MinusOne.type(), mongo::NumberLong); const SafeNum doubleResult(std::numeric_limits::max()+static_cast(1)); ASSERT_EQUALS(doubleResult.type(), mongo::NumberDouble); ASSERT_NOT_EQUALS(int64PlusOne, doubleResult); } TEST(Addition, OverflowDouble) { const SafeNum maxDouble(std::numeric_limits::max()); ASSERT_EQUALS(maxDouble.type(), mongo::NumberDouble); // can't just add one here, as max double is so sparse max == max+1 const SafeNum doublePlusMax(maxDouble + maxDouble); ASSERT_EQUALS(doublePlusMax.type(), mongo::NumberDouble); const SafeNum infinity(std::numeric_limits::infinity()); ASSERT_EQUALS(doublePlusMax, infinity); } TEST(Addition, Negative32to64) { const SafeNum minInt32(std::numeric_limits::min()); ASSERT_EQUALS(minInt32.type(), mongo::NumberInt); const SafeNum int32MinusOne(minInt32 + -1); ASSERT_EQUALS(int32MinusOne.type(), mongo::NumberLong); const SafeNum int32PlusOne(minInt32 + 1); ASSERT_EQUALS(int32PlusOne.type(), mongo::NumberInt); const SafeNum longResult(std::numeric_limits::min()-static_cast(1)); ASSERT_EQUALS(int32MinusOne, longResult); } TEST(Addition, Negative64toDouble) { const SafeNum minInt64(std::numeric_limits::min()); ASSERT_EQUALS(minInt64.type(), mongo::NumberLong); // We don't overflow int64 to double. const SafeNum int64MinusOne(minInt64 + -1); ASSERT_EQUALS(int64MinusOne.type(), mongo::EOO); const SafeNum int64PlusOne(minInt64 + 1); ASSERT_EQUALS(int64PlusOne.type(), mongo::NumberLong); const SafeNum doubleResult(std::numeric_limits::min()-static_cast(1)); ASSERT_EQUALS(doubleResult.type(), mongo::NumberDouble); ASSERT_NOT_EQUALS(int64MinusOne, doubleResult); } } // unnamed namespace