// matchertests.cpp : matcher unit tests
//
/**
* Copyright (C) 2008 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.
*/
#include
#include "mongo/db/db_raii.h"
#include "mongo/db/json.h"
#include "mongo/db/matcher/extensions_callback_disallow_extensions.h"
#include "mongo/db/matcher/extensions_callback_real.h"
#include "mongo/db/matcher/matcher.h"
#include "mongo/db/operation_context_impl.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/util/timer.h"
namespace MatcherTests {
using std::cout;
using std::endl;
using std::string;
class CollectionBase {
public:
CollectionBase() {}
virtual ~CollectionBase() {}
};
template
class Basic {
public:
void run() {
BSONObj query = fromjson("{\"a\":\"b\"}");
M m(query, ExtensionsCallbackDisallowExtensions());
ASSERT(m.matches(fromjson("{\"a\":\"b\"}")));
}
};
template
class DoubleEqual {
public:
void run() {
BSONObj query = fromjson("{\"a\":5}");
M m(query, ExtensionsCallbackDisallowExtensions());
ASSERT(m.matches(fromjson("{\"a\":5}")));
}
};
template
class MixedNumericEqual {
public:
void run() {
BSONObjBuilder query;
query.append("a", 5);
M m(query.done(), ExtensionsCallbackDisallowExtensions());
ASSERT(m.matches(fromjson("{\"a\":5}")));
}
};
template
class MixedNumericGt {
public:
void run() {
BSONObj query = fromjson("{\"a\":{\"$gt\":4}}");
M m(query, ExtensionsCallbackDisallowExtensions());
BSONObjBuilder b;
b.append("a", 5);
ASSERT(m.matches(b.done()));
}
};
template
class MixedNumericIN {
public:
void run() {
BSONObj query = fromjson("{ a : { $in : [4,6] } }");
ASSERT_EQUALS(4, query["a"].embeddedObject()["$in"].embeddedObject()["0"].number());
ASSERT_EQUALS(NumberInt, query["a"].embeddedObject()["$in"].embeddedObject()["0"].type());
M m(query, ExtensionsCallbackDisallowExtensions());
{
BSONObjBuilder b;
b.append("a", 4.0);
ASSERT(m.matches(b.done()));
}
{
BSONObjBuilder b;
b.append("a", 5);
ASSERT(!m.matches(b.done()));
}
{
BSONObjBuilder b;
b.append("a", 4);
ASSERT(m.matches(b.done()));
}
}
};
template
class MixedNumericEmbedded {
public:
void run() {
M m(BSON("a" << BSON("x" << 1)), ExtensionsCallbackDisallowExtensions());
ASSERT(m.matches(BSON("a" << BSON("x" << 1))));
ASSERT(m.matches(BSON("a" << BSON("x" << 1.0))));
}
};
template
class Size {
public:
void run() {
M m(fromjson("{a:{$size:4}}"), ExtensionsCallbackDisallowExtensions());
ASSERT(m.matches(fromjson("{a:[1,2,3,4]}")));
ASSERT(!m.matches(fromjson("{a:[1,2,3]}")));
ASSERT(!m.matches(fromjson("{a:[1,2,3,'a','b']}")));
ASSERT(!m.matches(fromjson("{a:[[1,2,3,4]]}")));
}
};
template
class WithinBox {
public:
void run() {
M m(fromjson("{loc:{$within:{$box:[{x: 4, y:4},[6,6]]}}}"),
ExtensionsCallbackDisallowExtensions());
ASSERT(!m.matches(fromjson("{loc: [3,4]}")));
ASSERT(m.matches(fromjson("{loc: [4,4]}")));
ASSERT(m.matches(fromjson("{loc: [5,5]}")));
ASSERT(m.matches(fromjson("{loc: [5,5.1]}")));
ASSERT(m.matches(fromjson("{loc: {x: 5, y:5.1}}")));
}
};
template
class WithinPolygon {
public:
void run() {
M m(fromjson("{loc:{$within:{$polygon:[{x:0,y:0},[0,5],[5,5],[5,0]]}}}"),
ExtensionsCallbackDisallowExtensions());
ASSERT(m.matches(fromjson("{loc: [3,4]}")));
ASSERT(m.matches(fromjson("{loc: [4,4]}")));
ASSERT(m.matches(fromjson("{loc: {x:5,y:5}}")));
ASSERT(!m.matches(fromjson("{loc: [5,5.1]}")));
ASSERT(!m.matches(fromjson("{loc: {}}")));
}
};
template
class WithinCenter {
public:
void run() {
M m(fromjson("{loc:{$within:{$center:[{x:30,y:30},10]}}}"),
ExtensionsCallbackDisallowExtensions());
ASSERT(!m.matches(fromjson("{loc: [3,4]}")));
ASSERT(m.matches(fromjson("{loc: {x:30,y:30}}")));
ASSERT(m.matches(fromjson("{loc: [20,30]}")));
ASSERT(m.matches(fromjson("{loc: [30,20]}")));
ASSERT(m.matches(fromjson("{loc: [40,30]}")));
ASSERT(m.matches(fromjson("{loc: [30,40]}")));
ASSERT(!m.matches(fromjson("{loc: [31,40]}")));
}
};
/** Test that MatchDetails::elemMatchKey() is set correctly after a match. */
template
class ElemMatchKey {
public:
void run() {
M matcher(BSON("a.b" << 1), ExtensionsCallbackDisallowExtensions());
MatchDetails details;
details.requestElemMatchKey();
ASSERT(!details.hasElemMatchKey());
ASSERT(matcher.matches(fromjson("{ a:[ { b:1 } ] }"), &details));
// The '0' entry of the 'a' array is matched.
ASSERT(details.hasElemMatchKey());
ASSERT_EQUALS(string("0"), details.elemMatchKey());
}
};
template
class WhereSimple1 {
public:
void run() {
OperationContextImpl txn;
const NamespaceString nss("unittests.matchertests");
AutoGetCollectionForRead ctx(&txn, nss);
M m(BSON("$where"
<< "function(){ return this.a == 1; }"),
ExtensionsCallbackReal(&txn, &nss));
ASSERT(m.matches(BSON("a" << 1)));
ASSERT(!m.matches(BSON("a" << 2)));
}
};
template
class TimingBase {
public:
long dotime(const BSONObj& patt, const BSONObj& obj) {
M m(patt, ExtensionsCallbackDisallowExtensions());
Timer t;
for (int i = 0; i < 900000; i++) {
if (!m.matches(obj)) {
ASSERT(0);
}
}
return t.millis();
}
};
template
class AllTiming : public TimingBase {
public:
void run() {
long normal = TimingBase::dotime(BSON("x" << 5), BSON("x" << 5));
long all =
TimingBase::dotime(BSON("x" << BSON("$all" << BSON_ARRAY(5))), BSON("x" << 5));
cout << "AllTiming " << demangleName(typeid(M)) << " normal: " << normal << " all: " << all
<< endl;
}
};
class All : public Suite {
public:
All() : Suite("matcher") {}
#define ADD_BOTH(TEST) add>();
void setupTests() {
ADD_BOTH(Basic);
ADD_BOTH(DoubleEqual);
ADD_BOTH(MixedNumericEqual);
ADD_BOTH(MixedNumericGt);
ADD_BOTH(MixedNumericIN);
ADD_BOTH(Size);
ADD_BOTH(MixedNumericEmbedded);
ADD_BOTH(ElemMatchKey);
ADD_BOTH(WhereSimple1);
ADD_BOTH(AllTiming);
ADD_BOTH(WithinBox);
ADD_BOTH(WithinCenter);
ADD_BOTH(WithinPolygon);
}
};
SuiteInstance dball;
} // namespace MatcherTests