/** * 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 . * * 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. */ /** Unit tests for MatchMatchExpression operator implementations in match_operators.{h,cpp}. */ #include "mongo/unittest/unittest.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" #include "mongo/db/matcher/expression.h" #include "mongo/db/matcher/expression_leaf.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/query/collation/collator_interface_mock.h" namespace mongo { using std::string; TEST(ComparisonMatchExpression, ComparisonMatchExpressionsWithUnequalCollatorsAreUnequal) { CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kReverseString); EqualityMatchExpression eq1; eq1.setCollator(&collator1); CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); EqualityMatchExpression eq2; eq2.setCollator(&collator2); ASSERT(!eq1.equivalent(&eq2)); } TEST(ComparisonMatchExpression, ComparisonMatchExpressionsWithEqualCollatorsAreEqual) { CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kAlwaysEqual); EqualityMatchExpression eq1; eq1.setCollator(&collator1); CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); EqualityMatchExpression eq2; eq2.setCollator(&collator2); ASSERT(eq1.equivalent(&eq2)); } TEST(ComparisonMatchExpression, StringMatchingWithNullCollatorUsesBinaryComparison) { BSONObj operand = BSON("a" << "string"); EqualityMatchExpression eq; ASSERT(eq.init("a", operand["a"]).isOK()); ASSERT(!eq.matchesBSON(BSON("a" << "string2"), NULL)); } TEST(ComparisonMatchExpression, StringMatchingRespectsCollation) { BSONObj operand = BSON("a" << "string"); CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); EqualityMatchExpression eq; eq.setCollator(&collator); ASSERT(eq.init("a", operand["a"]).isOK()); ASSERT(eq.matchesBSON(BSON("a" << "string2"), NULL)); } TEST(EqOp, MatchesElement) { BSONObj operand = BSON("a" << 5); BSONObj match = BSON("a" << 5.0); BSONObj notMatch = BSON("a" << 6); EqualityMatchExpression eq; eq.init("", operand["a"]); ASSERT(eq.matchesSingleElement(match.firstElement())); ASSERT(!eq.matchesSingleElement(notMatch.firstElement())); ASSERT(eq.equivalent(&eq)); } TEST(EqOp, InvalidEooOperand) { BSONObj operand; EqualityMatchExpression eq; ASSERT(!eq.init("", operand.firstElement()).isOK()); } TEST(EqOp, MatchesScalar) { BSONObj operand = BSON("a" << 5); EqualityMatchExpression eq; eq.init("a", operand["a"]); ASSERT(eq.matchesBSON(BSON("a" << 5.0), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << 4), NULL)); } TEST(EqOp, MatchesArrayValue) { BSONObj operand = BSON("a" << 5); EqualityMatchExpression eq; eq.init("a", operand["a"]); ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(5.0 << 6)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL)); } TEST(EqOp, MatchesReferencedObjectValue) { BSONObj operand = BSON("a.b" << 5); EqualityMatchExpression eq; eq.init("a.b", operand["a.b"]); ASSERT(eq.matchesBSON(BSON("a" << BSON("b" << 5)), NULL)); ASSERT(eq.matchesBSON(BSON("a" << BSON("b" << BSON_ARRAY(5))), NULL)); ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 5))), NULL)); } TEST(EqOp, MatchesReferencedArrayValue) { BSONObj operand = BSON("a.0" << 5); EqualityMatchExpression eq; eq.init("a.0", operand["a.0"]); ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(5))), NULL)); } TEST(EqOp, MatchesNull) { BSONObj operand = BSON("a" << BSONNULL); EqualityMatchExpression eq; eq.init("a", operand["a"]); ASSERT(eq.matchesBSON(BSONObj(), NULL)); ASSERT(eq.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << 4), NULL)); // A non-existent field is treated same way as an empty bson object ASSERT(eq.matchesBSON(BSON("b" << 4), NULL)); } // This test documents how the matcher currently works, // not necessarily how it should work ideally. TEST(EqOp, MatchesNestedNull) { BSONObj operand = BSON("a.b" << BSONNULL); EqualityMatchExpression eq; eq.init("a.b", operand["a.b"]); // null matches any empty object that is on a subpath of a.b ASSERT(eq.matchesBSON(BSONObj(), NULL)); ASSERT(eq.matchesBSON(BSON("a" << BSONObj()), NULL)); ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(BSONObj())), NULL)); ASSERT(eq.matchesBSON(BSON("a" << BSON("b" << BSONNULL)), NULL)); // b does not exist as an element in array under a. ASSERT(!eq.matchesBSON(BSON("a" << BSONArray()), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(BSONNULL)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2)), NULL)); // a.b exists but is not null. ASSERT(!eq.matchesBSON(BSON("a" << BSON("b" << 4)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON("b" << BSONObj())), NULL)); // A non-existent field is treated same way as an empty bson object ASSERT(eq.matchesBSON(BSON("b" << 4), NULL)); } TEST(EqOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); EqualityMatchExpression eq; eq.init("a", operand["a"]); ASSERT(eq.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << 4), NULL)); } TEST(EqOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); EqualityMatchExpression eq; ASSERT(eq.init("a", operand["a"]).isOK()); ASSERT(eq.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << 4), NULL)); } TEST(EqOp, MatchesFullArray) { BSONObj operand = BSON("a" << BSON_ARRAY(1 << 2)); EqualityMatchExpression eq; ASSERT(eq.init("a", operand["a"]).isOK()); ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 3)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(1)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << 1), NULL)); } TEST(EqOp, MatchesThroughNestedArray) { BSONObj operand = BSON("a.b.c.d" << 3); EqualityMatchExpression eq; eq.init("a.b.c.d", operand["a.b.c.d"]); BSONObj obj = fromjson("{a:{b:[{c:[{d:1},{d:2}]},{c:[{d:3}]}]}}"); ASSERT(eq.matchesBSON(obj, NULL)); } TEST(EqOp, ElemMatchKey) { BSONObj operand = BSON("a" << 5); EqualityMatchExpression eq; ASSERT(eq.init("a", operand["a"]).isOK()); MatchDetails details; details.requestElemMatchKey(); ASSERT(!eq.matchesBSON(BSON("a" << 4), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(eq.matchesBSON(BSON("a" << 5), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 5)), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("2", details.elemMatchKey()); } // SERVER-14886: when an array is being traversed explictly at the same time that a nested array // is being traversed implicitly, the elemMatch key should refer to the offset of the array // being implicitly traversed. TEST(EqOp, ElemMatchKeyWithImplicitAndExplicitTraversal) { BSONObj operand = BSON("a.0.b" << 3); BSONElement operandFirstElt = operand.firstElement(); EqualityMatchExpression eq; ASSERT(eq.init(operandFirstElt.fieldName(), operandFirstElt).isOK()); MatchDetails details; details.requestElemMatchKey(); BSONObj obj = fromjson("{a: [{b: [2, 3]}, {b: [4, 5]}]}"); ASSERT(eq.matchesBSON(obj, &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } TEST(EqOp, Equality1) { EqualityMatchExpression eq1; EqualityMatchExpression eq2; EqualityMatchExpression eq3; BSONObj operand = BSON("a" << 5 << "b" << 5 << "c" << 4); eq1.init("a", operand["a"]); eq2.init("a", operand["b"]); eq3.init("c", operand["c"]); ASSERT(eq1.equivalent(&eq1)); ASSERT(eq1.equivalent(&eq2)); ASSERT(!eq1.equivalent(&eq3)); } /** TEST( EqOp, MatchesIndexKeyScalar ) { BSONObj operand = BSON( "a" << 6 ); EqualityMatchExpression eq; ASSERT( eq.init( "a", operand[ "a" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == eq.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == eq.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == eq.matchesIndexKey( BSON( "" << BSON_ARRAY( 6 ) ), indexSpec ) ); } TEST( EqOp, MatchesIndexKeyMissing ) { BSONObj operand = BSON( "a" << 6 ); EqualityMatchExpression eq; ASSERT( eq.init( "a", operand[ "a" ] ).isOK() ); IndexSpec indexSpec( BSON( "b" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == eq.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == eq.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == eq.matchesIndexKey( BSON( "" << BSON_ARRAY( 8 << 6 ) ), indexSpec ) ); } TEST( EqOp, MatchesIndexKeyArray ) { BSONObj operand = BSON( "a" << BSON_ARRAY( 4 << 5 ) ); ComparisonMatchExpression eq ASSERT( eq.init( "a", operand[ "a" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == eq.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); } TEST( EqOp, MatchesIndexKeyArrayValue ) { BSONObj operand = BSON( "a" << 6 ); ComparisonMatchExpression eq ASSERT( eq.init( "a", operand[ "a" ] ).isOK() ); IndexSpec indexSpec( BSON( "loc" << "mockarrayvalue" << "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == eq.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 6 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == eq.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 4 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == eq.matchesIndexKey( BSON( "" << "dummygeohash" << "" << BSON_ARRAY( 8 << 6 ) ), indexSpec ) ); } */ TEST(LtOp, MatchesElement) { BSONObj operand = BSON("$lt" << 5); BSONObj match = BSON("a" << 4.5); BSONObj notMatch = BSON("a" << 6); BSONObj notMatchEqual = BSON("a" << 5); BSONObj notMatchWrongType = BSON("a" << "foo"); LTMatchExpression lt; ASSERT(lt.init("", operand["$lt"]).isOK()); ASSERT(lt.matchesSingleElement(match.firstElement())); ASSERT(!lt.matchesSingleElement(notMatch.firstElement())); ASSERT(!lt.matchesSingleElement(notMatchEqual.firstElement())); ASSERT(!lt.matchesSingleElement(notMatchWrongType.firstElement())); } TEST(LtOp, InvalidEooOperand) { BSONObj operand; LTMatchExpression lt; ASSERT(!lt.init("", operand.firstElement()).isOK()); } TEST(LtOp, MatchesScalar) { BSONObj operand = BSON("$lt" << 5); LTMatchExpression lt; ASSERT(lt.init("a", operand["$lt"]).isOK()); ASSERT(lt.matchesBSON(BSON("a" << 4.5), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << 6), NULL)); } TEST(LtOp, MatchesScalarEmptyKey) { BSONObj operand = BSON("$lt" << 5); LTMatchExpression lt; ASSERT(lt.init("", operand["$lt"]).isOK()); ASSERT(lt.matchesBSON(BSON("" << 4.5), NULL)); ASSERT(!lt.matchesBSON(BSON("" << 6), NULL)); } TEST(LtOp, MatchesArrayValue) { BSONObj operand = BSON("$lt" << 5); LTMatchExpression lt; ASSERT(lt.init("a", operand["$lt"]).isOK()); ASSERT(lt.matchesBSON(BSON("a" << BSON_ARRAY(6 << 4.5)), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL)); } TEST(LtOp, MatchesWholeArray) { BSONObj operand = BSON("$lt" << BSON_ARRAY(5)); LTMatchExpression lt; ASSERT(lt.init("a", operand["$lt"]).isOK()); ASSERT(lt.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(6)), NULL)); // Nested array. ASSERT(lt.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(4))), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(5))), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(6))), NULL)); } TEST(LtOp, MatchesNull) { BSONObj operand = BSON("$lt" << BSONNULL); LTMatchExpression lt; ASSERT(lt.init("a", operand["$lt"]).isOK()); ASSERT(!lt.matchesBSON(BSONObj(), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << 4), NULL)); // A non-existent field is treated same way as an empty bson object ASSERT(!lt.matchesBSON(BSON("b" << 4), NULL)); } TEST(LtOp, MatchesDotNotationNull) { BSONObj operand = BSON("$lt" << BSONNULL); LTMatchExpression lt; ASSERT(lt.init("a.b", operand["$lt"]).isOK()); ASSERT(!lt.matchesBSON(BSONObj(), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << 4), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSONObj()), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << BSONNULL))), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(BSON("a" << 4) << BSON("b" << 4))), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 4))), NULL)); } TEST(LtOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); LTMatchExpression lt; ASSERT(lt.init("a", operand["a"]).isOK()); ASSERT(!lt.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << 4), NULL)); } TEST(LtOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); LTMatchExpression lt; ASSERT(lt.init("a", operand["a"]).isOK()); ASSERT(!lt.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(lt.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(lt.matchesBSON(BSON("a" << 4), NULL)); } TEST(LtOp, ElemMatchKey) { BSONObj operand = BSON("$lt" << 5); LTMatchExpression lt; ASSERT(lt.init("a", operand["$lt"]).isOK()); MatchDetails details; details.requestElemMatchKey(); ASSERT(!lt.matchesBSON(BSON("a" << 6), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(lt.matchesBSON(BSON("a" << 4), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(lt.matchesBSON(BSON("a" << BSON_ARRAY(6 << 2 << 5)), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } /** TEST( LtOp, MatchesIndexKeyScalar ) { BSONObj operand = BSON( "$lt" << 6 ); LtOp lt; ASSERT( lt.init( "a", operand[ "$lt" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == lt.matchesIndexKey( BSON( "" << 3 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == lt.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == lt.matchesIndexKey( BSON( "" << BSON_ARRAY( 5 ) ), indexSpec ) ); } TEST( LtOp, MatchesIndexKeyMissing ) { BSONObj operand = BSON( "$lt" << 6 ); LtOp lt; ASSERT( lt.init( "a", operand[ "$lt" ] ).isOK() ); IndexSpec indexSpec( BSON( "b" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == lt.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == lt.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == lt.matchesIndexKey( BSON( "" << BSON_ARRAY( 8 << 6 ) ), indexSpec ) ); } TEST( LtOp, MatchesIndexKeyArray ) { BSONObj operand = BSON( "$lt" << BSON_ARRAY( 4 << 5 ) ); LtOp lt; ASSERT( lt.init( "a", operand[ "$lt" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == lt.matchesIndexKey( BSON( "" << 3 ), indexSpec ) ); } TEST( LtOp, MatchesIndexKeyArrayValue ) { BSONObj operand = BSON( "$lt" << 6 ); LtOp lt; ASSERT( lt.init( "a", operand[ "$lt" ] ).isOK() ); IndexSpec indexSpec( BSON( "loc" << "mockarrayvalue" << "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == lt.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 3 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == lt.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 6 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == lt.matchesIndexKey( BSON( "" << "dummygeohash" << "" << BSON_ARRAY( 8 << 6 << 4 ) ), indexSpec ) ); } */ TEST(LteOp, MatchesElement) { BSONObj operand = BSON("$lte" << 5); BSONObj match = BSON("a" << 4.5); BSONObj equalMatch = BSON("a" << 5); BSONObj notMatch = BSON("a" << 6); BSONObj notMatchWrongType = BSON("a" << "foo"); LTEMatchExpression lte; ASSERT(lte.init("", operand["$lte"]).isOK()); ASSERT(lte.matchesSingleElement(match.firstElement())); ASSERT(lte.matchesSingleElement(equalMatch.firstElement())); ASSERT(!lte.matchesSingleElement(notMatch.firstElement())); ASSERT(!lte.matchesSingleElement(notMatchWrongType.firstElement())); } TEST(LteOp, InvalidEooOperand) { BSONObj operand; LTEMatchExpression lte; ASSERT(!lte.init("", operand.firstElement()).isOK()); } TEST(LteOp, MatchesScalar) { BSONObj operand = BSON("$lte" << 5); LTEMatchExpression lte; ASSERT(lte.init("a", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << 4.5), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << 6), NULL)); } TEST(LteOp, MatchesArrayValue) { BSONObj operand = BSON("$lte" << 5); LTEMatchExpression lte; ASSERT(lte.init("a", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(6 << 4.5)), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL)); } TEST(LteOp, MatchesWholeArray) { BSONObj operand = BSON("$lte" << BSON_ARRAY(5)); LTEMatchExpression lte; ASSERT(lte.init("a", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << BSON_ARRAY(6)), NULL)); // Nested array. ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(4))), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(5))), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(6))), NULL)); } TEST(LteOp, MatchesNull) { BSONObj operand = BSON("$lte" << BSONNULL); LTEMatchExpression lte; ASSERT(lte.init("a", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSONObj(), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << 4), NULL)); // A non-existent field is treated same way as an empty bson object ASSERT(lte.matchesBSON(BSON("b" << 4), NULL)); } TEST(LteOp, MatchesDotNotationNull) { BSONObj operand = BSON("$lte" << BSONNULL); LTEMatchExpression lte; ASSERT(lte.init("a.b", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSONObj(), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(lte.matchesBSON(BSON("a" << 4), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSONObj()), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << BSONNULL))), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(BSON("a" << 4) << BSON("b" << 4))), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 4))), NULL)); } TEST(LteOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); LTEMatchExpression lte; ASSERT(lte.init("a", operand["a"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << 4), NULL)); } TEST(LteOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); LTEMatchExpression lte; ASSERT(lte.init("a", operand["a"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(lte.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(lte.matchesBSON(BSON("a" << 4), NULL)); } TEST(LteOp, ElemMatchKey) { BSONObj operand = BSON("$lte" << 5); LTEMatchExpression lte; ASSERT(lte.init("a", operand["$lte"]).isOK()); MatchDetails details; details.requestElemMatchKey(); ASSERT(!lte.matchesBSON(BSON("a" << 6), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(lte.matchesBSON(BSON("a" << 4), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(6 << 2 << 5)), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } /** TEST( LteOp, MatchesIndexKeyScalar ) { BSONObj operand = BSON( "$lte" << 6 ); LteOp lte; ASSERT( lte.init( "a", operand[ "$lte" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == lte.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == lte.matchesIndexKey( BSON( "" << 7 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == lte.matchesIndexKey( BSON( "" << BSON_ARRAY( 5 ) ), indexSpec ) ); } TEST( LteOp, MatchesIndexKeyMissing ) { BSONObj operand = BSON( "$lte" << 6 ); LteOp lte; ASSERT( lte.init( "a", operand[ "$lte" ] ).isOK() ); IndexSpec indexSpec( BSON( "b" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == lte.matchesIndexKey( BSON( "" << 7 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == lte.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == lte.matchesIndexKey( BSON( "" << BSON_ARRAY( 8 << 6 ) ), indexSpec ) ); } TEST( LteOp, MatchesIndexKeyArray ) { BSONObj operand = BSON( "$lte" << BSON_ARRAY( 4 << 5 ) ); LteOp lte; ASSERT( lte.init( "a", operand[ "$lte" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == lte.matchesIndexKey( BSON( "" << 3 ), indexSpec ) ); } TEST( LteOp, MatchesIndexKeyArrayValue ) { BSONObj operand = BSON( "$lte" << 6 ); LteOp lte; ASSERT( lte.init( "a", operand[ "$lte" ] ).isOK() ); IndexSpec indexSpec( BSON( "loc" << "mockarrayvalue" << "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == lte.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 3 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == lte.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 7 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == lte.matchesIndexKey( BSON( "" << "dummygeohash" << "" << BSON_ARRAY( 8 << 6 << 4 ) ), indexSpec ) ); } TEST( GtOp, MatchesElement ) { BSONObj operand = BSON( "$gt" << 5 ); BSONObj match = BSON( "a" << 5.5 ); BSONObj notMatch = BSON( "a" << 4 ); BSONObj notMatchEqual = BSON( "a" << 5 ); BSONObj notMatchWrongType = BSON( "a" << "foo" ); GtOp gt; ASSERT( gt.init( "", operand[ "$gt" ] ).isOK() ); ASSERT( gt.matchesSingleElement( match.firstElement() ) ); ASSERT( !gt.matchesSingleElement( notMatch.firstElement() ) ); ASSERT( !gt.matchesSingleElement( notMatchEqual.firstElement() ) ); ASSERT( !gt.matchesSingleElement( notMatchWrongType.firstElement() ) ); } */ TEST(GtOp, InvalidEooOperand) { BSONObj operand; GTMatchExpression gt; ASSERT(!gt.init("", operand.firstElement()).isOK()); } TEST(GtOp, MatchesScalar) { BSONObj operand = BSON("$gt" << 5); GTMatchExpression gt; ASSERT(gt.init("a", operand["$gt"]).isOK()); ASSERT(gt.matchesBSON(BSON("a" << 5.5), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << 4), NULL)); } TEST(GtOp, MatchesArrayValue) { BSONObj operand = BSON("$gt" << 5); GTMatchExpression gt; ASSERT(gt.init("a", operand["$gt"]).isOK()); ASSERT(gt.matchesBSON(BSON("a" << BSON_ARRAY(3 << 5.5)), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(2 << 4)), NULL)); } TEST(GtOp, MatchesWholeArray) { BSONObj operand = BSON("$gt" << BSON_ARRAY(5)); GTMatchExpression gt; ASSERT(gt.init("a", operand["$gt"]).isOK()); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); ASSERT(gt.matchesBSON(BSON("a" << BSON_ARRAY(6)), NULL)); // Nested array. // XXX: The following assertion documents current behavior. ASSERT(gt.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(4))), NULL)); // XXX: The following assertion documents current behavior. ASSERT(gt.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(5))), NULL)); ASSERT(gt.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(6))), NULL)); } TEST(GtOp, MatchesNull) { BSONObj operand = BSON("$gt" << BSONNULL); GTMatchExpression gt; ASSERT(gt.init("a", operand["$gt"]).isOK()); ASSERT(!gt.matchesBSON(BSONObj(), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << 4), NULL)); // A non-existent field is treated same way as an empty bson object ASSERT(!gt.matchesBSON(BSON("b" << 4), NULL)); } TEST(GtOp, MatchesDotNotationNull) { BSONObj operand = BSON("$gt" << BSONNULL); GTMatchExpression gt; ASSERT(gt.init("a.b", operand["$gt"]).isOK()); ASSERT(!gt.matchesBSON(BSONObj(), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << 4), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSONObj()), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << BSONNULL))), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(BSON("a" << 4) << BSON("b" << 4))), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 4))), NULL)); } TEST(GtOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); GTMatchExpression gt; ASSERT(gt.init("a", operand["a"]).isOK()); ASSERT(!gt.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(gt.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(gt.matchesBSON(BSON("a" << 4), NULL)); } TEST(GtOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); GTMatchExpression gt; ASSERT(gt.init("a", operand["a"]).isOK()); ASSERT(!gt.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << 4), NULL)); } TEST(GtOp, ElemMatchKey) { BSONObj operand = BSON("$gt" << 5); GTMatchExpression gt; ASSERT(gt.init("a", operand["$gt"]).isOK()); MatchDetails details; details.requestElemMatchKey(); ASSERT(!gt.matchesBSON(BSON("a" << 4), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(gt.matchesBSON(BSON("a" << 6), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(gt.matchesBSON(BSON("a" << BSON_ARRAY(2 << 6 << 5)), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } /** TEST( GtOp, MatchesIndexKeyScalar ) { BSONObj operand = BSON( "$gt" << 6 ); GtOp gt; ASSERT( gt.init( "a", operand[ "$gt" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == gt.matchesIndexKey( BSON( "" << 7 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == gt.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == gt.matchesIndexKey( BSON( "" << BSON_ARRAY( 9 ) ), indexSpec ) ); } TEST( GtOp, MatchesIndexKeyMissing ) { BSONObj operand = BSON( "$gt" << 6 ); GtOp gt; ASSERT( gt.init( "a", operand[ "$gt" ] ).isOK() ); IndexSpec indexSpec( BSON( "b" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == gt.matchesIndexKey( BSON( "" << 7 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == gt.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == gt.matchesIndexKey( BSON( "" << BSON_ARRAY( 8 << 6 ) ), indexSpec ) ); } TEST( GtOp, MatchesIndexKeyArray ) { BSONObj operand = BSON( "$gt" << BSON_ARRAY( 4 << 5 ) ); GtOp gt; ASSERT( gt.init( "a", operand[ "$gt" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == gt.matchesIndexKey( BSON( "" << 8 ), indexSpec ) ); } TEST( GtOp, MatchesIndexKeyArrayValue ) { BSONObj operand = BSON( "$gt" << 6 ); GtOp gt; ASSERT( gt.init( "a", operand[ "$gt" ] ).isOK() ); IndexSpec indexSpec( BSON( "loc" << "mockarrayvalue" << "a" << 1 ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == gt.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 7 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_False == gt.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 3 ), indexSpec ) ); ASSERT( MatchMatchExpression::PartialMatchResult_True == gt.matchesIndexKey( BSON( "" << "dummygeohash" << "" << BSON_ARRAY( 8 << 6 << 4 ) ), indexSpec ) ); } */ TEST(GteOp, MatchesElement) { BSONObj operand = BSON("$gte" << 5); BSONObj match = BSON("a" << 5.5); BSONObj equalMatch = BSON("a" << 5); BSONObj notMatch = BSON("a" << 4); BSONObj notMatchWrongType = BSON("a" << "foo"); GTEMatchExpression gte; ASSERT(gte.init("", operand["$gte"]).isOK()); ASSERT(gte.matchesSingleElement(match.firstElement())); ASSERT(gte.matchesSingleElement(equalMatch.firstElement())); ASSERT(!gte.matchesSingleElement(notMatch.firstElement())); ASSERT(!gte.matchesSingleElement(notMatchWrongType.firstElement())); } TEST(GteOp, InvalidEooOperand) { BSONObj operand; GTEMatchExpression gte; ASSERT(!gte.init("", operand.firstElement()).isOK()); } TEST(GteOp, MatchesScalar) { BSONObj operand = BSON("$gte" << 5); GTEMatchExpression gte; ASSERT(gte.init("a", operand["$gte"]).isOK()); ASSERT(gte.matchesBSON(BSON("a" << 5.5), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << 4), NULL)); } TEST(GteOp, MatchesArrayValue) { BSONObj operand = BSON("$gte" << 5); GTEMatchExpression gte; ASSERT(gte.init("a", operand["$gte"]).isOK()); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(4 << 5.5)), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2)), NULL)); } TEST(GteOp, MatchesWholeArray) { BSONObj operand = BSON("$gte" << BSON_ARRAY(5)); GTEMatchExpression gte; ASSERT(gte.init("a", operand["$gte"]).isOK()); ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(6)), NULL)); // Nested array. // XXX: The following assertion documents current behavior. ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(4))), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(5))), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(6))), NULL)); } TEST(GteOp, MatchesNull) { BSONObj operand = BSON("$gte" << BSONNULL); GTEMatchExpression gte; ASSERT(gte.init("a", operand["$gte"]).isOK()); ASSERT(gte.matchesBSON(BSONObj(), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << 4), NULL)); // A non-existent field is treated same way as an empty bson object ASSERT(gte.matchesBSON(BSON("b" << 4), NULL)); } TEST(GteOp, MatchesDotNotationNull) { BSONObj operand = BSON("$gte" << BSONNULL); GTEMatchExpression gte; ASSERT(gte.init("a.b", operand["$gte"]).isOK()); ASSERT(gte.matchesBSON(BSONObj(), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(gte.matchesBSON(BSON("a" << 4), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSONObj()), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << BSONNULL))), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON("a" << 4) << BSON("b" << 4))), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 4))), NULL)); } TEST(GteOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); GTEMatchExpression gte; ASSERT(gte.init("a", operand["a"]).isOK()); ASSERT(gte.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(gte.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(gte.matchesBSON(BSON("a" << 4), NULL)); } TEST(GteOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); GTEMatchExpression gte; ASSERT(gte.init("a", operand["a"]).isOK()); ASSERT(gte.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << 4), NULL)); } TEST(GteOp, ElemMatchKey) { BSONObj operand = BSON("$gte" << 5); GTEMatchExpression gte; ASSERT(gte.init("a", operand["$gte"]).isOK()); MatchDetails details; details.requestElemMatchKey(); ASSERT(!gte.matchesBSON(BSON("a" << 4), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(gte.matchesBSON(BSON("a" << 6), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(2 << 6 << 5)), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } TEST(RegexMatchExpression, MatchesElementExact) { BSONObj match = BSON("a" << "b"); BSONObj notMatch = BSON("a" << "c"); RegexMatchExpression regex; ASSERT(regex.init("", "b", "").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, TooLargePattern) { string tooLargePattern(50 * 1000, 'z'); RegexMatchExpression regex; ASSERT(!regex.init("a", tooLargePattern, "").isOK()); } TEST(RegexMatchExpression, MatchesElementSimplePrefix) { BSONObj match = BSON("x" << "abc"); BSONObj notMatch = BSON("x" << "adz"); RegexMatchExpression regex; ASSERT(regex.init("", "^ab", "").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementCaseSensitive) { BSONObj match = BSON("x" << "abc"); BSONObj notMatch = BSON("x" << "ABC"); RegexMatchExpression regex; ASSERT(regex.init("", "abc", "").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementCaseInsensitive) { BSONObj match = BSON("x" << "abc"); BSONObj matchUppercase = BSON("x" << "ABC"); BSONObj notMatch = BSON("x" << "abz"); RegexMatchExpression regex; ASSERT(regex.init("", "abc", "i").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(regex.matchesSingleElement(matchUppercase.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementMultilineOff) { BSONObj match = BSON("x" << "az"); BSONObj notMatch = BSON("x" << "\naz"); RegexMatchExpression regex; ASSERT(regex.init("", "^a", "").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementMultilineOn) { BSONObj match = BSON("x" << "az"); BSONObj matchMultiline = BSON("x" << "\naz"); BSONObj notMatch = BSON("x" << "\n\n"); RegexMatchExpression regex; ASSERT(regex.init("", "^a", "m").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(regex.matchesSingleElement(matchMultiline.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementExtendedOff) { BSONObj match = BSON("x" << "a b"); BSONObj notMatch = BSON("x" << "ab"); RegexMatchExpression regex; ASSERT(regex.init("", "a b", "").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementExtendedOn) { BSONObj match = BSON("x" << "ab"); BSONObj notMatch = BSON("x" << "a b"); RegexMatchExpression regex; ASSERT(regex.init("", "a b", "x").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementDotAllOff) { BSONObj match = BSON("x" << "a b"); BSONObj notMatch = BSON("x" << "a\nb"); RegexMatchExpression regex; ASSERT(regex.init("", "a.b", "").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementDotAllOn) { BSONObj match = BSON("x" << "a b"); BSONObj matchDotAll = BSON("x" << "a\nb"); BSONObj notMatch = BSON("x" << "ab"); RegexMatchExpression regex; ASSERT(regex.init("", "a.b", "s").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(regex.matchesSingleElement(matchDotAll.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementMultipleFlags) { BSONObj matchMultilineDotAll = BSON("x" << "\na\nb"); RegexMatchExpression regex; ASSERT(regex.init("", "^a.b", "ms").isOK()); ASSERT(regex.matchesSingleElement(matchMultilineDotAll.firstElement())); } TEST(RegexMatchExpression, MatchesElementRegexType) { BSONObj match = BSONObjBuilder().appendRegex("x", "yz", "i").obj(); BSONObj notMatchPattern = BSONObjBuilder().appendRegex("x", "r", "i").obj(); BSONObj notMatchFlags = BSONObjBuilder().appendRegex("x", "yz", "s").obj(); RegexMatchExpression regex; ASSERT(regex.init("", "yz", "i").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatchPattern.firstElement())); ASSERT(!regex.matchesSingleElement(notMatchFlags.firstElement())); } TEST(RegexMatchExpression, MatchesElementSymbolType) { BSONObj match = BSONObjBuilder().appendSymbol("x", "yz").obj(); BSONObj notMatch = BSONObjBuilder().appendSymbol("x", "gg").obj(); RegexMatchExpression regex; ASSERT(regex.init("", "yz", "").isOK()); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, MatchesElementWrongType) { BSONObj notMatchInt = BSON("x" << 1); BSONObj notMatchBool = BSON("x" << true); RegexMatchExpression regex; ASSERT(regex.init("", "1", "").isOK()); ASSERT(!regex.matchesSingleElement(notMatchInt.firstElement())); ASSERT(!regex.matchesSingleElement(notMatchBool.firstElement())); } TEST(RegexMatchExpression, MatchesElementUtf8) { BSONObj multiByteCharacter = BSON("x" << "\xc2\xa5"); RegexMatchExpression regex; ASSERT(regex.init("", "^.$", "").isOK()); ASSERT(regex.matchesSingleElement(multiByteCharacter.firstElement())); } TEST(RegexMatchExpression, MatchesScalar) { RegexMatchExpression regex; ASSERT(regex.init("a", "b", "").isOK()); ASSERT(regex.matchesBSON(BSON("a" << "b"), NULL)); ASSERT(!regex.matchesBSON(BSON("a" << "c"), NULL)); } TEST(RegexMatchExpression, MatchesArrayValue) { RegexMatchExpression regex; ASSERT(regex.init("a", "b", "").isOK()); ASSERT(regex.matchesBSON(BSON("a" << BSON_ARRAY("c" << "b")), NULL)); ASSERT(!regex.matchesBSON(BSON("a" << BSON_ARRAY("d" << "c")), NULL)); } TEST(RegexMatchExpression, MatchesNull) { RegexMatchExpression regex; ASSERT(regex.init("a", "b", "").isOK()); ASSERT(!regex.matchesBSON(BSONObj(), NULL)); ASSERT(!regex.matchesBSON(BSON("a" << BSONNULL), NULL)); } TEST(RegexMatchExpression, ElemMatchKey) { RegexMatchExpression regex; ASSERT(regex.init("a", "b", "").isOK()); MatchDetails details; details.requestElemMatchKey(); ASSERT(!regex.matchesBSON(BSON("a" << "c"), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(regex.matchesBSON(BSON("a" << "b"), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(regex.matchesBSON(BSON("a" << BSON_ARRAY("c" << "b")), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } TEST(RegexMatchExpression, Equality1) { RegexMatchExpression r1; RegexMatchExpression r2; RegexMatchExpression r3; RegexMatchExpression r4; ASSERT(r1.init("a", "b", "").isOK()); ASSERT(r2.init("a", "b", "x").isOK()); ASSERT(r3.init("a", "c", "").isOK()); ASSERT(r4.init("b", "b", "").isOK()); ASSERT(r1.equivalent(&r1)); ASSERT(!r1.equivalent(&r2)); ASSERT(!r1.equivalent(&r3)); ASSERT(!r1.equivalent(&r4)); } TEST(RegexMatchExpression, RegexCannotContainEmbeddedNullByte) { RegexMatchExpression regex; { const auto embeddedNull = "a\0b"_sd; ASSERT_NOT_OK(regex.init("path", embeddedNull, "")); } { const auto singleNullByte = "\0"_sd; ASSERT_NOT_OK(regex.init("path", singleNullByte, "")); } { const auto leadingNullByte = "\0bbbb"_sd; ASSERT_NOT_OK(regex.init("path", leadingNullByte, "")); } { const auto trailingNullByte = "bbbb\0"_sd; ASSERT_NOT_OK(regex.init("path", trailingNullByte, "")); } } TEST(RegexMatchExpression, RegexOptionsStringCannotContainEmbeddedNullByte) { RegexMatchExpression regex; { const auto embeddedNull = "a\0b"_sd; ASSERT_NOT_OK(regex.init("path", "pattern", embeddedNull)); } { const auto singleNullByte = "\0"_sd; ASSERT_NOT_OK(regex.init("path", "pattern", singleNullByte)); } { const auto leadingNullByte = "\0bbbb"_sd; ASSERT_NOT_OK(regex.init("path", "pattern", leadingNullByte)); } { const auto trailingNullByte = "bbbb\0"_sd; ASSERT_NOT_OK(regex.init("path", "pattern", trailingNullByte)); } } TEST(ModMatchExpression, MatchesElement) { BSONObj match = BSON("a" << 1); BSONObj largerMatch = BSON("a" << 4.0); BSONObj longLongMatch = BSON("a" << 68719476736LL); BSONObj notMatch = BSON("a" << 6); BSONObj negativeNotMatch = BSON("a" << -2); ModMatchExpression mod; ASSERT(mod.init("", 3, 1).isOK()); ASSERT(mod.matchesSingleElement(match.firstElement())); ASSERT(mod.matchesSingleElement(largerMatch.firstElement())); ASSERT(mod.matchesSingleElement(longLongMatch.firstElement())); ASSERT(!mod.matchesSingleElement(notMatch.firstElement())); ASSERT(!mod.matchesSingleElement(negativeNotMatch.firstElement())); } TEST(ModMatchExpression, ZeroDivisor) { ModMatchExpression mod; ASSERT(!mod.init("", 0, 1).isOK()); } TEST(ModMatchExpression, MatchesScalar) { ModMatchExpression mod; ASSERT(mod.init("a", 5, 2).isOK()); ASSERT(mod.matchesBSON(BSON("a" << 7.0), NULL)); ASSERT(!mod.matchesBSON(BSON("a" << 4), NULL)); } TEST(ModMatchExpression, MatchesArrayValue) { ModMatchExpression mod; ASSERT(mod.init("a", 5, 2).isOK()); ASSERT(mod.matchesBSON(BSON("a" << BSON_ARRAY(5 << 12LL)), NULL)); ASSERT(!mod.matchesBSON(BSON("a" << BSON_ARRAY(6 << 8)), NULL)); } TEST(ModMatchExpression, MatchesNull) { ModMatchExpression mod; ASSERT(mod.init("a", 5, 2).isOK()); ASSERT(!mod.matchesBSON(BSONObj(), NULL)); ASSERT(!mod.matchesBSON(BSON("a" << BSONNULL), NULL)); } TEST(ModMatchExpression, ElemMatchKey) { ModMatchExpression mod; ASSERT(mod.init("a", 5, 2).isOK()); MatchDetails details; details.requestElemMatchKey(); ASSERT(!mod.matchesBSON(BSON("a" << 4), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(mod.matchesBSON(BSON("a" << 2), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(mod.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 5)), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } TEST(ModMatchExpression, Equality1) { ModMatchExpression m1; ModMatchExpression m2; ModMatchExpression m3; ModMatchExpression m4; m1.init("a", 1, 2); m2.init("a", 2, 2); m3.init("a", 1, 1); m4.init("b", 1, 2); ASSERT(m1.equivalent(&m1)); ASSERT(!m1.equivalent(&m2)); ASSERT(!m1.equivalent(&m3)); ASSERT(!m1.equivalent(&m4)); } /** TEST( ModMatchExpression, MatchesIndexKey ) { BSONObj operand = BSON( "$mod" << BSON_ARRAY( 2 << 1 ) ); ModMatchExpression mod; ASSERT( mod.init( "a", operand[ "$mod" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); BSONObj indexKey = BSON( "" << 1 ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == mod.matchesIndexKey( indexKey, indexSpec ) ); } */ TEST(ExistsMatchExpression, MatchesElement) { BSONObj existsInt = BSON("a" << 5); BSONObj existsNull = BSON("a" << BSONNULL); BSONObj doesntExist = BSONObj(); ExistsMatchExpression exists; ASSERT(exists.init("").isOK()); ASSERT(exists.matchesSingleElement(existsInt.firstElement())); ASSERT(exists.matchesSingleElement(existsNull.firstElement())); ASSERT(!exists.matchesSingleElement(doesntExist.firstElement())); } TEST(ExistsMatchExpression, MatchesElementExistsTrueValue) { BSONObj exists = BSON("a" << 5); BSONObj missing = BSONObj(); ExistsMatchExpression existsTrueValue; ASSERT(existsTrueValue.init("").isOK()); ASSERT(existsTrueValue.matchesSingleElement(exists.firstElement())); ASSERT(!existsTrueValue.matchesSingleElement(missing.firstElement())); } TEST(ExistsMatchExpression, MatchesScalar) { ExistsMatchExpression exists; ASSERT(exists.init("a").isOK()); ASSERT(exists.matchesBSON(BSON("a" << 1), NULL)); ASSERT(exists.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!exists.matchesBSON(BSON("b" << 1), NULL)); } TEST(ExistsMatchExpression, MatchesArray) { ExistsMatchExpression exists; ASSERT(exists.init("a").isOK()); ASSERT(exists.matchesBSON(BSON("a" << BSON_ARRAY(4 << 5.5)), NULL)); } TEST(ExistsMatchExpression, ElemMatchKey) { ExistsMatchExpression exists; ASSERT(exists.init("a.b").isOK()); MatchDetails details; details.requestElemMatchKey(); ASSERT(!exists.matchesBSON(BSON("a" << 1), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(exists.matchesBSON(BSON("a" << BSON("b" << 6)), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(exists.matchesBSON(BSON("a" << BSON_ARRAY(2 << BSON("b" << 7))), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } TEST(ExistsMatchExpression, Equivalent) { ExistsMatchExpression e1; ExistsMatchExpression e2; e1.init("a"); e2.init("b"); ASSERT(e1.equivalent(&e1)); ASSERT(!e1.equivalent(&e2)); } /** TEST( ExistsMatchExpression, MatchesIndexKey ) { BSONObj operand = BSON( "$exists" << true ); ExistsMatchExpression exists; ASSERT( exists.init( "a", operand[ "$exists" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); BSONObj indexKey = BSON( "" << 1 ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == exists.matchesIndexKey( indexKey, indexSpec ) ); } */ TEST(TypeMatchExpression, MatchesElementStringType) { BSONObj match = BSON("a" << "abc"); BSONObj notMatch = BSON("a" << 5); TypeMatchExpression type; ASSERT(type.initWithBSONType("", String).isOK()); ASSERT(type.matchesSingleElement(match["a"])); ASSERT(!type.matchesSingleElement(notMatch["a"])); } TEST(TypeMatchExpression, MatchesElementNullType) { BSONObj match = BSON("a" << BSONNULL); BSONObj notMatch = BSON("a" << "abc"); TypeMatchExpression type; ASSERT(type.initWithBSONType("", jstNULL).isOK()); ASSERT(type.matchesSingleElement(match["a"])); ASSERT(!type.matchesSingleElement(notMatch["a"])); } TEST(TypeMatchExpression, MatchesElementNumber) { BSONObj match1 = BSON("a" << 1); BSONObj match2 = BSON("a" << 1LL); BSONObj match3 = BSON("a" << 2.5); BSONObj notMatch = BSON("a" << "abc"); ASSERT_EQ(BSONType::NumberInt, match1["a"].type()); ASSERT_EQ(BSONType::NumberLong, match2["a"].type()); ASSERT_EQ(BSONType::NumberDouble, match3["a"].type()); TypeMatchExpression type; ASSERT_OK(type.initAsMatchingAllNumbers("a")); ASSERT_EQ("a", type.path()); ASSERT_TRUE(type.matchesSingleElement(match1["a"])); ASSERT_TRUE(type.matchesSingleElement(match2["a"])); ASSERT_TRUE(type.matchesSingleElement(match3["a"])); ASSERT_FALSE(type.matchesSingleElement(notMatch["a"])); } TEST(TypeMatchExpression, InvalidTypeMatchExpressionTypeCode) { TypeMatchExpression type; ASSERT_NOT_OK(type.initWithBSONType("", JSTypeMax + 1)); } TEST(TypeMatchExpression, MatchesScalar) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a", Bool).isOK()); ASSERT(type.matchesBSON(BSON("a" << true), NULL)); ASSERT(!type.matchesBSON(BSON("a" << 1), NULL)); } TEST(TypeMatchExpression, MatchesArray) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a", NumberInt).isOK()); ASSERT(type.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(type.matchesBSON(BSON("a" << BSON_ARRAY(4 << "a")), NULL)); ASSERT(type.matchesBSON(BSON("a" << BSON_ARRAY("a" << 4)), NULL)); ASSERT(!type.matchesBSON(BSON("a" << BSON_ARRAY("a")), NULL)); ASSERT(!type.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(4))), NULL)); } TEST(TypeMatchExpression, MatchesOuterArray) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a", Array).isOK()); // The outer array is not matched. ASSERT(!type.matchesBSON(BSON("a" << BSONArray()), NULL)); ASSERT(!type.matchesBSON(BSON("a" << BSON_ARRAY(4 << "a")), NULL)); ASSERT(type.matchesBSON(BSON("a" << BSON_ARRAY(BSONArray() << 2)), NULL)); ASSERT(!type.matchesBSON(BSON("a" << "bar"), NULL)); } TEST(TypeMatchExpression, MatchesObject) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a", Object).isOK()); ASSERT(type.matchesBSON(BSON("a" << BSON("b" << 1)), NULL)); ASSERT(!type.matchesBSON(BSON("a" << 1), NULL)); } TEST(TypeMatchExpression, MatchesDotNotationFieldObject) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a.b", Object).isOK()); ASSERT(type.matchesBSON(BSON("a" << BSON("b" << BSON("c" << 1))), NULL)); ASSERT(!type.matchesBSON(BSON("a" << BSON("b" << 1)), NULL)); } TEST(TypeMatchExpression, MatchesDotNotationArrayElementArray) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a.0", Array).isOK()); ASSERT(type.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(1))), NULL)); ASSERT(!type.matchesBSON(BSON("a" << BSON_ARRAY("b")), NULL)); } TEST(TypeMatchExpression, MatchesDotNotationArrayElementScalar) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a.0", String).isOK()); ASSERT(type.matchesBSON(BSON("a" << BSON_ARRAY("b")), NULL)); ASSERT(!type.matchesBSON(BSON("a" << BSON_ARRAY(1)), NULL)); } TEST(TypeMatchExpression, MatchesDotNotationArrayElementObject) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a.0", Object).isOK()); ASSERT(type.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 1))), NULL)); ASSERT(!type.matchesBSON(BSON("a" << BSON_ARRAY(1)), NULL)); } TEST(TypeMatchExpression, MatchesNull) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a", jstNULL).isOK()); ASSERT(type.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!type.matchesBSON(BSON("a" << 4), NULL)); ASSERT(!type.matchesBSON(BSONObj(), NULL)); } TEST(TypeMatchExpression, ElemMatchKey) { TypeMatchExpression type; ASSERT(type.initWithBSONType("a.b", String).isOK()); MatchDetails details; details.requestElemMatchKey(); ASSERT(!type.matchesBSON(BSON("a" << 1), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(type.matchesBSON(BSON("a" << BSON("b" << "string")), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(type.matchesBSON(BSON("a" << BSON("b" << BSON_ARRAY("string"))), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("0", details.elemMatchKey()); ASSERT(type.matchesBSON(BSON("a" << BSON_ARRAY(2 << BSON("b" << BSON_ARRAY("string")))), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } TEST(TypeMatchExpression, Equivalent) { TypeMatchExpression e1; TypeMatchExpression e2; TypeMatchExpression e3; e1.initWithBSONType("a", String); e2.initWithBSONType("a", NumberDouble); e3.initWithBSONType("b", String); ASSERT(e1.equivalent(&e1)); ASSERT(!e1.equivalent(&e2)); ASSERT(!e1.equivalent(&e3)); } /** TEST( TypeMatchExpression, MatchesIndexKey ) { BSONObj operand = BSON( "$type" << 2 ); TypeMatchExpression type; ASSERT( type.init( "a", operand[ "$type" ] ).isOK() ); IndexSpec indexSpec( BSON( "a" << 1 ) ); BSONObj indexKey = BSON( "" << "q" ); ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == type.matchesIndexKey( indexKey, indexSpec ) ); } */ TEST(InMatchExpression, MatchesElementSingle) { BSONArray operand = BSON_ARRAY(1); BSONObj match = BSON("a" << 1); BSONObj notMatch = BSON("a" << 2); InMatchExpression in; in.addEquality(operand.firstElement()); ASSERT(in.matchesSingleElement(match["a"])); ASSERT(!in.matchesSingleElement(notMatch["a"])); } TEST(InMatchExpression, MatchesEmpty) { InMatchExpression in; in.init("a"); BSONObj notMatch = BSON("a" << 2); ASSERT(!in.matchesSingleElement(notMatch["a"])); ASSERT(!in.matchesBSON(BSON("a" << 1), NULL)); ASSERT(!in.matchesBSON(BSONObj(), NULL)); } TEST(InMatchExpression, MatchesElementMultiple) { BSONObj operand = BSON_ARRAY(1 << "r" << true << 1); InMatchExpression in; in.addEquality(operand[0]); in.addEquality(operand[1]); in.addEquality(operand[2]); in.addEquality(operand[3]); BSONObj matchFirst = BSON("a" << 1); BSONObj matchSecond = BSON("a" << "r"); BSONObj matchThird = BSON("a" << true); BSONObj notMatch = BSON("a" << false); ASSERT(in.matchesSingleElement(matchFirst["a"])); ASSERT(in.matchesSingleElement(matchSecond["a"])); ASSERT(in.matchesSingleElement(matchThird["a"])); ASSERT(!in.matchesSingleElement(notMatch["a"])); } TEST(InMatchExpression, MatchesScalar) { BSONObj operand = BSON_ARRAY(5); InMatchExpression in; in.init("a"); in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSON("a" << 5.0), NULL)); ASSERT(!in.matchesBSON(BSON("a" << 4), NULL)); } TEST(InMatchExpression, MatchesArrayValue) { BSONObj operand = BSON_ARRAY(5); InMatchExpression in; in.init("a"); in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSON("a" << BSON_ARRAY(5.0 << 6)), NULL)); ASSERT(!in.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL)); ASSERT(!in.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(5))), NULL)); } TEST(InMatchExpression, MatchesNull) { BSONObj operand = BSON_ARRAY(BSONNULL); InMatchExpression in; in.init("a"); in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSONObj(), NULL)); ASSERT(in.matchesBSON(BSON("a" << BSONNULL), NULL)); ASSERT(!in.matchesBSON(BSON("a" << 4), NULL)); // A non-existent field is treated same way as an empty bson object ASSERT(in.matchesBSON(BSON("b" << 4), NULL)); } TEST(InMatchExpression, MatchesUndefined) { BSONObj operand = BSON_ARRAY(BSONUndefined); InMatchExpression in; in.init("a"); Status s = in.addEquality(operand.firstElement()); ASSERT_NOT_OK(s); } TEST(InMatchExpression, MatchesMinKey) { BSONObj operand = BSON_ARRAY(MinKey); InMatchExpression in; in.init("a"); in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!in.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!in.matchesBSON(BSON("a" << 4), NULL)); } TEST(InMatchExpression, MatchesMaxKey) { BSONObj operand = BSON_ARRAY(MaxKey); InMatchExpression in; in.init("a"); in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!in.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!in.matchesBSON(BSON("a" << 4), NULL)); } TEST(InMatchExpression, MatchesFullArray) { BSONObj operand = BSON_ARRAY(BSON_ARRAY(1 << 2) << 4 << 5); InMatchExpression in; in.init("a"); in.addEquality(operand[0]); in.addEquality(operand[1]); in.addEquality(operand[2]); ASSERT(in.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2)), NULL)); ASSERT(!in.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 3)), NULL)); ASSERT(!in.matchesBSON(BSON("a" << BSON_ARRAY(1)), NULL)); ASSERT(!in.matchesBSON(BSON("a" << 1), NULL)); } TEST(InMatchExpression, ElemMatchKey) { BSONObj operand = BSON_ARRAY(5 << 2); InMatchExpression in; in.init("a"); in.addEquality(operand[0]); in.addEquality(operand[1]); MatchDetails details; details.requestElemMatchKey(); ASSERT(!in.matchesBSON(BSON("a" << 4), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(in.matchesBSON(BSON("a" << 5), &details)); ASSERT(!details.hasElemMatchKey()); ASSERT(in.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 5)), &details)); ASSERT(details.hasElemMatchKey()); ASSERT_EQUALS("1", details.elemMatchKey()); } TEST(InMatchExpression, InMatchExpressionsWithDifferentNumbersOfElementsAreUnequal) { BSONObj obj = BSON("" << "string"); InMatchExpression eq1; InMatchExpression eq2; eq1.addEquality(obj.firstElement()); ASSERT(!eq1.equivalent(&eq2)); } TEST(InMatchExpression, InMatchExpressionsWithUnequalCollatorsAreUnequal) { CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kReverseString); InMatchExpression eq1; eq1.setCollator(&collator1); CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); InMatchExpression eq2; eq2.setCollator(&collator2); ASSERT(!eq1.equivalent(&eq2)); } TEST(InMatchExpression, InMatchExpressionsWithEqualCollatorsAreEqual) { CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kAlwaysEqual); InMatchExpression eq1; eq1.setCollator(&collator1); CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); InMatchExpression eq2; eq2.setCollator(&collator2); ASSERT(eq1.equivalent(&eq2)); } TEST(InMatchExpression, InMatchExpressionsWithCollationEquivalentElementsAreEqual) { BSONObj obj1 = BSON("" << "string1"); BSONObj obj2 = BSON("" << "string2"); CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kAlwaysEqual); InMatchExpression eq1; eq1.setCollator(&collator1); CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); InMatchExpression eq2; eq2.setCollator(&collator2); eq1.addEquality(obj1.firstElement()); eq2.addEquality(obj2.firstElement()); ASSERT(eq1.equivalent(&eq2)); } TEST(InMatchExpression, InMatchExpressionsWithCollationNonEquivalentElementsAreUnequal) { BSONObj obj1 = BSON("" << "string1"); BSONObj obj2 = BSON("" << "string2"); CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kReverseString); InMatchExpression eq1; eq1.setCollator(&collator1); CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kReverseString); InMatchExpression eq2; eq2.setCollator(&collator2); eq1.addEquality(obj1.firstElement()); eq2.addEquality(obj2.firstElement()); ASSERT(!eq1.equivalent(&eq2)); } TEST(InMatchExpression, StringMatchingWithNullCollatorUsesBinaryComparison) { BSONArray operand = BSON_ARRAY("string"); BSONObj notMatch = BSON("a" << "string2"); InMatchExpression in; in.addEquality(operand.firstElement()); ASSERT(!in.matchesSingleElement(notMatch["a"])); } TEST(InMatchExpression, StringMatchingRespectsCollation) { BSONArray operand = BSON_ARRAY("string"); BSONObj match = BSON("a" << "string2"); CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); InMatchExpression in; in.setCollator(&collator); in.addEquality(operand.firstElement()); ASSERT(in.matchesSingleElement(match["a"])); } TEST(InMatchExpression, ChangingCollationAfterAddingEqualitiesPreservesEqualities) { BSONObj obj1 = BSON("" << "string1"); BSONObj obj2 = BSON("" << "string2"); CollatorInterfaceMock collatorAlwaysEqual(CollatorInterfaceMock::MockType::kAlwaysEqual); CollatorInterfaceMock collatorReverseString(CollatorInterfaceMock::MockType::kReverseString); InMatchExpression in; in.setCollator(&collatorAlwaysEqual); in.addEquality(obj1.firstElement()); in.addEquality(obj2.firstElement()); ASSERT(in.getEqualities().size() == 1); in.setCollator(&collatorReverseString); ASSERT(in.getEqualities().size() == 2); ASSERT(in.getEqualities().count(obj1.firstElement())); ASSERT(in.getEqualities().count(obj2.firstElement())); } std::vector bsonArrayToBitPositions(const BSONArray& ba) { std::vector bitPositions; // Convert BSONArray of bit positions to int vector for (const auto& elt : ba) { bitPositions.push_back(elt._numberInt()); } return bitPositions; } TEST(BitTestMatchExpression, DoesNotMatchOther) { std::vector bitPositions; BSONObj notMatch1 = fromjson("{a: {}}"); // Object BSONObj notMatch2 = fromjson("{a: null}"); // Null BSONObj notMatch3 = fromjson("{a: []}"); // Array BSONObj notMatch4 = fromjson("{a: true}"); // Boolean BSONObj notMatch5 = fromjson("{a: ''}"); // String BSONObj notMatch6 = fromjson("{a: 5.5}"); // Non-integral Double BSONObj notMatch7 = fromjson("{a: NaN}"); // NaN BSONObj notMatch8 = fromjson("{a: 1e100}"); // Too-Large Double BSONObj notMatch9 = fromjson("{a: ObjectId('000000000000000000000000')}"); // OID BSONObj notMatch10 = fromjson("{a: Date(54)}"); // Date BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitPositions)); ASSERT_OK(ballc.init("a", bitPositions)); ASSERT_OK(banys.init("a", bitPositions)); ASSERT_OK(banyc.init("a", bitPositions)); ASSERT_EQ((size_t)0, balls.numBitPositions()); ASSERT_EQ((size_t)0, ballc.numBitPositions()); ASSERT_EQ((size_t)0, banys.numBitPositions()); ASSERT_EQ((size_t)0, banyc.numBitPositions()); ASSERT(!balls.matchesSingleElement(notMatch1["a"])); ASSERT(!balls.matchesSingleElement(notMatch2["a"])); ASSERT(!balls.matchesSingleElement(notMatch3["a"])); ASSERT(!balls.matchesSingleElement(notMatch4["a"])); ASSERT(!balls.matchesSingleElement(notMatch5["a"])); ASSERT(!balls.matchesSingleElement(notMatch6["a"])); ASSERT(!balls.matchesSingleElement(notMatch7["a"])); ASSERT(!balls.matchesSingleElement(notMatch8["a"])); ASSERT(!balls.matchesSingleElement(notMatch9["a"])); ASSERT(!balls.matchesSingleElement(notMatch10["a"])); ASSERT(!ballc.matchesSingleElement(notMatch1["a"])); ASSERT(!ballc.matchesSingleElement(notMatch2["a"])); ASSERT(!ballc.matchesSingleElement(notMatch3["a"])); ASSERT(!ballc.matchesSingleElement(notMatch4["a"])); ASSERT(!ballc.matchesSingleElement(notMatch5["a"])); ASSERT(!ballc.matchesSingleElement(notMatch6["a"])); ASSERT(!ballc.matchesSingleElement(notMatch7["a"])); ASSERT(!ballc.matchesSingleElement(notMatch8["a"])); ASSERT(!ballc.matchesSingleElement(notMatch9["a"])); ASSERT(!ballc.matchesSingleElement(notMatch10["a"])); ASSERT(!banys.matchesSingleElement(notMatch1["a"])); ASSERT(!banys.matchesSingleElement(notMatch2["a"])); ASSERT(!banys.matchesSingleElement(notMatch3["a"])); ASSERT(!banys.matchesSingleElement(notMatch4["a"])); ASSERT(!banys.matchesSingleElement(notMatch5["a"])); ASSERT(!banys.matchesSingleElement(notMatch6["a"])); ASSERT(!banys.matchesSingleElement(notMatch7["a"])); ASSERT(!banys.matchesSingleElement(notMatch8["a"])); ASSERT(!banys.matchesSingleElement(notMatch9["a"])); ASSERT(!banys.matchesSingleElement(notMatch10["a"])); ASSERT(!banyc.matchesSingleElement(notMatch1["a"])); ASSERT(!banyc.matchesSingleElement(notMatch2["a"])); ASSERT(!banyc.matchesSingleElement(notMatch3["a"])); ASSERT(!banyc.matchesSingleElement(notMatch4["a"])); ASSERT(!banyc.matchesSingleElement(notMatch5["a"])); ASSERT(!banyc.matchesSingleElement(notMatch6["a"])); ASSERT(!banyc.matchesSingleElement(notMatch7["a"])); ASSERT(!banyc.matchesSingleElement(notMatch8["a"])); ASSERT(!banyc.matchesSingleElement(notMatch9["a"])); ASSERT(!banyc.matchesSingleElement(notMatch10["a"])); } TEST(BitTestMatchExpression, MatchBinaryWithLongBitMask) { long long bitMask = 54; BSONObj match = fromjson("{a: {$binary: 'NgAAAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"); BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitMask)); ASSERT_OK(ballc.init("a", bitMask)); ASSERT_OK(banys.init("a", bitMask)); ASSERT_OK(banyc.init("a", bitMask)); std::vector bitPositions = balls.getBitPositions(); ASSERT(balls.matchesSingleElement(match["a"])); ASSERT(!ballc.matchesSingleElement(match["a"])); ASSERT(banys.matchesSingleElement(match["a"])); ASSERT(!banyc.matchesSingleElement(match["a"])); } TEST(BitTestMatchExpression, MatchLongWithBinaryBitMask) { const char* bitMaskSet = "\x36\x00\x00\x00"; const char* bitMaskClear = "\xC9\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; BSONObj match = fromjson("{a: 54}"); BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitMaskSet, 4)); ASSERT_OK(ballc.init("a", bitMaskClear, 9)); ASSERT_OK(banys.init("a", bitMaskSet, 4)); ASSERT_OK(banyc.init("a", bitMaskClear, 9)); ASSERT(balls.matchesSingleElement(match["a"])); ASSERT(ballc.matchesSingleElement(match["a"])); ASSERT(banys.matchesSingleElement(match["a"])); ASSERT(banyc.matchesSingleElement(match["a"])); } TEST(BitTestMatchExpression, MatchesEmpty) { std::vector bitPositions; BSONObj match1 = fromjson("{a: NumberInt(54)}"); BSONObj match2 = fromjson("{a: NumberLong(54)}"); BSONObj match3 = fromjson("{a: 54.0}"); BSONObj match4 = fromjson("{a: {$binary: '2AAAAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"); BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitPositions)); ASSERT_OK(ballc.init("a", bitPositions)); ASSERT_OK(banys.init("a", bitPositions)); ASSERT_OK(banyc.init("a", bitPositions)); ASSERT_EQ((size_t)0, balls.numBitPositions()); ASSERT_EQ((size_t)0, ballc.numBitPositions()); ASSERT_EQ((size_t)0, banys.numBitPositions()); ASSERT_EQ((size_t)0, banyc.numBitPositions()); ASSERT(balls.matchesSingleElement(match1["a"])); ASSERT(balls.matchesSingleElement(match2["a"])); ASSERT(balls.matchesSingleElement(match3["a"])); ASSERT(balls.matchesSingleElement(match4["a"])); ASSERT(ballc.matchesSingleElement(match1["a"])); ASSERT(ballc.matchesSingleElement(match2["a"])); ASSERT(ballc.matchesSingleElement(match3["a"])); ASSERT(ballc.matchesSingleElement(match4["a"])); ASSERT(!banys.matchesSingleElement(match1["a"])); ASSERT(!banys.matchesSingleElement(match2["a"])); ASSERT(!banys.matchesSingleElement(match3["a"])); ASSERT(!banys.matchesSingleElement(match4["a"])); ASSERT(!banyc.matchesSingleElement(match1["a"])); ASSERT(!banyc.matchesSingleElement(match2["a"])); ASSERT(!banyc.matchesSingleElement(match3["a"])); ASSERT(!banyc.matchesSingleElement(match4["a"])); } TEST(BitTestMatchExpression, MatchesInteger) { BSONArray bas = BSON_ARRAY(1 << 2 << 4 << 5); BSONArray bac = BSON_ARRAY(0 << 3 << 600); std::vector bitPositionsSet = bsonArrayToBitPositions(bas); std::vector bitPositionsClear = bsonArrayToBitPositions(bac); BSONObj match1 = fromjson("{a: NumberInt(54)}"); BSONObj match2 = fromjson("{a: NumberLong(54)}"); BSONObj match3 = fromjson("{a: 54.0}"); BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitPositionsSet)); ASSERT_OK(ballc.init("a", bitPositionsClear)); ASSERT_OK(banys.init("a", bitPositionsSet)); ASSERT_OK(banyc.init("a", bitPositionsClear)); ASSERT_EQ((size_t)4, balls.numBitPositions()); ASSERT_EQ((size_t)3, ballc.numBitPositions()); ASSERT_EQ((size_t)4, banys.numBitPositions()); ASSERT_EQ((size_t)3, banyc.numBitPositions()); ASSERT(balls.matchesSingleElement(match1["a"])); ASSERT(balls.matchesSingleElement(match2["a"])); ASSERT(balls.matchesSingleElement(match3["a"])); ASSERT(ballc.matchesSingleElement(match1["a"])); ASSERT(ballc.matchesSingleElement(match2["a"])); ASSERT(ballc.matchesSingleElement(match3["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match3["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match3["a"])); } TEST(BitTestMatchExpression, MatchesNegativeInteger) { BSONArray bas = BSON_ARRAY(1 << 3 << 6 << 7 << 33); BSONArray bac = BSON_ARRAY(0 << 2 << 4 << 5); std::vector bitPositionsSet = bsonArrayToBitPositions(bas); std::vector bitPositionsClear = bsonArrayToBitPositions(bac); BSONObj match1 = fromjson("{a: NumberInt(-54)}"); BSONObj match2 = fromjson("{a: NumberLong(-54)}"); BSONObj match3 = fromjson("{a: -54.0}"); BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitPositionsSet)); ASSERT_OK(ballc.init("a", bitPositionsClear)); ASSERT_OK(banys.init("a", bitPositionsSet)); ASSERT_OK(banyc.init("a", bitPositionsClear)); ASSERT_EQ((size_t)5, balls.numBitPositions()); ASSERT_EQ((size_t)4, ballc.numBitPositions()); ASSERT_EQ((size_t)5, banys.numBitPositions()); ASSERT_EQ((size_t)4, banyc.numBitPositions()); ASSERT(balls.matchesSingleElement(match1["a"])); ASSERT(balls.matchesSingleElement(match2["a"])); ASSERT(balls.matchesSingleElement(match3["a"])); ASSERT(ballc.matchesSingleElement(match1["a"])); ASSERT(ballc.matchesSingleElement(match2["a"])); ASSERT(ballc.matchesSingleElement(match3["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match3["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match3["a"])); } TEST(BitTestMatchExpression, MatchesIntegerWithBitMask) { long long bitMaskSet = 54; long long bitMaskClear = 201; BSONObj match1 = fromjson("{a: NumberInt(54)}"); BSONObj match2 = fromjson("{a: NumberLong(54)}"); BSONObj match3 = fromjson("{a: 54.0}"); BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitMaskSet)); ASSERT_OK(ballc.init("a", bitMaskClear)); ASSERT_OK(banys.init("a", bitMaskSet)); ASSERT_OK(banyc.init("a", bitMaskClear)); ASSERT(balls.matchesSingleElement(match1["a"])); ASSERT(balls.matchesSingleElement(match2["a"])); ASSERT(balls.matchesSingleElement(match3["a"])); ASSERT(ballc.matchesSingleElement(match1["a"])); ASSERT(ballc.matchesSingleElement(match2["a"])); ASSERT(ballc.matchesSingleElement(match3["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match3["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match3["a"])); } TEST(BitTestMatchExpression, MatchesNegativeIntegerWithBitMask) { long long bitMaskSet = 10; long long bitMaskClear = 5; BSONObj match1 = fromjson("{a: NumberInt(-54)}"); BSONObj match2 = fromjson("{a: NumberLong(-54)}"); BSONObj match3 = fromjson("{a: -54.0}"); BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitMaskSet)); ASSERT_OK(ballc.init("a", bitMaskClear)); ASSERT_OK(banys.init("a", bitMaskSet)); ASSERT_OK(banyc.init("a", bitMaskClear)); ASSERT(balls.matchesSingleElement(match1["a"])); ASSERT(balls.matchesSingleElement(match2["a"])); ASSERT(balls.matchesSingleElement(match3["a"])); ASSERT(ballc.matchesSingleElement(match1["a"])); ASSERT(ballc.matchesSingleElement(match2["a"])); ASSERT(ballc.matchesSingleElement(match3["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match3["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match3["a"])); } TEST(BitTestMatchExpression, DoesNotMatchInteger) { BSONArray bas = BSON_ARRAY(1 << 2 << 4 << 5 << 6); BSONArray bac = BSON_ARRAY(0 << 3 << 1); std::vector bitPositionsSet = bsonArrayToBitPositions(bas); std::vector bitPositionsClear = bsonArrayToBitPositions(bac); BSONObj match1 = fromjson("{a: NumberInt(54)}"); BSONObj match2 = fromjson("{a: NumberLong(54)}"); BSONObj match3 = fromjson("{a: 54.0}"); BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitPositionsSet)); ASSERT_OK(ballc.init("a", bitPositionsClear)); ASSERT_OK(banys.init("a", bitPositionsSet)); ASSERT_OK(banyc.init("a", bitPositionsClear)); ASSERT_EQ((size_t)5, balls.numBitPositions()); ASSERT_EQ((size_t)3, ballc.numBitPositions()); ASSERT_EQ((size_t)5, banys.numBitPositions()); ASSERT_EQ((size_t)3, banyc.numBitPositions()); ASSERT(!balls.matchesSingleElement(match1["a"])); ASSERT(!balls.matchesSingleElement(match2["a"])); ASSERT(!balls.matchesSingleElement(match3["a"])); ASSERT(!ballc.matchesSingleElement(match1["a"])); ASSERT(!ballc.matchesSingleElement(match2["a"])); ASSERT(!ballc.matchesSingleElement(match3["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match3["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match3["a"])); } TEST(BitTestMatchExpression, DoesNotMatchIntegerWithBitMask) { long long bitMaskSet = 118; long long bitMaskClear = 11; BSONObj match1 = fromjson("{a: NumberInt(54)}"); BSONObj match2 = fromjson("{a: NumberLong(54)}"); BSONObj match3 = fromjson("{a: 54.0}"); BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitMaskSet)); ASSERT_OK(ballc.init("a", bitMaskClear)); ASSERT_OK(banys.init("a", bitMaskSet)); ASSERT_OK(banyc.init("a", bitMaskClear)); ASSERT(!balls.matchesSingleElement(match1["a"])); ASSERT(!balls.matchesSingleElement(match2["a"])); ASSERT(!balls.matchesSingleElement(match3["a"])); ASSERT(!ballc.matchesSingleElement(match1["a"])); ASSERT(!ballc.matchesSingleElement(match2["a"])); ASSERT(!ballc.matchesSingleElement(match3["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match3["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match3["a"])); } TEST(BitTestMatchExpression, MatchesBinary1) { BSONArray bas = BSON_ARRAY(1 << 2 << 4 << 5); BSONArray bac = BSON_ARRAY(0 << 3 << 600); std::vector bitPositionsSet = bsonArrayToBitPositions(bas); std::vector bitPositionsClear = bsonArrayToBitPositions(bac); BSONObj match1 = fromjson("{a: {$binary: 'NgAAAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"); // Base64 to Binary: 00110110... BSONObj match2 = fromjson("{a: {$binary: 'NgAjqwetkqwklEWRbWERKKJREtbq', $type: '00'}}"); // Base64 to Binary: 00110110... BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitPositionsSet)); ASSERT_OK(ballc.init("a", bitPositionsClear)); ASSERT_OK(banys.init("a", bitPositionsSet)); ASSERT_OK(banyc.init("a", bitPositionsClear)); ASSERT_EQ((size_t)4, balls.numBitPositions()); ASSERT_EQ((size_t)3, ballc.numBitPositions()); ASSERT_EQ((size_t)4, banys.numBitPositions()); ASSERT_EQ((size_t)3, banyc.numBitPositions()); ASSERT(balls.matchesSingleElement(match1["a"])); ASSERT(balls.matchesSingleElement(match2["a"])); ASSERT(ballc.matchesSingleElement(match1["a"])); ASSERT(ballc.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); } TEST(BitTestMatchExpression, MatchesBinary2) { BSONArray bas = BSON_ARRAY(21 << 22 << 8 << 9); BSONArray bac = BSON_ARRAY(20 << 23 << 612); std::vector bitPositionsSet = bsonArrayToBitPositions(bas); std::vector bitPositionsClear = bsonArrayToBitPositions(bac); BSONObj match1 = fromjson("{a: {$binary: 'AANgAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"); // Base64 to Binary: 00000000 00000011 01100000 BSONObj match2 = fromjson("{a: {$binary: 'JANgqwetkqwklEWRbWERKKJREtbq', $type: '00'}}"); // Base64 to Binary: ........ 00000011 01100000 BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitPositionsSet)); ASSERT_OK(ballc.init("a", bitPositionsClear)); ASSERT_OK(banys.init("a", bitPositionsSet)); ASSERT_OK(banyc.init("a", bitPositionsClear)); ASSERT_EQ((size_t)4, balls.numBitPositions()); ASSERT_EQ((size_t)3, ballc.numBitPositions()); ASSERT_EQ((size_t)4, banys.numBitPositions()); ASSERT_EQ((size_t)3, banyc.numBitPositions()); ASSERT(balls.matchesSingleElement(match1["a"])); ASSERT(balls.matchesSingleElement(match2["a"])); ASSERT(ballc.matchesSingleElement(match1["a"])); ASSERT(ballc.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); } TEST(BitTestMatchExpression, MatchesBinaryWithBitMask) { const char* bas = "\0\x03\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; const char* bac = "\0\xFC\x9F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; BSONObj match1 = fromjson("{a: {$binary: 'AANgAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"); // Base64 to Binary: 00000000 00000011 01100000 BSONObj match2 = fromjson("{a: {$binary: 'JANgAwetkqwklEWRbWERKKJREtbq', $type: '00'}}"); // Base64 to Binary: ........ 00000011 01100000 BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bas, 21)); ASSERT_OK(ballc.init("a", bac, 21)); ASSERT_OK(banys.init("a", bas, 21)); ASSERT_OK(banyc.init("a", bac, 21)); ASSERT(balls.matchesSingleElement(match1["a"])); ASSERT(balls.matchesSingleElement(match2["a"])); ASSERT(ballc.matchesSingleElement(match1["a"])); ASSERT(ballc.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); } TEST(BitTestMatchExpression, DoesNotMatchBinary1) { BSONArray bas = BSON_ARRAY(1 << 2 << 4 << 5 << 6); BSONArray bac = BSON_ARRAY(0 << 3 << 1); std::vector bitPositionsSet = bsonArrayToBitPositions(bas); std::vector bitPositionsClear = bsonArrayToBitPositions(bac); BSONObj match1 = fromjson("{a: {$binary: 'NgAAAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"); // Base64 to Binary: 00110110... BSONObj match2 = fromjson("{a: {$binary: 'NgAjqwetkqwklEWRbWERKKJREtbq', $type: '00'}}"); // Base64 to Binary: 00110110... BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitPositionsSet)); ASSERT_OK(ballc.init("a", bitPositionsClear)); ASSERT_OK(banys.init("a", bitPositionsSet)); ASSERT_OK(banyc.init("a", bitPositionsClear)); ASSERT_EQ((size_t)5, balls.numBitPositions()); ASSERT_EQ((size_t)3, ballc.numBitPositions()); ASSERT_EQ((size_t)5, banys.numBitPositions()); ASSERT_EQ((size_t)3, banyc.numBitPositions()); ASSERT(!balls.matchesSingleElement(match1["a"])); ASSERT(!balls.matchesSingleElement(match2["a"])); ASSERT(!ballc.matchesSingleElement(match1["a"])); ASSERT(!ballc.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); } TEST(BitTestMatchExpression, DoesNotMatchBinary2) { BSONArray bas = BSON_ARRAY(21 << 22 << 23 << 24 << 25); BSONArray bac = BSON_ARRAY(20 << 23 << 21); std::vector bitPositionsSet = bsonArrayToBitPositions(bas); std::vector bitPositionsClear = bsonArrayToBitPositions(bac); BSONObj match1 = fromjson("{a: {$binary: 'AANgAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"); // Base64 to Binary: 00000000 00000011 01100000 BSONObj match2 = fromjson("{a: {$binary: 'JANgqwetkqwklEWRbWERKKJREtbq', $type: '00'}}"); // Base64 to Binary: ........ 00000011 01100000 BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bitPositionsSet)); ASSERT_OK(ballc.init("a", bitPositionsClear)); ASSERT_OK(banys.init("a", bitPositionsSet)); ASSERT_OK(banyc.init("a", bitPositionsClear)); ASSERT_EQ((size_t)5, balls.numBitPositions()); ASSERT_EQ((size_t)3, ballc.numBitPositions()); ASSERT_EQ((size_t)5, banys.numBitPositions()); ASSERT_EQ((size_t)3, banyc.numBitPositions()); ASSERT(!balls.matchesSingleElement(match1["a"])); ASSERT(!balls.matchesSingleElement(match2["a"])); ASSERT(!ballc.matchesSingleElement(match1["a"])); ASSERT(!ballc.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); } TEST(BitTestMatchExpression, DoesNotMatchBinaryWithBitMask) { const char* bas = "\0\x03\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xFF"; const char* bac = "\0\xFD\x9F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xFF"; BSONObj match1 = fromjson("{a: {$binary: 'AANgAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"); // Base64 to Binary: 00000000 00000011 01100000 BSONObj match2 = fromjson("{a: {$binary: 'JANgAwetkqwklEWRbWERKKJREtbq', $type: '00'}}"); // Base64 to Binary: ........ 00000011 01100000 BitsAllSetMatchExpression balls; BitsAllClearMatchExpression ballc; BitsAnySetMatchExpression banys; BitsAnyClearMatchExpression banyc; ASSERT_OK(balls.init("a", bas, 22)); ASSERT_OK(ballc.init("a", bac, 22)); ASSERT_OK(banys.init("a", bas, 22)); ASSERT_OK(banyc.init("a", bac, 22)); ASSERT(!balls.matchesSingleElement(match1["a"])); ASSERT(!balls.matchesSingleElement(match2["a"])); ASSERT(!ballc.matchesSingleElement(match1["a"])); ASSERT(!ballc.matchesSingleElement(match2["a"])); ASSERT(banys.matchesSingleElement(match1["a"])); ASSERT(banys.matchesSingleElement(match2["a"])); ASSERT(banyc.matchesSingleElement(match1["a"])); ASSERT(banyc.matchesSingleElement(match2["a"])); } }