/** * Copyright (C) 2013 MongoDB 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 . * * 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. */ /** * tests for BSONObjBuilder */ #include "mongo/db/jsobj.h" #include "mongo/db/json.h" #include #include "mongo/unittest/unittest.h" namespace { using std::string; using std::stringstream; using mongo::BSONElement; using mongo::BSONObj; using mongo::BSONObjBuilder; using mongo::BSONType; const long long maxEncodableInt = (1 << 30) - 1; const long long minEncodableInt = -maxEncodableInt; const long long maxInt = (std::numeric_limits::max)(); const long long minInt = (std::numeric_limits::min)(); const long long maxEncodableDouble = (1LL << 40) - 1; const long long minEncodableDouble = -maxEncodableDouble; const long long maxDouble = (1LL << std::numeric_limits::digits) - 1; const long long minDouble = -maxDouble; const long long maxLongLong = (std::numeric_limits::max)(); const long long minLongLong = (std::numeric_limits::min)(); template void assertBSONTypeEquals(BSONType actual, BSONType expected, T value, int i) { if (expected != actual) { stringstream ss; ss << "incorrect type in bson object for " << (i+1) << "-th test value " << value << ". actual: " << mongo::typeName(actual) << "; expected: " << mongo::typeName(expected); const string msg = ss.str(); FAIL(msg); } } /** * current conversion ranges in append(unsigned n) * dbl/int max/min in comments refer to max/min encodable constants * 0 <= n <= uint_max -----> int */ TEST(BSONObjBuilderTest, AppendUnsignedInt) { struct { unsigned int v; BSONType t; } data[] = { { 0, mongo::NumberInt }, { 100, mongo::NumberInt }, { maxEncodableInt, mongo::NumberInt }, { maxEncodableInt + 1, mongo::NumberInt }, { static_cast(maxInt), mongo::NumberInt }, { static_cast(maxInt) + 1U, mongo::NumberInt }, { (std::numeric_limits::max)(), mongo::NumberInt }, { 0, mongo::Undefined } }; for (int i=0; data[i].t != mongo::Undefined; i++) { unsigned int v = data[i].v; BSONObjBuilder b; b.append("a", v); BSONObj o = b.obj(); ASSERT_EQUALS(o.nFields(), 1); BSONElement e = o.getField("a"); unsigned int n = e.numberLong(); ASSERT_EQUALS(n, v); assertBSONTypeEquals(e.type(), data[i].t, v, i); } } /** * current conversion ranges in appendIntOrLL(long long n) * dbl/int max/min in comments refer to max/min encodable constants * n < dbl_min -----> long long * dbl_min <= n < int_min -----> double * int_min <= n <= int_max -----> int * int_max < n <= dbl_max -----> double * dbl_max < n -----> long long */ TEST(BSONObjBuilderTest, AppendIntOrLL) { struct { long long v; BSONType t; } data[] = { { 0, mongo::NumberInt }, { -100, mongo::NumberInt }, { 100, mongo::NumberInt }, { -(maxInt / 2 - 1), mongo::NumberInt }, { maxInt / 2 - 1, mongo::NumberInt }, { -(maxInt / 2), mongo::NumberLong }, { maxInt / 2, mongo::NumberLong }, { minEncodableInt, mongo::NumberLong }, { maxEncodableInt, mongo::NumberLong }, { minEncodableInt - 1, mongo::NumberLong }, { maxEncodableInt + 1, mongo::NumberLong }, { minInt, mongo::NumberLong }, { maxInt, mongo::NumberLong }, { minInt - 1, mongo::NumberLong }, { maxInt + 1, mongo::NumberLong }, { minLongLong, mongo::NumberLong }, { maxLongLong, mongo::NumberLong }, { 0, mongo::Undefined } }; for (int i=0; data[i].t != mongo::Undefined; i++) { long long v = data[i].v; BSONObjBuilder b; b.appendIntOrLL("a", v); BSONObj o = b.obj(); ASSERT_EQUALS(o.nFields(), 1); BSONElement e = o.getField("a"); long long n = e.numberLong(); ASSERT_EQUALS(n, v); assertBSONTypeEquals(e.type(), data[i].t, v, i); } } /** * current conversion ranges in appendNumber(size_t n) * dbl/int max/min in comments refer to max/min encodable constants * 0 <= n <= int_max -----> int * int_max < n -----> long long */ TEST(BSONObjBuilderTest, AppendNumberSizeT) { struct { size_t v; BSONType t; } data[] = { { 0, mongo::NumberInt }, { 100, mongo::NumberInt }, { maxEncodableInt, mongo::NumberInt }, { maxEncodableInt + 1, mongo::NumberLong }, { size_t(maxInt), mongo::NumberLong }, { size_t(maxInt) + 1U, mongo::NumberLong }, { (std::numeric_limits::max)(), mongo::NumberLong }, { 0, mongo::Undefined } }; for (int i=0; data[i].t != mongo::Undefined; i++) { size_t v = data[i].v; BSONObjBuilder b; b.appendNumber("a", v); BSONObj o = b.obj(); ASSERT_EQUALS(o.nFields(), 1); BSONElement e = o.getField("a"); size_t n = e.numberLong(); ASSERT_EQUALS(n, v); assertBSONTypeEquals(e.type(), data[i].t, v, i); } } /** * current conversion ranges in appendNumber(long long n) * dbl/int max/min in comments refer to max/min encodable constants * n < dbl_min -----> long long * dbl_min <= n < int_min -----> double * int_min <= n <= int_max -----> int * int_max < n <= dbl_max -----> double * dbl_max < n -----> long long */ TEST(BSONObjBuilderTest, AppendNumberLongLong) { struct { long long v; BSONType t; } data[] = { { 0, mongo::NumberInt }, { -100, mongo::NumberInt }, { 100, mongo::NumberInt }, { minEncodableInt, mongo::NumberInt }, { maxEncodableInt, mongo::NumberInt }, { minEncodableInt - 1, mongo::NumberDouble }, { maxEncodableInt + 1, mongo::NumberDouble }, { minInt, mongo::NumberDouble }, { maxInt, mongo::NumberDouble }, { minInt - 1, mongo::NumberDouble }, { maxInt + 1, mongo::NumberDouble }, { minEncodableDouble, mongo::NumberDouble }, { maxEncodableDouble, mongo::NumberDouble }, { minEncodableDouble - 1, mongo::NumberLong }, { maxEncodableDouble + 1, mongo::NumberLong }, { minDouble, mongo::NumberLong }, { maxDouble, mongo::NumberLong }, { minDouble - 1, mongo::NumberLong }, { maxDouble + 1, mongo::NumberLong }, { minLongLong, mongo::NumberLong }, { maxLongLong, mongo::NumberLong }, { 0, mongo::Undefined } }; for (int i=0; data[i].t != mongo::Undefined; i++) { long long v = data[i].v; BSONObjBuilder b; b.appendNumber("a", v); BSONObj o = b.obj(); ASSERT_EQUALS(o.nFields(), 1); BSONElement e = o.getField("a"); if (data[i].t != mongo::NumberDouble) { long long n = e.numberLong(); ASSERT_EQUALS(n, v); } else { double n = e.numberDouble(); ASSERT_APPROX_EQUAL(n, static_cast(v), 0.001); } assertBSONTypeEquals(e.type(), data[i].t, v, i); } } TEST(BSONObjBuilderTest, StreamLongLongMin) { BSONObj o = BSON("a" << std::numeric_limits::min()); ASSERT_EQUALS(o.nFields(), 1); BSONElement e = o.getField("a"); long long n = e.numberLong(); ASSERT_EQUALS(n, std::numeric_limits::min()); } TEST(BSONObjBuilderTest, AppendNumberLongLongMinCompareObject) { BSONObjBuilder b; b.appendNumber("a", std::numeric_limits::min()); BSONObj o1 = b.obj(); BSONObj o2 = BSON("a" << std::numeric_limits::min()); ASSERT_EQUALS(o1, o2); } TEST(BSONObjBuilderTest, AppendMaxTimestampConversion) { BSONObjBuilder b; b.appendMaxForType("a", mongo::bsonTimestamp); BSONObj o1 = b.obj(); BSONElement e = o1.getField("a"); ASSERT_FALSE(e.eoo()); mongo::Timestamp timestamp = e.timestamp(); ASSERT_FALSE(timestamp.isNull()); } } // unnamed namespace