1 /* api_db.cc: tests which need a backend
2 *
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2012,2013 Olly Betts
6 * Copyright 2006,2007,2008,2009 Lemur Consulting Ltd
7 *
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.
12 *
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.
17 *
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
22 */
23
24 #include <config.h>
25
26 #include "api_db.h"
27
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().
35
36 #include <xapian.h>
37
38 #include "backendmanager.h"
39 #include "backendmanager_local.h"
40 #include "testsuite.h"
41 #include "testutils.h"
42 #include "unixcmds.h"
43 #include "utils.h"
44
45 #include "apitest.h"
46
47 using namespace std;
48
49 static Xapian::Query
query(const string & t)50 query(const string &t)
51 {
52 return Xapian::Query(Xapian::Stem("english")(t));
53 }
54
55 // #######################################################################
56 // # Tests start here
57
58 // tests Xapian::Database::get_termfreq() and Xapian::Database::term_exists()
DEFINE_TESTCASE(termstats,backend)59 DEFINE_TESTCASE(termstats, backend) {
60 Xapian::Database db(get_database("apitest_simpledata"));
61
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);
68
69 return true;
70 }
71
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();
81
82 {
83 Xapian::Database db = Xapian::Auto::open_stub(dbpath);
84 Xapian::Enquire enquire(db);
85 enquire.set_query(Xapian::Query("word"));
86 enquire.get_mset(0, 10);
87 }
88 {
89 Xapian::Database db(dbpath);
90 Xapian::Enquire enquire(db);
91 enquire.set_query(Xapian::Query("word"));
92 enquire.get_mset(0, 10);
93 }
94
95 return true;
96 }
97
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();
108
109 {
110 Xapian::Database db = Xapian::Auto::open_stub(dbpath);
111 Xapian::Enquire enquire(db);
112 enquire.set_query(Xapian::Query("word"));
113 enquire.get_mset(0, 10);
114 }
115 {
116 Xapian::Database db(dbpath);
117 Xapian::Enquire enquire(db);
118 enquire.set_query(Xapian::Query("word"));
119 enquire.get_mset(0, 10);
120 }
121
122 return true;
123 }
124
125 // Regression test - bad entries were ignored after a good entry prior to 1.0.8.
126 DEFINE_TESTCASE(stubdb3, backend && !inmemory && !remote) {
127 // Only works for backends which have a path.
128 mkdir(".stub", 0755);
129 const char * dbpath = ".stub/stubdb3";
130 ofstream out(dbpath);
131 TEST(out.is_open());
132 out << "auto ../" << get_database_path("apitest_simpledata") << "\n"
133 "bad line here\n";
134 out.close();
135
136 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
137 Xapian::Database db = Xapian::Auto::open_stub(dbpath));
138
139 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
140 Xapian::Database db(dbpath));
141
142 return true;
143 }
144
145 // Test a stub database with just a bad entry.
146 DEFINE_TESTCASE(stubdb4, backend && !inmemory && !remote) {
147 // Only works for backends which have a path.
148 mkdir(".stub", 0755);
149 const char * dbpath = ".stub/stubdb4";
150 ofstream out(dbpath);
151 TEST(out.is_open());
152 out << "bad line here\n";
153 out.close();
154
155 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
156 Xapian::Database db = Xapian::Auto::open_stub(dbpath));
157
158 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
159 Xapian::Database db(dbpath));
160
161 return true;
162 }
163
164 // Test a stub database with a bad entry with no spaces (prior to 1.1.0 this
165 // was deliberately allowed, though not documented.
166 DEFINE_TESTCASE(stubdb5, backend && !inmemory && !remote) {
167 // Only works for backends which have a path.
168 mkdir(".stub", 0755);
169 const char * dbpath = ".stub/stubdb5";
170 ofstream out(dbpath);
171 TEST(out.is_open());
172 out << "bad\n"
173 "auto ../" << get_database_path("apitest_simpledata") << endl;
174 out.close();
175
176 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
177 Xapian::Database db = Xapian::Auto::open_stub(dbpath));
178
179 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
180 Xapian::Database db(dbpath));
181
182 return true;
183 }
184
185 // Test a stub database with an inmemory database (new feature in 1.1.0).
DEFINE_TESTCASE(stubdb6,inmemory)186 DEFINE_TESTCASE(stubdb6, inmemory) {
187 mkdir(".stub", 0755);
188 const char * dbpath = ".stub/stubdb6";
189 ofstream out(dbpath);
190 TEST(out.is_open());
191 out << "inmemory\n";
192 out.close();
193
194 // Read-only tests:
195 {
196 Xapian::Database db = Xapian::Auto::open_stub(dbpath);
197 TEST_EQUAL(db.get_doccount(), 0);
198 Xapian::Enquire enquire(db);
199 enquire.set_query(Xapian::Query("word"));
200 Xapian::MSet mset = enquire.get_mset(0, 10);
201 TEST(mset.empty());
202 }
203 {
204 Xapian::Database db(dbpath);
205 TEST_EQUAL(db.get_doccount(), 0);
206 Xapian::Enquire enquire(db);
207 enquire.set_query(Xapian::Query("word"));
208 Xapian::MSet mset = enquire.get_mset(0, 10);
209 TEST(mset.empty());
210 }
211
212 // Writable tests:
213 {
214 Xapian::WritableDatabase db;
215 db = Xapian::Auto::open_stub(dbpath, Xapian::DB_OPEN);
216 TEST_EQUAL(db.get_doccount(), 0);
217 db.add_document(Xapian::Document());
218 TEST_EQUAL(db.get_doccount(), 1);
219 }
220 {
221 Xapian::WritableDatabase db(dbpath, Xapian::DB_OPEN);
222 TEST_EQUAL(db.get_doccount(), 0);
223 db.add_document(Xapian::Document());
224 TEST_EQUAL(db.get_doccount(), 1);
225 }
226
227 return true;
228 }
229
230 #if 0 // the "force error" mechanism is no longer in place...
231 class MyErrorHandler : public Xapian::ErrorHandler {
232 public:
233 int count;
234
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;
240 }
241
242 MyErrorHandler() : count (0) {}
243 };
244
245 // tests error handler in multimatch().
246 //DEFINE_TESTCASE(multierrhandler1, backend) {
247 MyErrorHandler myhandler;
248
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"));
258
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;
334 }
335 tout << "db=" << dbs << "\n";
336 Xapian::Enquire enquire(dbs, &myhandler);
337
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);
342
343 tout << "query=" << myquery << "\n";
344 // retrieve the top ten results
345 Xapian::MSet mymset = enquire.get_mset(0, 10);
346
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;
362 }
363 TEST_EQUAL(myhandler.count, errcount);
364 errcount += 1;
365 }
366
367 return true;
368 }
369 #endif
370
371 class myMatchDecider : public Xapian::MatchDecider {
372 public:
operator ()(const Xapian::Document & doc) const373 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;
376 }
377 };
378
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"));
384
385 myMatchDecider myfunctor;
386
387 Xapian::MSet mymset = enquire.get_mset(0, 100, 0, &myfunctor);
388
389 vector<bool> docid_checked(db.get_lastdocid());
390
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;
406 }
407
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);
412
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);
425
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);
443
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)));
448 }
449 }
450
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);
468
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);
487
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);
504
505 return true;
506 }
507
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"));
513
514 myMatchDecider myfunctor;
515
516 Xapian::MSet mymset = enquire.get_mset(0, 100, 0, NULL, &myfunctor);
517
518 vector<bool> docid_checked(db.get_lastdocid());
519
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;
529 }
530
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)));
535 }
536 }
537
538 return true;
539 }
540
541 class myMatchDecider2 : public Xapian::MatchDecider {
542 public:
operator ()(const Xapian::Document & doc) const543 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;
546 }
547 };
548
549
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);
557
558 myMatchDecider2 myfunctor;
559
560 Xapian::MSet mset1 = enquire.get_mset(0, 2, 0, NULL, &myfunctor);
561 Xapian::MSet mset2 = enquire.get_mset(0, 1000, 0, NULL, &myfunctor);
562
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());
567
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());
570
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());
574
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());
579
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());
582
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());
589
590 return true;
591 }
592
593 // tests that mset iterators on msets compare correctly.
DEFINE_TESTCASE(msetiterator1,backend)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);
598
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);
612
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);
628
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());
634
635 return true;
636 }
637
638 // tests that mset iterators on empty msets compare equal.
DEFINE_TESTCASE(msetiterator2,backend)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);
643
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);
654
655 return true;
656 }
657
658 // tests that begin().get_document() works when first != 0
DEFINE_TESTCASE(msetiterator3,backend)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"));
663
664 Xapian::MSet mymset = enquire.get_mset(2, 10);
665
666 TEST(!mymset.empty());
667 Xapian::Document doc(mymset.begin().get_document());
668 TEST(!doc.get_data().empty());
669
670 return true;
671 }
672
673 // tests that eset iterators on empty esets compare equal.
DEFINE_TESTCASE(esetiterator1,backend)674 DEFINE_TESTCASE(esetiterator1, backend) {
675 Xapian::Enquire enquire(get_database("apitest_simpledata"));
676 enquire.set_query(Xapian::Query("this"));
677
678 Xapian::MSet mymset = enquire.get_mset(0, 10);
679 TEST(mymset.size() >= 2);
680
681 Xapian::RSet myrset;
682 Xapian::MSetIterator i = mymset.begin();
683 myrset.add_document(*i);
684 myrset.add_document(*(++i));
685
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();
693
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);
700
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);
712
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());
718
719 return true;
720 }
721
722 // tests that eset iterators on empty esets compare equal.
DEFINE_TESTCASE(esetiterator2,backend)723 DEFINE_TESTCASE(esetiterator2, backend) {
724 Xapian::Enquire enquire(get_database("apitest_simpledata"));
725 enquire.set_query(Xapian::Query("this"));
726
727 Xapian::MSet mymset = enquire.get_mset(0, 10);
728 TEST(mymset.size() >= 2);
729
730 Xapian::RSet myrset;
731 Xapian::MSetIterator i = mymset.begin();
732 myrset.add_document(*i);
733 myrset.add_document(*(++i));
734
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);
746
747 return true;
748 }
749
750 // tests the collapse-on-key
DEFINE_TESTCASE(collapsekey1,backend)751 DEFINE_TESTCASE(collapsekey1, backend) {
752 Xapian::Enquire enquire(get_database("apitest_simpledata"));
753 enquire.set_query(Xapian::Query("this"));
754
755 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
756 Xapian::doccount mymsize1 = mymset1.size();
757
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);
761
762 TEST_AND_EXPLAIN(mymsize1 > mymset.size(),
763 "Had no fewer items when performing collapse: don't know whether it worked.");
764
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;
771 }
772 }
773
774 return true;
775 }
776
777 // tests that collapse-on-key modifies the predicted bounds for the number of
778 // matches appropriately.
DEFINE_TESTCASE(collapsekey2,backend)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"));
785
786 Xapian::MSet mset1 = enquire.get_mset(0, 1);
787
788 // Test that if no duplicates are found, then the upper bound remains
789 // unchanged and the lower bound drops.
790 {
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);
795
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());
798 }
799
800 return true;
801 }
802
803 // tests that collapse-on-key modifies the predicted bounds for the number of
804 // matches appropriately.
DEFINE_TESTCASE(collapsekey3,backend)805 DEFINE_TESTCASE(collapsekey3, backend) {
806 Xapian::Enquire enquire(get_database("apitest_simpledata"));
807 enquire.set_query(Xapian::Query("this"));
808
809 Xapian::MSet mymset1 = enquire.get_mset(0, 3);
810
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);
814
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.");
819
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;
826 }
827 }
828
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).
834 {
835 Xapian::valueno value_no = 1000;
836 enquire.set_collapse_key(value_no);
837 Xapian::MSet mymset = enquire.get_mset(0, 3);
838
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());
841
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;
848 }
849 }
850
851 return true;
852 }
853
854 // tests that collapse-on-key modifies the predicted bounds for the number of
855 // matches appropriately even when no results are requested.
DEFINE_TESTCASE(collapsekey4,backend)856 DEFINE_TESTCASE(collapsekey4, backend) {
857 Xapian::Enquire enquire(get_database("apitest_simpledata"));
858 enquire.set_query(Xapian::Query("this"));
859
860 Xapian::MSet mymset1 = enquire.get_mset(0, 0);
861
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);
865
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.");
870
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;
877 }
878 }
879
880 return true;
881 }
882
883 // test for keepalives
DEFINE_TESTCASE(keepalive1,remote)884 DEFINE_TESTCASE(keepalive1, remote) {
885 Xapian::Database db(get_remote_database("apitest_simpledata", 5000));
886
887 /* Test that keep-alives work */
888 for (int i = 0; i < 10; ++i) {
889 sleep(2);
890 db.keep_alive();
891 }
892 Xapian::Enquire enquire(db);
893 enquire.set_query(Xapian::Query("word"));
894 enquire.get_mset(0, 10);
895
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));
901
902 return true;
903 }
904
905 // test that iterating through all terms in a database works.
DEFINE_TESTCASE(allterms1,backend)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);
912
913 Xapian::TermIterator ati2 = ati;
914
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";
923 }
924 TEST(*ati == "three");
925 TEST(ati.get_termfreq() == 3);
926
927 #if 0
928 TEST(ati2 != db.allterms_end());
929 TEST(*ati2 == "one");
930 TEST(ati2.get_termfreq() == 1);
931 #endif
932
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);
940
941 #if 0
942 TEST(ati2 != db.allterms_end());
943 TEST(*ati2 == "three");
944 TEST(ati2.get_termfreq() == 3);
945 #endif
946
947 ati++;
948 TEST(ati == db.allterms_end());
949
950 return true;
951 }
952
953 // test that iterating through all terms in two databases works.
DEFINE_TESTCASE(allterms2,backend)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();
959
960 TEST(ati != db.allterms_end());
961 TEST(*ati == "five");
962 TEST(ati.get_termfreq() == 2);
963 ati++;
964
965 TEST(ati != db.allterms_end());
966 TEST(*ati == "four");
967 TEST(ati.get_termfreq() == 1);
968
969 ati++;
970 TEST(ati != db.allterms_end());
971 TEST(*ati == "one");
972 TEST(ati.get_termfreq() == 1);
973
974 ++ati;
975 TEST(ati != db.allterms_end());
976 TEST(*ati == "six");
977 TEST(ati.get_termfreq() == 3);
978
979 ati++;
980 TEST(ati != db.allterms_end());
981 TEST(*ati == "three");
982 TEST(ati.get_termfreq() == 3);
983
984 ati++;
985 TEST(ati != db.allterms_end());
986 TEST(*ati == "two");
987 TEST(ati.get_termfreq() == 2);
988
989 ati++;
990 TEST(ati == db.allterms_end());
991
992 return true;
993 }
994
995 // test that skip_to sets at_end (regression test)
DEFINE_TESTCASE(allterms3,backend)996 DEFINE_TESTCASE(allterms3, backend) {
997 Xapian::Database db;
998 db.add_database(get_database("apitest_allterms"));
999 Xapian::TermIterator ati = db.allterms_begin();
1000
1001 ati.skip_to(string("zzzzzz"));
1002 TEST(ati == db.allterms_end());
1003
1004 return true;
1005 }
1006
1007 // test that next ignores extra entries due to long posting lists being
1008 // chunked (regression test for quartz)
DEFINE_TESTCASE(allterms4,backend)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");
1013
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());
1020
1021 return true;
1022 }
1023
1024 // test that skip_to with an exact match sets the current term (regression test
1025 // for quartz)
DEFINE_TESTCASE(allterms5,backend)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");
1033
1034 return true;
1035 }
1036
1037 // test allterms iterators with prefixes
DEFINE_TESTCASE(allterms6,backend)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"));
1042
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"));
1051
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"));
1060
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"));
1067
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"));
1076
1077 ati = db.allterms_begin("absent");
1078 TEST(ati == db.allterms_end("absent"));
1079
1080 return true;
1081 }
1082
1083 // test that searching for a term with a special characters in it works
DEFINE_TESTCASE(specialterms1,backend)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");
1090
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);
1097
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);
1106 }
1107 }
1108 }
1109
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);
1116
1117 return true;
1118 }
1119
1120 // test that terms with a special characters in appear correctly when iterating
1121 // allterms
DEFINE_TESTCASE(specialterms2,backend)1122 DEFINE_TESTCASE(specialterms2, backend) {
1123 Xapian::Database db(get_database("apitest_space"));
1124
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());
1136
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));
1145
1146 return true;
1147 }
1148
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"));
1154
1155 Xapian::Enquire enquire1(mydb1);
1156 Xapian::Enquire enquire2(mydb2);
1157
1158 Xapian::Query myquery = query("is");
1159
1160 enquire1.set_query(myquery);
1161 enquire2.set_query(myquery);
1162
1163 Xapian::RSet myrset1;
1164 Xapian::RSet myrset2;
1165 myrset1.add_document(4);
1166 myrset2.add_document(2);
1167
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);
1172
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);
1177
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);
1182
1183 return true;
1184 }
1185
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);
1190
1191 Xapian::Database mydb2(get_database("apitest_simpledata"));
1192 mydb2.add_database(get_database("apitest_simpledata2"));
1193 Xapian::Enquire enquire2(mydb2);
1194
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);
1202
1203 // Retrieve all the ESet results in each of the three setups:
1204
1205 // This is the single database one.
1206 Xapian::ESet eset1 = enquire1.get_eset(1000, rset1);
1207
1208 // This is the multi database with approximation
1209 Xapian::ESet eset2 = enquire2.get_eset(1000, rset2);
1210
1211 // This is the multi database without approximation
1212 Xapian::ESet eset3 = enquire2.get_eset(1000, rset2, Xapian::Enquire::USE_EXACT_TERMFREQ);
1213
1214 TEST_EQUAL(eset1.size(), eset3.size());
1215
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;
1223 }
1224 TEST(i == eset1.end());
1225 TEST(j == eset3.end());
1226
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;
1234 }
1235 ++i;
1236 ++j;
1237 }
1238 TEST(!eset1_eq_eset2);
1239
1240 return true;
1241 }
1242
1243 // tests that opening a non-existent postlist returns an empty list
DEFINE_TESTCASE(postlist1,backend)1244 DEFINE_TESTCASE(postlist1, backend) {
1245 Xapian::Database db(get_database("apitest_simpledata"));
1246
1247 TEST_EQUAL(db.postlist_begin("rosebud"), db.postlist_end("rosebud"));
1248
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));
1253 }
1254
1255 // A regression test (no, really!)
1256 TEST_NOT_EQUAL(db.postlist_begin("a"), db.postlist_end("a"));
1257
1258 return true;
1259 }
1260
1261 // tests that a Xapian::PostingIterator works as an STL iterator
DEFINE_TESTCASE(postlist2,backend)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");
1267
1268 TEST(p.get_description() != "Xapian::PostingIterator(pos=END)");
1269
1270 // test operator= creates a copy which compares equal
1271 Xapian::PostingIterator p_copy = p;
1272 TEST_EQUAL(p, p_copy);
1273
1274 TEST(p_copy.get_description() != "Xapian::PostingIterator(pos=END)");
1275
1276 // test copy constructor creates a copy which compares equal
1277 Xapian::PostingIterator p_clone(p);
1278 TEST_EQUAL(p, p_clone);
1279
1280 TEST(p_clone.get_description() != "Xapian::PostingIterator(pos=END)");
1281
1282 vector<Xapian::docid> v(p, pend);
1283
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++;
1291 }
1292 TEST_EQUAL(p, pend);
1293
1294 TEST_STRINGS_EQUAL(p.get_description(),
1295 "Xapian::PostingIterator(pos=END)");
1296 TEST_STRINGS_EQUAL(pend.get_description(),
1297 "Xapian::PostingIterator(pos=END)");
1298
1299 return true;
1300 }
1301
1302 // tests that a Xapian::PostingIterator still works when the DB is deleted
DEFINE_TESTCASE(postlist3,backend)1303 DEFINE_TESTCASE(postlist3, backend) {
1304 Xapian::PostingIterator u;
1305 {
1306 Xapian::Database db_temp(get_database("apitest_simpledata"));
1307 u = db_temp.postlist_begin("this");
1308 }
1309
1310 Xapian::Database db(get_database("apitest_simpledata"));
1311 Xapian::PostingIterator p = db.postlist_begin("this");
1312 Xapian::PostingIterator pend = db.postlist_end("this");
1313
1314 while (p != pend) {
1315 TEST_EQUAL(*p, *u);
1316 p++;
1317 u++;
1318 }
1319 return true;
1320 }
1321
1322 // tests skip_to
DEFINE_TESTCASE(postlist4,backend)1323 DEFINE_TESTCASE(postlist4, backend) {
1324 Xapian::Database db(get_database("apitest_simpledata"));
1325 Xapian::PostingIterator i = db.postlist_begin("this");
1326 i.skip_to(1);
1327 i.skip_to(999999999);
1328 TEST(i == db.postlist_end("this"));
1329 return true;
1330 }
1331
1332 // tests long postlists
DEFINE_TESTCASE(postlist5,backend)1333 DEFINE_TESTCASE(postlist5, backend) {
1334 Xapian::Database db(get_database("apitest_manydocs"));
1335 // Allow for databases which don't support length
1336 if (db.get_avlength() != 1)
1337 TEST_EQUAL_DOUBLE(db.get_avlength(), 4);
1338 Xapian::PostingIterator i = db.postlist_begin("this");
1339 unsigned int j = 1;
1340 while (i != db.postlist_end("this")) {
1341 TEST_EQUAL(*i, j);
1342 i++;
1343 j++;
1344 }
1345 TEST_EQUAL(j, 513);
1346 return true;
1347 }
1348
1349 // tests document length in postlists
DEFINE_TESTCASE(postlist6,backend)1350 DEFINE_TESTCASE(postlist6, backend) {
1351 Xapian::Database db(get_database("apitest_simpledata"));
1352 Xapian::PostingIterator i = db.postlist_begin("this");
1353 TEST(i != db.postlist_end("this"));
1354 while (i != db.postlist_end("this")) {
1355 TEST_EQUAL(i.get_doclength(), db.get_doclength(*i));
1356 TEST_REL(i.get_wdf(),<=,i.get_doclength());
1357 ++i;
1358 }
1359 return true;
1360 }
1361
1362 // tests collection frequency
DEFINE_TESTCASE(collfreq1,backend)1363 DEFINE_TESTCASE(collfreq1, backend) {
1364 Xapian::Database db(get_database("apitest_simpledata"));
1365
1366 TEST_EQUAL(db.get_collection_freq("this"), 11);
1367 TEST_EQUAL(db.get_collection_freq("first"), 1);
1368 TEST_EQUAL(db.get_collection_freq("last"), 0);
1369 TEST_EQUAL(db.get_collection_freq("word"), 9);
1370
1371 Xapian::Database db1(get_database("apitest_simpledata", "apitest_simpledata2"));
1372 Xapian::Database db2(get_database("apitest_simpledata"));
1373 db2.add_database(get_database("apitest_simpledata2"));
1374
1375 TEST_EQUAL(db1.get_collection_freq("this"), 15);
1376 TEST_EQUAL(db1.get_collection_freq("first"), 1);
1377 TEST_EQUAL(db1.get_collection_freq("last"), 0);
1378 TEST_EQUAL(db1.get_collection_freq("word"), 11);
1379 TEST_EQUAL(db2.get_collection_freq("this"), 15);
1380 TEST_EQUAL(db2.get_collection_freq("first"), 1);
1381 TEST_EQUAL(db2.get_collection_freq("last"), 0);
1382 TEST_EQUAL(db2.get_collection_freq("word"), 11);
1383
1384 return true;
1385 }
1386
1387 // Regression test for split msets being incorrect when sorting
DEFINE_TESTCASE(sortvalue1,backend)1388 DEFINE_TESTCASE(sortvalue1, backend) {
1389 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1390 enquire.set_query(Xapian::Query("this"));
1391
1392 for (int pass = 1; pass <= 2; ++pass) {
1393 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
1394 tout << "Sorting on value " << value_no << endl;
1395 enquire.set_sort_by_value(value_no, true);
1396 Xapian::MSet allbset = enquire.get_mset(0, 100);
1397 Xapian::MSet partbset1 = enquire.get_mset(0, 3);
1398 Xapian::MSet partbset2 = enquire.get_mset(3, 97);
1399 TEST_EQUAL(allbset.size(), partbset1.size() + partbset2.size());
1400
1401 bool ok = true;
1402 int n = 0;
1403 Xapian::MSetIterator i, j;
1404 j = allbset.begin();
1405 for (i = partbset1.begin(); i != partbset1.end(); ++i) {
1406 tout << "Entry " << n << ": " << *i << " | " << *j << endl;
1407 TEST(j != allbset.end());
1408 if (*i != *j) ok = false;
1409 ++j;
1410 ++n;
1411 }
1412 tout << "===\n";
1413 for (i = partbset2.begin(); i != partbset2.end(); ++i) {
1414 tout << "Entry " << n << ": " << *i << " | " << *j << endl;
1415 TEST(j != allbset.end());
1416 if (*i != *j) ok = false;
1417 ++j;
1418 ++n;
1419 }
1420 TEST(j == allbset.end());
1421 if (!ok)
1422 FAIL_TEST("Split msets aren't consistent with unsplit");
1423 }
1424 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1425 }
1426
1427 return true;
1428 }
1429
1430 // consistency check match - vary mset size and check results agree.
1431 // consistency1 will run on the remote backend, but it's particularly slow
1432 // with that, and testing it there doesn't actually improve the test
1433 // coverage really.
1434 DEFINE_TESTCASE(consistency1, backend && !remote) {
1435 Xapian::Database db(get_database("etext"));
1436 Xapian::Enquire enquire(db);
1437 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR, Xapian::Query("the"), Xapian::Query("sky")));
1438 Xapian::doccount lots = 214;
1439 Xapian::MSet bigmset = enquire.get_mset(0, lots);
1440 TEST_EQUAL(bigmset.size(), lots);
1441 try {
1442 for (Xapian::doccount start = 0; start < lots; ++start) {
1443 for (Xapian::doccount size = 0; size < lots - start; ++size) {
1444 Xapian::MSet mset = enquire.get_mset(start, size);
1445 if (mset.size()) {
1446 TEST_EQUAL(start + mset.size(),
1447 min(start + size, bigmset.size()));
1448 } else if (size) {
1449 // tout << start << mset.size() << bigmset.size() << endl;
1450 TEST(start >= bigmset.size());
1451 }
1452 for (Xapian::doccount i = 0; i < mset.size(); ++i) {
1453 TEST_EQUAL(*mset[i], *bigmset[start + i]);
1454 TEST_EQUAL_DOUBLE(mset[i].get_weight(),
1455 bigmset[start + i].get_weight());
1456 }
1457 }
1458 }
1459 }
1460 catch (const Xapian::NetworkTimeoutError &) {
1461 // consistency1 is a long test - may timeout with the remote backend...
1462 SKIP_TEST("Test taking too long");
1463 }
1464 return true;
1465 }
1466
1467 // tests that specifying a nonexistent input file throws an exception.
DEFINE_TESTCASE(flintdatabaseopeningerror1,flint)1468 DEFINE_TESTCASE(flintdatabaseopeningerror1, flint) {
1469 #ifdef XAPIAN_HAS_FLINT_BACKEND
1470 mkdir(".flint", 0755);
1471
1472 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1473 Xapian::Flint::open(".flint/nosuchdirectory"));
1474 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1475 Xapian::Flint::open(".flint/nosuchdirectory", Xapian::DB_OPEN));
1476
1477 mkdir(".flint/emptydirectory", 0700);
1478 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1479 Xapian::Flint::open(".flint/emptydirectory"));
1480
1481 touch(".flint/somefile");
1482 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1483 Xapian::Flint::open(".flint/somefile"));
1484 TEST_EXCEPTION(Xapian::DatabaseOpeningError,
1485 Xapian::Flint::open(".flint/somefile", Xapian::DB_OPEN));
1486 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1487 Xapian::Flint::open(".flint/somefile", Xapian::DB_CREATE));
1488 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1489 Xapian::Flint::open(".flint/somefile", Xapian::DB_CREATE_OR_OPEN));
1490 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1491 Xapian::Flint::open(".flint/somefile", Xapian::DB_CREATE_OR_OVERWRITE));
1492 #endif
1493
1494 return true;
1495 }
1496
1497 /// Tests that appropriate error is thrown for database format change.
DEFINE_TESTCASE(flintdatabaseformaterror1,flint)1498 DEFINE_TESTCASE(flintdatabaseformaterror1, flint) {
1499 #ifdef XAPIAN_HAS_FLINT_BACKEND
1500 string dbdir = test_driver::get_srcdir();
1501 dbdir += "/testdata/flint-0.9.9";
1502
1503 // We should get a version error when we try and open the database for
1504 // reading now.
1505 TEST_EXCEPTION(Xapian::DatabaseVersionError,
1506 Xapian::Database db(dbdir));
1507
1508 // Also test when explicitly opening as a flint database.
1509 TEST_EXCEPTION(Xapian::DatabaseVersionError,
1510 (void)Xapian::Flint::open(dbdir));
1511
1512 // Clean up the "flintlock" file that we will have created while trying
1513 // to open the database.
1514 unlink((dbdir + "/flintlock").c_str());
1515 #endif
1516
1517 return true;
1518 }
1519
1520 /// Test that an old database can be successfully overwritten when using
1521 // Xapian::DB_CREATE_OR_OVERWRITE.
DEFINE_TESTCASE(flintdatabaseformaterror2,flint)1522 DEFINE_TESTCASE(flintdatabaseformaterror2, flint) {
1523 #ifdef XAPIAN_HAS_FLINT_BACKEND
1524 string flint099 = test_driver::get_srcdir();
1525 flint099 += "/testdata/flint-0.9.9";
1526
1527 mkdir(".flint", 0755);
1528 string dbdir = ".flint/test_flintdatabaseformaterror2";
1529
1530 rm_rf(dbdir);
1531 cp_R(flint099, dbdir);
1532
1533 (void)Xapian::WritableDatabase(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
1534
1535 rm_rf(dbdir);
1536 cp_R(flint099, dbdir);
1537
1538 // Also test when explicitly opening as a flint database.
1539 (void)Xapian::Flint::open(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
1540 #endif
1541
1542 return true;
1543 }
1544
1545 // regression test for not releasing lock on error.
DEFINE_TESTCASE(flintdatabaseformaterror3,flint)1546 DEFINE_TESTCASE(flintdatabaseformaterror3, flint) {
1547 #ifdef XAPIAN_HAS_FLINT_BACKEND
1548 string flint099 = test_driver::get_srcdir();
1549 flint099 += "/testdata/flint-0.9.9";
1550
1551 mkdir(".flint", 0755);
1552 string dbdir = ".flint/test_flintdatabaseformaterror3";
1553
1554 rm_rf(dbdir);
1555 cp_R(flint099, dbdir);
1556
1557 TEST_EXCEPTION(Xapian::DatabaseVersionError,
1558 Xapian::WritableDatabase(dbdir, Xapian::DB_CREATE_OR_OPEN));
1559
1560 // This used to throw a DatabaseLockError: "Unable to acquire database
1561 // write lock on .flint/formatdb: already locked"
1562 Xapian::WritableDatabase(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
1563 #endif
1564
1565 return true;
1566 }
1567
1568 // Test that 1.0.2 and later can open 1.0.1 databases.
DEFINE_TESTCASE(flintbackwardcompat1,flint)1569 DEFINE_TESTCASE(flintbackwardcompat1, flint) {
1570 #ifdef XAPIAN_HAS_FLINT_BACKEND
1571 string flint101 = test_driver::get_srcdir();
1572 flint101 += "/testdata/flint-1.0.1";
1573
1574 mkdir(".flint", 0755);
1575 string dbdir = ".flint/test_flintbackwardcompat1";
1576
1577 rm_rf(dbdir);
1578 cp_R(flint101, dbdir);
1579
1580 // Check we can open the older format for reading.
1581 {
1582 Xapian::Database db(dbdir);
1583 TEST_EQUAL(db.get_doccount(), 0);
1584 }
1585
1586 // Check we can open the older format for update.
1587 {
1588 Xapian::WritableDatabase db(dbdir, Xapian::DB_OPEN);
1589 TEST_EQUAL(db.get_doccount(), 0);
1590 }
1591 #endif
1592
1593 return true;
1594 }
1595
1596 // Test that 1.0.3 and later can open 1.0.2 databases.
DEFINE_TESTCASE(flintbackwardcompat2,flint)1597 DEFINE_TESTCASE(flintbackwardcompat2, flint) {
1598 #ifdef XAPIAN_HAS_FLINT_BACKEND
1599 string flint102 = test_driver::get_srcdir();
1600 flint102 += "/testdata/flint-1.0.2";
1601
1602 mkdir(".flint", 0755);
1603 string dbdir = ".flint/test_flintbackwardcompat2";
1604
1605 rm_rf(dbdir);
1606 cp_R(flint102, dbdir);
1607
1608 // Check we can open the older format for reading.
1609 {
1610 Xapian::Database db(dbdir);
1611 TEST_EQUAL(db.get_doccount(), 0);
1612 }
1613
1614 // Check we can open the older format for update.
1615 {
1616 Xapian::WritableDatabase db(dbdir, Xapian::DB_OPEN);
1617 TEST_EQUAL(db.get_doccount(), 0);
1618 }
1619 #endif
1620
1621 return true;
1622 }
1623
1624 /// Test opening of a flint database
DEFINE_TESTCASE(flintdatabaseopen1,flint)1625 DEFINE_TESTCASE(flintdatabaseopen1, flint) {
1626 #ifdef XAPIAN_HAS_FLINT_BACKEND
1627 const string dbdir = ".flint/test_flintdatabaseopen1";
1628 mkdir(".flint", 0755);
1629
1630 {
1631 rm_rf(dbdir);
1632 Xapian::WritableDatabase wdb =
1633 Xapian::Flint::open(dbdir, Xapian::DB_CREATE);
1634 TEST_EXCEPTION(Xapian::DatabaseLockError,
1635 Xapian::Flint::open(dbdir, Xapian::DB_OPEN));
1636 Xapian::Flint::open(dbdir);
1637 }
1638
1639 {
1640 rm_rf(dbdir);
1641 Xapian::WritableDatabase wdb =
1642 Xapian::Flint::open(dbdir, Xapian::DB_CREATE_OR_OPEN);
1643 TEST_EXCEPTION(Xapian::DatabaseLockError,
1644 Xapian::Flint::open(dbdir, Xapian::DB_CREATE_OR_OVERWRITE));
1645 Xapian::Flint::open(dbdir);
1646 }
1647
1648 {
1649 rm_rf(dbdir);
1650 Xapian::WritableDatabase wdb =
1651 Xapian::Flint::open(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
1652 TEST_EXCEPTION(Xapian::DatabaseLockError,
1653 Xapian::Flint::open(dbdir, Xapian::DB_CREATE_OR_OPEN));
1654 Xapian::Flint::open(dbdir);
1655 }
1656
1657 {
1658 TEST_EXCEPTION(Xapian::DatabaseCreateError,
1659 Xapian::Flint::open(dbdir, Xapian::DB_CREATE));
1660 Xapian::WritableDatabase wdb =
1661 Xapian::Flint::open(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
1662 Xapian::Flint::open(dbdir);
1663 }
1664
1665 {
1666 Xapian::WritableDatabase wdb =
1667 Xapian::Flint::open(dbdir, Xapian::DB_CREATE_OR_OPEN);
1668 Xapian::Flint::open(dbdir);
1669 }
1670
1671 {
1672 Xapian::WritableDatabase wdb =
1673 Xapian::Flint::open(dbdir, Xapian::DB_OPEN);
1674 Xapian::Flint::open(dbdir);
1675 }
1676 #endif
1677
1678 return true;
1679 }
1680
1681 // feature test for Enquire:
1682 // set_sort_by_value
1683 // set_sort_by_value_then_relevance
1684 // set_sort_by_relevance_then_value
1685 // Prior to 1.2.17 and 1.3.2, order8 and order9 were swapped, and
1686 // set_sort_by_relevance_then_value was buggy, so this testcase now serves as
1687 // a regression test for that bug.
DEFINE_TESTCASE(sortrel1,backend)1688 DEFINE_TESTCASE(sortrel1, backend) {
1689 Xapian::Enquire enquire(get_database("apitest_sortrel"));
1690 enquire.set_sort_by_value(1, true);
1691 enquire.set_query(Xapian::Query("woman"));
1692
1693 const Xapian::docid order1[] = { 1,2,3,4,5,6,7,8,9 };
1694 const Xapian::docid order2[] = { 2,1,3,6,5,4,7,9,8 };
1695 const Xapian::docid order3[] = { 3,2,1,6,5,4,9,8,7 };
1696 const Xapian::docid order4[] = { 7,8,9,4,5,6,1,2,3 };
1697 const Xapian::docid order5[] = { 9,8,7,6,5,4,3,2,1 };
1698 const Xapian::docid order6[] = { 7,9,8,6,5,4,2,1,3 };
1699 const Xapian::docid order7[] = { 7,9,8,6,5,4,2,1,3 };
1700 const Xapian::docid order8[] = { 2,6,7,1,5,9,3,4,8 };
1701 const Xapian::docid order9[] = { 7,6,2,9,5,1,8,4,3 };
1702
1703 Xapian::MSet mset;
1704 size_t i;
1705
1706 mset = enquire.get_mset(0, 10);
1707 TEST_EQUAL(mset.size(), sizeof(order1) / sizeof(Xapian::docid));
1708 for (i = 0; i < sizeof(order1) / sizeof(Xapian::docid); ++i) {
1709 TEST_EQUAL(*mset[i], order1[i]);
1710 }
1711
1712 enquire.set_sort_by_value_then_relevance(1, true);
1713
1714 mset = enquire.get_mset(0, 10);
1715 TEST_EQUAL(mset.size(), sizeof(order2) / sizeof(Xapian::docid));
1716 for (i = 0; i < sizeof(order2) / sizeof(Xapian::docid); ++i) {
1717 TEST_EQUAL(*mset[i], order2[i]);
1718 }
1719
1720 enquire.set_sort_by_value(1, true);
1721
1722 mset = enquire.get_mset(0, 10);
1723 TEST_EQUAL(mset.size(), sizeof(order1) / sizeof(Xapian::docid));
1724 for (i = 0; i < sizeof(order1) / sizeof(Xapian::docid); ++i) {
1725 TEST_EQUAL(*mset[i], order1[i]);
1726 }
1727
1728 enquire.set_sort_by_value_then_relevance(1, true);
1729 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1730
1731 mset = enquire.get_mset(0, 10);
1732 TEST_EQUAL(mset.size(), sizeof(order2) / sizeof(Xapian::docid));
1733 for (i = 0; i < sizeof(order2) / sizeof(Xapian::docid); ++i) {
1734 TEST_EQUAL(*mset[i], order2[i]);
1735 }
1736
1737 enquire.set_sort_by_value(1, true);
1738 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1739
1740 mset = enquire.get_mset(0, 10);
1741 TEST_EQUAL(mset.size(), sizeof(order3) / sizeof(Xapian::docid));
1742 for (i = 0; i < sizeof(order3) / sizeof(Xapian::docid); ++i) {
1743 TEST_EQUAL(*mset[i], order3[i]);
1744 }
1745
1746 enquire.set_sort_by_value(1, false);
1747 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1748 mset = enquire.get_mset(0, 10);
1749 TEST_EQUAL(mset.size(), sizeof(order4) / sizeof(Xapian::docid));
1750 for (i = 0; i < sizeof(order4) / sizeof(Xapian::docid); ++i) {
1751 TEST_EQUAL(*mset[i], order4[i]);
1752 }
1753
1754 enquire.set_sort_by_value(1, false);
1755 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1756 mset = enquire.get_mset(0, 10);
1757 TEST_EQUAL(mset.size(), sizeof(order5) / sizeof(Xapian::docid));
1758 for (i = 0; i < sizeof(order5) / sizeof(Xapian::docid); ++i) {
1759 TEST_EQUAL(*mset[i], order5[i]);
1760 }
1761
1762 enquire.set_sort_by_value_then_relevance(1, false);
1763 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1764 mset = enquire.get_mset(0, 10);
1765 TEST_EQUAL(mset.size(), sizeof(order6) / sizeof(Xapian::docid));
1766 for (i = 0; i < sizeof(order6) / sizeof(Xapian::docid); ++i) {
1767 TEST_EQUAL(*mset[i], order6[i]);
1768 }
1769
1770 enquire.set_sort_by_value_then_relevance(1, false);
1771 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1772 mset = enquire.get_mset(0, 10);
1773 TEST_EQUAL(mset.size(), sizeof(order7) / sizeof(Xapian::docid));
1774 for (i = 0; i < sizeof(order7) / sizeof(Xapian::docid); ++i) {
1775 TEST_EQUAL(*mset[i], order7[i]);
1776 }
1777
1778 enquire.set_sort_by_relevance_then_value(1, true);
1779 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1780 mset = enquire.get_mset(0, 10);
1781 TEST_EQUAL(mset.size(), sizeof(order8) / sizeof(Xapian::docid));
1782 for (i = 0; i < sizeof(order8) / sizeof(Xapian::docid); ++i) {
1783 TEST_EQUAL(*mset[i], order8[i]);
1784 }
1785
1786 enquire.set_sort_by_relevance_then_value(1, true);
1787 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1788 mset = enquire.get_mset(0, 10);
1789 TEST_EQUAL(mset.size(), sizeof(order8) / sizeof(Xapian::docid));
1790 for (i = 0; i < sizeof(order8) / sizeof(Xapian::docid); ++i) {
1791 TEST_EQUAL(*mset[i], order8[i]);
1792 }
1793
1794 enquire.set_sort_by_relevance_then_value(1, false);
1795 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1796 mset = enquire.get_mset(0, 10);
1797 TEST_EQUAL(mset.size(), sizeof(order9) / sizeof(Xapian::docid));
1798 for (i = 0; i < sizeof(order9) / sizeof(Xapian::docid); ++i) {
1799 TEST_EQUAL(*mset[i], order9[i]);
1800 }
1801
1802 enquire.set_sort_by_relevance_then_value(1, false);
1803 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1804 mset = enquire.get_mset(0, 10);
1805 TEST_EQUAL(mset.size(), sizeof(order9) / sizeof(Xapian::docid));
1806 for (i = 0; i < sizeof(order9) / sizeof(Xapian::docid); ++i) {
1807 TEST_EQUAL(*mset[i], order9[i]);
1808 }
1809
1810 return true;
1811 }
1812
1813 // Test network stats and local stats give the same results.
DEFINE_TESTCASE(netstats1,remote)1814 DEFINE_TESTCASE(netstats1, remote) {
1815 BackendManagerLocal local_manager;
1816 local_manager.set_datadir(test_driver::get_srcdir() + "/testdata/");
1817
1818 const char * words[] = { "paragraph", "word" };
1819 Xapian::Query query(Xapian::Query::OP_OR, words, words + 2);
1820 const size_t MSET_SIZE = 10;
1821
1822 Xapian::RSet rset;
1823 rset.add_document(4);
1824 rset.add_document(9);
1825
1826 Xapian::MSet mset_alllocal;
1827 {
1828 Xapian::Database db;
1829 db.add_database(local_manager.get_database("apitest_simpledata"));
1830 db.add_database(local_manager.get_database("apitest_simpledata2"));
1831
1832 Xapian::Enquire enq(db);
1833 enq.set_query(query);
1834 mset_alllocal = enq.get_mset(0, MSET_SIZE, &rset);
1835 }
1836
1837 {
1838 Xapian::Database db;
1839 db.add_database(local_manager.get_database("apitest_simpledata"));
1840 db.add_database(get_database("apitest_simpledata2"));
1841
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()));
1851 }
1852
1853 {
1854 Xapian::Database db;
1855 db.add_database(get_database("apitest_simpledata"));
1856 db.add_database(local_manager.get_database("apitest_simpledata2"));
1857
1858 Xapian::Enquire enq(db);
1859 enq.set_query(query);
1860 Xapian::MSet mset = enq.get_mset(0, MSET_SIZE, &rset);
1861 TEST_EQUAL(mset.get_matches_lower_bound(), mset_alllocal.get_matches_lower_bound());
1862 TEST_EQUAL(mset.get_matches_upper_bound(), mset_alllocal.get_matches_upper_bound());
1863 TEST_EQUAL(mset.get_matches_estimated(), mset_alllocal.get_matches_estimated());
1864 TEST_EQUAL(mset.get_max_attained(), mset_alllocal.get_max_attained());
1865 TEST_EQUAL(mset.size(), mset_alllocal.size());
1866 TEST(mset_range_is_same(mset, 0, mset_alllocal, 0, mset.size()));
1867 }
1868
1869 {
1870 Xapian::Database db;
1871 db.add_database(get_database("apitest_simpledata"));
1872 db.add_database(get_database("apitest_simpledata2"));
1873
1874 Xapian::Enquire enq(db);
1875 enq.set_query(query);
1876 Xapian::MSet mset = enq.get_mset(0, MSET_SIZE, &rset);
1877 TEST_EQUAL(mset.get_matches_lower_bound(), mset_alllocal.get_matches_lower_bound());
1878 TEST_EQUAL(mset.get_matches_upper_bound(), mset_alllocal.get_matches_upper_bound());
1879 TEST_EQUAL(mset.get_matches_estimated(), mset_alllocal.get_matches_estimated());
1880 TEST_EQUAL(mset.get_max_attained(), mset_alllocal.get_max_attained());
1881 TEST_EQUAL(mset.size(), mset_alllocal.size());
1882 TEST(mset_range_is_same(mset, 0, mset_alllocal, 0, mset.size()));
1883 }
1884
1885 return true;
1886 }
1887
1888 // Coordinate matching - scores 1 for each matching term
1889 class MyWeight : public Xapian::Weight {
1890 double scale_factor;
1891
1892 public:
clone() const1893 MyWeight * clone() const {
1894 return new MyWeight;
1895 }
init(double factor)1896 void init(double factor) {
1897 scale_factor = factor;
1898 }
MyWeight()1899 MyWeight() { }
~MyWeight()1900 ~MyWeight() { }
name() const1901 std::string name() const { return "MyWeight"; }
serialise() const1902 string serialise() const { return string(); }
unserialise(const string &) const1903 MyWeight * unserialise(const string &) const { return new MyWeight; }
get_sumpart(Xapian::termcount,Xapian::termcount) const1904 Xapian::weight get_sumpart(Xapian::termcount, Xapian::termcount) const {
1905 return scale_factor;
1906 }
get_maxpart() const1907 Xapian::weight get_maxpart() const { return scale_factor; }
1908
get_sumextra(Xapian::termcount) const1909 Xapian::weight get_sumextra(Xapian::termcount) const { return 0; }
get_maxextra() const1910 Xapian::weight get_maxextra() const { return 0; }
1911 };
1912
1913 // tests user weighting scheme.
1914 // Would work with remote if we registered the weighting scheme.
1915 // FIXME: do this so we also test that functionality...
1916 DEFINE_TESTCASE(userweight1, backend && !remote) {
1917 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1918 enquire.set_weighting_scheme(MyWeight());
1919 const char * query[] = { "this", "line", "paragraph", "rubbish" };
1920 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR, query,
1921 query + sizeof(query) / sizeof(query[0])));
1922 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
1923 // MyWeight scores 1 for each matching term, so the weight should equal
1924 // the number of matching terms.
1925 for (Xapian::MSetIterator i = mymset1.begin(); i != mymset1.end(); ++i) {
1926 Xapian::termcount matching_terms = 0;
1927 Xapian::TermIterator t = enquire.get_matching_terms_begin(i);
1928 while (t != enquire.get_matching_terms_end(i)) {
1929 ++matching_terms;
1930 ++t;
1931 }
1932 TEST_EQUAL(i.get_weight(), matching_terms);
1933 }
1934
1935 return true;
1936 }
1937
1938 // tests MatchAll queries
1939 // This is a regression test, which failed with assertion failures in
1940 // revision 9094. Also check that the results aren't ranked by relevance
1941 // (regression test for bug fixed in 1.0.9).
DEFINE_TESTCASE(matchall1,backend)1942 DEFINE_TESTCASE(matchall1, backend) {
1943 Xapian::Database db(get_database("apitest_simpledata"));
1944 Xapian::Enquire enquire(db);
1945 enquire.set_query(Xapian::Query::MatchAll);
1946 Xapian::MSet mset = enquire.get_mset(0, 10);
1947 TEST_EQUAL(mset.get_matches_lower_bound(), db.get_doccount());
1948 TEST_EQUAL(mset.get_uncollapsed_matches_lower_bound(), db.get_doccount());
1949
1950 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR,
1951 Xapian::Query("nosuchterm"),
1952 Xapian::Query::MatchAll));
1953 mset = enquire.get_mset(0, 10);
1954 TEST_EQUAL(mset.get_matches_lower_bound(), db.get_doccount());
1955 TEST_EQUAL(mset.get_uncollapsed_matches_lower_bound(), db.get_doccount());
1956
1957 // Check that the results aren't ranked by relevance (fixed in 1.0.9).
1958 TEST(mset.size() > 1);
1959 TEST_EQUAL(mset[mset.size() - 1].get_weight(), 0);
1960 TEST_EQUAL(*mset[0], 1);
1961 TEST_EQUAL(*mset[mset.size() - 1], mset.size());
1962
1963 return true;
1964 }
1965
1966 // Test using a ValueSetMatchDecider
1967 DEFINE_TESTCASE(valuesetmatchdecider2, backend && !remote) {
1968 Xapian::Database db(get_database("apitest_phrase"));
1969 Xapian::Enquire enq(db);
1970 enq.set_query(Xapian::Query("leav"));
1971
1972 Xapian::ValueSetMatchDecider vsmd1(1, true);
1973 vsmd1.add_value("n");
1974 Xapian::ValueSetMatchDecider vsmd2(1, false);
1975 vsmd2.add_value("n");
1976
1977 Xapian::MSet mymset = enq.get_mset(0, 20);
1978 mset_expect_order(mymset, 8, 6, 4, 5, 7, 10, 12, 11, 13, 9, 14);
1979 mymset = enq.get_mset(0, 20, 0, NULL, &vsmd1);
1980 mset_expect_order(mymset, 6, 12);
1981 mymset = enq.get_mset(0, 20, 0, NULL, &vsmd2);
1982 mset_expect_order(mymset, 8, 4, 5, 7, 10, 11, 13, 9, 14);
1983
1984 return true;
1985 }
1986