Support: quest -f cjk_ngram
[xapian.git] / xapian-core / tests / api_db.cc
blobe288535490dd8e00f7310e121fbac3be3a4e72e7
1 /* api_db.cc: tests which need a backend
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2011,2012,2013,2015 Olly Betts
6 * Copyright 2006,2007,2008,2009 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
21 * USA
24 #include <config.h>
26 #include "api_db.h"
28 #include <algorithm>
29 #include <fstream>
30 #include <map>
31 #include <string>
32 #include <vector>
33 #include "safesysstat.h" // For mkdir().
34 #include "safeunistd.h" // For sleep().
36 #include <xapian.h>
38 #include "backendmanager.h"
39 #include "backendmanager_local.h"
40 #include "testsuite.h"
41 #include "testutils.h"
42 #include "unixcmds.h"
44 #include "apitest.h"
46 using namespace std;
48 static Xapian::Query
49 query(const string &t)
51 return Xapian::Query(Xapian::Stem("english")(t));
54 // #######################################################################
55 // # Tests start here
57 // tests Xapian::Database::get_termfreq() and Xapian::Database::term_exists()
58 DEFINE_TESTCASE(termstats, backend) {
59 Xapian::Database db(get_database("apitest_simpledata"));
61 TEST(!db.term_exists("corn"));
62 TEST_EQUAL(db.get_termfreq("corn"), 0);
63 TEST(db.term_exists("banana"));
64 TEST_EQUAL(db.get_termfreq("banana"), 1);
65 TEST(db.term_exists("paragraph"));
66 TEST_EQUAL(db.get_termfreq("paragraph"), 5);
68 return true;
71 // Check that stub databases work.
72 DEFINE_TESTCASE(stubdb1, backend && !inmemory && !remote) {
73 // Only works for backends which have a path.
74 mkdir(".stub", 0755);
75 const char * dbpath = ".stub/stubdb1";
76 ofstream out(dbpath);
77 TEST(out.is_open());
78 out << "auto ../" << get_database_path("apitest_simpledata") << endl;
79 out.close();
82 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB);
83 Xapian::Enquire enquire(db);
84 enquire.set_query(Xapian::Query("word"));
85 enquire.get_mset(0, 10);
88 Xapian::Database db(dbpath);
89 Xapian::Enquire enquire(db);
90 enquire.set_query(Xapian::Query("word"));
91 enquire.get_mset(0, 10);
94 return true;
97 // Check that stub databases work remotely.
98 DEFINE_TESTCASE(stubdb2, backend && !inmemory && !remote) {
99 // Only works for backends which have a path.
100 mkdir(".stub", 0755);
101 const char * dbpath = ".stub/stubdb2";
102 ofstream out(dbpath);
103 TEST(out.is_open());
104 out << "remote :" << BackendManager::get_xapian_progsrv_command()
105 << ' ' << get_database_path("apitest_simpledata") << endl;
106 out.close();
109 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB);
110 Xapian::Enquire enquire(db);
111 enquire.set_query(Xapian::Query("word"));
112 enquire.get_mset(0, 10);
115 Xapian::Database db(dbpath);
116 Xapian::Enquire enquire(db);
117 enquire.set_query(Xapian::Query("word"));
118 enquire.get_mset(0, 10);
121 return true;
124 // Regression test - bad entries were ignored after a good entry prior to 1.0.8.
125 DEFINE_TESTCASE(stubdb3, backend && !inmemory && !remote) {
126 // Only works for backends which have a path.
127 mkdir(".stub", 0755);
128 const char * dbpath = ".stub/stubdb3";
129 ofstream out(dbpath);
130 TEST(out.is_open());
131 out << "auto ../" << get_database_path("apitest_simpledata") << "\n"
132 "bad line here\n";
133 out.close();
135 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
136 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB));
138 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
139 Xapian::Database db(dbpath));
141 return true;
144 // Test a stub database with just a bad entry.
145 DEFINE_TESTCASE(stubdb4, backend && !inmemory && !remote) {
146 // Only works for backends which have a path.
147 mkdir(".stub", 0755);
148 const char * dbpath = ".stub/stubdb4";
149 ofstream out(dbpath);
150 TEST(out.is_open());
151 out << "bad line here\n";
152 out.close();
154 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
155 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB));
157 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
158 Xapian::Database db(dbpath));
160 return true;
163 // Test a stub database with a bad entry with no spaces (prior to 1.1.0 this
164 // was deliberately allowed, though not documented.
165 DEFINE_TESTCASE(stubdb5, backend && !inmemory && !remote) {
166 // Only works for backends which have a path.
167 mkdir(".stub", 0755);
168 const char * dbpath = ".stub/stubdb5";
169 ofstream out(dbpath);
170 TEST(out.is_open());
171 out << "bad\n"
172 "auto ../" << get_database_path("apitest_simpledata") << endl;
173 out.close();
175 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
176 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB));
178 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
179 Xapian::Database db(dbpath));
181 return true;
184 // Test a stub database with an inmemory database (new feature in 1.1.0).
185 DEFINE_TESTCASE(stubdb6, inmemory) {
186 mkdir(".stub", 0755);
187 const char * dbpath = ".stub/stubdb6";
188 ofstream out(dbpath);
189 TEST(out.is_open());
190 out << "inmemory\n";
191 out.close();
193 // Read-only tests:
195 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB);
196 TEST_EQUAL(db.get_doccount(), 0);
197 Xapian::Enquire enquire(db);
198 enquire.set_query(Xapian::Query("word"));
199 Xapian::MSet mset = enquire.get_mset(0, 10);
200 TEST(mset.empty());
203 Xapian::Database db(dbpath);
204 TEST_EQUAL(db.get_doccount(), 0);
205 Xapian::Enquire enquire(db);
206 enquire.set_query(Xapian::Query("word"));
207 Xapian::MSet mset = enquire.get_mset(0, 10);
208 TEST(mset.empty());
211 // Writable tests:
213 Xapian::WritableDatabase db(dbpath,
214 Xapian::DB_OPEN|Xapian::DB_BACKEND_STUB);
215 TEST_EQUAL(db.get_doccount(), 0);
216 db.add_document(Xapian::Document());
217 TEST_EQUAL(db.get_doccount(), 1);
220 Xapian::WritableDatabase db(dbpath,
221 Xapian::DB_OPEN|Xapian::DB_BACKEND_STUB);
222 TEST_EQUAL(db.get_doccount(), 0);
223 db.add_document(Xapian::Document());
224 TEST_EQUAL(db.get_doccount(), 1);
227 return true;
230 #if 0 // the "force error" mechanism is no longer in place...
231 class MyErrorHandler : public Xapian::ErrorHandler {
232 public:
233 int count;
235 bool handle_error(Xapian::Error & error) {
236 ++count;
237 tout << "Error handling caught: " << error.get_description()
238 << ", count is now " << count << "\n";
239 return true;
242 MyErrorHandler() : count (0) {}
245 // tests error handler in multimatch().
246 DEFINE_TESTCASE(multierrhandler1, backend) {
247 MyErrorHandler myhandler;
249 Xapian::Database mydb2(get_database("apitest_simpledata"));
250 Xapian::Database mydb3(get_database("apitest_simpledata2"));
251 int errcount = 1;
252 for (int testcount = 0; testcount < 14; testcount ++) {
253 tout << "testcount=" << testcount << "\n";
254 Xapian::Database mydb4(get_database("-e", "apitest_termorder"));
255 Xapian::Database mydb5(get_network_database("apitest_termorder", 1));
256 Xapian::Database mydb6(get_database("-e2", "apitest_termorder"));
257 Xapian::Database mydb7(get_database("-e3", "apitest_simpledata"));
259 Xapian::Database dbs;
260 switch (testcount) {
261 case 0:
262 dbs.add_database(mydb2);
263 dbs.add_database(mydb3);
264 dbs.add_database(mydb4);
265 break;
266 case 1:
267 dbs.add_database(mydb4);
268 dbs.add_database(mydb2);
269 dbs.add_database(mydb3);
270 break;
271 case 2:
272 dbs.add_database(mydb3);
273 dbs.add_database(mydb4);
274 dbs.add_database(mydb2);
275 break;
276 case 3:
277 dbs.add_database(mydb2);
278 dbs.add_database(mydb3);
279 dbs.add_database(mydb5);
280 sleep(1);
281 break;
282 case 4:
283 dbs.add_database(mydb5);
284 dbs.add_database(mydb2);
285 dbs.add_database(mydb3);
286 sleep(1);
287 break;
288 case 5:
289 dbs.add_database(mydb3);
290 dbs.add_database(mydb5);
291 dbs.add_database(mydb2);
292 sleep(1);
293 break;
294 case 6:
295 dbs.add_database(mydb2);
296 dbs.add_database(mydb3);
297 dbs.add_database(mydb6);
298 break;
299 case 7:
300 dbs.add_database(mydb6);
301 dbs.add_database(mydb2);
302 dbs.add_database(mydb3);
303 break;
304 case 8:
305 dbs.add_database(mydb3);
306 dbs.add_database(mydb6);
307 dbs.add_database(mydb2);
308 break;
309 case 9:
310 dbs.add_database(mydb2);
311 dbs.add_database(mydb3);
312 dbs.add_database(mydb7);
313 break;
314 case 10:
315 dbs.add_database(mydb7);
316 dbs.add_database(mydb2);
317 dbs.add_database(mydb3);
318 break;
319 case 11:
320 dbs.add_database(mydb3);
321 dbs.add_database(mydb7);
322 dbs.add_database(mydb2);
323 break;
324 case 12:
325 dbs.add_database(mydb2);
326 dbs.add_database(mydb6);
327 dbs.add_database(mydb7);
328 break;
329 case 13:
330 dbs.add_database(mydb2);
331 dbs.add_database(mydb7);
332 dbs.add_database(mydb6);
333 break;
335 tout << "db=" << dbs << "\n";
336 Xapian::Enquire enquire(dbs, &myhandler);
338 // make a query
339 Xapian::Query myquery = query(Xapian::Query::OP_OR, "inmemory", "word");
340 enquire.set_weighting_scheme(Xapian::BoolWeight());
341 enquire.set_query(myquery);
343 tout << "query=" << myquery << "\n";
344 // retrieve the top ten results
345 Xapian::MSet mymset = enquire.get_mset(0, 10);
347 switch (testcount) {
348 case 0: case 3: case 6: case 9:
349 mset_expect_order(mymset, 2, 4, 10);
350 break;
351 case 1: case 4: case 7: case 10:
352 mset_expect_order(mymset, 3, 5, 11);
353 break;
354 case 2: case 5: case 8: case 11:
355 mset_expect_order(mymset, 1, 6, 12);
356 break;
357 case 12:
358 case 13:
359 mset_expect_order(mymset, 4, 10);
360 errcount += 1;
361 break;
363 TEST_EQUAL(myhandler.count, errcount);
364 errcount += 1;
367 return true;
369 #endif
371 class myMatchDecider : public Xapian::MatchDecider {
372 public:
373 bool operator()(const Xapian::Document &doc) const {
374 // Note that this is not recommended usage of get_data()
375 return doc.get_data().find("This is") != string::npos;
379 // Test Xapian::MatchDecider functor.
380 DEFINE_TESTCASE(matchdecider1, backend && !remote) {
381 Xapian::Database db(get_database("apitest_simpledata"));
382 Xapian::Enquire enquire(db);
383 enquire.set_query(Xapian::Query("this"));
385 myMatchDecider myfunctor;
387 Xapian::MSet mymset = enquire.get_mset(0, 100, 0, &myfunctor);
389 vector<bool> docid_checked(db.get_lastdocid());
391 // Check that we get the expected number of matches, and that they
392 // satisfy the condition.
393 Xapian::MSetIterator i = mymset.begin();
394 TEST(i != mymset.end());
395 TEST_EQUAL(mymset.size(), 3);
396 TEST_EQUAL(mymset.get_matches_lower_bound(), 3);
397 TEST_EQUAL(mymset.get_matches_upper_bound(), 3);
398 TEST_EQUAL(mymset.get_matches_estimated(), 3);
399 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 3);
400 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 3);
401 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 3);
402 for ( ; i != mymset.end(); ++i) {
403 const Xapian::Document doc(i.get_document());
404 TEST(myfunctor(doc));
405 docid_checked[*i] = true;
408 // Check that there are some documents which aren't accepted by the match
409 // decider.
410 mymset = enquire.get_mset(0, 100);
411 TEST(mymset.size() > 3);
413 // Check that the bounds are appropriate even if we don't ask for any
414 // actual matches.
415 mymset = enquire.get_mset(0, 0, 0, &myfunctor);
416 TEST_EQUAL(mymset.size(), 0);
417 TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
418 TEST_EQUAL(mymset.get_matches_upper_bound(), 6);
419 TEST_REL(mymset.get_matches_estimated(),>,0);
420 TEST_REL(mymset.get_matches_estimated(),<=,6);
421 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 0);
422 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 6);
423 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
424 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
426 // Check that the bounds are appropriate if we ask for only one hit.
427 // (Regression test - until SVN 10256, we didn't reduce the lower_bound
428 // appropriately, and returned 6 here.)
429 mymset = enquire.get_mset(0, 1, 0, &myfunctor);
430 TEST_EQUAL(mymset.size(), 1);
431 TEST_REL(mymset.get_matches_lower_bound(),>=,1);
432 TEST_REL(mymset.get_matches_lower_bound(),<=,3);
433 TEST_REL(mymset.get_matches_upper_bound(),>=,3);
434 TEST_REL(mymset.get_matches_upper_bound(),<=,6);
435 TEST_REL(mymset.get_matches_estimated(),>,0);
436 TEST_REL(mymset.get_matches_estimated(),<=,6);
437 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,1);
438 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),<=,3);
439 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),>=,3);
440 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),<=,6);
441 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
442 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
444 // Check that the other documents don't satisfy the condition.
445 for (Xapian::docid did = 1; did < docid_checked.size(); ++did) {
446 if (!docid_checked[did]) {
447 TEST(!myfunctor(db.get_document(did)));
451 // Check that the bounds are appropriate if a collapse key is used.
452 // Use a value which is never set so we don't actually discard anything.
453 enquire.set_collapse_key(99);
454 mymset = enquire.get_mset(0, 1, 0, &myfunctor);
455 TEST_EQUAL(mymset.size(), 1);
456 TEST_REL(mymset.get_matches_lower_bound(),>=,1);
457 TEST_REL(mymset.get_matches_lower_bound(),<=,3);
458 TEST_REL(mymset.get_matches_upper_bound(),>=,3);
459 TEST_REL(mymset.get_matches_upper_bound(),<=,6);
460 TEST_REL(mymset.get_matches_estimated(),>,0);
461 TEST_REL(mymset.get_matches_estimated(),<=,6);
462 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,1);
463 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),<=,3);
464 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),>=,3);
465 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),<=,6);
466 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
467 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
469 // Check that the bounds are appropriate if a percentage cutoff is in
470 // use. Set a 1% threshold so we don't actually discard anything.
471 enquire.set_collapse_key(Xapian::BAD_VALUENO);
472 enquire.set_cutoff(1);
473 mymset = enquire.get_mset(0, 1, 0, &myfunctor);
474 TEST_EQUAL(mymset.size(), 1);
475 TEST_REL(mymset.get_matches_lower_bound(),>=,1);
476 TEST_REL(mymset.get_matches_lower_bound(),<=,3);
477 TEST_REL(mymset.get_matches_upper_bound(),>=,3);
478 TEST_REL(mymset.get_matches_upper_bound(),<=,6);
479 TEST_REL(mymset.get_matches_estimated(),>,0);
480 TEST_REL(mymset.get_matches_estimated(),<=,6);
481 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,1);
482 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),<=,3);
483 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),>=,3);
484 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),<=,6);
485 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
486 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
488 // And now with both a collapse key and percentage cutoff.
489 enquire.set_collapse_key(99);
490 mymset = enquire.get_mset(0, 1, 0, &myfunctor);
491 TEST_EQUAL(mymset.size(), 1);
492 TEST_REL(mymset.get_matches_lower_bound(),>=,1);
493 TEST_REL(mymset.get_matches_lower_bound(),<=,3);
494 TEST_REL(mymset.get_matches_upper_bound(),>=,3);
495 TEST_REL(mymset.get_matches_upper_bound(),<=,6);
496 TEST_REL(mymset.get_matches_estimated(),>,0);
497 TEST_REL(mymset.get_matches_estimated(),<=,6);
498 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,1);
499 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),<=,3);
500 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),>=,3);
501 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),<=,6);
502 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
503 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
505 return true;
508 // Test Xapian::MatchDecider functor used as a match spy.
509 DEFINE_TESTCASE(matchdecider2, backend && !remote) {
510 Xapian::Database db(get_database("apitest_simpledata"));
511 Xapian::Enquire enquire(db);
512 enquire.set_query(Xapian::Query("this"));
514 myMatchDecider myfunctor;
516 Xapian::MSet mymset = enquire.get_mset(0, 100, 0, NULL, &myfunctor);
518 vector<bool> docid_checked(db.get_lastdocid());
520 // Check that we get the expected number of matches, and that they
521 // satisfy the condition.
522 Xapian::MSetIterator i = mymset.begin();
523 TEST(i != mymset.end());
524 TEST_EQUAL(mymset.size(), 3);
525 for ( ; i != mymset.end(); ++i) {
526 const Xapian::Document doc(i.get_document());
527 TEST(myfunctor(doc));
528 docid_checked[*i] = true;
531 // Check that the other documents don't satisfy the condition.
532 for (Xapian::docid did = 1; did < docid_checked.size(); ++did) {
533 if (!docid_checked[did]) {
534 TEST(!myfunctor(db.get_document(did)));
538 return true;
541 class myMatchDecider2 : public Xapian::MatchDecider {
542 public:
543 bool operator()(const Xapian::Document &doc) const {
544 // Note that this is not recommended usage of get_data()
545 return doc.get_data().find("We produce") == string::npos;
550 // Regression test for lower bound using functor, sorting and collapsing.
551 DEFINE_TESTCASE(matchdecider3, backend && !remote) {
552 Xapian::Database db(get_database("etext"));
553 Xapian::Enquire enquire(db);
554 enquire.set_query(Xapian::Query(""));
555 enquire.set_collapse_key(12);
556 enquire.set_sort_by_value(11, true);
558 myMatchDecider2 myfunctor;
560 Xapian::MSet mset1 = enquire.get_mset(0, 2, 0, NULL, &myfunctor);
561 Xapian::MSet mset2 = enquire.get_mset(0, 1000, 0, NULL, &myfunctor);
563 // mset2 should contain all the hits, so the statistics should be exact.
564 TEST_EQUAL(mset2.get_matches_estimated(), mset2.size());
565 TEST_EQUAL(mset2.get_matches_lower_bound(), mset2.get_matches_estimated());
566 TEST_EQUAL(mset2.get_matches_estimated(), mset2.get_matches_upper_bound());
568 TEST_REL(mset2.get_uncollapsed_matches_lower_bound(),<=,mset2.get_uncollapsed_matches_estimated());
569 TEST_REL(mset2.get_uncollapsed_matches_estimated(),<=,mset2.get_uncollapsed_matches_upper_bound());
571 // Check that the lower bound in mset1 is not greater than the known
572 // number of hits. This failed until revision 10811.
573 TEST_REL(mset1.get_matches_lower_bound(),<=,mset2.size());
575 // Check that the bounds for mset1 make sense.
576 TEST_REL(mset1.get_matches_lower_bound(),<=,mset1.get_matches_estimated());
577 TEST_REL(mset1.get_matches_estimated(),<=,mset1.get_matches_upper_bound());
578 TEST_REL(mset1.size(),<=,mset1.get_matches_upper_bound());
580 TEST_REL(mset1.get_uncollapsed_matches_lower_bound(),<=,mset1.get_uncollapsed_matches_estimated());
581 TEST_REL(mset1.get_uncollapsed_matches_estimated(),<=,mset1.get_uncollapsed_matches_upper_bound());
583 // The uncollapsed match would match all documents but the one the
584 // matchdecider rejects.
585 TEST_REL(mset1.get_uncollapsed_matches_upper_bound(),>=,db.get_doccount() - 1);
586 TEST_REL(mset1.get_uncollapsed_matches_upper_bound(),<=,db.get_doccount());
587 TEST_REL(mset2.get_uncollapsed_matches_upper_bound(),>=,db.get_doccount() - 1);
588 TEST_REL(mset2.get_uncollapsed_matches_upper_bound(),<=,db.get_doccount());
590 return true;
593 // tests that mset iterators on msets compare correctly.
594 DEFINE_TESTCASE(msetiterator1, backend) {
595 Xapian::Enquire enquire(get_database("apitest_simpledata"));
596 enquire.set_query(Xapian::Query("this"));
597 Xapian::MSet mymset = enquire.get_mset(0, 2);
599 Xapian::MSetIterator j;
600 j = mymset.begin();
601 Xapian::MSetIterator k = mymset.end();
602 Xapian::MSetIterator l(j);
603 Xapian::MSetIterator m(k);
604 Xapian::MSetIterator n = mymset.begin();
605 Xapian::MSetIterator o = mymset.begin();
606 TEST_NOT_EQUAL(j, k);
607 TEST_NOT_EQUAL(l, m);
608 TEST_EQUAL(k, m);
609 TEST_EQUAL(j, l);
610 TEST_EQUAL(j, j);
611 TEST_EQUAL(k, k);
613 k = j;
614 TEST_EQUAL(j, k);
615 TEST_EQUAL(j, o);
616 k++;
617 TEST_NOT_EQUAL(j, k);
618 TEST_NOT_EQUAL(k, l);
619 TEST_NOT_EQUAL(k, m);
620 TEST_NOT_EQUAL(k, o);
621 o++;
622 TEST_EQUAL(k, o);
623 k++;
624 TEST_NOT_EQUAL(j, k);
625 TEST_NOT_EQUAL(k, l);
626 TEST_EQUAL(k, m);
627 TEST_EQUAL(n, l);
629 n = m;
630 TEST_NOT_EQUAL(n, l);
631 TEST_EQUAL(n, m);
632 TEST_NOT_EQUAL(n, mymset.begin());
633 TEST_EQUAL(n, mymset.end());
635 return true;
638 // tests that mset iterators on empty msets compare equal.
639 DEFINE_TESTCASE(msetiterator2, backend) {
640 Xapian::Enquire enquire(get_database("apitest_simpledata"));
641 enquire.set_query(Xapian::Query("this"));
642 Xapian::MSet mymset = enquire.get_mset(0, 0);
644 Xapian::MSetIterator j = mymset.begin();
645 Xapian::MSetIterator k = mymset.end();
646 Xapian::MSetIterator l(j);
647 Xapian::MSetIterator m(k);
648 TEST_EQUAL(j, k);
649 TEST_EQUAL(l, m);
650 TEST_EQUAL(k, m);
651 TEST_EQUAL(j, l);
652 TEST_EQUAL(j, j);
653 TEST_EQUAL(k, k);
655 return true;
658 // tests that begin().get_document() works when first != 0
659 DEFINE_TESTCASE(msetiterator3, backend) {
660 Xapian::Database mydb(get_database("apitest_simpledata"));
661 Xapian::Enquire enquire(mydb);
662 enquire.set_query(Xapian::Query("this"));
664 Xapian::MSet mymset = enquire.get_mset(2, 10);
666 TEST(!mymset.empty());
667 Xapian::Document doc(mymset.begin().get_document());
668 TEST(!doc.get_data().empty());
670 return true;
673 // tests that eset iterators on empty esets compare equal.
674 DEFINE_TESTCASE(esetiterator1, backend) {
675 Xapian::Enquire enquire(get_database("apitest_simpledata"));
676 enquire.set_query(Xapian::Query("this"));
678 Xapian::MSet mymset = enquire.get_mset(0, 10);
679 TEST(mymset.size() >= 2);
681 Xapian::RSet myrset;
682 Xapian::MSetIterator i = mymset.begin();
683 myrset.add_document(*i);
684 myrset.add_document(*(++i));
686 Xapian::ESet myeset = enquire.get_eset(2, myrset);
687 Xapian::ESetIterator j;
688 j = myeset.begin();
689 Xapian::ESetIterator k = myeset.end();
690 Xapian::ESetIterator l(j);
691 Xapian::ESetIterator m(k);
692 Xapian::ESetIterator n = myeset.begin();
694 TEST_NOT_EQUAL(j, k);
695 TEST_NOT_EQUAL(l, m);
696 TEST_EQUAL(k, m);
697 TEST_EQUAL(j, l);
698 TEST_EQUAL(j, j);
699 TEST_EQUAL(k, k);
701 k = j;
702 TEST_EQUAL(j, k);
703 k++;
704 TEST_NOT_EQUAL(j, k);
705 TEST_NOT_EQUAL(k, l);
706 TEST_NOT_EQUAL(k, m);
707 k++;
708 TEST_NOT_EQUAL(j, k);
709 TEST_NOT_EQUAL(k, l);
710 TEST_EQUAL(k, m);
711 TEST_EQUAL(n, l);
713 n = m;
714 TEST_NOT_EQUAL(n, l);
715 TEST_EQUAL(n, m);
716 TEST_NOT_EQUAL(n, myeset.begin());
717 TEST_EQUAL(n, myeset.end());
719 return true;
722 // tests that eset iterators on empty esets compare equal.
723 DEFINE_TESTCASE(esetiterator2, backend) {
724 Xapian::Enquire enquire(get_database("apitest_simpledata"));
725 enquire.set_query(Xapian::Query("this"));
727 Xapian::MSet mymset = enquire.get_mset(0, 10);
728 TEST(mymset.size() >= 2);
730 Xapian::RSet myrset;
731 Xapian::MSetIterator i = mymset.begin();
732 myrset.add_document(*i);
733 myrset.add_document(*(++i));
735 Xapian::ESet myeset = enquire.get_eset(0, myrset);
736 Xapian::ESetIterator j = myeset.begin();
737 Xapian::ESetIterator k = myeset.end();
738 Xapian::ESetIterator l(j);
739 Xapian::ESetIterator m(k);
740 TEST_EQUAL(j, k);
741 TEST_EQUAL(l, m);
742 TEST_EQUAL(k, m);
743 TEST_EQUAL(j, l);
744 TEST_EQUAL(j, j);
745 TEST_EQUAL(k, k);
747 return true;
750 // tests the collapse-on-key
751 DEFINE_TESTCASE(collapsekey1, backend) {
752 Xapian::Enquire enquire(get_database("apitest_simpledata"));
753 enquire.set_query(Xapian::Query("this"));
755 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
756 Xapian::doccount mymsize1 = mymset1.size();
758 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
759 enquire.set_collapse_key(value_no);
760 Xapian::MSet mymset = enquire.get_mset(0, 100);
762 TEST_AND_EXPLAIN(mymsize1 > mymset.size(),
763 "Had no fewer items when performing collapse: don't know whether it worked.");
765 map<string, Xapian::docid> values;
766 Xapian::MSetIterator i = mymset.begin();
767 for ( ; i != mymset.end(); ++i) {
768 string value = i.get_document().get_value(value_no);
769 TEST(values[value] == 0 || value.empty());
770 values[value] = *i;
774 return true;
777 // tests that collapse-on-key modifies the predicted bounds for the number of
778 // matches appropriately.
779 DEFINE_TESTCASE(collapsekey2, backend) {
780 SKIP_TEST("Don't have a suitable database currently");
781 // FIXME: this needs an appropriate database creating, but that's quite
782 // subtle to do it seems.
783 Xapian::Enquire enquire(get_database("apitest_simpledata2"));
784 enquire.set_query(Xapian::Query("this"));
786 Xapian::MSet mset1 = enquire.get_mset(0, 1);
788 // Test that if no duplicates are found, then the upper bound remains
789 // unchanged and the lower bound drops.
791 enquire.set_query(Xapian::Query("this"));
792 Xapian::valueno value_no = 3;
793 enquire.set_collapse_key(value_no);
794 Xapian::MSet mset = enquire.get_mset(0, 1);
796 TEST_REL(mset.get_matches_lower_bound(),<,mset1.get_matches_lower_bound());
797 TEST_EQUAL(mset.get_matches_upper_bound(), mset1.get_matches_upper_bound());
800 return true;
803 // tests that collapse-on-key modifies the predicted bounds for the number of
804 // matches appropriately.
805 DEFINE_TESTCASE(collapsekey3, backend) {
806 Xapian::Enquire enquire(get_database("apitest_simpledata"));
807 enquire.set_query(Xapian::Query("this"));
809 Xapian::MSet mymset1 = enquire.get_mset(0, 3);
811 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
812 enquire.set_collapse_key(value_no);
813 Xapian::MSet mymset = enquire.get_mset(0, 3);
815 TEST_AND_EXPLAIN(mymset1.get_matches_lower_bound() > mymset.get_matches_lower_bound(),
816 "Lower bound was not lower when performing collapse: don't know whether it worked.");
817 TEST_AND_EXPLAIN(mymset1.get_matches_upper_bound() > mymset.get_matches_upper_bound(),
818 "Upper bound was not lower when performing collapse: don't know whether it worked.");
820 map<string, Xapian::docid> values;
821 Xapian::MSetIterator i = mymset.begin();
822 for ( ; i != mymset.end(); ++i) {
823 string value = i.get_document().get_value(value_no);
824 TEST(values[value] == 0 || value.empty());
825 values[value] = *i;
829 // Test that if the collapse value is always empty, then the upper bound
830 // remains unchanged, and the lower bound is the same or lower (it can be
831 // lower because the matcher counts the number of documents with empty
832 // collapse keys, but may have rejected a document because its weight is
833 // too low for the proto-MSet before it even looks at its collapse key).
835 Xapian::valueno value_no = 1000;
836 enquire.set_collapse_key(value_no);
837 Xapian::MSet mymset = enquire.get_mset(0, 3);
839 TEST(mymset.get_matches_lower_bound() <= mymset1.get_matches_lower_bound());
840 TEST_EQUAL(mymset.get_matches_upper_bound(), mymset1.get_matches_upper_bound());
842 map<string, Xapian::docid> values;
843 Xapian::MSetIterator i = mymset.begin();
844 for ( ; i != mymset.end(); ++i) {
845 string value = i.get_document().get_value(value_no);
846 TEST(values[value] == 0 || value.empty());
847 values[value] = *i;
851 return true;
854 // tests that collapse-on-key modifies the predicted bounds for the number of
855 // matches appropriately even when no results are requested.
856 DEFINE_TESTCASE(collapsekey4, backend) {
857 Xapian::Enquire enquire(get_database("apitest_simpledata"));
858 enquire.set_query(Xapian::Query("this"));
860 Xapian::MSet mymset1 = enquire.get_mset(0, 0);
862 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
863 enquire.set_collapse_key(value_no);
864 Xapian::MSet mymset = enquire.get_mset(0, 0);
866 TEST_AND_EXPLAIN(mymset.get_matches_lower_bound() == 1,
867 "Lower bound was not 1 when performing collapse but not asking for any results.");
868 TEST_AND_EXPLAIN(mymset1.get_matches_upper_bound() == mymset.get_matches_upper_bound(),
869 "Upper bound was changed when performing collapse but not asking for any results.");
871 map<string, Xapian::docid> values;
872 Xapian::MSetIterator i = mymset.begin();
873 for ( ; i != mymset.end(); ++i) {
874 string value = i.get_document().get_value(value_no);
875 TEST(values[value] == 0 || value.empty());
876 values[value] = *i;
880 return true;
883 // test for keepalives
884 DEFINE_TESTCASE(keepalive1, remote) {
885 Xapian::Database db(get_remote_database("apitest_simpledata", 5000));
887 /* Test that keep-alives work */
888 for (int i = 0; i < 10; ++i) {
889 sleep(2);
890 db.keep_alive();
892 Xapian::Enquire enquire(db);
893 enquire.set_query(Xapian::Query("word"));
894 enquire.get_mset(0, 10);
896 /* Test that things break without keepalives */
897 sleep(10);
898 enquire.set_query(Xapian::Query("word"));
899 TEST_EXCEPTION(Xapian::NetworkError,
900 enquire.get_mset(0, 10));
902 return true;
905 // test that iterating through all terms in a database works.
906 DEFINE_TESTCASE(allterms1, backend) {
907 Xapian::Database db(get_database("apitest_allterms"));
908 Xapian::TermIterator ati = db.allterms_begin();
909 TEST(ati != db.allterms_end());
910 TEST_EQUAL(*ati, "one");
911 TEST_EQUAL(ati.get_termfreq(), 1);
913 Xapian::TermIterator ati2 = ati;
915 ati++;
916 TEST(ati != db.allterms_end());
917 if (verbose) {
918 tout << "*ati = '" << *ati << "'\n";
919 tout << "*ati.length = '" << (*ati).length() << "'\n";
920 tout << "*ati == \"one\" = " << (*ati == "one") << "\n";
921 tout << "*ati[3] = " << ((*ati)[3]) << "\n";
922 tout << "*ati = '" << *ati << "'\n";
924 TEST(*ati == "three");
925 TEST(ati.get_termfreq() == 3);
927 #if 0
928 TEST(ati2 != db.allterms_end());
929 TEST(*ati2 == "one");
930 TEST(ati2.get_termfreq() == 1);
931 #endif
933 ++ati;
934 #if 0
935 ++ati2;
936 #endif
937 TEST(ati != db.allterms_end());
938 TEST(*ati == "two");
939 TEST(ati.get_termfreq() == 2);
941 #if 0
942 TEST(ati2 != db.allterms_end());
943 TEST(*ati2 == "three");
944 TEST(ati2.get_termfreq() == 3);
945 #endif
947 ati++;
948 TEST(ati == db.allterms_end());
950 return true;
953 // test that iterating through all terms in two databases works.
954 DEFINE_TESTCASE(allterms2, backend) {
955 Xapian::Database db;
956 db.add_database(get_database("apitest_allterms"));
957 db.add_database(get_database("apitest_allterms2"));
958 Xapian::TermIterator ati = db.allterms_begin();
960 TEST(ati != db.allterms_end());
961 TEST(*ati == "five");
962 TEST(ati.get_termfreq() == 2);
963 ati++;
965 TEST(ati != db.allterms_end());
966 TEST(*ati == "four");
967 TEST(ati.get_termfreq() == 1);
969 ati++;
970 TEST(ati != db.allterms_end());
971 TEST(*ati == "one");
972 TEST(ati.get_termfreq() == 1);
974 ++ati;
975 TEST(ati != db.allterms_end());
976 TEST(*ati == "six");
977 TEST(ati.get_termfreq() == 3);
979 ati++;
980 TEST(ati != db.allterms_end());
981 TEST(*ati == "three");
982 TEST(ati.get_termfreq() == 3);
984 ati++;
985 TEST(ati != db.allterms_end());
986 TEST(*ati == "two");
987 TEST(ati.get_termfreq() == 2);
989 ati++;
990 TEST(ati == db.allterms_end());
992 return true;
995 // test that skip_to sets at_end (regression test)
996 DEFINE_TESTCASE(allterms3, backend) {
997 Xapian::Database db;
998 db.add_database(get_database("apitest_allterms"));
999 Xapian::TermIterator ati = db.allterms_begin();
1001 ati.skip_to(string("zzzzzz"));
1002 TEST(ati == db.allterms_end());
1004 return true;
1007 // test that next ignores extra entries due to long posting lists being
1008 // chunked (regression test for quartz)
1009 DEFINE_TESTCASE(allterms4, backend) {
1010 // apitest_allterms4 contains 682 documents each containing just the word
1011 // "foo". 682 was the magic number which started to cause Quartz problems.
1012 Xapian::Database db = get_database("apitest_allterms4");
1014 Xapian::TermIterator i = db.allterms_begin();
1015 TEST(i != db.allterms_end());
1016 TEST(*i == "foo");
1017 TEST(i.get_termfreq() == 682);
1018 ++i;
1019 TEST(i == db.allterms_end());
1021 return true;
1024 // test that skip_to with an exact match sets the current term (regression test
1025 // for quartz)
1026 DEFINE_TESTCASE(allterms5, backend) {
1027 Xapian::Database db;
1028 db.add_database(get_database("apitest_allterms"));
1029 Xapian::TermIterator ati = db.allterms_begin();
1030 ati.skip_to("three");
1031 TEST(ati != db.allterms_end());
1032 TEST_EQUAL(*ati, "three");
1034 return true;
1037 // test allterms iterators with prefixes
1038 DEFINE_TESTCASE(allterms6, backend) {
1039 Xapian::Database db;
1040 db.add_database(get_database("apitest_allterms"));
1041 db.add_database(get_database("apitest_allterms2"));
1043 Xapian::TermIterator ati = db.allterms_begin("three");
1044 TEST(ati != db.allterms_end("three"));
1045 TEST_EQUAL(*ati, "three");
1046 ati.skip_to("three");
1047 TEST(ati != db.allterms_end("three"));
1048 TEST_EQUAL(*ati, "three");
1049 ati++;
1050 TEST(ati == db.allterms_end("three"));
1052 ati = db.allterms_begin("thre");
1053 TEST(ati != db.allterms_end("thre"));
1054 TEST_EQUAL(*ati, "three");
1055 ati.skip_to("three");
1056 TEST(ati != db.allterms_end("thre"));
1057 TEST_EQUAL(*ati, "three");
1058 ati++;
1059 TEST(ati == db.allterms_end("thre"));
1061 ati = db.allterms_begin("f");
1062 TEST(ati != db.allterms_end("f"));
1063 TEST_EQUAL(*ati, "five");
1064 TEST(ati != db.allterms_end("f"));
1065 ati.skip_to("three");
1066 TEST(ati == db.allterms_end("f"));
1068 ati = db.allterms_begin("f");
1069 TEST(ati != db.allterms_end("f"));
1070 TEST_EQUAL(*ati, "five");
1071 ati++;
1072 TEST(ati != db.allterms_end("f"));
1073 TEST_EQUAL(*ati, "four");
1074 ati++;
1075 TEST(ati == db.allterms_end("f"));
1077 ati = db.allterms_begin("absent");
1078 TEST(ati == db.allterms_end("absent"));
1080 return true;
1083 // test that searching for a term with a special characters in it works
1084 DEFINE_TESTCASE(specialterms1, backend) {
1085 Xapian::Enquire enquire(get_database("apitest_space"));
1086 Xapian::MSet mymset;
1087 Xapian::doccount count;
1088 Xapian::MSetIterator m;
1089 Xapian::Stem stemmer("english");
1091 enquire.set_query(stemmer("new\nline"));
1092 mymset = enquire.get_mset(0, 10);
1093 TEST_MSET_SIZE(mymset, 1);
1094 count = 0;
1095 for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
1096 TEST_EQUAL(count, 1);
1098 for (Xapian::valueno value_no = 0; value_no < 7; ++value_no) {
1099 string value = mymset.begin().get_document().get_value(value_no);
1100 TEST_NOT_EQUAL(value, "");
1101 if (value_no == 0) {
1102 TEST(value.size() > 263);
1103 TEST_EQUAL(static_cast<unsigned char>(value[262]), 255);
1104 for (int k = 0; k < 256; k++) {
1105 TEST_EQUAL(static_cast<unsigned char>(value[k+7]), k);
1110 enquire.set_query(stemmer(string("big\0zero", 8)));
1111 mymset = enquire.get_mset(0, 10);
1112 TEST_MSET_SIZE(mymset, 1);
1113 count = 0;
1114 for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
1115 TEST_EQUAL(count, 1);
1117 return true;
1120 // test that terms with a special characters in appear correctly when iterating
1121 // allterms
1122 DEFINE_TESTCASE(specialterms2, backend) {
1123 Xapian::Database db(get_database("apitest_space"));
1125 // Check the terms are all as expected (after stemming) and that allterms
1126 // copes with iterating over them.
1127 Xapian::TermIterator t;
1128 t = db.allterms_begin();
1129 TEST_EQUAL(*t, "back\\slash"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1130 TEST_EQUAL(*t, string("big\0zero", 8)); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1131 TEST_EQUAL(*t, "new\nlin"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1132 TEST_EQUAL(*t, "one\x01on"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1133 TEST_EQUAL(*t, "space man"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1134 TEST_EQUAL(*t, "tab\tbi"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1135 TEST_EQUAL(*t, "tu\x02tu"); ++t; TEST_EQUAL(t, db.allterms_end());
1137 // Now check that skip_to exactly a term containing a zero byte works.
1138 // This is a regression test for flint and quartz - an Assert() used to
1139 // fire in debug builds (the Assert was wrong - the actual code handled
1140 // this OK).
1141 t = db.allterms_begin();
1142 t.skip_to(string("big\0zero", 8));
1143 TEST_NOT_EQUAL(t, db.allterms_end());
1144 TEST_EQUAL(*t, string("big\0zero", 8));
1146 return true;
1149 // test that rsets behave correctly with multiDBs
1150 DEFINE_TESTCASE(rsetmultidb2, backend && !multi) {
1151 Xapian::Database mydb1(get_database("apitest_rset", "apitest_simpledata2"));
1152 Xapian::Database mydb2(get_database("apitest_rset"));
1153 mydb2.add_database(get_database("apitest_simpledata2"));
1155 Xapian::Enquire enquire1(mydb1);
1156 Xapian::Enquire enquire2(mydb2);
1158 Xapian::Query myquery = query("is");
1160 enquire1.set_query(myquery);
1161 enquire2.set_query(myquery);
1163 Xapian::RSet myrset1;
1164 Xapian::RSet myrset2;
1165 myrset1.add_document(4);
1166 myrset2.add_document(2);
1168 Xapian::MSet mymset1a = enquire1.get_mset(0, 10);
1169 Xapian::MSet mymset1b = enquire1.get_mset(0, 10, &myrset1);
1170 Xapian::MSet mymset2a = enquire2.get_mset(0, 10);
1171 Xapian::MSet mymset2b = enquire2.get_mset(0, 10, &myrset2);
1173 mset_expect_order(mymset1a, 4, 3);
1174 mset_expect_order(mymset1b, 4, 3);
1175 mset_expect_order(mymset2a, 2, 5);
1176 mset_expect_order(mymset2b, 2, 5);
1178 TEST(mset_range_is_same_weights(mymset1a, 0, mymset2a, 0, 2));
1179 TEST(mset_range_is_same_weights(mymset1b, 0, mymset2b, 0, 2));
1180 TEST_NOT_EQUAL(mymset1a, mymset1b);
1181 TEST_NOT_EQUAL(mymset2a, mymset2b);
1183 return true;
1186 // tests an expand across multiple databases
1187 DEFINE_TESTCASE(multiexpand1, backend && !multi) {
1188 Xapian::Database mydb1(get_database("apitest_simpledata", "apitest_simpledata2"));
1189 Xapian::Enquire enquire1(mydb1);
1191 Xapian::Database mydb2(get_database("apitest_simpledata"));
1192 mydb2.add_database(get_database("apitest_simpledata2"));
1193 Xapian::Enquire enquire2(mydb2);
1195 // make simple equivalent rsets, with a document from each database in each.
1196 Xapian::RSet rset1;
1197 Xapian::RSet rset2;
1198 rset1.add_document(1);
1199 rset1.add_document(7);
1200 rset2.add_document(1);
1201 rset2.add_document(2);
1203 // Retrieve all the ESet results in each of the three setups:
1205 // This is the single database one.
1206 Xapian::ESet eset1 = enquire1.get_eset(1000, rset1);
1208 // This is the multi database with approximation
1209 Xapian::ESet eset2 = enquire2.get_eset(1000, rset2);
1211 // This is the multi database without approximation
1212 Xapian::ESet eset3 = enquire2.get_eset(1000, rset2, Xapian::Enquire::USE_EXACT_TERMFREQ);
1214 TEST_EQUAL(eset1.size(), eset3.size());
1216 Xapian::ESetIterator i = eset1.begin();
1217 Xapian::ESetIterator j = eset3.begin();
1218 while (i != eset1.end() && j != eset3.end()) {
1219 TEST_EQUAL(*i, *j);
1220 TEST_EQUAL(i.get_weight(), j.get_weight());
1221 ++i;
1222 ++j;
1224 TEST(i == eset1.end());
1225 TEST(j == eset3.end());
1227 bool eset1_eq_eset2 = true;
1228 i = eset1.begin();
1229 j = eset2.begin();
1230 while (i != eset1.end() && j != eset2.end()) {
1231 if (i.get_weight() != j.get_weight()) {
1232 eset1_eq_eset2 = false;
1233 break;
1235 ++i;
1236 ++j;
1238 TEST(!eset1_eq_eset2);
1240 return true;
1243 // tests that opening a non-existent postlist returns an empty list
1244 DEFINE_TESTCASE(postlist1, backend) {
1245 Xapian::Database db(get_database("apitest_simpledata"));
1247 TEST_EQUAL(db.postlist_begin("rosebud"), db.postlist_end("rosebud"));
1249 string s = "let_us_see_if_we_can_break_it_with_a_really_really_long_term.";
1250 for (int i = 0; i < 8; ++i) {
1251 s += s;
1252 TEST_EQUAL(db.postlist_begin(s), db.postlist_end(s));
1255 // A regression test (no, really!)
1256 TEST_NOT_EQUAL(db.postlist_begin("a"), db.postlist_end("a"));
1258 return true;
1261 // tests that a Xapian::PostingIterator works as an STL iterator
1262 DEFINE_TESTCASE(postlist2, backend) {
1263 Xapian::Database db(get_database("apitest_simpledata"));
1264 Xapian::PostingIterator p;
1265 p = db.postlist_begin("this");
1266 Xapian::PostingIterator pend = db.postlist_end("this");
1268 TEST(p.get_description() != "PostingIterator()");
1270 // test operator= creates a copy which compares equal
1271 Xapian::PostingIterator p_copy = p;
1272 TEST_EQUAL(p, p_copy);
1274 TEST(p_copy.get_description() != "PostingIterator()");
1276 // test copy constructor creates a copy which compares equal
1277 Xapian::PostingIterator p_clone(p);
1278 TEST_EQUAL(p, p_clone);
1280 TEST(p_clone.get_description() != "PostingIterator()");
1282 vector<Xapian::docid> v(p, pend);
1284 p = db.postlist_begin("this");
1285 pend = db.postlist_end("this");
1286 vector<Xapian::docid>::const_iterator i;
1287 for (i = v.begin(); i != v.end(); ++i) {
1288 TEST_NOT_EQUAL(p, pend);
1289 TEST_EQUAL(*i, *p);
1290 p++;
1292 TEST_EQUAL(p, pend);
1294 TEST_STRINGS_EQUAL(p.get_description(), "PostingIterator()");
1295 TEST_STRINGS_EQUAL(pend.get_description(), "PostingIterator()");
1297 return true;
1300 // tests that a Xapian::PostingIterator still works when the DB is deleted
1301 DEFINE_TESTCASE(postlist3, backend) {
1302 Xapian::PostingIterator u;
1304 Xapian::Database db_temp(get_database("apitest_simpledata"));
1305 u = db_temp.postlist_begin("this");
1308 Xapian::Database db(get_database("apitest_simpledata"));
1309 Xapian::PostingIterator p = db.postlist_begin("this");
1310 Xapian::PostingIterator pend = db.postlist_end("this");
1312 while (p != pend) {
1313 TEST_EQUAL(*p, *u);
1314 p++;
1315 u++;
1317 return true;
1320 // tests skip_to
1321 DEFINE_TESTCASE(postlist4, backend) {
1322 Xapian::Database db(get_database("apitest_simpledata"));
1323 Xapian::PostingIterator i = db.postlist_begin("this");
1324 i.skip_to(1);
1325 i.skip_to(999999999);
1326 TEST(i == db.postlist_end("this"));
1327 return true;
1330 // tests long postlists
1331 DEFINE_TESTCASE(postlist5, backend) {
1332 Xapian::Database db(get_database("apitest_manydocs"));
1333 // Allow for databases which don't support length
1334 if (db.get_avlength() != 1)
1335 TEST_EQUAL_DOUBLE(db.get_avlength(), 4);
1336 Xapian::PostingIterator i = db.postlist_begin("this");
1337 unsigned int j = 1;
1338 while (i != db.postlist_end("this")) {
1339 TEST_EQUAL(*i, j);
1340 i++;
1341 j++;
1343 TEST_EQUAL(j, 513);
1344 return true;
1347 // tests document length in postlists
1348 DEFINE_TESTCASE(postlist6, backend) {
1349 Xapian::Database db(get_database("apitest_simpledata"));
1350 Xapian::PostingIterator i = db.postlist_begin("this");
1351 TEST(i != db.postlist_end("this"));
1352 while (i != db.postlist_end("this")) {
1353 TEST_EQUAL(i.get_doclength(), db.get_doclength(*i));
1354 TEST_EQUAL(i.get_unique_terms(), db.get_unique_terms(*i));
1355 TEST_REL(i.get_wdf(),<=,i.get_doclength());
1356 TEST_REL(1,<=,i.get_unique_terms());
1357 // The next two aren't necessarily true if there are terms with wdf=0
1358 // in the document, but that isn't the case here.
1359 TEST_REL(i.get_unique_terms(),<=,i.get_doclength());
1360 TEST_REL(i.get_wdf() + i.get_unique_terms() - 1,<=,i.get_doclength());
1361 ++i;
1363 return true;
1366 // tests collection frequency
1367 DEFINE_TESTCASE(collfreq1, backend) {
1368 Xapian::Database db(get_database("apitest_simpledata"));
1370 TEST_EQUAL(db.get_collection_freq("this"), 11);
1371 TEST_EQUAL(db.get_collection_freq("first"), 1);
1372 TEST_EQUAL(db.get_collection_freq("last"), 0);
1373 TEST_EQUAL(db.get_collection_freq("word"), 9);
1375 Xapian::Database db1(get_database("apitest_simpledata", "apitest_simpledata2"));
1376 Xapian::Database db2(get_database("apitest_simpledata"));
1377 db2.add_database(get_database("apitest_simpledata2"));
1379 TEST_EQUAL(db1.get_collection_freq("this"), 15);
1380 TEST_EQUAL(db1.get_collection_freq("first"), 1);
1381 TEST_EQUAL(db1.get_collection_freq("last"), 0);
1382 TEST_EQUAL(db1.get_collection_freq("word"), 11);
1383 TEST_EQUAL(db2.get_collection_freq("this"), 15);
1384 TEST_EQUAL(db2.get_collection_freq("first"), 1);
1385 TEST_EQUAL(db2.get_collection_freq("last"), 0);
1386 TEST_EQUAL(db2.get_collection_freq("word"), 11);
1388 return true;
1391 // Regression test for split msets being incorrect when sorting
1392 DEFINE_TESTCASE(sortvalue1, backend) {
1393 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1394 enquire.set_query(Xapian::Query("this"));
1396 for (int pass = 1; pass <= 2; ++pass) {
1397 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
1398 tout << "Sorting on value " << value_no << endl;
1399 enquire.set_sort_by_value(value_no, true);
1400 Xapian::MSet allbset = enquire.get_mset(0, 100);
1401 Xapian::MSet partbset1 = enquire.get_mset(0, 3);
1402 Xapian::MSet partbset2 = enquire.get_mset(3, 97);
1403 TEST_EQUAL(allbset.size(), partbset1.size() + partbset2.size());
1405 bool ok = true;
1406 int n = 0;
1407 Xapian::MSetIterator i, j;
1408 j = allbset.begin();
1409 for (i = partbset1.begin(); i != partbset1.end(); ++i) {
1410 tout << "Entry " << n << ": " << *i << " | " << *j << endl;
1411 TEST(j != allbset.end());
1412 if (*i != *j) ok = false;
1413 ++j;
1414 ++n;
1416 tout << "===\n";
1417 for (i = partbset2.begin(); i != partbset2.end(); ++i) {
1418 tout << "Entry " << n << ": " << *i << " | " << *j << endl;
1419 TEST(j != allbset.end());
1420 if (*i != *j) ok = false;
1421 ++j;
1422 ++n;
1424 TEST(j == allbset.end());
1425 if (!ok)
1426 FAIL_TEST("Split msets aren't consistent with unsplit");
1428 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1431 return true;
1434 // consistency check match - vary mset size and check results agree.
1435 // consistency1 will run on the remote backend, but it's particularly slow
1436 // with that, and testing it there doesn't actually improve the test
1437 // coverage really.
1438 DEFINE_TESTCASE(consistency1, backend && !remote) {
1439 Xapian::Database db(get_database("etext"));
1440 Xapian::Enquire enquire(db);
1441 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR, Xapian::Query("the"), Xapian::Query("sky")));
1442 Xapian::doccount lots = 214;
1443 Xapian::MSet bigmset = enquire.get_mset(0, lots);
1444 TEST_EQUAL(bigmset.size(), lots);
1445 try {
1446 for (Xapian::doccount start = 0; start < lots; ++start) {
1447 for (Xapian::doccount size = 0; size < lots - start; ++size) {
1448 Xapian::MSet mset = enquire.get_mset(start, size);
1449 if (mset.size()) {
1450 TEST_EQUAL(start + mset.size(),
1451 min(start + size, bigmset.size()));
1452 } else if (size) {
1453 // tout << start << mset.size() << bigmset.size() << endl;
1454 TEST(start >= bigmset.size());
1456 for (Xapian::doccount i = 0; i < mset.size(); ++i) {
1457 TEST_EQUAL(*mset[i], *bigmset[start + i]);
1458 TEST_EQUAL_DOUBLE(mset[i].get_weight(),
1459 bigmset[start + i].get_weight());
1464 catch (const Xapian::NetworkTimeoutError &) {
1465 // consistency1 is a long test - may timeout with the remote backend...
1466 SKIP_TEST("Test taking too long");
1468 return true;
1471 // tests that specifying a nonexistent input file throws an exception.
1472 DEFINE_TESTCASE(chertdatabaseopeningerror1, chert) {
1473 #ifdef XAPIAN_HAS_CHERT_BACKEND
1474 mkdir(".chert", 0755);
1476 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1477 Xapian::Database(".chert/nosuchdirectory",
1478 Xapian::DB_BACKEND_CHERT));
1479 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1480 Xapian::WritableDatabase(".chert/nosuchdirectory",
1481 Xapian::DB_OPEN|Xapian::DB_BACKEND_CHERT));
1483 mkdir(".chert/emptydirectory", 0700);
1484 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1485 Xapian::Database(".chert/emptydirectory",
1486 Xapian::DB_BACKEND_CHERT));
1488 touch(".chert/somefile");
1489 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1490 Xapian::Database(".chert/somefile",
1491 Xapian::DB_BACKEND_CHERT));
1492 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1493 Xapian::WritableDatabase(".chert/somefile",
1494 Xapian::DB_OPEN|Xapian::DB_BACKEND_CHERT));
1495 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1496 Xapian::WritableDatabase(".chert/somefile",
1497 Xapian::DB_CREATE|Xapian::DB_BACKEND_CHERT));
1498 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1499 Xapian::WritableDatabase(".chert/somefile",
1500 Xapian::DB_CREATE_OR_OPEN|Xapian::DB_BACKEND_CHERT));
1501 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1502 Xapian::WritableDatabase(".chert/somefile",
1503 Xapian::DB_CREATE_OR_OVERWRITE|Xapian::DB_BACKEND_CHERT));
1504 #endif
1506 return true;
1509 /// Test opening of a chert database
1510 DEFINE_TESTCASE(chertdatabaseopen1, chert) {
1511 #ifdef XAPIAN_HAS_CHERT_BACKEND
1512 const string dbdir = ".chert/test_chertdatabaseopen1";
1513 mkdir(".chert", 0755);
1516 rm_rf(dbdir);
1517 Xapian::WritableDatabase wdb(dbdir,
1518 Xapian::DB_CREATE|Xapian::DB_BACKEND_CHERT);
1519 TEST_EXCEPTION(Xapian::DatabaseLockError,
1520 Xapian::WritableDatabase(dbdir,
1521 Xapian::DB_OPEN|Xapian::DB_BACKEND_CHERT));
1522 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1526 rm_rf(dbdir);
1527 Xapian::WritableDatabase wdb(dbdir,
1528 Xapian::DB_CREATE_OR_OPEN|Xapian::DB_BACKEND_CHERT);
1529 TEST_EXCEPTION(Xapian::DatabaseLockError,
1530 Xapian::WritableDatabase(dbdir,
1531 Xapian::DB_CREATE_OR_OVERWRITE|Xapian::DB_BACKEND_CHERT));
1532 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1536 rm_rf(dbdir);
1537 Xapian::WritableDatabase wdb(dbdir,
1538 Xapian::DB_CREATE_OR_OVERWRITE|Xapian::DB_BACKEND_CHERT);
1539 TEST_EXCEPTION(Xapian::DatabaseLockError,
1540 Xapian::WritableDatabase(dbdir,
1541 Xapian::DB_CREATE_OR_OPEN|Xapian::DB_BACKEND_CHERT));
1542 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1546 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1547 Xapian::WritableDatabase(dbdir,
1548 Xapian::DB_CREATE|Xapian::DB_BACKEND_CHERT));
1549 Xapian::WritableDatabase wdb(dbdir,
1550 Xapian::DB_CREATE_OR_OVERWRITE|Xapian::DB_BACKEND_CHERT);
1551 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1555 Xapian::WritableDatabase wdb(dbdir,
1556 Xapian::DB_CREATE_OR_OPEN|Xapian::DB_BACKEND_CHERT);
1557 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1561 Xapian::WritableDatabase wdb(dbdir,
1562 Xapian::DB_OPEN|Xapian::DB_BACKEND_CHERT);
1563 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1565 #endif
1567 return true;
1570 // feature test for Enquire:
1571 // set_sort_by_value
1572 // set_sort_by_value_then_relevance
1573 // set_sort_by_relevance_then_value
1574 // Prior to 1.2.17 and 1.3.2, order8 and order9 were swapped, and
1575 // set_sort_by_relevance_then_value was buggy, so this testcase now serves as
1576 // a regression test for that bug.
1577 DEFINE_TESTCASE(sortrel1, backend) {
1578 Xapian::Enquire enquire(get_database("apitest_sortrel"));
1579 enquire.set_sort_by_value(1, true);
1580 enquire.set_query(Xapian::Query("woman"));
1582 const Xapian::docid order1[] = { 1,2,3,4,5,6,7,8,9 };
1583 const Xapian::docid order2[] = { 2,1,3,6,5,4,7,9,8 };
1584 const Xapian::docid order3[] = { 3,2,1,6,5,4,9,8,7 };
1585 const Xapian::docid order4[] = { 7,8,9,4,5,6,1,2,3 };
1586 const Xapian::docid order5[] = { 9,8,7,6,5,4,3,2,1 };
1587 const Xapian::docid order6[] = { 7,9,8,6,5,4,2,1,3 };
1588 const Xapian::docid order7[] = { 7,9,8,6,5,4,2,1,3 };
1589 const Xapian::docid order8[] = { 2,6,7,1,5,9,3,4,8 };
1590 const Xapian::docid order9[] = { 7,6,2,9,5,1,8,4,3 };
1592 Xapian::MSet mset;
1593 size_t i;
1595 mset = enquire.get_mset(0, 10);
1596 TEST_EQUAL(mset.size(), sizeof(order1) / sizeof(Xapian::docid));
1597 for (i = 0; i < sizeof(order1) / sizeof(Xapian::docid); ++i) {
1598 TEST_EQUAL(*mset[i], order1[i]);
1601 enquire.set_sort_by_value_then_relevance(1, true);
1603 mset = enquire.get_mset(0, 10);
1604 TEST_EQUAL(mset.size(), sizeof(order2) / sizeof(Xapian::docid));
1605 for (i = 0; i < sizeof(order2) / sizeof(Xapian::docid); ++i) {
1606 TEST_EQUAL(*mset[i], order2[i]);
1609 enquire.set_sort_by_value(1, true);
1611 mset = enquire.get_mset(0, 10);
1612 TEST_EQUAL(mset.size(), sizeof(order1) / sizeof(Xapian::docid));
1613 for (i = 0; i < sizeof(order1) / sizeof(Xapian::docid); ++i) {
1614 TEST_EQUAL(*mset[i], order1[i]);
1617 enquire.set_sort_by_value_then_relevance(1, true);
1618 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1620 mset = enquire.get_mset(0, 10);
1621 TEST_EQUAL(mset.size(), sizeof(order2) / sizeof(Xapian::docid));
1622 for (i = 0; i < sizeof(order2) / sizeof(Xapian::docid); ++i) {
1623 TEST_EQUAL(*mset[i], order2[i]);
1626 enquire.set_sort_by_value(1, true);
1627 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1629 mset = enquire.get_mset(0, 10);
1630 TEST_EQUAL(mset.size(), sizeof(order3) / sizeof(Xapian::docid));
1631 for (i = 0; i < sizeof(order3) / sizeof(Xapian::docid); ++i) {
1632 TEST_EQUAL(*mset[i], order3[i]);
1635 enquire.set_sort_by_value(1, false);
1636 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1637 mset = enquire.get_mset(0, 10);
1638 TEST_EQUAL(mset.size(), sizeof(order4) / sizeof(Xapian::docid));
1639 for (i = 0; i < sizeof(order4) / sizeof(Xapian::docid); ++i) {
1640 TEST_EQUAL(*mset[i], order4[i]);
1643 enquire.set_sort_by_value(1, false);
1644 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1645 mset = enquire.get_mset(0, 10);
1646 TEST_EQUAL(mset.size(), sizeof(order5) / sizeof(Xapian::docid));
1647 for (i = 0; i < sizeof(order5) / sizeof(Xapian::docid); ++i) {
1648 TEST_EQUAL(*mset[i], order5[i]);
1651 enquire.set_sort_by_value_then_relevance(1, false);
1652 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1653 mset = enquire.get_mset(0, 10);
1654 TEST_EQUAL(mset.size(), sizeof(order6) / sizeof(Xapian::docid));
1655 for (i = 0; i < sizeof(order6) / sizeof(Xapian::docid); ++i) {
1656 TEST_EQUAL(*mset[i], order6[i]);
1659 enquire.set_sort_by_value_then_relevance(1, false);
1660 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1661 mset = enquire.get_mset(0, 10);
1662 TEST_EQUAL(mset.size(), sizeof(order7) / sizeof(Xapian::docid));
1663 for (i = 0; i < sizeof(order7) / sizeof(Xapian::docid); ++i) {
1664 TEST_EQUAL(*mset[i], order7[i]);
1667 enquire.set_sort_by_relevance_then_value(1, true);
1668 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1669 mset = enquire.get_mset(0, 10);
1670 TEST_EQUAL(mset.size(), sizeof(order8) / sizeof(Xapian::docid));
1671 for (i = 0; i < sizeof(order8) / sizeof(Xapian::docid); ++i) {
1672 TEST_EQUAL(*mset[i], order8[i]);
1675 enquire.set_sort_by_relevance_then_value(1, true);
1676 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1677 mset = enquire.get_mset(0, 10);
1678 TEST_EQUAL(mset.size(), sizeof(order8) / sizeof(Xapian::docid));
1679 for (i = 0; i < sizeof(order8) / sizeof(Xapian::docid); ++i) {
1680 TEST_EQUAL(*mset[i], order8[i]);
1683 enquire.set_sort_by_relevance_then_value(1, false);
1684 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1685 mset = enquire.get_mset(0, 10);
1686 TEST_EQUAL(mset.size(), sizeof(order9) / sizeof(Xapian::docid));
1687 for (i = 0; i < sizeof(order9) / sizeof(Xapian::docid); ++i) {
1688 TEST_EQUAL(*mset[i], order9[i]);
1691 enquire.set_sort_by_relevance_then_value(1, false);
1692 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1693 mset = enquire.get_mset(0, 10);
1694 TEST_EQUAL(mset.size(), sizeof(order9) / sizeof(Xapian::docid));
1695 for (i = 0; i < sizeof(order9) / sizeof(Xapian::docid); ++i) {
1696 TEST_EQUAL(*mset[i], order9[i]);
1699 return true;
1702 // Test network stats and local stats give the same results.
1703 DEFINE_TESTCASE(netstats1, remote) {
1704 BackendManagerLocal local_manager;
1705 local_manager.set_datadir(test_driver::get_srcdir() + "/testdata/");
1707 const char * words[] = { "paragraph", "word" };
1708 Xapian::Query query(Xapian::Query::OP_OR, words, words + 2);
1709 const size_t MSET_SIZE = 10;
1711 Xapian::RSet rset;
1712 rset.add_document(4);
1713 rset.add_document(9);
1715 Xapian::MSet mset_alllocal;
1717 Xapian::Database db;
1718 db.add_database(local_manager.get_database("apitest_simpledata"));
1719 db.add_database(local_manager.get_database("apitest_simpledata2"));
1721 Xapian::Enquire enq(db);
1722 enq.set_query(query);
1723 mset_alllocal = enq.get_mset(0, MSET_SIZE, &rset);
1727 Xapian::Database db;
1728 db.add_database(local_manager.get_database("apitest_simpledata"));
1729 db.add_database(get_database("apitest_simpledata2"));
1731 Xapian::Enquire enq(db);
1732 enq.set_query(query);
1733 Xapian::MSet mset = enq.get_mset(0, MSET_SIZE, &rset);
1734 TEST_EQUAL(mset.get_matches_lower_bound(), mset_alllocal.get_matches_lower_bound());
1735 TEST_EQUAL(mset.get_matches_upper_bound(), mset_alllocal.get_matches_upper_bound());
1736 TEST_EQUAL(mset.get_matches_estimated(), mset_alllocal.get_matches_estimated());
1737 TEST_EQUAL(mset.get_max_attained(), mset_alllocal.get_max_attained());
1738 TEST_EQUAL(mset.size(), mset_alllocal.size());
1739 TEST(mset_range_is_same(mset, 0, mset_alllocal, 0, mset.size()));
1743 Xapian::Database db;
1744 db.add_database(get_database("apitest_simpledata"));
1745 db.add_database(local_manager.get_database("apitest_simpledata2"));
1747 Xapian::Enquire enq(db);
1748 enq.set_query(query);
1749 Xapian::MSet mset = enq.get_mset(0, MSET_SIZE, &rset);
1750 TEST_EQUAL(mset.get_matches_lower_bound(), mset_alllocal.get_matches_lower_bound());
1751 TEST_EQUAL(mset.get_matches_upper_bound(), mset_alllocal.get_matches_upper_bound());
1752 TEST_EQUAL(mset.get_matches_estimated(), mset_alllocal.get_matches_estimated());
1753 TEST_EQUAL(mset.get_max_attained(), mset_alllocal.get_max_attained());
1754 TEST_EQUAL(mset.size(), mset_alllocal.size());
1755 TEST(mset_range_is_same(mset, 0, mset_alllocal, 0, mset.size()));
1759 Xapian::Database db;
1760 db.add_database(get_database("apitest_simpledata"));
1761 db.add_database(get_database("apitest_simpledata2"));
1763 Xapian::Enquire enq(db);
1764 enq.set_query(query);
1765 Xapian::MSet mset = enq.get_mset(0, MSET_SIZE, &rset);
1766 TEST_EQUAL(mset.get_matches_lower_bound(), mset_alllocal.get_matches_lower_bound());
1767 TEST_EQUAL(mset.get_matches_upper_bound(), mset_alllocal.get_matches_upper_bound());
1768 TEST_EQUAL(mset.get_matches_estimated(), mset_alllocal.get_matches_estimated());
1769 TEST_EQUAL(mset.get_max_attained(), mset_alllocal.get_max_attained());
1770 TEST_EQUAL(mset.size(), mset_alllocal.size());
1771 TEST(mset_range_is_same(mset, 0, mset_alllocal, 0, mset.size()));
1774 return true;
1777 // Coordinate matching - scores 1 for each matching term
1778 class MyWeight : public Xapian::Weight {
1779 double scale_factor;
1781 public:
1782 MyWeight * clone() const {
1783 return new MyWeight;
1785 void init(double factor) {
1786 scale_factor = factor;
1788 MyWeight() { }
1789 ~MyWeight() { }
1790 std::string name() const { return "MyWeight"; }
1791 string serialise() const { return string(); }
1792 MyWeight * unserialise(const string &) const { return new MyWeight; }
1793 double get_sumpart(Xapian::termcount, Xapian::termcount) const {
1794 return scale_factor;
1796 double get_sumpart(Xapian::termcount, Xapian::termcount, Xapian::termcount) const {
1797 return scale_factor;
1799 double get_maxpart() const { return scale_factor; }
1801 double get_sumextra(Xapian::termcount, Xapian::termcount) const { return 0; }
1802 double get_maxextra() const { return 0; }
1805 // tests user weighting scheme.
1806 // Would work with remote if we registered the weighting scheme.
1807 // FIXME: do this so we also test that functionality...
1808 DEFINE_TESTCASE(userweight1, backend && !remote) {
1809 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1810 enquire.set_weighting_scheme(MyWeight());
1811 const char * query[] = { "this", "line", "paragraph", "rubbish" };
1812 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR, query,
1813 query + sizeof(query) / sizeof(query[0])));
1814 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
1815 // MyWeight scores 1 for each matching term, so the weight should equal
1816 // the number of matching terms.
1817 for (Xapian::MSetIterator i = mymset1.begin(); i != mymset1.end(); ++i) {
1818 Xapian::termcount matching_terms = 0;
1819 Xapian::TermIterator t = enquire.get_matching_terms_begin(i);
1820 while (t != enquire.get_matching_terms_end(i)) {
1821 ++matching_terms;
1822 ++t;
1824 TEST_EQUAL(i.get_weight(), matching_terms);
1827 return true;
1830 // tests MatchAll queries
1831 // This is a regression test, which failed with assertion failures in
1832 // revision 9094. Also check that the results aren't ranked by relevance
1833 // (regression test for bug fixed in 1.0.9).
1834 DEFINE_TESTCASE(matchall1, backend) {
1835 Xapian::Database db(get_database("apitest_simpledata"));
1836 Xapian::Enquire enquire(db);
1837 enquire.set_query(Xapian::Query::MatchAll);
1838 Xapian::MSet mset = enquire.get_mset(0, 10);
1839 TEST_EQUAL(mset.get_matches_lower_bound(), db.get_doccount());
1840 TEST_EQUAL(mset.get_uncollapsed_matches_lower_bound(), db.get_doccount());
1842 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR,
1843 Xapian::Query("nosuchterm"),
1844 Xapian::Query::MatchAll));
1845 mset = enquire.get_mset(0, 10);
1846 TEST_EQUAL(mset.get_matches_lower_bound(), db.get_doccount());
1847 TEST_EQUAL(mset.get_uncollapsed_matches_lower_bound(), db.get_doccount());
1849 // Check that the results aren't ranked by relevance (fixed in 1.0.9).
1850 TEST(mset.size() > 1);
1851 TEST_EQUAL(mset[mset.size() - 1].get_weight(), 0);
1852 TEST_EQUAL(*mset[0], 1);
1853 TEST_EQUAL(*mset[mset.size() - 1], mset.size());
1855 return true;
1858 // Test using a ValueSetMatchDecider
1859 DEFINE_TESTCASE(valuesetmatchdecider2, backend && !remote) {
1860 Xapian::Database db(get_database("apitest_phrase"));
1861 Xapian::Enquire enq(db);
1862 enq.set_query(Xapian::Query("leav"));
1864 Xapian::ValueSetMatchDecider vsmd1(1, true);
1865 vsmd1.add_value("n");
1866 Xapian::ValueSetMatchDecider vsmd2(1, false);
1867 vsmd2.add_value("n");
1869 Xapian::MSet mymset = enq.get_mset(0, 20);
1870 mset_expect_order(mymset, 8, 6, 4, 5, 7, 10, 12, 11, 13, 9, 14);
1871 mymset = enq.get_mset(0, 20, 0, NULL, &vsmd1);
1872 mset_expect_order(mymset, 6, 12);
1873 mymset = enq.get_mset(0, 20, 0, NULL, &vsmd2);
1874 mset_expect_order(mymset, 8, 4, 5, 7, 10, 11, 13, 9, 14);
1876 return true;