Fix whitespace irregularities in code
[xapian.git] / xapian-core / tests / api_db.cc
blob5f589f2a53a40fd0e0c4556e89a9b354e9c8f4e7
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,2016 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 "safenetdb.h" // For gai_strerror().
34 #include "safesysstat.h" // For mkdir().
35 #include "safeunistd.h" // For sleep().
37 #include <xapian.h>
39 #include "backendmanager.h"
40 #include "backendmanager_local.h"
41 #include "testsuite.h"
42 #include "testutils.h"
43 #include "unixcmds.h"
45 #include "apitest.h"
47 using namespace std;
49 static Xapian::Query
50 query(const string &t)
52 return Xapian::Query(Xapian::Stem("english")(t));
55 // #######################################################################
56 // # Tests start here
58 // tests Xapian::Database::get_termfreq() and Xapian::Database::term_exists()
59 DEFINE_TESTCASE(termstats, backend) {
60 Xapian::Database db(get_database("apitest_simpledata"));
62 TEST(!db.term_exists("corn"));
63 TEST_EQUAL(db.get_termfreq("corn"), 0);
64 TEST(db.term_exists("banana"));
65 TEST_EQUAL(db.get_termfreq("banana"), 1);
66 TEST(db.term_exists("paragraph"));
67 TEST_EQUAL(db.get_termfreq("paragraph"), 5);
69 return true;
72 // Check that stub databases work.
73 DEFINE_TESTCASE(stubdb1, backend && !inmemory && !remote) {
74 // Only works for backends which have a path.
75 mkdir(".stub", 0755);
76 const char * dbpath = ".stub/stubdb1";
77 ofstream out(dbpath);
78 TEST(out.is_open());
79 out << "auto ../" << get_database_path("apitest_simpledata") << endl;
80 out.close();
83 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB);
84 Xapian::Enquire enquire(db);
85 enquire.set_query(Xapian::Query("word"));
86 enquire.get_mset(0, 10);
89 Xapian::Database db(dbpath);
90 Xapian::Enquire enquire(db);
91 enquire.set_query(Xapian::Query("word"));
92 enquire.get_mset(0, 10);
95 return true;
98 // Check that stub databases work remotely.
99 DEFINE_TESTCASE(stubdb2, backend && !inmemory && !remote) {
100 // Only works for backends which have a path.
101 mkdir(".stub", 0755);
102 const char * dbpath = ".stub/stubdb2";
103 ofstream out(dbpath);
104 TEST(out.is_open());
105 out << "remote :" << BackendManager::get_xapian_progsrv_command()
106 << ' ' << get_database_path("apitest_simpledata") << endl;
107 out.close();
110 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB);
111 Xapian::Enquire enquire(db);
112 enquire.set_query(Xapian::Query("word"));
113 enquire.get_mset(0, 10);
116 Xapian::Database db(dbpath);
117 Xapian::Enquire enquire(db);
118 enquire.set_query(Xapian::Query("word"));
119 enquire.get_mset(0, 10);
122 out.open(dbpath);
123 TEST(out.is_open());
124 out << "remote" << endl;
125 out.close();
127 // Quietly ignored prior to 1.4.1.
128 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
129 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB)
132 // Quietly ignored prior to 1.4.1.
133 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
134 Xapian::WritableDatabase db(dbpath, Xapian::DB_BACKEND_STUB)
137 out.open(dbpath);
138 TEST(out.is_open());
139 out << "remote foo" << endl;
140 out.close();
142 // Quietly ignored prior to 1.4.1.
143 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
144 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB)
147 // Quietly ignored prior to 1.4.1.
148 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
149 Xapian::WritableDatabase db(dbpath, Xapian::DB_BACKEND_STUB)
152 out.open(dbpath);
153 TEST(out.is_open());
154 out << "remote [::1]:80" << endl;
155 out.close();
157 try {
158 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB);
159 } catch (const Xapian::NetworkError& e) {
160 // 1.4.0 threw (Linux):
161 // NetworkError: Couldn't resolve host [ (context: remote:tcp([:0)) (No address associated with hostname)
162 // 1.4.1 throws (because we don't actually support IPv6 yet) on Linux (EAI_ADDRFAMILY):
163 // NetworkError: Couldn't resolve host ::1 (context: remote:tcp(::1:80)) (nodename nor servname provided, or not known)
164 // or on OS X (EAI_NONAME):
165 // NetworkError: Couldn't resolve host ::1 (context: remote:tcp(::1:80)) (Address family for hostname not supported)
166 // So we test the message instead of the error string for portability.
167 TEST(e.get_msg().find("host ::1") != string::npos);
170 try {
171 Xapian::WritableDatabase db(dbpath, Xapian::DB_BACKEND_STUB);
172 } catch (const Xapian::NetworkError& e) {
173 // 1.4.0 threw (Linux):
174 // NetworkError: Couldn't resolve host [ (context: remote:tcp([:0)) (No address associated with hostname)
175 // 1.4.1 throws (because we don't actually support IPv6 yet) on Linux (EAI_ADDRFAMILY):
176 // NetworkError: Couldn't resolve host ::1 (context: remote:tcp(::1:80)) (nodename nor servname provided, or not known)
177 // or on OS X (EAI_NONAME):
178 // NetworkError: Couldn't resolve host ::1 (context: remote:tcp(::1:80)) (Address family for hostname not supported)
179 // So we test the message instead of the error string for portability.
180 TEST(e.get_msg().find("host ::1") != string::npos);
183 out.open(dbpath);
184 TEST(out.is_open());
185 // Invalid - the port number is required.
186 out << "remote [::1]" << endl;
187 out.close();
189 // 1.4.0 threw:
190 // NetworkError: Couldn't resolve host [ (context: remote:tcp([:0)) (No address associated with hostname)
191 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
192 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB);
195 // 1.4.0 threw:
196 // NetworkError: Couldn't resolve host [ (context: remote:tcp([:0)) (No address associated with hostname)
197 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
198 Xapian::WritableDatabase db(dbpath, Xapian::DB_BACKEND_STUB);
201 return true;
204 // Regression test - bad entries were ignored after a good entry prior to 1.0.8.
205 DEFINE_TESTCASE(stubdb3, backend && !inmemory && !remote) {
206 // Only works for backends which have a path.
207 mkdir(".stub", 0755);
208 const char * dbpath = ".stub/stubdb3";
209 ofstream out(dbpath);
210 TEST(out.is_open());
211 out << "auto ../" << get_database_path("apitest_simpledata") << "\n"
212 "bad line here\n";
213 out.close();
215 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
216 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB));
218 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
219 Xapian::Database db(dbpath));
221 return true;
224 // Test a stub database with just a bad entry.
225 DEFINE_TESTCASE(stubdb4, backend && !inmemory && !remote) {
226 // Only works for backends which have a path.
227 mkdir(".stub", 0755);
228 const char * dbpath = ".stub/stubdb4";
229 ofstream out(dbpath);
230 TEST(out.is_open());
231 out << "bad line here\n";
232 out.close();
234 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
235 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB));
237 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
238 Xapian::Database db(dbpath));
240 return true;
243 // Test a stub database with a bad entry with no spaces (prior to 1.1.0 this
244 // was deliberately allowed, though not documented.
245 DEFINE_TESTCASE(stubdb5, backend && !inmemory && !remote) {
246 // Only works for backends which have a path.
247 mkdir(".stub", 0755);
248 const char * dbpath = ".stub/stubdb5";
249 ofstream out(dbpath);
250 TEST(out.is_open());
251 out << "bad\n"
252 "auto ../" << get_database_path("apitest_simpledata") << endl;
253 out.close();
255 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
256 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB));
258 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
259 Xapian::Database db(dbpath));
261 return true;
264 // Test a stub database with an inmemory database (new feature in 1.1.0).
265 DEFINE_TESTCASE(stubdb6, inmemory) {
266 mkdir(".stub", 0755);
267 const char * dbpath = ".stub/stubdb6";
268 ofstream out(dbpath);
269 TEST(out.is_open());
270 out << "inmemory\n";
271 out.close();
273 // Read-only tests:
275 Xapian::Database db(dbpath, Xapian::DB_BACKEND_STUB);
276 TEST_EQUAL(db.get_doccount(), 0);
277 Xapian::Enquire enquire(db);
278 enquire.set_query(Xapian::Query("word"));
279 Xapian::MSet mset = enquire.get_mset(0, 10);
280 TEST(mset.empty());
283 Xapian::Database db(dbpath);
284 TEST_EQUAL(db.get_doccount(), 0);
285 Xapian::Enquire enquire(db);
286 enquire.set_query(Xapian::Query("word"));
287 Xapian::MSet mset = enquire.get_mset(0, 10);
288 TEST(mset.empty());
291 // Writable tests:
293 Xapian::WritableDatabase db(dbpath,
294 Xapian::DB_OPEN|Xapian::DB_BACKEND_STUB);
295 TEST_EQUAL(db.get_doccount(), 0);
296 db.add_document(Xapian::Document());
297 TEST_EQUAL(db.get_doccount(), 1);
300 Xapian::WritableDatabase db(dbpath,
301 Xapian::DB_OPEN|Xapian::DB_BACKEND_STUB);
302 TEST_EQUAL(db.get_doccount(), 0);
303 db.add_document(Xapian::Document());
304 TEST_EQUAL(db.get_doccount(), 1);
307 return true;
310 #if 0 // the "force error" mechanism is no longer in place...
311 class MyErrorHandler : public Xapian::ErrorHandler {
312 public:
313 int count;
315 bool handle_error(Xapian::Error & error) {
316 ++count;
317 tout << "Error handling caught: " << error.get_description()
318 << ", count is now " << count << "\n";
319 return true;
322 MyErrorHandler() : count (0) {}
325 // tests error handler in multimatch().
326 DEFINE_TESTCASE(multierrhandler1, backend) {
327 MyErrorHandler myhandler;
329 Xapian::Database mydb2(get_database("apitest_simpledata"));
330 Xapian::Database mydb3(get_database("apitest_simpledata2"));
331 int errcount = 1;
332 for (int testcount = 0; testcount < 14; testcount ++) {
333 tout << "testcount=" << testcount << "\n";
334 Xapian::Database mydb4(get_database("-e", "apitest_termorder"));
335 Xapian::Database mydb5(get_network_database("apitest_termorder", 1));
336 Xapian::Database mydb6(get_database("-e2", "apitest_termorder"));
337 Xapian::Database mydb7(get_database("-e3", "apitest_simpledata"));
339 Xapian::Database dbs;
340 switch (testcount) {
341 case 0:
342 dbs.add_database(mydb2);
343 dbs.add_database(mydb3);
344 dbs.add_database(mydb4);
345 break;
346 case 1:
347 dbs.add_database(mydb4);
348 dbs.add_database(mydb2);
349 dbs.add_database(mydb3);
350 break;
351 case 2:
352 dbs.add_database(mydb3);
353 dbs.add_database(mydb4);
354 dbs.add_database(mydb2);
355 break;
356 case 3:
357 dbs.add_database(mydb2);
358 dbs.add_database(mydb3);
359 dbs.add_database(mydb5);
360 sleep(1);
361 break;
362 case 4:
363 dbs.add_database(mydb5);
364 dbs.add_database(mydb2);
365 dbs.add_database(mydb3);
366 sleep(1);
367 break;
368 case 5:
369 dbs.add_database(mydb3);
370 dbs.add_database(mydb5);
371 dbs.add_database(mydb2);
372 sleep(1);
373 break;
374 case 6:
375 dbs.add_database(mydb2);
376 dbs.add_database(mydb3);
377 dbs.add_database(mydb6);
378 break;
379 case 7:
380 dbs.add_database(mydb6);
381 dbs.add_database(mydb2);
382 dbs.add_database(mydb3);
383 break;
384 case 8:
385 dbs.add_database(mydb3);
386 dbs.add_database(mydb6);
387 dbs.add_database(mydb2);
388 break;
389 case 9:
390 dbs.add_database(mydb2);
391 dbs.add_database(mydb3);
392 dbs.add_database(mydb7);
393 break;
394 case 10:
395 dbs.add_database(mydb7);
396 dbs.add_database(mydb2);
397 dbs.add_database(mydb3);
398 break;
399 case 11:
400 dbs.add_database(mydb3);
401 dbs.add_database(mydb7);
402 dbs.add_database(mydb2);
403 break;
404 case 12:
405 dbs.add_database(mydb2);
406 dbs.add_database(mydb6);
407 dbs.add_database(mydb7);
408 break;
409 case 13:
410 dbs.add_database(mydb2);
411 dbs.add_database(mydb7);
412 dbs.add_database(mydb6);
413 break;
415 tout << "db=" << dbs << "\n";
416 Xapian::Enquire enquire(dbs, &myhandler);
418 // make a query
419 Xapian::Query myquery = query(Xapian::Query::OP_OR, "inmemory", "word");
420 enquire.set_weighting_scheme(Xapian::BoolWeight());
421 enquire.set_query(myquery);
423 tout << "query=" << myquery << "\n";
424 // retrieve the top ten results
425 Xapian::MSet mymset = enquire.get_mset(0, 10);
427 switch (testcount) {
428 case 0: case 3: case 6: case 9:
429 mset_expect_order(mymset, 2, 4, 10);
430 break;
431 case 1: case 4: case 7: case 10:
432 mset_expect_order(mymset, 3, 5, 11);
433 break;
434 case 2: case 5: case 8: case 11:
435 mset_expect_order(mymset, 1, 6, 12);
436 break;
437 case 12:
438 case 13:
439 mset_expect_order(mymset, 4, 10);
440 errcount += 1;
441 break;
443 TEST_EQUAL(myhandler.count, errcount);
444 errcount += 1;
447 return true;
449 #endif
451 class myMatchDecider : public Xapian::MatchDecider {
452 public:
453 bool operator()(const Xapian::Document &doc) const {
454 // Note that this is not recommended usage of get_data()
455 return doc.get_data().find("This is") != string::npos;
459 // Test Xapian::MatchDecider functor.
460 DEFINE_TESTCASE(matchdecider1, backend && !remote) {
461 Xapian::Database db(get_database("apitest_simpledata"));
462 Xapian::Enquire enquire(db);
463 enquire.set_query(Xapian::Query("this"));
465 myMatchDecider myfunctor;
467 Xapian::MSet mymset = enquire.get_mset(0, 100, 0, &myfunctor);
469 vector<bool> docid_checked(db.get_lastdocid());
471 // Check that we get the expected number of matches, and that they
472 // satisfy the condition.
473 Xapian::MSetIterator i = mymset.begin();
474 TEST(i != mymset.end());
475 TEST_EQUAL(mymset.size(), 3);
476 TEST_EQUAL(mymset.get_matches_lower_bound(), 3);
477 TEST_EQUAL(mymset.get_matches_upper_bound(), 3);
478 TEST_EQUAL(mymset.get_matches_estimated(), 3);
479 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 3);
480 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 3);
481 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 3);
482 for ( ; i != mymset.end(); ++i) {
483 const Xapian::Document doc(i.get_document());
484 TEST(myfunctor(doc));
485 docid_checked[*i] = true;
488 // Check that there are some documents which aren't accepted by the match
489 // decider.
490 mymset = enquire.get_mset(0, 100);
491 TEST(mymset.size() > 3);
493 // Check that the bounds are appropriate even if we don't ask for any
494 // actual matches.
495 mymset = enquire.get_mset(0, 0, 0, &myfunctor);
496 TEST_EQUAL(mymset.size(), 0);
497 TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
498 TEST_EQUAL(mymset.get_matches_upper_bound(), 6);
499 TEST_REL(mymset.get_matches_estimated(),>,0);
500 TEST_REL(mymset.get_matches_estimated(),<=,6);
501 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 0);
502 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 6);
503 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
504 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
506 // Check that the bounds are appropriate if we ask for only one hit.
507 // (Regression test - until SVN 10256, we didn't reduce the lower_bound
508 // appropriately, and returned 6 here.)
509 mymset = enquire.get_mset(0, 1, 0, &myfunctor);
510 TEST_EQUAL(mymset.size(), 1);
511 TEST_REL(mymset.get_matches_lower_bound(),>=,1);
512 TEST_REL(mymset.get_matches_lower_bound(),<=,3);
513 TEST_REL(mymset.get_matches_upper_bound(),>=,3);
514 TEST_REL(mymset.get_matches_upper_bound(),<=,6);
515 TEST_REL(mymset.get_matches_estimated(),>,0);
516 TEST_REL(mymset.get_matches_estimated(),<=,6);
517 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,1);
518 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),<=,3);
519 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),>=,3);
520 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),<=,6);
521 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
522 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
524 // Check that the other documents don't satisfy the condition.
525 for (Xapian::docid did = 1; did < docid_checked.size(); ++did) {
526 if (!docid_checked[did]) {
527 TEST(!myfunctor(db.get_document(did)));
531 // Check that the bounds are appropriate if a collapse key is used.
532 // Use a value which is never set so we don't actually discard anything.
533 enquire.set_collapse_key(99);
534 mymset = enquire.get_mset(0, 1, 0, &myfunctor);
535 TEST_EQUAL(mymset.size(), 1);
536 TEST_REL(mymset.get_matches_lower_bound(),>=,1);
537 TEST_REL(mymset.get_matches_lower_bound(),<=,3);
538 TEST_REL(mymset.get_matches_upper_bound(),>=,3);
539 TEST_REL(mymset.get_matches_upper_bound(),<=,6);
540 TEST_REL(mymset.get_matches_estimated(),>,0);
541 TEST_REL(mymset.get_matches_estimated(),<=,6);
542 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,1);
543 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),<=,3);
544 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),>=,3);
545 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),<=,6);
546 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
547 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
549 // Check that the bounds are appropriate if a percentage cutoff is in
550 // use. Set a 1% threshold so we don't actually discard anything.
551 enquire.set_collapse_key(Xapian::BAD_VALUENO);
552 enquire.set_cutoff(1);
553 mymset = enquire.get_mset(0, 1, 0, &myfunctor);
554 TEST_EQUAL(mymset.size(), 1);
555 TEST_REL(mymset.get_matches_lower_bound(),>=,1);
556 TEST_REL(mymset.get_matches_lower_bound(),<=,3);
557 TEST_REL(mymset.get_matches_upper_bound(),>=,3);
558 TEST_REL(mymset.get_matches_upper_bound(),<=,6);
559 TEST_REL(mymset.get_matches_estimated(),>,0);
560 TEST_REL(mymset.get_matches_estimated(),<=,6);
561 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,1);
562 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),<=,3);
563 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),>=,3);
564 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),<=,6);
565 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
566 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
568 // And now with both a collapse key and percentage cutoff.
569 enquire.set_collapse_key(99);
570 mymset = enquire.get_mset(0, 1, 0, &myfunctor);
571 TEST_EQUAL(mymset.size(), 1);
572 TEST_REL(mymset.get_matches_lower_bound(),>=,1);
573 TEST_REL(mymset.get_matches_lower_bound(),<=,3);
574 TEST_REL(mymset.get_matches_upper_bound(),>=,3);
575 TEST_REL(mymset.get_matches_upper_bound(),<=,6);
576 TEST_REL(mymset.get_matches_estimated(),>,0);
577 TEST_REL(mymset.get_matches_estimated(),<=,6);
578 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,1);
579 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),<=,3);
580 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),>=,3);
581 TEST_REL(mymset.get_uncollapsed_matches_upper_bound(),<=,6);
582 TEST_REL(mymset.get_uncollapsed_matches_estimated(),>,0);
583 TEST_REL(mymset.get_uncollapsed_matches_estimated(),<=,6);
585 return true;
588 // Test Xapian::MatchDecider functor used as a match spy.
589 DEFINE_TESTCASE(matchdecider2, backend && !remote) {
590 Xapian::Database db(get_database("apitest_simpledata"));
591 Xapian::Enquire enquire(db);
592 enquire.set_query(Xapian::Query("this"));
594 myMatchDecider myfunctor;
596 Xapian::MSet mymset = enquire.get_mset(0, 100, 0, NULL, &myfunctor);
598 vector<bool> docid_checked(db.get_lastdocid());
600 // Check that we get the expected number of matches, and that they
601 // satisfy the condition.
602 Xapian::MSetIterator i = mymset.begin();
603 TEST(i != mymset.end());
604 TEST_EQUAL(mymset.size(), 3);
605 for ( ; i != mymset.end(); ++i) {
606 const Xapian::Document doc(i.get_document());
607 TEST(myfunctor(doc));
608 docid_checked[*i] = true;
611 // Check that the other documents don't satisfy the condition.
612 for (Xapian::docid did = 1; did < docid_checked.size(); ++did) {
613 if (!docid_checked[did]) {
614 TEST(!myfunctor(db.get_document(did)));
618 return true;
621 class myMatchDecider2 : public Xapian::MatchDecider {
622 public:
623 bool operator()(const Xapian::Document &doc) const {
624 // Note that this is not recommended usage of get_data()
625 return doc.get_data().find("We produce") == string::npos;
630 // Regression test for lower bound using functor, sorting and collapsing.
631 DEFINE_TESTCASE(matchdecider3, backend && !remote) {
632 Xapian::Database db(get_database("etext"));
633 Xapian::Enquire enquire(db);
634 enquire.set_query(Xapian::Query(""));
635 enquire.set_collapse_key(12);
636 enquire.set_sort_by_value(11, true);
638 myMatchDecider2 myfunctor;
640 Xapian::MSet mset1 = enquire.get_mset(0, 2, 0, NULL, &myfunctor);
641 Xapian::MSet mset2 = enquire.get_mset(0, 1000, 0, NULL, &myfunctor);
643 // mset2 should contain all the hits, so the statistics should be exact.
644 TEST_EQUAL(mset2.get_matches_estimated(), mset2.size());
645 TEST_EQUAL(mset2.get_matches_lower_bound(), mset2.get_matches_estimated());
646 TEST_EQUAL(mset2.get_matches_estimated(), mset2.get_matches_upper_bound());
648 TEST_REL(mset2.get_uncollapsed_matches_lower_bound(),<=,mset2.get_uncollapsed_matches_estimated());
649 TEST_REL(mset2.get_uncollapsed_matches_estimated(),<=,mset2.get_uncollapsed_matches_upper_bound());
651 // Check that the lower bound in mset1 is not greater than the known
652 // number of hits. This failed until revision 10811.
653 TEST_REL(mset1.get_matches_lower_bound(),<=,mset2.size());
655 // Check that the bounds for mset1 make sense.
656 TEST_REL(mset1.get_matches_lower_bound(),<=,mset1.get_matches_estimated());
657 TEST_REL(mset1.get_matches_estimated(),<=,mset1.get_matches_upper_bound());
658 TEST_REL(mset1.size(),<=,mset1.get_matches_upper_bound());
660 TEST_REL(mset1.get_uncollapsed_matches_lower_bound(),<=,mset1.get_uncollapsed_matches_estimated());
661 TEST_REL(mset1.get_uncollapsed_matches_estimated(),<=,mset1.get_uncollapsed_matches_upper_bound());
663 // The uncollapsed match would match all documents but the one the
664 // matchdecider rejects.
665 TEST_REL(mset1.get_uncollapsed_matches_upper_bound(),>=,db.get_doccount() - 1);
666 TEST_REL(mset1.get_uncollapsed_matches_upper_bound(),<=,db.get_doccount());
667 TEST_REL(mset2.get_uncollapsed_matches_upper_bound(),>=,db.get_doccount() - 1);
668 TEST_REL(mset2.get_uncollapsed_matches_upper_bound(),<=,db.get_doccount());
670 return true;
673 // tests that mset iterators on msets compare correctly.
674 DEFINE_TESTCASE(msetiterator1, backend) {
675 Xapian::Enquire enquire(get_database("apitest_simpledata"));
676 enquire.set_query(Xapian::Query("this"));
677 Xapian::MSet mymset = enquire.get_mset(0, 2);
679 Xapian::MSetIterator j;
680 j = mymset.begin();
681 Xapian::MSetIterator k = mymset.end();
682 Xapian::MSetIterator l(j);
683 Xapian::MSetIterator m(k);
684 Xapian::MSetIterator n = mymset.begin();
685 Xapian::MSetIterator o = mymset.begin();
686 TEST_NOT_EQUAL(j, k);
687 TEST_NOT_EQUAL(l, m);
688 TEST_EQUAL(k, m);
689 TEST_EQUAL(j, l);
690 TEST_EQUAL(j, j);
691 TEST_EQUAL(k, k);
693 k = j;
694 TEST_EQUAL(j, k);
695 TEST_EQUAL(j, o);
696 k++;
697 TEST_NOT_EQUAL(j, k);
698 TEST_NOT_EQUAL(k, l);
699 TEST_NOT_EQUAL(k, m);
700 TEST_NOT_EQUAL(k, o);
701 o++;
702 TEST_EQUAL(k, o);
703 k++;
704 TEST_NOT_EQUAL(j, k);
705 TEST_NOT_EQUAL(k, l);
706 TEST_EQUAL(k, m);
707 TEST_EQUAL(n, l);
709 n = m;
710 TEST_NOT_EQUAL(n, l);
711 TEST_EQUAL(n, m);
712 TEST_NOT_EQUAL(n, mymset.begin());
713 TEST_EQUAL(n, mymset.end());
715 return true;
718 // tests that mset iterators on empty msets compare equal.
719 DEFINE_TESTCASE(msetiterator2, backend) {
720 Xapian::Enquire enquire(get_database("apitest_simpledata"));
721 enquire.set_query(Xapian::Query("this"));
722 Xapian::MSet mymset = enquire.get_mset(0, 0);
724 Xapian::MSetIterator j = mymset.begin();
725 Xapian::MSetIterator k = mymset.end();
726 Xapian::MSetIterator l(j);
727 Xapian::MSetIterator m(k);
728 TEST_EQUAL(j, k);
729 TEST_EQUAL(l, m);
730 TEST_EQUAL(k, m);
731 TEST_EQUAL(j, l);
732 TEST_EQUAL(j, j);
733 TEST_EQUAL(k, k);
735 return true;
738 // tests that begin().get_document() works when first != 0
739 DEFINE_TESTCASE(msetiterator3, backend) {
740 Xapian::Database mydb(get_database("apitest_simpledata"));
741 Xapian::Enquire enquire(mydb);
742 enquire.set_query(Xapian::Query("this"));
744 Xapian::MSet mymset = enquire.get_mset(2, 10);
746 TEST(!mymset.empty());
747 Xapian::Document doc(mymset.begin().get_document());
748 TEST(!doc.get_data().empty());
750 return true;
753 // tests that eset iterators on empty esets compare equal.
754 DEFINE_TESTCASE(esetiterator1, backend) {
755 Xapian::Enquire enquire(get_database("apitest_simpledata"));
756 enquire.set_query(Xapian::Query("this"));
758 Xapian::MSet mymset = enquire.get_mset(0, 10);
759 TEST(mymset.size() >= 2);
761 Xapian::RSet myrset;
762 Xapian::MSetIterator i = mymset.begin();
763 myrset.add_document(*i);
764 myrset.add_document(*(++i));
766 Xapian::ESet myeset = enquire.get_eset(2, myrset);
767 Xapian::ESetIterator j;
768 j = myeset.begin();
769 Xapian::ESetIterator k = myeset.end();
770 Xapian::ESetIterator l(j);
771 Xapian::ESetIterator m(k);
772 Xapian::ESetIterator n = myeset.begin();
774 TEST_NOT_EQUAL(j, k);
775 TEST_NOT_EQUAL(l, m);
776 TEST_EQUAL(k, m);
777 TEST_EQUAL(j, l);
778 TEST_EQUAL(j, j);
779 TEST_EQUAL(k, k);
781 k = j;
782 TEST_EQUAL(j, k);
783 k++;
784 TEST_NOT_EQUAL(j, k);
785 TEST_NOT_EQUAL(k, l);
786 TEST_NOT_EQUAL(k, m);
787 k++;
788 TEST_NOT_EQUAL(j, k);
789 TEST_NOT_EQUAL(k, l);
790 TEST_EQUAL(k, m);
791 TEST_EQUAL(n, l);
793 n = m;
794 TEST_NOT_EQUAL(n, l);
795 TEST_EQUAL(n, m);
796 TEST_NOT_EQUAL(n, myeset.begin());
797 TEST_EQUAL(n, myeset.end());
799 return true;
802 // tests that eset iterators on empty esets compare equal.
803 DEFINE_TESTCASE(esetiterator2, backend) {
804 Xapian::Enquire enquire(get_database("apitest_simpledata"));
805 enquire.set_query(Xapian::Query("this"));
807 Xapian::MSet mymset = enquire.get_mset(0, 10);
808 TEST(mymset.size() >= 2);
810 Xapian::RSet myrset;
811 Xapian::MSetIterator i = mymset.begin();
812 myrset.add_document(*i);
813 myrset.add_document(*(++i));
815 Xapian::ESet myeset = enquire.get_eset(0, myrset);
816 Xapian::ESetIterator j = myeset.begin();
817 Xapian::ESetIterator k = myeset.end();
818 Xapian::ESetIterator l(j);
819 Xapian::ESetIterator m(k);
820 TEST_EQUAL(j, k);
821 TEST_EQUAL(l, m);
822 TEST_EQUAL(k, m);
823 TEST_EQUAL(j, l);
824 TEST_EQUAL(j, j);
825 TEST_EQUAL(k, k);
827 return true;
830 // tests the collapse-on-key
831 DEFINE_TESTCASE(collapsekey1, backend) {
832 Xapian::Enquire enquire(get_database("apitest_simpledata"));
833 enquire.set_query(Xapian::Query("this"));
835 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
836 Xapian::doccount mymsize1 = mymset1.size();
838 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
839 enquire.set_collapse_key(value_no);
840 Xapian::MSet mymset = enquire.get_mset(0, 100);
842 TEST_AND_EXPLAIN(mymsize1 > mymset.size(),
843 "Had no fewer items when performing collapse: don't know whether it worked.");
845 map<string, Xapian::docid> values;
846 Xapian::MSetIterator i = mymset.begin();
847 for ( ; i != mymset.end(); ++i) {
848 string value = i.get_document().get_value(value_no);
849 TEST(values[value] == 0 || value.empty());
850 values[value] = *i;
854 return true;
857 // tests that collapse-on-key modifies the predicted bounds for the number of
858 // matches appropriately.
859 DEFINE_TESTCASE(collapsekey2, backend) {
860 SKIP_TEST("Don't have a suitable database currently");
861 // FIXME: this needs an appropriate database creating, but that's quite
862 // subtle to do it seems.
863 Xapian::Enquire enquire(get_database("apitest_simpledata2"));
864 enquire.set_query(Xapian::Query("this"));
866 Xapian::MSet mset1 = enquire.get_mset(0, 1);
868 // Test that if no duplicates are found, then the upper bound remains
869 // unchanged and the lower bound drops.
871 enquire.set_query(Xapian::Query("this"));
872 Xapian::valueno value_no = 3;
873 enquire.set_collapse_key(value_no);
874 Xapian::MSet mset = enquire.get_mset(0, 1);
876 TEST_REL(mset.get_matches_lower_bound(),<,mset1.get_matches_lower_bound());
877 TEST_EQUAL(mset.get_matches_upper_bound(), mset1.get_matches_upper_bound());
880 return true;
883 // tests that collapse-on-key modifies the predicted bounds for the number of
884 // matches appropriately.
885 DEFINE_TESTCASE(collapsekey3, backend) {
886 Xapian::Enquire enquire(get_database("apitest_simpledata"));
887 enquire.set_query(Xapian::Query("this"));
889 Xapian::MSet mymset1 = enquire.get_mset(0, 3);
891 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
892 enquire.set_collapse_key(value_no);
893 Xapian::MSet mymset = enquire.get_mset(0, 3);
895 TEST_AND_EXPLAIN(mymset1.get_matches_lower_bound() > mymset.get_matches_lower_bound(),
896 "Lower bound was not lower when performing collapse: don't know whether it worked.");
897 TEST_AND_EXPLAIN(mymset1.get_matches_upper_bound() > mymset.get_matches_upper_bound(),
898 "Upper bound was not lower when performing collapse: don't know whether it worked.");
900 map<string, Xapian::docid> values;
901 Xapian::MSetIterator i = mymset.begin();
902 for ( ; i != mymset.end(); ++i) {
903 string value = i.get_document().get_value(value_no);
904 TEST(values[value] == 0 || value.empty());
905 values[value] = *i;
909 // Test that if the collapse value is always empty, then the upper bound
910 // remains unchanged, and the lower bound is the same or lower (it can be
911 // lower because the matcher counts the number of documents with empty
912 // collapse keys, but may have rejected a document because its weight is
913 // too low for the proto-MSet before it even looks at its collapse key).
915 Xapian::valueno value_no = 1000;
916 enquire.set_collapse_key(value_no);
917 Xapian::MSet mymset = enquire.get_mset(0, 3);
919 TEST(mymset.get_matches_lower_bound() <= mymset1.get_matches_lower_bound());
920 TEST_EQUAL(mymset.get_matches_upper_bound(), mymset1.get_matches_upper_bound());
922 map<string, Xapian::docid> values;
923 Xapian::MSetIterator i = mymset.begin();
924 for ( ; i != mymset.end(); ++i) {
925 string value = i.get_document().get_value(value_no);
926 TEST(values[value] == 0 || value.empty());
927 values[value] = *i;
931 return true;
934 // tests that collapse-on-key modifies the predicted bounds for the number of
935 // matches appropriately even when no results are requested.
936 DEFINE_TESTCASE(collapsekey4, backend) {
937 Xapian::Enquire enquire(get_database("apitest_simpledata"));
938 enquire.set_query(Xapian::Query("this"));
940 Xapian::MSet mymset1 = enquire.get_mset(0, 0);
942 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
943 enquire.set_collapse_key(value_no);
944 Xapian::MSet mymset = enquire.get_mset(0, 0);
946 TEST_AND_EXPLAIN(mymset.get_matches_lower_bound() == 1,
947 "Lower bound was not 1 when performing collapse but not asking for any results.");
948 TEST_AND_EXPLAIN(mymset1.get_matches_upper_bound() == mymset.get_matches_upper_bound(),
949 "Upper bound was changed when performing collapse but not asking for any results.");
951 map<string, Xapian::docid> values;
952 Xapian::MSetIterator i = mymset.begin();
953 for ( ; i != mymset.end(); ++i) {
954 string value = i.get_document().get_value(value_no);
955 TEST(values[value] == 0 || value.empty());
956 values[value] = *i;
960 return true;
963 // test for keepalives
964 DEFINE_TESTCASE(keepalive1, remote) {
965 Xapian::Database db(get_remote_database("apitest_simpledata", 5000));
967 /* Test that keep-alives work */
968 for (int i = 0; i < 10; ++i) {
969 sleep(2);
970 db.keep_alive();
972 Xapian::Enquire enquire(db);
973 enquire.set_query(Xapian::Query("word"));
974 enquire.get_mset(0, 10);
976 /* Test that things break without keepalives */
977 sleep(10);
978 enquire.set_query(Xapian::Query("word"));
979 TEST_EXCEPTION(Xapian::NetworkError,
980 enquire.get_mset(0, 10));
982 return true;
985 // test that iterating through all terms in a database works.
986 DEFINE_TESTCASE(allterms1, backend) {
987 Xapian::Database db(get_database("apitest_allterms"));
988 Xapian::TermIterator ati = db.allterms_begin();
989 TEST(ati != db.allterms_end());
990 TEST_EQUAL(*ati, "one");
991 TEST_EQUAL(ati.get_termfreq(), 1);
993 Xapian::TermIterator ati2 = ati;
995 ati++;
996 TEST(ati != db.allterms_end());
997 if (verbose) {
998 tout << "*ati = '" << *ati << "'\n";
999 tout << "*ati.length = '" << (*ati).length() << "'\n";
1000 tout << "*ati == \"one\" = " << (*ati == "one") << "\n";
1001 tout << "*ati[3] = " << ((*ati)[3]) << "\n";
1002 tout << "*ati = '" << *ati << "'\n";
1004 TEST(*ati == "three");
1005 TEST(ati.get_termfreq() == 3);
1007 #if 0
1008 TEST(ati2 != db.allterms_end());
1009 TEST(*ati2 == "one");
1010 TEST(ati2.get_termfreq() == 1);
1011 #endif
1013 ++ati;
1014 #if 0
1015 ++ati2;
1016 #endif
1017 TEST(ati != db.allterms_end());
1018 TEST(*ati == "two");
1019 TEST(ati.get_termfreq() == 2);
1021 #if 0
1022 TEST(ati2 != db.allterms_end());
1023 TEST(*ati2 == "three");
1024 TEST(ati2.get_termfreq() == 3);
1025 #endif
1027 ati++;
1028 TEST(ati == db.allterms_end());
1030 return true;
1033 // test that iterating through all terms in two databases works.
1034 DEFINE_TESTCASE(allterms2, backend) {
1035 Xapian::Database db;
1036 db.add_database(get_database("apitest_allterms"));
1037 db.add_database(get_database("apitest_allterms2"));
1038 Xapian::TermIterator ati = db.allterms_begin();
1040 TEST(ati != db.allterms_end());
1041 TEST(*ati == "five");
1042 TEST(ati.get_termfreq() == 2);
1043 ati++;
1045 TEST(ati != db.allterms_end());
1046 TEST(*ati == "four");
1047 TEST(ati.get_termfreq() == 1);
1049 ati++;
1050 TEST(ati != db.allterms_end());
1051 TEST(*ati == "one");
1052 TEST(ati.get_termfreq() == 1);
1054 ++ati;
1055 TEST(ati != db.allterms_end());
1056 TEST(*ati == "six");
1057 TEST(ati.get_termfreq() == 3);
1059 ati++;
1060 TEST(ati != db.allterms_end());
1061 TEST(*ati == "three");
1062 TEST(ati.get_termfreq() == 3);
1064 ati++;
1065 TEST(ati != db.allterms_end());
1066 TEST(*ati == "two");
1067 TEST(ati.get_termfreq() == 2);
1069 ati++;
1070 TEST(ati == db.allterms_end());
1072 return true;
1075 // test that skip_to sets at_end (regression test)
1076 DEFINE_TESTCASE(allterms3, backend) {
1077 Xapian::Database db;
1078 db.add_database(get_database("apitest_allterms"));
1079 Xapian::TermIterator ati = db.allterms_begin();
1081 ati.skip_to(string("zzzzzz"));
1082 TEST(ati == db.allterms_end());
1084 return true;
1087 // test that next ignores extra entries due to long posting lists being
1088 // chunked (regression test for quartz)
1089 DEFINE_TESTCASE(allterms4, backend) {
1090 // apitest_allterms4 contains 682 documents each containing just the word
1091 // "foo". 682 was the magic number which started to cause Quartz problems.
1092 Xapian::Database db = get_database("apitest_allterms4");
1094 Xapian::TermIterator i = db.allterms_begin();
1095 TEST(i != db.allterms_end());
1096 TEST(*i == "foo");
1097 TEST(i.get_termfreq() == 682);
1098 ++i;
1099 TEST(i == db.allterms_end());
1101 return true;
1104 // test that skip_to with an exact match sets the current term (regression test
1105 // for quartz)
1106 DEFINE_TESTCASE(allterms5, backend) {
1107 Xapian::Database db;
1108 db.add_database(get_database("apitest_allterms"));
1109 Xapian::TermIterator ati = db.allterms_begin();
1110 ati.skip_to("three");
1111 TEST(ati != db.allterms_end());
1112 TEST_EQUAL(*ati, "three");
1114 return true;
1117 // test allterms iterators with prefixes
1118 DEFINE_TESTCASE(allterms6, backend) {
1119 Xapian::Database db;
1120 db.add_database(get_database("apitest_allterms"));
1121 db.add_database(get_database("apitest_allterms2"));
1123 Xapian::TermIterator ati = db.allterms_begin("three");
1124 TEST(ati != db.allterms_end("three"));
1125 TEST_EQUAL(*ati, "three");
1126 ati.skip_to("three");
1127 TEST(ati != db.allterms_end("three"));
1128 TEST_EQUAL(*ati, "three");
1129 ati++;
1130 TEST(ati == db.allterms_end("three"));
1132 ati = db.allterms_begin("thre");
1133 TEST(ati != db.allterms_end("thre"));
1134 TEST_EQUAL(*ati, "three");
1135 ati.skip_to("three");
1136 TEST(ati != db.allterms_end("thre"));
1137 TEST_EQUAL(*ati, "three");
1138 ati++;
1139 TEST(ati == db.allterms_end("thre"));
1141 ati = db.allterms_begin("f");
1142 TEST(ati != db.allterms_end("f"));
1143 TEST_EQUAL(*ati, "five");
1144 TEST(ati != db.allterms_end("f"));
1145 ati.skip_to("three");
1146 TEST(ati == db.allterms_end("f"));
1148 ati = db.allterms_begin("f");
1149 TEST(ati != db.allterms_end("f"));
1150 TEST_EQUAL(*ati, "five");
1151 ati++;
1152 TEST(ati != db.allterms_end("f"));
1153 TEST_EQUAL(*ati, "four");
1154 ati++;
1155 TEST(ati == db.allterms_end("f"));
1157 ati = db.allterms_begin("absent");
1158 TEST(ati == db.allterms_end("absent"));
1160 return true;
1163 // test that searching for a term with a special characters in it works
1164 DEFINE_TESTCASE(specialterms1, backend) {
1165 Xapian::Enquire enquire(get_database("apitest_space"));
1166 Xapian::MSet mymset;
1167 Xapian::doccount count;
1168 Xapian::MSetIterator m;
1169 Xapian::Stem stemmer("english");
1171 enquire.set_query(stemmer("new\nline"));
1172 mymset = enquire.get_mset(0, 10);
1173 TEST_MSET_SIZE(mymset, 1);
1174 count = 0;
1175 for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
1176 TEST_EQUAL(count, 1);
1178 for (Xapian::valueno value_no = 0; value_no < 7; ++value_no) {
1179 string value = mymset.begin().get_document().get_value(value_no);
1180 TEST_NOT_EQUAL(value, "");
1181 if (value_no == 0) {
1182 TEST(value.size() > 263);
1183 TEST_EQUAL(static_cast<unsigned char>(value[262]), 255);
1184 for (int k = 0; k < 256; k++) {
1185 TEST_EQUAL(static_cast<unsigned char>(value[k + 7]), k);
1190 enquire.set_query(stemmer(string("big\0zero", 8)));
1191 mymset = enquire.get_mset(0, 10);
1192 TEST_MSET_SIZE(mymset, 1);
1193 count = 0;
1194 for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
1195 TEST_EQUAL(count, 1);
1197 return true;
1200 // test that terms with a special characters in appear correctly when iterating
1201 // allterms
1202 DEFINE_TESTCASE(specialterms2, backend) {
1203 Xapian::Database db(get_database("apitest_space"));
1205 // Check the terms are all as expected (after stemming) and that allterms
1206 // copes with iterating over them.
1207 Xapian::TermIterator t;
1208 t = db.allterms_begin();
1209 TEST_EQUAL(*t, "back\\slash"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1210 TEST_EQUAL(*t, string("big\0zero", 8)); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1211 TEST_EQUAL(*t, "new\nlin"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1212 TEST_EQUAL(*t, "one\x01on"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1213 TEST_EQUAL(*t, "space man"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1214 TEST_EQUAL(*t, "tab\tbi"); ++t; TEST_NOT_EQUAL(t, db.allterms_end());
1215 TEST_EQUAL(*t, "tu\x02tu"); ++t; TEST_EQUAL(t, db.allterms_end());
1217 // Now check that skip_to exactly a term containing a zero byte works.
1218 // This is a regression test for flint and quartz - an Assert() used to
1219 // fire in debug builds (the Assert was wrong - the actual code handled
1220 // this OK).
1221 t = db.allterms_begin();
1222 t.skip_to(string("big\0zero", 8));
1223 TEST_NOT_EQUAL(t, db.allterms_end());
1224 TEST_EQUAL(*t, string("big\0zero", 8));
1226 return true;
1229 // test that rsets behave correctly with multiDBs
1230 DEFINE_TESTCASE(rsetmultidb2, backend && !multi) {
1231 Xapian::Database mydb1(get_database("apitest_rset", "apitest_simpledata2"));
1232 Xapian::Database mydb2(get_database("apitest_rset"));
1233 mydb2.add_database(get_database("apitest_simpledata2"));
1235 Xapian::Enquire enquire1(mydb1);
1236 Xapian::Enquire enquire2(mydb2);
1238 Xapian::Query myquery = query("is");
1240 enquire1.set_query(myquery);
1241 enquire2.set_query(myquery);
1243 Xapian::RSet myrset1;
1244 Xapian::RSet myrset2;
1245 myrset1.add_document(4);
1246 myrset2.add_document(2);
1248 Xapian::MSet mymset1a = enquire1.get_mset(0, 10);
1249 Xapian::MSet mymset1b = enquire1.get_mset(0, 10, &myrset1);
1250 Xapian::MSet mymset2a = enquire2.get_mset(0, 10);
1251 Xapian::MSet mymset2b = enquire2.get_mset(0, 10, &myrset2);
1253 mset_expect_order(mymset1a, 4, 3);
1254 mset_expect_order(mymset1b, 4, 3);
1255 mset_expect_order(mymset2a, 2, 5);
1256 mset_expect_order(mymset2b, 2, 5);
1258 TEST(mset_range_is_same_weights(mymset1a, 0, mymset2a, 0, 2));
1259 TEST(mset_range_is_same_weights(mymset1b, 0, mymset2b, 0, 2));
1260 TEST_NOT_EQUAL(mymset1a, mymset1b);
1261 TEST_NOT_EQUAL(mymset2a, mymset2b);
1263 return true;
1266 // tests an expand across multiple databases
1267 DEFINE_TESTCASE(multiexpand1, backend && !multi) {
1268 Xapian::Database mydb1(get_database("apitest_simpledata", "apitest_simpledata2"));
1269 Xapian::Enquire enquire1(mydb1);
1271 Xapian::Database mydb2(get_database("apitest_simpledata"));
1272 mydb2.add_database(get_database("apitest_simpledata2"));
1273 Xapian::Enquire enquire2(mydb2);
1275 // make simple equivalent rsets, with a document from each database in each.
1276 Xapian::RSet rset1;
1277 Xapian::RSet rset2;
1278 rset1.add_document(1);
1279 rset1.add_document(7);
1280 rset2.add_document(1);
1281 rset2.add_document(2);
1283 // Retrieve all the ESet results in each of the three setups:
1285 // This is the single database one.
1286 Xapian::ESet eset1 = enquire1.get_eset(1000, rset1);
1288 // This is the multi database with approximation
1289 Xapian::ESet eset2 = enquire2.get_eset(1000, rset2);
1291 // This is the multi database without approximation
1292 Xapian::ESet eset3 = enquire2.get_eset(1000, rset2, Xapian::Enquire::USE_EXACT_TERMFREQ);
1294 TEST_EQUAL(eset1.size(), eset3.size());
1296 Xapian::ESetIterator i = eset1.begin();
1297 Xapian::ESetIterator j = eset3.begin();
1298 while (i != eset1.end() && j != eset3.end()) {
1299 TEST_EQUAL(*i, *j);
1300 TEST_EQUAL(i.get_weight(), j.get_weight());
1301 ++i;
1302 ++j;
1304 TEST(i == eset1.end());
1305 TEST(j == eset3.end());
1307 bool eset1_eq_eset2 = true;
1308 i = eset1.begin();
1309 j = eset2.begin();
1310 while (i != eset1.end() && j != eset2.end()) {
1311 if (i.get_weight() != j.get_weight()) {
1312 eset1_eq_eset2 = false;
1313 break;
1315 ++i;
1316 ++j;
1318 TEST(!eset1_eq_eset2);
1320 return true;
1323 // tests that opening a non-existent postlist returns an empty list
1324 DEFINE_TESTCASE(postlist1, backend) {
1325 Xapian::Database db(get_database("apitest_simpledata"));
1327 TEST_EQUAL(db.postlist_begin("rosebud"), db.postlist_end("rosebud"));
1329 string s = "let_us_see_if_we_can_break_it_with_a_really_really_long_term.";
1330 for (int i = 0; i < 8; ++i) {
1331 s += s;
1332 TEST_EQUAL(db.postlist_begin(s), db.postlist_end(s));
1335 // A regression test (no, really!)
1336 TEST_NOT_EQUAL(db.postlist_begin("a"), db.postlist_end("a"));
1338 return true;
1341 // tests that a Xapian::PostingIterator works as an STL iterator
1342 DEFINE_TESTCASE(postlist2, backend) {
1343 Xapian::Database db(get_database("apitest_simpledata"));
1344 Xapian::PostingIterator p;
1345 p = db.postlist_begin("this");
1346 Xapian::PostingIterator pend = db.postlist_end("this");
1348 TEST(p.get_description() != "PostingIterator()");
1350 // test operator= creates a copy which compares equal
1351 Xapian::PostingIterator p_copy = p;
1352 TEST_EQUAL(p, p_copy);
1354 TEST(p_copy.get_description() != "PostingIterator()");
1356 // test copy constructor creates a copy which compares equal
1357 Xapian::PostingIterator p_clone(p);
1358 TEST_EQUAL(p, p_clone);
1360 TEST(p_clone.get_description() != "PostingIterator()");
1362 vector<Xapian::docid> v(p, pend);
1364 p = db.postlist_begin("this");
1365 pend = db.postlist_end("this");
1366 vector<Xapian::docid>::const_iterator i;
1367 for (i = v.begin(); i != v.end(); ++i) {
1368 TEST_NOT_EQUAL(p, pend);
1369 TEST_EQUAL(*i, *p);
1370 p++;
1372 TEST_EQUAL(p, pend);
1374 TEST_STRINGS_EQUAL(p.get_description(), "PostingIterator()");
1375 TEST_STRINGS_EQUAL(pend.get_description(), "PostingIterator()");
1377 return true;
1380 // tests that a Xapian::PostingIterator still works when the DB is deleted
1381 DEFINE_TESTCASE(postlist3, backend) {
1382 Xapian::PostingIterator u;
1384 Xapian::Database db_temp(get_database("apitest_simpledata"));
1385 u = db_temp.postlist_begin("this");
1388 Xapian::Database db(get_database("apitest_simpledata"));
1389 Xapian::PostingIterator p = db.postlist_begin("this");
1390 Xapian::PostingIterator pend = db.postlist_end("this");
1392 while (p != pend) {
1393 TEST_EQUAL(*p, *u);
1394 p++;
1395 u++;
1397 return true;
1400 // tests skip_to
1401 DEFINE_TESTCASE(postlist4, backend) {
1402 Xapian::Database db(get_database("apitest_simpledata"));
1403 Xapian::PostingIterator i = db.postlist_begin("this");
1404 i.skip_to(1);
1405 i.skip_to(999999999);
1406 TEST(i == db.postlist_end("this"));
1407 return true;
1410 // tests long postlists
1411 DEFINE_TESTCASE(postlist5, backend) {
1412 Xapian::Database db(get_database("apitest_manydocs"));
1413 // Allow for databases which don't support length
1414 if (db.get_avlength() != 1)
1415 TEST_EQUAL_DOUBLE(db.get_avlength(), 4);
1416 Xapian::PostingIterator i = db.postlist_begin("this");
1417 unsigned int j = 1;
1418 while (i != db.postlist_end("this")) {
1419 TEST_EQUAL(*i, j);
1420 i++;
1421 j++;
1423 TEST_EQUAL(j, 513);
1424 return true;
1427 // tests document length in postlists
1428 DEFINE_TESTCASE(postlist6, backend) {
1429 Xapian::Database db(get_database("apitest_simpledata"));
1430 Xapian::PostingIterator i = db.postlist_begin("this");
1431 TEST(i != db.postlist_end("this"));
1432 while (i != db.postlist_end("this")) {
1433 TEST_EQUAL(i.get_doclength(), db.get_doclength(*i));
1434 TEST_EQUAL(i.get_unique_terms(), db.get_unique_terms(*i));
1435 TEST_REL(i.get_wdf(),<=,i.get_doclength());
1436 TEST_REL(1,<=,i.get_unique_terms());
1437 // The next two aren't necessarily true if there are terms with wdf=0
1438 // in the document, but that isn't the case here.
1439 TEST_REL(i.get_unique_terms(),<=,i.get_doclength());
1440 TEST_REL(i.get_wdf() + i.get_unique_terms() - 1,<=,i.get_doclength());
1441 ++i;
1443 return true;
1446 // tests collection frequency
1447 DEFINE_TESTCASE(collfreq1, backend) {
1448 Xapian::Database db(get_database("apitest_simpledata"));
1450 TEST_EQUAL(db.get_collection_freq("this"), 11);
1451 TEST_EQUAL(db.get_collection_freq("first"), 1);
1452 TEST_EQUAL(db.get_collection_freq("last"), 0);
1453 TEST_EQUAL(db.get_collection_freq("word"), 9);
1455 Xapian::Database db1(get_database("apitest_simpledata", "apitest_simpledata2"));
1456 Xapian::Database db2(get_database("apitest_simpledata"));
1457 db2.add_database(get_database("apitest_simpledata2"));
1459 TEST_EQUAL(db1.get_collection_freq("this"), 15);
1460 TEST_EQUAL(db1.get_collection_freq("first"), 1);
1461 TEST_EQUAL(db1.get_collection_freq("last"), 0);
1462 TEST_EQUAL(db1.get_collection_freq("word"), 11);
1463 TEST_EQUAL(db2.get_collection_freq("this"), 15);
1464 TEST_EQUAL(db2.get_collection_freq("first"), 1);
1465 TEST_EQUAL(db2.get_collection_freq("last"), 0);
1466 TEST_EQUAL(db2.get_collection_freq("word"), 11);
1468 return true;
1471 // Regression test for split msets being incorrect when sorting
1472 DEFINE_TESTCASE(sortvalue1, backend) {
1473 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1474 enquire.set_query(Xapian::Query("this"));
1476 for (int pass = 1; pass <= 2; ++pass) {
1477 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
1478 tout << "Sorting on value " << value_no << endl;
1479 enquire.set_sort_by_value(value_no, true);
1480 Xapian::MSet allbset = enquire.get_mset(0, 100);
1481 Xapian::MSet partbset1 = enquire.get_mset(0, 3);
1482 Xapian::MSet partbset2 = enquire.get_mset(3, 97);
1483 TEST_EQUAL(allbset.size(), partbset1.size() + partbset2.size());
1485 bool ok = true;
1486 int n = 0;
1487 Xapian::MSetIterator i, j;
1488 j = allbset.begin();
1489 for (i = partbset1.begin(); i != partbset1.end(); ++i) {
1490 tout << "Entry " << n << ": " << *i << " | " << *j << endl;
1491 TEST(j != allbset.end());
1492 if (*i != *j) ok = false;
1493 ++j;
1494 ++n;
1496 tout << "===\n";
1497 for (i = partbset2.begin(); i != partbset2.end(); ++i) {
1498 tout << "Entry " << n << ": " << *i << " | " << *j << endl;
1499 TEST(j != allbset.end());
1500 if (*i != *j) ok = false;
1501 ++j;
1502 ++n;
1504 TEST(j == allbset.end());
1505 if (!ok)
1506 FAIL_TEST("Split msets aren't consistent with unsplit");
1508 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1511 return true;
1514 // consistency check match - vary mset size and check results agree.
1515 // consistency1 will run on the remote backend, but it's particularly slow
1516 // with that, and testing it there doesn't actually improve the test
1517 // coverage really.
1518 DEFINE_TESTCASE(consistency1, backend && !remote) {
1519 Xapian::Database db(get_database("etext"));
1520 Xapian::Enquire enquire(db);
1521 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR, Xapian::Query("the"), Xapian::Query("sky")));
1522 Xapian::doccount lots = 214;
1523 Xapian::MSet bigmset = enquire.get_mset(0, lots);
1524 TEST_EQUAL(bigmset.size(), lots);
1525 try {
1526 for (Xapian::doccount start = 0; start < lots; ++start) {
1527 for (Xapian::doccount size = 0; size < lots - start; ++size) {
1528 Xapian::MSet mset = enquire.get_mset(start, size);
1529 if (mset.size()) {
1530 TEST_EQUAL(start + mset.size(),
1531 min(start + size, bigmset.size()));
1532 } else if (size) {
1533 // tout << start << mset.size() << bigmset.size() << endl;
1534 TEST(start >= bigmset.size());
1536 for (Xapian::doccount i = 0; i < mset.size(); ++i) {
1537 TEST_EQUAL(*mset[i], *bigmset[start + i]);
1538 TEST_EQUAL_DOUBLE(mset[i].get_weight(),
1539 bigmset[start + i].get_weight());
1543 } catch (const Xapian::NetworkTimeoutError &) {
1544 // consistency1 is a long test - may timeout with the remote backend...
1545 SKIP_TEST("Test taking too long");
1547 return true;
1550 // tests that specifying a nonexistent input file throws an exception.
1551 DEFINE_TESTCASE(chertdatabaseopeningerror1, chert) {
1552 #ifdef XAPIAN_HAS_CHERT_BACKEND
1553 mkdir(".chert", 0755);
1555 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1556 Xapian::Database(".chert/nosuchdirectory",
1557 Xapian::DB_BACKEND_CHERT));
1558 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1559 Xapian::WritableDatabase(".chert/nosuchdirectory",
1560 Xapian::DB_OPEN|Xapian::DB_BACKEND_CHERT));
1562 mkdir(".chert/emptydirectory", 0700);
1563 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1564 Xapian::Database(".chert/emptydirectory",
1565 Xapian::DB_BACKEND_CHERT));
1567 touch(".chert/somefile");
1568 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1569 Xapian::Database(".chert/somefile",
1570 Xapian::DB_BACKEND_CHERT));
1571 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1572 Xapian::WritableDatabase(".chert/somefile",
1573 Xapian::DB_OPEN|Xapian::DB_BACKEND_CHERT));
1574 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1575 Xapian::WritableDatabase(".chert/somefile",
1576 Xapian::DB_CREATE|Xapian::DB_BACKEND_CHERT));
1577 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1578 Xapian::WritableDatabase(".chert/somefile",
1579 Xapian::DB_CREATE_OR_OPEN|Xapian::DB_BACKEND_CHERT));
1580 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1581 Xapian::WritableDatabase(".chert/somefile",
1582 Xapian::DB_CREATE_OR_OVERWRITE|Xapian::DB_BACKEND_CHERT));
1583 #endif
1585 return true;
1588 /// Test opening of a chert database
1589 DEFINE_TESTCASE(chertdatabaseopen1, chert) {
1590 #ifdef XAPIAN_HAS_CHERT_BACKEND
1591 const string dbdir = ".chert/test_chertdatabaseopen1";
1592 mkdir(".chert", 0755);
1595 rm_rf(dbdir);
1596 Xapian::WritableDatabase wdb(dbdir,
1597 Xapian::DB_CREATE|Xapian::DB_BACKEND_CHERT);
1598 TEST_EXCEPTION(Xapian::DatabaseLockError,
1599 Xapian::WritableDatabase(dbdir,
1600 Xapian::DB_OPEN|Xapian::DB_BACKEND_CHERT));
1601 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1605 rm_rf(dbdir);
1606 Xapian::WritableDatabase wdb(dbdir,
1607 Xapian::DB_CREATE_OR_OPEN|Xapian::DB_BACKEND_CHERT);
1608 TEST_EXCEPTION(Xapian::DatabaseLockError,
1609 Xapian::WritableDatabase(dbdir,
1610 Xapian::DB_CREATE_OR_OVERWRITE|Xapian::DB_BACKEND_CHERT));
1611 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1615 rm_rf(dbdir);
1616 Xapian::WritableDatabase wdb(dbdir,
1617 Xapian::DB_CREATE_OR_OVERWRITE|Xapian::DB_BACKEND_CHERT);
1618 TEST_EXCEPTION(Xapian::DatabaseLockError,
1619 Xapian::WritableDatabase(dbdir,
1620 Xapian::DB_CREATE_OR_OPEN|Xapian::DB_BACKEND_CHERT));
1621 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1625 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1626 Xapian::WritableDatabase(dbdir,
1627 Xapian::DB_CREATE|Xapian::DB_BACKEND_CHERT));
1628 Xapian::WritableDatabase wdb(dbdir,
1629 Xapian::DB_CREATE_OR_OVERWRITE|Xapian::DB_BACKEND_CHERT);
1630 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1634 Xapian::WritableDatabase wdb(dbdir,
1635 Xapian::DB_CREATE_OR_OPEN|Xapian::DB_BACKEND_CHERT);
1636 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1640 Xapian::WritableDatabase wdb(dbdir,
1641 Xapian::DB_OPEN|Xapian::DB_BACKEND_CHERT);
1642 Xapian::Database(dbdir, Xapian::DB_BACKEND_CHERT);
1644 #endif
1646 return true;
1649 // feature test for Enquire:
1650 // set_sort_by_value
1651 // set_sort_by_value_then_relevance
1652 // set_sort_by_relevance_then_value
1653 // Prior to 1.2.17 and 1.3.2, order8 and order9 were swapped, and
1654 // set_sort_by_relevance_then_value was buggy, so this testcase now serves as
1655 // a regression test for that bug.
1656 DEFINE_TESTCASE(sortrel1, backend) {
1657 Xapian::Enquire enquire(get_database("apitest_sortrel"));
1658 enquire.set_sort_by_value(1, true);
1659 enquire.set_query(Xapian::Query("woman"));
1661 const Xapian::docid order1[] = { 1,2,3,4,5,6,7,8,9 };
1662 const Xapian::docid order2[] = { 2,1,3,6,5,4,7,9,8 };
1663 const Xapian::docid order3[] = { 3,2,1,6,5,4,9,8,7 };
1664 const Xapian::docid order4[] = { 7,8,9,4,5,6,1,2,3 };
1665 const Xapian::docid order5[] = { 9,8,7,6,5,4,3,2,1 };
1666 const Xapian::docid order6[] = { 7,9,8,6,5,4,2,1,3 };
1667 const Xapian::docid order7[] = { 7,9,8,6,5,4,2,1,3 };
1668 const Xapian::docid order8[] = { 2,6,7,1,5,9,3,4,8 };
1669 const Xapian::docid order9[] = { 7,6,2,9,5,1,8,4,3 };
1671 Xapian::MSet mset;
1672 size_t i;
1674 mset = enquire.get_mset(0, 10);
1675 TEST_EQUAL(mset.size(), sizeof(order1) / sizeof(Xapian::docid));
1676 for (i = 0; i < sizeof(order1) / sizeof(Xapian::docid); ++i) {
1677 TEST_EQUAL(*mset[i], order1[i]);
1680 enquire.set_sort_by_value_then_relevance(1, true);
1682 mset = enquire.get_mset(0, 10);
1683 TEST_EQUAL(mset.size(), sizeof(order2) / sizeof(Xapian::docid));
1684 for (i = 0; i < sizeof(order2) / sizeof(Xapian::docid); ++i) {
1685 TEST_EQUAL(*mset[i], order2[i]);
1688 enquire.set_sort_by_value(1, true);
1690 mset = enquire.get_mset(0, 10);
1691 TEST_EQUAL(mset.size(), sizeof(order1) / sizeof(Xapian::docid));
1692 for (i = 0; i < sizeof(order1) / sizeof(Xapian::docid); ++i) {
1693 TEST_EQUAL(*mset[i], order1[i]);
1696 enquire.set_sort_by_value_then_relevance(1, true);
1697 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1699 mset = enquire.get_mset(0, 10);
1700 TEST_EQUAL(mset.size(), sizeof(order2) / sizeof(Xapian::docid));
1701 for (i = 0; i < sizeof(order2) / sizeof(Xapian::docid); ++i) {
1702 TEST_EQUAL(*mset[i], order2[i]);
1705 enquire.set_sort_by_value(1, true);
1706 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1708 mset = enquire.get_mset(0, 10);
1709 TEST_EQUAL(mset.size(), sizeof(order3) / sizeof(Xapian::docid));
1710 for (i = 0; i < sizeof(order3) / sizeof(Xapian::docid); ++i) {
1711 TEST_EQUAL(*mset[i], order3[i]);
1714 enquire.set_sort_by_value(1, false);
1715 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1716 mset = enquire.get_mset(0, 10);
1717 TEST_EQUAL(mset.size(), sizeof(order4) / sizeof(Xapian::docid));
1718 for (i = 0; i < sizeof(order4) / sizeof(Xapian::docid); ++i) {
1719 TEST_EQUAL(*mset[i], order4[i]);
1722 enquire.set_sort_by_value(1, false);
1723 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1724 mset = enquire.get_mset(0, 10);
1725 TEST_EQUAL(mset.size(), sizeof(order5) / sizeof(Xapian::docid));
1726 for (i = 0; i < sizeof(order5) / sizeof(Xapian::docid); ++i) {
1727 TEST_EQUAL(*mset[i], order5[i]);
1730 enquire.set_sort_by_value_then_relevance(1, false);
1731 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1732 mset = enquire.get_mset(0, 10);
1733 TEST_EQUAL(mset.size(), sizeof(order6) / sizeof(Xapian::docid));
1734 for (i = 0; i < sizeof(order6) / sizeof(Xapian::docid); ++i) {
1735 TEST_EQUAL(*mset[i], order6[i]);
1738 enquire.set_sort_by_value_then_relevance(1, false);
1739 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1740 mset = enquire.get_mset(0, 10);
1741 TEST_EQUAL(mset.size(), sizeof(order7) / sizeof(Xapian::docid));
1742 for (i = 0; i < sizeof(order7) / sizeof(Xapian::docid); ++i) {
1743 TEST_EQUAL(*mset[i], order7[i]);
1746 enquire.set_sort_by_relevance_then_value(1, true);
1747 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1748 mset = enquire.get_mset(0, 10);
1749 TEST_EQUAL(mset.size(), sizeof(order8) / sizeof(Xapian::docid));
1750 for (i = 0; i < sizeof(order8) / sizeof(Xapian::docid); ++i) {
1751 TEST_EQUAL(*mset[i], order8[i]);
1754 enquire.set_sort_by_relevance_then_value(1, true);
1755 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1756 mset = enquire.get_mset(0, 10);
1757 TEST_EQUAL(mset.size(), sizeof(order8) / sizeof(Xapian::docid));
1758 for (i = 0; i < sizeof(order8) / sizeof(Xapian::docid); ++i) {
1759 TEST_EQUAL(*mset[i], order8[i]);
1762 enquire.set_sort_by_relevance_then_value(1, false);
1763 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1764 mset = enquire.get_mset(0, 10);
1765 TEST_EQUAL(mset.size(), sizeof(order9) / sizeof(Xapian::docid));
1766 for (i = 0; i < sizeof(order9) / sizeof(Xapian::docid); ++i) {
1767 TEST_EQUAL(*mset[i], order9[i]);
1770 enquire.set_sort_by_relevance_then_value(1, false);
1771 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1772 mset = enquire.get_mset(0, 10);
1773 TEST_EQUAL(mset.size(), sizeof(order9) / sizeof(Xapian::docid));
1774 for (i = 0; i < sizeof(order9) / sizeof(Xapian::docid); ++i) {
1775 TEST_EQUAL(*mset[i], order9[i]);
1778 return true;
1781 // Test network stats and local stats give the same results.
1782 DEFINE_TESTCASE(netstats1, remote) {
1783 BackendManagerLocal local_manager;
1784 local_manager.set_datadir(test_driver::get_srcdir() + "/testdata/");
1786 const char * words[] = { "paragraph", "word" };
1787 Xapian::Query query(Xapian::Query::OP_OR, words, words + 2);
1788 const size_t MSET_SIZE = 10;
1790 Xapian::RSet rset;
1791 rset.add_document(4);
1792 rset.add_document(9);
1794 Xapian::MSet mset_alllocal;
1796 Xapian::Database db;
1797 db.add_database(local_manager.get_database("apitest_simpledata"));
1798 db.add_database(local_manager.get_database("apitest_simpledata2"));
1800 Xapian::Enquire enq(db);
1801 enq.set_query(query);
1802 mset_alllocal = enq.get_mset(0, MSET_SIZE, &rset);
1806 Xapian::Database db;
1807 db.add_database(local_manager.get_database("apitest_simpledata"));
1808 db.add_database(get_database("apitest_simpledata2"));
1810 Xapian::Enquire enq(db);
1811 enq.set_query(query);
1812 Xapian::MSet mset = enq.get_mset(0, MSET_SIZE, &rset);
1813 TEST_EQUAL(mset.get_matches_lower_bound(), mset_alllocal.get_matches_lower_bound());
1814 TEST_EQUAL(mset.get_matches_upper_bound(), mset_alllocal.get_matches_upper_bound());
1815 TEST_EQUAL(mset.get_matches_estimated(), mset_alllocal.get_matches_estimated());
1816 TEST_EQUAL(mset.get_max_attained(), mset_alllocal.get_max_attained());
1817 TEST_EQUAL(mset.size(), mset_alllocal.size());
1818 TEST(mset_range_is_same(mset, 0, mset_alllocal, 0, mset.size()));
1822 Xapian::Database db;
1823 db.add_database(get_database("apitest_simpledata"));
1824 db.add_database(local_manager.get_database("apitest_simpledata2"));
1826 Xapian::Enquire enq(db);
1827 enq.set_query(query);
1828 Xapian::MSet mset = enq.get_mset(0, MSET_SIZE, &rset);
1829 TEST_EQUAL(mset.get_matches_lower_bound(), mset_alllocal.get_matches_lower_bound());
1830 TEST_EQUAL(mset.get_matches_upper_bound(), mset_alllocal.get_matches_upper_bound());
1831 TEST_EQUAL(mset.get_matches_estimated(), mset_alllocal.get_matches_estimated());
1832 TEST_EQUAL(mset.get_max_attained(), mset_alllocal.get_max_attained());
1833 TEST_EQUAL(mset.size(), mset_alllocal.size());
1834 TEST(mset_range_is_same(mset, 0, mset_alllocal, 0, mset.size()));
1838 Xapian::Database db;
1839 db.add_database(get_database("apitest_simpledata"));
1840 db.add_database(get_database("apitest_simpledata2"));
1842 Xapian::Enquire enq(db);
1843 enq.set_query(query);
1844 Xapian::MSet mset = enq.get_mset(0, MSET_SIZE, &rset);
1845 TEST_EQUAL(mset.get_matches_lower_bound(), mset_alllocal.get_matches_lower_bound());
1846 TEST_EQUAL(mset.get_matches_upper_bound(), mset_alllocal.get_matches_upper_bound());
1847 TEST_EQUAL(mset.get_matches_estimated(), mset_alllocal.get_matches_estimated());
1848 TEST_EQUAL(mset.get_max_attained(), mset_alllocal.get_max_attained());
1849 TEST_EQUAL(mset.size(), mset_alllocal.size());
1850 TEST(mset_range_is_same(mset, 0, mset_alllocal, 0, mset.size()));
1853 return true;
1856 // Coordinate matching - scores 1 for each matching term
1857 class MyWeight : public Xapian::Weight {
1858 double scale_factor;
1860 public:
1861 MyWeight * clone() const {
1862 return new MyWeight;
1864 void init(double factor) {
1865 scale_factor = factor;
1867 MyWeight() { }
1868 ~MyWeight() { }
1869 std::string name() const { return "MyWeight"; }
1870 string serialise() const { return string(); }
1871 MyWeight * unserialise(const string &) const { return new MyWeight; }
1872 double get_sumpart(Xapian::termcount, Xapian::termcount, Xapian::termcount) const {
1873 return scale_factor;
1875 double get_maxpart() const { return scale_factor; }
1877 double get_sumextra(Xapian::termcount, Xapian::termcount) const { return 0; }
1878 double get_maxextra() const { return 0; }
1881 // tests user weighting scheme.
1882 // Would work with remote if we registered the weighting scheme.
1883 // FIXME: do this so we also test that functionality...
1884 DEFINE_TESTCASE(userweight1, backend && !remote) {
1885 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1886 enquire.set_weighting_scheme(MyWeight());
1887 const char * query[] = { "this", "line", "paragraph", "rubbish" };
1888 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR, query,
1889 query + sizeof(query) / sizeof(query[0])));
1890 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
1891 // MyWeight scores 1 for each matching term, so the weight should equal
1892 // the number of matching terms.
1893 for (Xapian::MSetIterator i = mymset1.begin(); i != mymset1.end(); ++i) {
1894 Xapian::termcount matching_terms = 0;
1895 Xapian::TermIterator t = enquire.get_matching_terms_begin(i);
1896 while (t != enquire.get_matching_terms_end(i)) {
1897 ++matching_terms;
1898 ++t;
1900 TEST_EQUAL(i.get_weight(), matching_terms);
1903 return true;
1906 // tests MatchAll queries
1907 // This is a regression test, which failed with assertion failures in
1908 // revision 9094. Also check that the results aren't ranked by relevance
1909 // (regression test for bug fixed in 1.0.9).
1910 DEFINE_TESTCASE(matchall1, backend) {
1911 Xapian::Database db(get_database("apitest_simpledata"));
1912 Xapian::Enquire enquire(db);
1913 enquire.set_query(Xapian::Query::MatchAll);
1914 Xapian::MSet mset = enquire.get_mset(0, 10);
1915 TEST_EQUAL(mset.get_matches_lower_bound(), db.get_doccount());
1916 TEST_EQUAL(mset.get_uncollapsed_matches_lower_bound(), db.get_doccount());
1918 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR,
1919 Xapian::Query("nosuchterm"),
1920 Xapian::Query::MatchAll));
1921 mset = enquire.get_mset(0, 10);
1922 TEST_EQUAL(mset.get_matches_lower_bound(), db.get_doccount());
1923 TEST_EQUAL(mset.get_uncollapsed_matches_lower_bound(), db.get_doccount());
1925 // Check that the results aren't ranked by relevance (fixed in 1.0.9).
1926 TEST(mset.size() > 1);
1927 TEST_EQUAL(mset[mset.size() - 1].get_weight(), 0);
1928 TEST_EQUAL(*mset[0], 1);
1929 TEST_EQUAL(*mset[mset.size() - 1], mset.size());
1931 return true;
1934 // Test using a ValueSetMatchDecider
1935 DEFINE_TESTCASE(valuesetmatchdecider2, backend && !remote) {
1936 Xapian::Database db(get_database("apitest_phrase"));
1937 Xapian::Enquire enq(db);
1938 enq.set_query(Xapian::Query("leav"));
1940 Xapian::ValueSetMatchDecider vsmd1(1, true);
1941 vsmd1.add_value("n");
1942 Xapian::ValueSetMatchDecider vsmd2(1, false);
1943 vsmd2.add_value("n");
1945 Xapian::MSet mymset = enq.get_mset(0, 20);
1946 mset_expect_order(mymset, 8, 6, 4, 5, 7, 10, 12, 11, 13, 9, 14);
1947 mymset = enq.get_mset(0, 20, 0, NULL, &vsmd1);
1948 mset_expect_order(mymset, 6, 12);
1949 mymset = enq.get_mset(0, 20, 0, NULL, &vsmd2);
1950 mset_expect_order(mymset, 8, 4, 5, 7, 10, 11, 13, 9, 14);
1952 return true;