1 /* api_nodb.cc: tests which don't use any of the backends
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2015 Olly Betts
6 * Copyright 2006 Lemur Consulting Ltd
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
31 #include "testsuite.h"
32 #include "testutils.h"
41 DEFINE_TESTCASE(trivial1
, !backend
) {
45 // tests that get_query_terms() returns the terms in the right order
46 DEFINE_TESTCASE(getqterms1
, !backend
) {
47 list
<string
> answers_list
;
48 answers_list
.push_back("one");
49 answers_list
.push_back("two");
50 answers_list
.push_back("three");
51 answers_list
.push_back("four");
53 Xapian::Query
myquery(Xapian::Query::OP_OR
,
54 Xapian::Query(Xapian::Query::OP_AND
,
55 Xapian::Query("one", 1, 1),
56 Xapian::Query("three", 1, 3)),
57 Xapian::Query(Xapian::Query::OP_OR
,
58 Xapian::Query("four", 1, 4),
59 Xapian::Query("two", 1, 2)));
63 Xapian::TermIterator t
;
64 for (t
= myquery
.get_terms_begin(); t
!= myquery
.get_terms_end(); ++t
)
67 TEST(list1
== answers_list
);
68 list
<string
> list2(myquery
.get_terms_begin(), myquery
.get_terms_end());
69 TEST(list2
== answers_list
);
73 // tests that get_query_terms() doesn't SEGV on an empty query
74 // (regression test for bug in 0.9.0)
75 DEFINE_TESTCASE(getqterms2
, !backend
) {
76 Xapian::Query empty_query
;
77 TEST_EQUAL(empty_query
.get_terms_begin(), empty_query
.get_terms_end());
81 // tests that empty queries work correctly
82 DEFINE_TESTCASE(emptyquery2
, !backend
) {
83 // test that Query::empty() is true for an empty query.
84 TEST(Xapian::Query().empty());
85 // test that an empty query has length 0
86 TEST(Xapian::Query().get_length() == 0);
87 vector
<Xapian::Query
> v
;
88 TEST(Xapian::Query(Xapian::Query::OP_OR
, v
.begin(), v
.end()).empty());
89 TEST(Xapian::Query(Xapian::Query::OP_OR
, v
.begin(), v
.end()).get_length() == 0);
93 /// Regression test for behaviour for an empty query with AND_NOT.
94 DEFINE_TESTCASE(emptyquery3
, !backend
) {
95 static const Xapian::Query::op ops
[] = {
96 Xapian::Query::OP_AND
,
98 Xapian::Query::OP_XOR
,
99 Xapian::Query::OP_AND_MAYBE
,
100 Xapian::Query::OP_AND_NOT
103 for (size_t i
= 0; i
< sizeof(ops
) / sizeof(ops
[0]); ++i
) {
104 tout
<< "Testing op #" << i
<< endl
;
106 Xapian::Query
q("test");
107 Xapian::Query
qcombine(ops
[i
], empty
, q
);
108 tout
<< qcombine
.get_description() << endl
;
109 Xapian::Query
qcombine2(ops
[i
], q
, empty
);
110 tout
<< qcombine2
.get_description() << endl
;
111 Xapian::Query
qcombine3(ops
[i
], empty
, empty
);
112 tout
<< qcombine3
.get_description() << endl
;
118 // tests that query lengths are calculated correctly
119 DEFINE_TESTCASE(querylen1
, !backend
) {
120 // test that a simple query has the right length
121 Xapian::Query myquery
;
122 myquery
= Xapian::Query(Xapian::Query::OP_OR
,
123 Xapian::Query("foo"),
124 Xapian::Query("bar"));
125 myquery
= Xapian::Query(Xapian::Query::OP_AND
,
127 Xapian::Query(Xapian::Query::OP_OR
,
128 Xapian::Query("wibble"),
129 Xapian::Query("spoon")));
131 TEST_EQUAL(myquery
.get_length(), 4);
132 TEST(!myquery
.empty());
136 // tests that query lengths are calculated correctly
137 DEFINE_TESTCASE(querylen2
, !backend
) {
138 // test with an even bigger and strange query
144 Xapian::Query queries
[3] = {
145 Xapian::Query("wibble"),
146 Xapian::Query("wobble"),
147 Xapian::Query(Xapian::Query::OP_OR
, string("jelly"), string("belly"))
150 Xapian::Query myquery
;
151 vector
<string
> v1(terms
, terms
+ 3);
152 vector
<Xapian::Query
> v2(queries
, queries
+ 3);
153 vector
<Xapian::Query
*> v3
;
154 Xapian::Query
query1(Xapian::Query::OP_AND
, string("ball"), string("club"));
155 Xapian::Query
query2("ring");
156 v3
.push_back(&query1
);
157 v3
.push_back(&query2
);
159 Xapian::Query myq1
= Xapian::Query(Xapian::Query::OP_AND
, v1
.begin(), v1
.end());
160 tout
<< "myq1=" << myq1
<< "\n";
161 TEST_EQUAL(myq1
.get_length(), 3);
163 Xapian::Query myq2_1
= Xapian::Query(Xapian::Query::OP_OR
, v2
.begin(), v2
.end());
164 tout
<< "myq2_1=" << myq2_1
<< "\n";
165 TEST_EQUAL(myq2_1
.get_length(), 4);
167 Xapian::Query myq2_2
= Xapian::Query(Xapian::Query::OP_AND
, v3
.begin(), v3
.end());
168 tout
<< "myq2_2=" << myq2_2
<< "\n";
169 TEST_EQUAL(myq2_2
.get_length(), 3);
171 Xapian::Query myq2
= Xapian::Query(Xapian::Query::OP_OR
, myq2_1
, myq2_2
);
172 tout
<< "myq2=" << myq2
<< "\n";
173 TEST_EQUAL(myq2
.get_length(), 7);
175 myquery
= Xapian::Query(Xapian::Query::OP_OR
, myq1
, myq2
);
176 tout
<< "myquery=" << myquery
<< "\n";
177 TEST_EQUAL(myquery
.get_length(), 10);
182 // tests that queries validate correctly
183 DEFINE_TESTCASE(queryvalid1
, !backend
) {
184 Xapian::Query
q2(Xapian::Query::OP_XOR
, Xapian::Query("foo"), Xapian::Query("bar"));
185 tout
<< "XOR (\"foo\", \"bar\") checked" << endl
;
189 /** Check we no longer flatten subqueries combined with the same operator.
191 * Prior to 1.3.0 we did flatten these, but it's simpler to just handle this
192 * when we convert the query to a PostList tree, and that works better with
193 * Query objects being immutable.
195 DEFINE_TESTCASE(dontflattensubqueries1
, !backend
) {
196 Xapian::Query queries1
[3] = {
197 Xapian::Query("wibble"),
198 Xapian::Query("wobble"),
199 Xapian::Query(Xapian::Query::OP_OR
, string("jelly"), string("belly"))
202 Xapian::Query queries2
[3] = {
203 Xapian::Query(Xapian::Query::OP_AND
, string("jelly"), string("belly")),
204 Xapian::Query("wibble"),
205 Xapian::Query("wobble")
208 vector
<Xapian::Query
> vec1(queries1
, queries1
+ 3);
209 Xapian::Query
myquery1(Xapian::Query::OP_OR
, vec1
.begin(), vec1
.end());
210 TEST_EQUAL(myquery1
.get_description(),
211 "Query((wibble OR wobble OR (jelly OR belly)))");
213 vector
<Xapian::Query
> vec2(queries2
, queries2
+ 3);
214 Xapian::Query
myquery2(Xapian::Query::OP_AND
, vec2
.begin(), vec2
.end());
215 TEST_EQUAL(myquery2
.get_description(),
216 "Query(((jelly AND belly) AND wibble AND wobble))");
221 // test behaviour when creating a query from an empty vector
222 DEFINE_TESTCASE(emptyquerypart1
, !backend
) {
223 vector
<string
> emptyterms
;
224 Xapian::Query
query(Xapian::Query::OP_OR
, emptyterms
.begin(), emptyterms
.end());
225 TEST(Xapian::Query(Xapian::Query::OP_AND
, query
, Xapian::Query("x")).empty());
226 TEST(Xapian::Query(Xapian::Query::OP_AND
, query
, Xapian::Query("x")).get_length() == 0);
227 TEST(!Xapian::Query(Xapian::Query::OP_OR
, query
, Xapian::Query("x")).empty());
228 TEST(Xapian::Query(Xapian::Query::OP_OR
, query
, Xapian::Query("x")).get_length() == 1);
232 DEFINE_TESTCASE(stemlangs1
, !backend
) {
233 string langs
= Xapian::Stem::get_available_languages();
234 tout
<< "available languages '" << langs
<< "'" << endl
;
235 TEST(!langs
.empty());
237 // Also test the language codes and none.
238 langs
+= " da nl en fi fr de hu it no pt ro ru es sv tr none";
240 string::size_type i
= 0;
242 string::size_type spc
= langs
.find(' ', i
);
243 // The only spaces in langs should be a single one between each pair
244 // of language names.
245 TEST_NOT_EQUAL(i
, spc
);
247 // Try making a stemmer for this language. We should be able to create
248 // it without an exception being thrown.
249 string
language(langs
, i
, spc
- i
);
250 tout
<< "checking language code '" << language
<< "' works" << endl
;
251 Xapian::Stem
stemmer(language
);
252 if (language
.size() > 2) {
253 string
expected("Xapian::Stem(");
254 expected
+= language
;
256 TEST_EQUAL(stemmer
.get_description(), expected
);
259 if (spc
== string::npos
) break;
263 // Stem("") should give an object which doesn't change any input.
264 Xapian::Stem stem_nothing
= Xapian::Stem("");
265 TEST_EQUAL(stem_nothing
.get_description(), "Xapian::Stem(none)");
270 // Some simple tests of the built in weighting schemes.
271 DEFINE_TESTCASE(weight1
, !backend
) {
274 Xapian::BoolWeight boolweight
;
275 TEST_EQUAL(boolweight
.name(), "Xapian::BoolWeight");
276 wt
= Xapian::BoolWeight().unserialise(boolweight
.serialise());
277 TEST_EQUAL(boolweight
.serialise(), wt
->serialise());
280 Xapian::TradWeight tradweight_dflt
;
281 Xapian::TradWeight
tradweight(1.0);
282 TEST_EQUAL(tradweight
.name(), "Xapian::TradWeight");
283 TEST_EQUAL(tradweight_dflt
.serialise(), tradweight
.serialise());
284 wt
= Xapian::TradWeight().unserialise(tradweight
.serialise());
285 TEST_EQUAL(tradweight
.serialise(), wt
->serialise());
288 Xapian::TradWeight
tradweight2(2.0);
289 TEST_NOT_EQUAL(tradweight
.serialise(), tradweight2
.serialise());
291 Xapian::BM25Weight bm25weight_dflt
;
292 Xapian::BM25Weight
bm25weight(1, 0, 1, 0.5, 0.5);
293 TEST_EQUAL(bm25weight
.name(), "Xapian::BM25Weight");
294 TEST_EQUAL(bm25weight_dflt
.serialise(), bm25weight
.serialise());
295 wt
= Xapian::BM25Weight().unserialise(bm25weight
.serialise());
296 TEST_EQUAL(bm25weight
.serialise(), wt
->serialise());
299 Xapian::BM25Weight
bm25weight2(1, 0.5, 1, 0.5, 0.5);
300 TEST_NOT_EQUAL(bm25weight
.serialise(), bm25weight2
.serialise());
302 Xapian::TfIdfWeight tfidfweight_dflt
;
303 Xapian::TfIdfWeight
tfidfweight("ntn");
304 TEST_EQUAL(tfidfweight
.name(), "Xapian::TfIdfWeight");
305 TEST_EQUAL(tfidfweight_dflt
.serialise(), tfidfweight
.serialise());
306 wt
= Xapian::TfIdfWeight().unserialise(tfidfweight
.serialise());
307 TEST_EQUAL(tfidfweight
.serialise(), wt
->serialise());
310 Xapian::TfIdfWeight
tfidfweight2("bpn");
311 TEST_NOT_EQUAL(tfidfweight
.serialise(), tfidfweight2
.serialise());
313 Xapian::InL2Weight inl2weight_dflt
;
314 Xapian::InL2Weight
inl2weight(1.0);
315 TEST_EQUAL(inl2weight
.name(), "Xapian::InL2Weight");
316 TEST_EQUAL(inl2weight_dflt
.serialise(), inl2weight
.serialise());
317 wt
= Xapian::InL2Weight().unserialise(inl2weight
.serialise());
318 TEST_EQUAL(inl2weight
.serialise(), wt
->serialise());
321 Xapian::InL2Weight
inl2weight2(2.0);
322 TEST_NOT_EQUAL(inl2weight
.serialise(), inl2weight2
.serialise());
324 Xapian::IfB2Weight ifb2weight_dflt
;
325 Xapian::IfB2Weight
ifb2weight(1.0);
326 TEST_EQUAL(ifb2weight
.name(), "Xapian::IfB2Weight");
327 TEST_EQUAL(ifb2weight_dflt
.serialise(), ifb2weight
.serialise());
328 wt
= Xapian::IfB2Weight().unserialise(ifb2weight
.serialise());
329 TEST_EQUAL(ifb2weight
.serialise(), wt
->serialise());
332 Xapian::IfB2Weight
ifb2weight2(2.0);
333 TEST_NOT_EQUAL(ifb2weight
.serialise(), ifb2weight2
.serialise());
335 Xapian::IneB2Weight ineb2weight_dflt
;
336 Xapian::IneB2Weight
ineb2weight(1.0);
337 TEST_EQUAL(ineb2weight
.name(), "Xapian::IneB2Weight");
338 TEST_EQUAL(ineb2weight_dflt
.serialise(), ineb2weight
.serialise());
339 wt
= Xapian::IneB2Weight().unserialise(ineb2weight
.serialise());
340 TEST_EQUAL(ineb2weight
.serialise(), wt
->serialise());
343 Xapian::IneB2Weight
ineb2weight2(2.0);
344 TEST_NOT_EQUAL(ineb2weight
.serialise(), ineb2weight2
.serialise());
346 Xapian::BB2Weight bb2weight_dflt
;
347 Xapian::BB2Weight
bb2weight(1.0);
348 TEST_EQUAL(bb2weight
.name(), "Xapian::BB2Weight");
349 TEST_EQUAL(bb2weight_dflt
.serialise(), bb2weight
.serialise());
350 wt
= Xapian::BB2Weight().unserialise(bb2weight
.serialise());
351 TEST_EQUAL(bb2weight
.serialise(), wt
->serialise());
354 Xapian::BB2Weight
bb2weight2(2.0);
355 TEST_NOT_EQUAL(bb2weight
.serialise(), bb2weight2
.serialise());
357 Xapian::DLHWeight dlhweight
;
358 TEST_EQUAL(dlhweight
.name(), "Xapian::DLHWeight");
359 wt
= Xapian::DLHWeight().unserialise(dlhweight
.serialise());
360 TEST_EQUAL(dlhweight
.serialise(), wt
->serialise());
363 Xapian::PL2Weight pl2weight_dflt
;
364 Xapian::PL2Weight
pl2weight(1.0);
365 TEST_EQUAL(pl2weight
.name(), "Xapian::PL2Weight");
366 TEST_EQUAL(pl2weight_dflt
.serialise(), pl2weight
.serialise());
367 wt
= Xapian::PL2Weight().unserialise(pl2weight
.serialise());
368 TEST_EQUAL(pl2weight
.serialise(), wt
->serialise());
371 Xapian::PL2Weight
pl2weight2(2.0);
372 TEST_NOT_EQUAL(pl2weight
.serialise(), pl2weight2
.serialise());
374 Xapian::DPHWeight dphweight
;
375 TEST_EQUAL(dphweight
.name(), "Xapian::DPHWeight");
376 wt
= Xapian::DPHWeight().unserialise(dphweight
.serialise());
377 TEST_EQUAL(dphweight
.serialise(), wt
->serialise());
380 Xapian::LMWeight unigramlmweight_dflt
;
381 Xapian::LMWeight
unigramlmweight(32000, Xapian::Weight::DIRICHLET_SMOOTHING
, 2034.0, 0.0);
382 TEST_EQUAL(unigramlmweight
.name(), "Xapian::LMWeight");
383 TEST_NOT_EQUAL(unigramlmweight_dflt
.serialise(), unigramlmweight
.serialise());
384 wt
= Xapian::LMWeight().unserialise(unigramlmweight
.serialise());
385 TEST_EQUAL(unigramlmweight
.serialise(), wt
->serialise());
392 DEFINE_TESTCASE(nosuchdb1
, !backend
) {
393 // This is a "nodb" test because it doesn't test a particular backend.
395 Xapian::Database
db("NOsuChdaTabASe");
396 } catch (const Xapian::DatabaseOpeningError
& e
) {
397 // We don't really require this exact message, but in Xapian <= 1.1.0
398 // this gave "Couldn't detect type of database".
399 TEST_STRINGS_EQUAL(e
.get_msg(), "Couldn't stat 'NOsuChdaTabASe'");
405 // Feature tests for value manipulations.
406 DEFINE_TESTCASE(addvalue1
, !backend
) {
407 // Regression test for add_value on an existing value (bug#82).
408 Xapian::Document doc
;
409 doc
.add_value(1, "original");
410 doc
.add_value(1, "replacement");
411 TEST_EQUAL(doc
.get_value(1), "replacement");
413 doc
.add_value(2, "too");
414 doc
.add_value(3, "free");
415 doc
.add_value(4, "for");
419 TEST_EQUAL(doc
.get_value(0), "");
420 TEST_EQUAL(doc
.get_value(1), "replacement");
421 TEST_EQUAL(doc
.get_value(2), "");
422 TEST_EQUAL(doc
.get_value(3), "free");
423 TEST_EQUAL(doc
.get_value(4), "");
428 // tests that the collapsing on termpos optimisation gives correct query length
429 DEFINE_TESTCASE(poscollapse2
, !backend
) {
430 Xapian::Query
q(Xapian::Query::OP_OR
, Xapian::Query("this", 1, 1), Xapian::Query("this", 1, 1));
431 TEST_EQUAL(q
.get_length(), 2);
435 // regression test of querying an uninitialised database: should report an
436 // error; used to segfault with 1.0.0.
437 DEFINE_TESTCASE(uninitdb1
, !backend
) {
439 TEST_EXCEPTION(Xapian::InvalidArgumentError
,
440 Xapian::Enquire
enq(db
));
444 // Test a scaleweight query applied to a match nothing query
445 DEFINE_TESTCASE(scaleweight3
, !backend
) {
446 Xapian::Query
matchnothing(Xapian::Query::MatchNothing
);
447 Xapian::Query
query(Xapian::Query::OP_SCALE_WEIGHT
, matchnothing
, 3.0);
448 TEST_EQUAL(query
.get_description(), "Query()");
452 // Regression test - before 1.1.0, you could add docid 0 to an RSet.
453 DEFINE_TESTCASE(rset3
, !backend
) {
455 TEST_EXCEPTION(Xapian::InvalidArgumentError
, rset
.add_document(0));
457 TEST_EQUAL(rset
.size(), 0);
458 rset
.add_document(1);
459 rset
.add_document(static_cast<Xapian::docid
>(-1));
460 TEST_EXCEPTION(Xapian::InvalidArgumentError
, rset
.add_document(0));
462 TEST_EQUAL(rset
.size(), 2);
466 // Regression test - RSet::get_description() gave a malformed answer in 1.0.7.
467 DEFINE_TESTCASE(rset4
, !backend
) {
469 rset
.add_document(1);
470 // In 1.0.7 this gave: RSet(RSet(RSet::Internal(, 1))
471 TEST_STRINGS_EQUAL(rset
.get_description(), "RSet(RSet::Internal(1))");
475 // Direct test of ValueSetMatchDecider
476 DEFINE_TESTCASE(valuesetmatchdecider1
, !backend
) {
477 Xapian::ValueSetMatchDecider
vsmd1(0, true);
478 vsmd1
.add_value("42");
479 Xapian::ValueSetMatchDecider
vsmd2(0, false);
480 vsmd2
.remove_value("nosuch"); // Test removing a value which isn't present.
481 vsmd2
.add_value("42");
482 Xapian::ValueSetMatchDecider
vsmd3(0, true);
483 vsmd3
.add_value("42");
484 vsmd3
.add_value("blah");
486 Xapian::Document doc
;
490 doc
.add_value(0, "42");
494 doc
.add_value(0, "blah");
499 vsmd3
.remove_value("nosuch"); // Test removing a value which isn't present.
500 vsmd3
.remove_value("blah");
504 doc
.add_value(0, "42");
512 // Test that asking for the termfreq on an empty mset raises an exception.
513 DEFINE_TESTCASE(emptymset1
, !backend
) {
514 Xapian::MSet emptymset
;
515 TEST_EXCEPTION(Xapian::InvalidOperationError
,
516 emptymset
.get_termfreq("foo"));
520 DEFINE_TESTCASE(expanddeciderfilterprefix1
, !backend
) {
521 string prefix
= "tw";
522 Xapian::ExpandDeciderFilterPrefix
decider(prefix
);
523 TEST(!decider("one"));
526 TEST(!decider("Two"));
527 TEST(decider("two"));
528 TEST(decider("twitter"));
529 TEST(decider(prefix
));