/** * 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" #include "mongo/unittest/death_test.h" namespace mongo { using std::string; TEST(ComparisonMatchExpression, ComparisonMatchExpressionsWithUnequalCollatorsAreUnequal) { BSONObj operand = BSON("a" << 5); CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kReverseString); EqualityMatchExpression eq1("a", operand["a"]); eq1.setCollator(&collator1); CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); EqualityMatchExpression eq2("a", operand["a"]); eq2.setCollator(&collator2); ASSERT(!eq1.equivalent(&eq2)); } TEST(ComparisonMatchExpression, ComparisonMatchExpressionsWithEqualCollatorsAreEqual) { BSONObj operand = BSON("a" << 5); CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kAlwaysEqual); EqualityMatchExpression eq1("a", operand["a"]); eq1.setCollator(&collator1); CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); EqualityMatchExpression eq2("a", operand["a"]); eq2.setCollator(&collator2); ASSERT(eq1.equivalent(&eq2)); } TEST(ComparisonMatchExpression, StringMatchingWithNullCollatorUsesBinaryComparison) { BSONObj operand = BSON("a" << "string"); EqualityMatchExpression eq("a", operand["a"]); ASSERT(!eq.matchesBSON(BSON("a" << "string2"), NULL)); } TEST(ComparisonMatchExpression, StringMatchingRespectsCollation) { BSONObj operand = BSON("a" << "string"); CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); EqualityMatchExpression eq("a", operand["a"]); eq.setCollator(&collator); 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("", operand["a"]); ASSERT(eq.matchesSingleElement(match.firstElement())); ASSERT(!eq.matchesSingleElement(notMatch.firstElement())); ASSERT(eq.equivalent(&eq)); } DEATH_TEST(EqOp, InvalidEooOperand, "Invariant failure _rhs") { BSONObj operand; EqualityMatchExpression eq("", operand.firstElement()); } TEST(EqOp, MatchesScalar) { BSONObj operand = BSON("a" << 5); EqualityMatchExpression eq("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("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("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("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("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("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("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(eq.matchesBSON(minKeyObj, NULL)); ASSERT(!eq.matchesBSON(maxKeyObj, NULL)); ASSERT(!eq.matchesBSON(numObj, NULL)); ASSERT(eq.matchesSingleElement(minKeyObj.firstElement())); ASSERT(!eq.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(!eq.matchesSingleElement(numObj.firstElement())); } TEST(EqOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); EqualityMatchExpression eq("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(!eq.matchesBSON(minKeyObj, NULL)); ASSERT(eq.matchesBSON(maxKeyObj, NULL)); ASSERT(!eq.matchesBSON(numObj, NULL)); ASSERT(!eq.matchesSingleElement(minKeyObj.firstElement())); ASSERT(eq.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(!eq.matchesSingleElement(numObj.firstElement())); } TEST(EqOp, MatchesFullArray) { BSONObj operand = BSON("a" << BSON_ARRAY(1 << 2)); EqualityMatchExpression eq("a", operand["a"]); 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("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("a", operand["a"]); 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(operandFirstElt.fieldName(), operandFirstElt); 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) { BSONObj operand = BSON("a" << 5 << "b" << 5 << "c" << 4); EqualityMatchExpression eq1("a", operand["a"]); EqualityMatchExpression eq2("a", operand["b"]); EqualityMatchExpression eq3("c", operand["c"]); ASSERT(eq1.equivalent(&eq1)); ASSERT(eq1.equivalent(&eq2)); ASSERT(!eq1.equivalent(&eq3)); } 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("", operand["$lt"]); ASSERT(lt.matchesSingleElement(match.firstElement())); ASSERT(!lt.matchesSingleElement(notMatch.firstElement())); ASSERT(!lt.matchesSingleElement(notMatchEqual.firstElement())); ASSERT(!lt.matchesSingleElement(notMatchWrongType.firstElement())); } DEATH_TEST(LtOp, InvalidEooOperand, "Invariant failure _rhs") { BSONObj operand; LTMatchExpression lt("", operand.firstElement()); } TEST(LtOp, MatchesScalar) { BSONObj operand = BSON("$lt" << 5); LTMatchExpression lt("a", operand["$lt"]); 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("", operand["$lt"]); ASSERT(lt.matchesBSON(BSON("" << 4.5), NULL)); ASSERT(!lt.matchesBSON(BSON("" << 6), NULL)); } TEST(LtOp, MatchesArrayValue) { BSONObj operand = BSON("$lt" << 5); LTMatchExpression lt("a", operand["$lt"]); 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("a", operand["$lt"]); 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("a", operand["$lt"]); 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("a.b", operand["$lt"]); 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("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(!lt.matchesBSON(minKeyObj, NULL)); ASSERT(!lt.matchesBSON(maxKeyObj, NULL)); ASSERT(!lt.matchesBSON(numObj, NULL)); ASSERT(!lt.matchesSingleElement(minKeyObj.firstElement())); ASSERT(!lt.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(!lt.matchesSingleElement(numObj.firstElement())); } TEST(LtOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); LTMatchExpression lt("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(lt.matchesBSON(minKeyObj, NULL)); ASSERT(!lt.matchesBSON(maxKeyObj, NULL)); ASSERT(lt.matchesBSON(numObj, NULL)); ASSERT(lt.matchesSingleElement(minKeyObj.firstElement())); ASSERT(!lt.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(lt.matchesSingleElement(numObj.firstElement())); } TEST(LtOp, ElemMatchKey) { BSONObj operand = BSON("$lt" << 5); LTMatchExpression lt("a", operand["$lt"]); 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(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("", operand["$lte"]); ASSERT(lte.matchesSingleElement(match.firstElement())); ASSERT(lte.matchesSingleElement(equalMatch.firstElement())); ASSERT(!lte.matchesSingleElement(notMatch.firstElement())); ASSERT(!lte.matchesSingleElement(notMatchWrongType.firstElement())); } DEATH_TEST(LteOp, InvalidEooOperand, "Invariant failure _rhs") { BSONObj operand; LTEMatchExpression lte("", operand.firstElement()); } TEST(LteOp, MatchesScalar) { BSONObj operand = BSON("$lte" << 5); LTEMatchExpression lte("a", operand["$lte"]); 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("a", operand["$lte"]); 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("a", operand["$lte"]); 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("a", operand["$lte"]); 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("a.b", operand["$lte"]); 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("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(lte.matchesBSON(minKeyObj, NULL)); ASSERT(!lte.matchesBSON(maxKeyObj, NULL)); ASSERT(!lte.matchesBSON(numObj, NULL)); ASSERT(lte.matchesSingleElement(minKeyObj.firstElement())); ASSERT(!lte.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(!lte.matchesSingleElement(numObj.firstElement())); } TEST(LteOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); LTEMatchExpression lte("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(lte.matchesBSON(minKeyObj, NULL)); ASSERT(lte.matchesBSON(maxKeyObj, NULL)); ASSERT(lte.matchesBSON(numObj, NULL)); ASSERT(lte.matchesSingleElement(minKeyObj.firstElement())); ASSERT(lte.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(lte.matchesSingleElement(numObj.firstElement())); } TEST(LteOp, ElemMatchKey) { BSONObj operand = BSON("$lte" << 5); LTEMatchExpression lte("a", operand["$lte"]); 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()); } DEATH_TEST(GtOp, InvalidEooOperand, "Invariant failure _rhs") { BSONObj operand; GTMatchExpression gt("", operand.firstElement()); } TEST(GtOp, MatchesScalar) { BSONObj operand = BSON("$gt" << 5); GTMatchExpression gt("a", operand["$gt"]); 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("a", operand["$gt"]); 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("a", operand["$gt"]); 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("a", operand["$gt"]); 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("a.b", operand["$gt"]); 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("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(!gt.matchesBSON(minKeyObj, NULL)); ASSERT(gt.matchesBSON(maxKeyObj, NULL)); ASSERT(gt.matchesBSON(numObj, NULL)); ASSERT(!gt.matchesSingleElement(minKeyObj.firstElement())); ASSERT(gt.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(gt.matchesSingleElement(numObj.firstElement())); } TEST(GtOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); GTMatchExpression gt("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(!gt.matchesBSON(minKeyObj, NULL)); ASSERT(!gt.matchesBSON(maxKeyObj, NULL)); ASSERT(!gt.matchesBSON(numObj, NULL)); ASSERT(!gt.matchesSingleElement(minKeyObj.firstElement())); ASSERT(!gt.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(!gt.matchesSingleElement(numObj.firstElement())); } TEST(GtOp, ElemMatchKey) { BSONObj operand = BSON("$gt" << 5); GTMatchExpression gt("a", operand["$gt"]); 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(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("", operand["$gte"]); ASSERT(gte.matchesSingleElement(match.firstElement())); ASSERT(gte.matchesSingleElement(equalMatch.firstElement())); ASSERT(!gte.matchesSingleElement(notMatch.firstElement())); ASSERT(!gte.matchesSingleElement(notMatchWrongType.firstElement())); } DEATH_TEST(GteOp, InvalidEooOperand, "Invariant failure _rhs") { BSONObj operand; GTEMatchExpression gte("", operand.firstElement()); } TEST(GteOp, MatchesScalar) { BSONObj operand = BSON("$gte" << 5); GTEMatchExpression gte("a", operand["$gte"]); 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("a", operand["$gte"]); 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("a", operand["$gte"]); 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("a", operand["$gte"]); 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("a.b", operand["$gte"]); 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("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(gte.matchesBSON(minKeyObj, NULL)); ASSERT(gte.matchesBSON(maxKeyObj, NULL)); ASSERT(gte.matchesBSON(numObj, NULL)); ASSERT(gte.matchesSingleElement(minKeyObj.firstElement())); ASSERT(gte.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(gte.matchesSingleElement(numObj.firstElement())); } TEST(GteOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); GTEMatchExpression gte("a", operand["a"]); BSONObj minKeyObj = BSON("a" << MinKey); BSONObj maxKeyObj = BSON("a" << MaxKey); BSONObj numObj = BSON("a" << 4); ASSERT(!gte.matchesBSON(minKeyObj, NULL)); ASSERT(gte.matchesBSON(maxKeyObj, NULL)); ASSERT(!gte.matchesBSON(numObj, NULL)); ASSERT(!gte.matchesSingleElement(minKeyObj.firstElement())); ASSERT(gte.matchesSingleElement(maxKeyObj.firstElement())); ASSERT(!gte.matchesSingleElement(numObj.firstElement())); } TEST(GteOp, ElemMatchKey) { BSONObj operand = BSON("$gte" << 5); GTEMatchExpression gte("a", operand["$gte"]); 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("", "b", ""); ASSERT(regex.matchesSingleElement(match.firstElement())); ASSERT(!regex.matchesSingleElement(notMatch.firstElement())); } TEST(RegexMatchExpression, TooLargePattern) { string tooLargePattern(50 * 1000, 'z'); ASSERT_THROWS_CODE(RegexMatchExpression regex("a", tooLargePattern, ""), AssertionException, ErrorCodes::BadValue); } TEST(RegexMatchExpression, MatchesElementSimplePrefix) { BSONObj match = BSON("x" << "abc"); BSONObj notMatch = BSON("x" << "adz"); RegexMatchExpression regex("", "^ab", ""); 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("", "abc", ""); 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("", "abc", "i"); 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("", "^a", ""); 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("", "^a", "m"); 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("", "a b", ""); 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("", "a b", "x"); 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("", "a.b", ""); 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("", "a.b", "s"); 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("", "^a.b", "ms"); 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("", "yz", "i"); 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("", "yz", ""); 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("", "1", ""); ASSERT(!regex.matchesSingleElement(notMatchInt.firstElement())); ASSERT(!regex.matchesSingleElement(notMatchBool.firstElement())); } TEST(RegexMatchExpression, MatchesElementUtf8) { BSONObj multiByteCharacter = BSON("x" << "\xc2\xa5"); RegexMatchExpression regex("", "^.$", ""); ASSERT(regex.matchesSingleElement(multiByteCharacter.firstElement())); } TEST(RegexMatchExpression, MatchesScalar) { RegexMatchExpression regex("a", "b", ""); ASSERT(regex.matchesBSON(BSON("a" << "b"), NULL)); ASSERT(!regex.matchesBSON(BSON("a" << "c"), NULL)); } TEST(RegexMatchExpression, MatchesArrayValue) { RegexMatchExpression regex("a", "b", ""); 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("a", "b", ""); ASSERT(!regex.matchesBSON(BSONObj(), NULL)); ASSERT(!regex.matchesBSON(BSON("a" << BSONNULL), NULL)); } TEST(RegexMatchExpression, ElemMatchKey) { RegexMatchExpression regex("a", "b", ""); 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("a", "b", ""); RegexMatchExpression r2("a", "b", "x"); RegexMatchExpression r3("a", "c", ""); RegexMatchExpression r4("b", "b", ""); ASSERT(r1.equivalent(&r1)); ASSERT(!r1.equivalent(&r2)); ASSERT(!r1.equivalent(&r3)); ASSERT(!r1.equivalent(&r4)); } TEST(RegexMatchExpression, RegexCannotContainEmbeddedNullByte) { { const auto embeddedNull = "a\0b"_sd; ASSERT_THROWS_CODE(RegexMatchExpression regex("path", embeddedNull, ""), AssertionException, ErrorCodes::BadValue); } { const auto singleNullByte = "\0"_sd; ASSERT_THROWS_CODE(RegexMatchExpression regex("path", singleNullByte, ""), AssertionException, ErrorCodes::BadValue); } { const auto leadingNullByte = "\0bbbb"_sd; ASSERT_THROWS_CODE(RegexMatchExpression regex("path", leadingNullByte, ""), AssertionException, ErrorCodes::BadValue); } { const auto trailingNullByte = "bbbb\0"_sd; ASSERT_THROWS_CODE(RegexMatchExpression regex("path", trailingNullByte, ""), AssertionException, ErrorCodes::BadValue); } } TEST(RegexMatchExpression, RegexOptionsStringCannotContainEmbeddedNullByte) { { const auto embeddedNull = "a\0b"_sd; ASSERT_THROWS_CODE(RegexMatchExpression regex("path", "pattern", embeddedNull), AssertionException, ErrorCodes::BadValue); } { const auto singleNullByte = "\0"_sd; ASSERT_THROWS_CODE(RegexMatchExpression regex("path", "pattern", singleNullByte), AssertionException, ErrorCodes::BadValue); } { const auto leadingNullByte = "\0bbbb"_sd; ASSERT_THROWS_CODE(RegexMatchExpression regex("path", "pattern", leadingNullByte), AssertionException, ErrorCodes::BadValue); } { const auto trailingNullByte = "bbbb\0"_sd; ASSERT_THROWS_CODE(RegexMatchExpression regex("path", "pattern", trailingNullByte), AssertionException, ErrorCodes::BadValue); } } TEST(RegexMatchExpression, MalformedRegexAcceptedButMatchesNothing) { RegexMatchExpression regex("a", "[(*ACCEPT)", ""); ASSERT_FALSE(regex.matchesBSON(BSON("a" << ""))); ASSERT_FALSE(regex.matchesBSON(BSON("a" << "["))); } TEST(RegexMatchExpression, RegexAcceptsUCPOption) { RegexMatchExpression regex("a", "(*UCP)(\\w|\u304C)", ""); ASSERT(regex.matchesBSON(BSON("a" << "k"))); ASSERT(regex.matchesBSON(BSON("a" << "\u304B"))); ASSERT(regex.matchesBSON(BSON("a" << "\u304C"))); } 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("", 3, 1); 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) { ASSERT_THROWS_CODE(ModMatchExpression mod("", 0, 1), AssertionException, ErrorCodes::BadValue); } TEST(ModMatchExpression, MatchesScalar) { ModMatchExpression mod("a", 5, 2); ASSERT(mod.matchesBSON(BSON("a" << 7.0), NULL)); ASSERT(!mod.matchesBSON(BSON("a" << 4), NULL)); } TEST(ModMatchExpression, MatchesArrayValue) { ModMatchExpression mod("a", 5, 2); 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("a", 5, 2); ASSERT(!mod.matchesBSON(BSONObj(), NULL)); ASSERT(!mod.matchesBSON(BSON("a" << BSONNULL), NULL)); } TEST(ModMatchExpression, ElemMatchKey) { ModMatchExpression mod("a", 5, 2); 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("a", 1, 2); ModMatchExpression m2("a", 2, 2); ModMatchExpression m3("a", 1, 1); ModMatchExpression m4("b", 1, 2); ASSERT(m1.equivalent(&m1)); ASSERT(!m1.equivalent(&m2)); ASSERT(!m1.equivalent(&m3)); ASSERT(!m1.equivalent(&m4)); } TEST(ExistsMatchExpression, MatchesElement) { BSONObj existsInt = BSON("a" << 5); BSONObj existsNull = BSON("a" << BSONNULL); BSONObj doesntExist = BSONObj(); ExistsMatchExpression exists(""); 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.matchesSingleElement(exists.firstElement())); ASSERT(!existsTrueValue.matchesSingleElement(missing.firstElement())); } TEST(ExistsMatchExpression, MatchesScalar) { ExistsMatchExpression exists("a"); 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("a"); ASSERT(exists.matchesBSON(BSON("a" << BSON_ARRAY(4 << 5.5)), NULL)); } TEST(ExistsMatchExpression, ElemMatchKey) { ExistsMatchExpression exists("a.b"); 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("a"); ExistsMatchExpression e2("b"); ASSERT(e1.equivalent(&e1)); ASSERT(!e1.equivalent(&e2)); } TEST(InMatchExpression, MatchesElementSingle) { BSONArray operand = BSON_ARRAY(1); BSONObj match = BSON("a" << 1); BSONObj notMatch = BSON("a" << 2); InMatchExpression in(""); std::vector equalities{operand.firstElement()}; ASSERT_OK(in.setEqualities(std::move(equalities))); ASSERT(in.matchesSingleElement(match["a"])); ASSERT(!in.matchesSingleElement(notMatch["a"])); } TEST(InMatchExpression, MatchesEmpty) { InMatchExpression in("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(""); std::vector equalities{operand[0], operand[1], operand[2], operand[3]}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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("a"); std::vector equalities{operand.firstElement()}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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("a"); std::vector equalities{operand.firstElement()}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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("a"); std::vector equalities{operand.firstElement()}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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("a"); std::vector equalities{operand.firstElement()}; ASSERT_NOT_OK(in.setEqualities(std::move(equalities))); } TEST(InMatchExpression, MatchesMinKey) { BSONObj operand = BSON_ARRAY(MinKey); InMatchExpression in("a"); std::vector equalities{operand.firstElement()}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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("a"); std::vector equalities{operand.firstElement()}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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("a"); std::vector equalities{operand[0], operand[1], operand[2]}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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("a"); std::vector equalities{operand[0], operand[1]}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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(""); std::vector equalities{obj.firstElement()}; ASSERT_OK(eq1.setEqualities(std::move(equalities))); 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); std::vector equalities1{obj1.firstElement()}; ASSERT_OK(eq1.setEqualities(std::move(equalities1))); std::vector equalities2{obj2.firstElement()}; ASSERT_OK(eq2.setEqualities(std::move(equalities2))); 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); std::vector equalities1{obj1.firstElement()}; ASSERT_OK(eq1.setEqualities(std::move(equalities1))); std::vector equalities2{obj2.firstElement()}; ASSERT_OK(eq2.setEqualities(std::move(equalities2))); ASSERT(!eq1.equivalent(&eq2)); } TEST(InMatchExpression, StringMatchingWithNullCollatorUsesBinaryComparison) { BSONArray operand = BSON_ARRAY("string"); BSONObj notMatch = BSON("a" << "string2"); InMatchExpression in(""); std::vector equalities{operand.firstElement()}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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); std::vector equalities{operand.firstElement()}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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); std::vector equalities{obj1.firstElement(), obj2.firstElement()}; ASSERT_OK(in.setEqualities(std::move(equalities))); 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("a", bitPositions); BitsAllClearMatchExpression ballc("a", bitPositions); BitsAnySetMatchExpression banys("a", bitPositions); BitsAnyClearMatchExpression banyc("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("a", bitMask); BitsAllClearMatchExpression ballc("a", bitMask); BitsAnySetMatchExpression banys("a", bitMask); BitsAnyClearMatchExpression banyc("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("a", bitMaskSet, 4); BitsAllClearMatchExpression ballc("a", bitMaskClear, 9); BitsAnySetMatchExpression banys("a", bitMaskSet, 4); BitsAnyClearMatchExpression banyc("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("a", bitPositions); BitsAllClearMatchExpression ballc("a", bitPositions); BitsAnySetMatchExpression banys("a", bitPositions); BitsAnyClearMatchExpression banyc("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("a", bitPositionsSet); BitsAllClearMatchExpression ballc("a", bitPositionsClear); BitsAnySetMatchExpression banys("a", bitPositionsSet); BitsAnyClearMatchExpression banyc("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("a", bitPositionsSet); BitsAllClearMatchExpression ballc("a", bitPositionsClear); BitsAnySetMatchExpression banys("a", bitPositionsSet); BitsAnyClearMatchExpression banyc("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("a", bitMaskSet); BitsAllClearMatchExpression ballc("a", bitMaskClear); BitsAnySetMatchExpression banys("a", bitMaskSet); BitsAnyClearMatchExpression banyc("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("a", bitMaskSet); BitsAllClearMatchExpression ballc("a", bitMaskClear); BitsAnySetMatchExpression banys("a", bitMaskSet); BitsAnyClearMatchExpression banyc("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("a", bitPositionsSet); BitsAllClearMatchExpression ballc("a", bitPositionsClear); BitsAnySetMatchExpression banys("a", bitPositionsSet); BitsAnyClearMatchExpression banyc("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("a", bitMaskSet); BitsAllClearMatchExpression ballc("a", bitMaskClear); BitsAnySetMatchExpression banys("a", bitMaskSet); BitsAnyClearMatchExpression banyc("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("a", bitPositionsSet); BitsAllClearMatchExpression ballc("a", bitPositionsClear); BitsAnySetMatchExpression banys("a", bitPositionsSet); BitsAnyClearMatchExpression banyc("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("a", bitPositionsSet); BitsAllClearMatchExpression ballc("a", bitPositionsClear); BitsAnySetMatchExpression banys("a", bitPositionsSet); BitsAnyClearMatchExpression banyc("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("a", bas, 21); BitsAllClearMatchExpression ballc("a", bac, 21); BitsAnySetMatchExpression banys("a", bas, 21); BitsAnyClearMatchExpression banyc("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("a", bitPositionsSet); BitsAllClearMatchExpression ballc("a", bitPositionsClear); BitsAnySetMatchExpression banys("a", bitPositionsSet); BitsAnyClearMatchExpression banyc("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("a", bitPositionsSet); BitsAllClearMatchExpression ballc("a", bitPositionsClear); BitsAnySetMatchExpression banys("a", bitPositionsSet); BitsAnyClearMatchExpression banyc("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("a", bas, 22); BitsAllClearMatchExpression ballc("a", bac, 22); BitsAnySetMatchExpression banys("a", bas, 22); BitsAnyClearMatchExpression banyc("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"])); } }