Branch data Line data Source code
1 : : /** @file api_backend.cc
2 : : * @brief Backend-related tests.
3 : : */
4 : : /* Copyright (C) 2008,2009,2010,2011 Olly Betts
5 : : * Copyright (C) 2010 Richard Boulton
6 : : *
7 : : * This program is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU General Public License as
9 : : * published by the Free Software Foundation; either version 2 of the
10 : : * License, or (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program; if not, write to the Free Software
19 : : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 : : * USA
21 : : */
22 : :
23 : : #include <config.h>
24 : :
25 : : #include "api_backend.h"
26 : :
27 : : #define XAPIAN_DEPRECATED(X) X
28 : : #include <xapian.h>
29 : :
30 : : #include "str.h"
31 : : #include "testsuite.h"
32 : : #include "testutils.h"
33 : : #include "utils.h"
34 : :
35 : : #include "apitest.h"
36 : :
37 : : #include "safeunistd.h"
38 : :
39 : : using namespace std;
40 : :
41 : : /// Regression test - lockfile should honour umask, was only user-readable.
42 : 3 : DEFINE_TESTCASE(lockfileumask1, brass || chert || flint) {
43 : : #if !defined __WIN32__ && !defined __CYGWIN__ && !defined __EMX__
44 : 3 : mode_t old_umask = umask(022);
45 : : try {
46 : 3 : Xapian::WritableDatabase db = get_named_writable_database("lockfileumask1");
47 : :
48 : 3 : string path = get_named_writable_database_path("lockfileumask1");
49 : 3 : path += "/flintlock";
50 : :
51 : : struct stat statbuf;
52 [ - + # # ]: 3 : TEST(stat(path, &statbuf) == 0);
53 [ - + ][ # # ]: 3 : TEST_EQUAL(statbuf.st_mode & 0777, 0644);
54 : 0 : } catch (...) {
55 : 0 : umask(old_umask);
56 : 0 : throw;
57 : : }
58 : :
59 : 3 : umask(old_umask);
60 : : #endif
61 : :
62 : 3 : return true;
63 : : }
64 : :
65 : : /// Check that the backend handles total document length > 0xffffffff.
66 : 10 : DEFINE_TESTCASE(totaldoclen1, writable) {
67 : 10 : Xapian::WritableDatabase db = get_writable_database();
68 : 10 : Xapian::Document doc;
69 : 10 : doc.add_posting("foo", 1, 2000000000);
70 : 10 : db.add_document(doc);
71 : 10 : db.add_document(doc);
72 [ - + # # ]: 10 : TEST_EQUAL(db.get_avlength(), 2000000000);
73 : 10 : db.commit();
74 [ - + # # ]: 10 : TEST_EQUAL(db.get_avlength(), 2000000000);
75 [ + + ]: 10 : if (get_dbtype() != "inmemory") {
76 : : // InMemory doesn't support get_writable_database_as_database().
77 : 9 : Xapian::Database dbr = get_writable_database_as_database();
78 [ - + # # ]: 9 : TEST_EQUAL(dbr.get_avlength(), 2000000000);
79 : : }
80 : 10 : return true;
81 : : }
82 : :
83 : 13 : DEFINE_TESTCASE(dbstats1, backend) {
84 : 13 : Xapian::Database db = get_database("etext");
85 : :
86 : : // Use precalculated values to avoid expending CPU cycles to calculate
87 : : // these every time without improving test coverage.
88 : 13 : const Xapian::termcount min_len = 2;
89 : 13 : const Xapian::termcount max_len = 532;
90 : 13 : const Xapian::termcount max_wdf = 22;
91 : :
92 [ + + + + ]: 13 : if (get_dbtype().find("chert") != string::npos ||
[ + + ][ # # ]
[ # # ][ + - ]
[ + + ]
93 : : get_dbtype().find("brass") != string::npos) {
94 : : // Should be exact for brass and chert as no deletions have happened.
95 [ - + ][ # # ]: 8 : TEST_EQUAL(db.get_doclength_upper_bound(), max_len);
96 [ - + ][ # # ]: 8 : TEST_EQUAL(db.get_doclength_lower_bound(), min_len);
97 : : } else {
98 : : // For other backends, we usually give rather loose bounds.
99 [ - + ][ # # ]: 5 : TEST_REL(db.get_doclength_upper_bound(),>=,max_len);
100 [ - + ][ # # ]: 5 : TEST_REL(db.get_doclength_lower_bound(),<=,min_len);
101 : : }
102 : :
103 [ - + ][ # # ]: 13 : TEST_REL(db.get_wdf_upper_bound("the"),>=,max_wdf);
104 : :
105 : 13 : return true;
106 : : }
107 : :
108 : : /// Check handling of alldocs on an empty database.
109 : 13 : DEFINE_TESTCASE(alldocspl3, backend) {
110 : 13 : Xapian::Database db = get_database(string());
111 : :
112 [ - + ][ # # ]: 13 : TEST_EQUAL(db.get_termfreq(string()), 0);
113 [ - + ][ # # ]: 13 : TEST_EQUAL(db.get_collection_freq(string()), 0);
114 [ - + ][ # # ]: 13 : TEST(db.postlist_begin(string()) == db.postlist_end(string()));
115 : :
116 : 13 : return true;
117 : : }
118 : :
119 : : /// Regression test for bug#392 in ModifiedPostList iteration, fixed in 1.0.15.
120 : 10 : DEFINE_TESTCASE(modifiedpostlist1, writable) {
121 : 10 : Xapian::WritableDatabase db = get_writable_database();
122 : 10 : Xapian::Document a, b;
123 : 10 : Xapian::Enquire enq(db);
124 : :
125 : 10 : a.add_term("T");
126 : 10 : enq.set_query(Xapian::Query("T"));
127 : :
128 : 10 : db.replace_document(2, a);
129 : 10 : db.commit();
130 : 10 : db.replace_document(1, a);
131 : 10 : db.replace_document(1, b);
132 : :
133 : 10 : mset_expect_order(enq.get_mset(0, 2), 2);
134 : :
135 : 10 : return true;
136 : : }
137 : :
138 : : /// Regression test for chert bug fixed in 1.1.3 (ticket#397).
139 : 10 : DEFINE_TESTCASE(doclenaftercommit1, writable) {
140 : 10 : Xapian::WritableDatabase db = get_writable_database();
141 [ + - ][ - + ]: 20 : TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_doclength(1));
[ # # ][ - + ]
142 : 10 : db.replace_document(1, Xapian::Document());
143 : 10 : db.commit();
144 [ - + # # ]: 10 : TEST_EQUAL(db.get_doclength(1), 0);;
145 : 10 : return true;
146 : : }
147 : :
148 : 10 : DEFINE_TESTCASE(valuesaftercommit1, writable) {
149 : 10 : Xapian::WritableDatabase db = get_writable_database();
150 : 10 : Xapian::Document doc;
151 : 10 : doc.add_value(0, "value");
152 : 10 : db.replace_document(2, doc);
153 : 10 : db.commit();
154 : 10 : db.replace_document(1, doc);
155 : 10 : db.replace_document(3, doc);
156 [ - + ][ # # ]: 10 : TEST_EQUAL(db.get_document(3).get_value(0), "value");
157 : 10 : db.commit();
158 [ - + ][ # # ]: 10 : TEST_EQUAL(db.get_document(3).get_value(0), "value");
159 : 10 : return true;
160 : : }
161 : :
162 : 3 : DEFINE_TESTCASE(lockfilefd0or1, brass || chert || flint) {
163 : : #if !defined __WIN32__ && !defined __CYGWIN__ && !defined __EMX__
164 : 3 : int old_stdin = dup(0);
165 : 3 : int old_stdout = dup(1);
166 : : try {
167 : : // With fd 0 available.
168 : 3 : close(0);
169 : : {
170 : 3 : Xapian::WritableDatabase db = get_writable_database();
171 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::DatabaseLockError,
[ # # ][ - + ]
172 : 3 : (void)get_writable_database_again());
173 : : }
174 : : // With fd 0 and fd 1 available.
175 : 3 : close(1);
176 : : {
177 : 3 : Xapian::WritableDatabase db = get_writable_database();
178 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::DatabaseLockError,
[ # # ][ - + ]
179 : 3 : (void)get_writable_database_again());
180 : : }
181 : : // With fd 1 available.
182 : 3 : dup2(old_stdin, 0);
183 : : {
184 : 3 : Xapian::WritableDatabase db = get_writable_database();
185 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::DatabaseLockError,
[ # # ][ - + ]
186 : 3 : (void)get_writable_database_again());
187 : : }
188 : 0 : } catch (...) {
189 : 0 : dup2(old_stdin, 0);
190 : 0 : dup2(old_stdout, 1);
191 : 0 : close(old_stdin);
192 : 0 : close(old_stdout);
193 : 0 : throw;
194 : : }
195 : :
196 : 3 : dup2(old_stdout, 1);
197 : 3 : close(old_stdin);
198 : 3 : close(old_stdout);
199 : : #endif
200 : :
201 : 3 : return true;
202 : : }
203 : :
204 [ # # ][ - + ]: 12 : struct MyMatchDecider : public Xapian::MatchDecider {
205 : : mutable bool called;
206 : :
207 : 12 : MyMatchDecider() : called(false) { }
208 : :
209 : 0 : bool operator()(const Xapian::Document &) const {
210 : 0 : called = true;
211 : 0 : return true;
212 : : }
213 : : };
214 : :
215 : : /// Test Xapian::MatchDecider with remote backend fails.
216 : 6 : DEFINE_TESTCASE(matchdecider4, remote) {
217 : 6 : Xapian::Database db(get_database("apitest_simpledata"));
218 : 6 : Xapian::Enquire enquire(db);
219 : 6 : enquire.set_query(Xapian::Query("paragraph"));
220 : :
221 : 6 : MyMatchDecider mdecider, mspyold;
222 : 6 : Xapian::MSet mset;
223 : :
224 [ + - ][ - + ]: 12 : TEST_EXCEPTION(Xapian::UnimplementedError,
[ # # ][ - + ]
225 : : mset = enquire.get_mset(0, 10, NULL, &mdecider));
226 [ - + # # ]: 6 : TEST(!mdecider.called);
227 : :
228 [ + - ][ - + ]: 12 : TEST_EXCEPTION(Xapian::UnimplementedError,
[ # # ][ - + ]
229 : : mset = enquire.get_mset(0, 10, 0, NULL, NULL, &mspyold));
230 [ - + # # ]: 6 : TEST(!mspyold.called);
231 : :
232 [ + - ][ - + ]: 12 : TEST_EXCEPTION(Xapian::UnimplementedError,
[ # # ][ - + ]
233 : : mset = enquire.get_mset(0, 10, 0, NULL, &mdecider, &mspyold));
234 [ - + # # ]: 6 : TEST(!mdecider.called);
235 [ - + ][ # # ]: 6 : TEST(!mspyold.called);
236 : :
237 : 6 : return true;
238 : : }
239 : :
240 : : /** Check that replacing an unmodified document doesn't increase the automatic
241 : : * flush counter. Regression test for bug fixed in 1.1.4/1.0.18.
242 : : */
243 : 3 : DEFINE_TESTCASE(replacedoc7, writable && !inmemory && !remote) {
244 : : // The inmemory backend doesn't batch changes, so there's nothing to
245 : : // check there.
246 : : //
247 : : // The remote backend doesn't implement the lazy replacement of documents
248 : : // optimisation currently.
249 : 3 : Xapian::WritableDatabase db(get_writable_database());
250 : 3 : Xapian::Document doc;
251 : 3 : doc.set_data("fish");
252 : 3 : doc.add_term("Hlocalhost");
253 : 3 : doc.add_posting("hello", 1);
254 : 3 : doc.add_posting("world", 2);
255 : 3 : doc.add_value(1, "myvalue");
256 : 3 : db.add_document(doc);
257 : 3 : db.commit();
258 : :
259 : : // We add a second document, and then replace the first document with
260 : : // itself 10000 times. If the document count for the database reopened
261 : : // read-only is 2, then we triggered an automatic commit.
262 : :
263 : 3 : doc.add_term("XREV2");
264 : 3 : db.add_document(doc);
265 : :
266 [ + + ]: 30003 : for (int i = 0; i < 10000; ++i) {
267 : 30000 : doc = db.get_document(1);
268 : 30000 : db.replace_document(1, doc);
269 : : }
270 : :
271 : 3 : Xapian::Database rodb(get_writable_database_as_database());
272 [ - + # # ]: 3 : TEST_EQUAL(rodb.get_doccount(), 1);
273 : :
274 : 3 : db.flush();
275 : 3 : rodb.reopen();
276 : :
277 [ - + # # ]: 3 : TEST_EQUAL(rodb.get_doccount(), 2);
278 : 3 : return true;
279 : : }
280 : :
281 : : /** Check that replacing a document deleted since the last flush works.
282 : : * Prior to 1.1.4/1.0.18, this failed to update the collection frequency and
283 : : * wdf, and caused an assertion failure when assertions were enabled.
284 : : */
285 : 10 : DEFINE_TESTCASE(replacedoc8, writable) {
286 : 10 : Xapian::WritableDatabase db(get_writable_database());
287 : : {
288 : 10 : Xapian::Document doc;
289 : 10 : doc.set_data("fish");
290 : 10 : doc.add_term("takeaway");
291 : 10 : db.add_document(doc);
292 : : }
293 : 10 : db.delete_document(1);
294 : : {
295 : 10 : Xapian::Document doc;
296 : 10 : doc.set_data("chips");
297 : 10 : doc.add_term("takeaway", 2);
298 : 10 : db.replace_document(1, doc);
299 : : }
300 : 10 : db.flush();
301 [ - + ][ # # ]: 10 : TEST_EQUAL(db.get_collection_freq("takeaway"), 2);
302 : 10 : Xapian::PostingIterator p = db.postlist_begin("takeaway");
303 [ - + ][ # # ]: 10 : TEST(p != db.postlist_end("takeaway"));
304 [ - + ][ # # ]: 10 : TEST_EQUAL(p.get_wdf(), 2);
305 : 10 : return true;
306 : : }
307 : :
308 : : /// Test coverage for DatabaseModifiedError.
309 : 3 : DEFINE_TESTCASE(databasemodified1, writable && !inmemory && !remote) {
310 : : // The inmemory backend doesn't support revisions.
311 : : //
312 : : // The remote backend doesn't work as expected here, I think due to
313 : : // test harness issues.
314 : 3 : Xapian::WritableDatabase db(get_writable_database());
315 : 3 : Xapian::Document doc;
316 : 3 : doc.set_data("cargo");
317 : 3 : doc.add_term("abc");
318 : 3 : doc.add_term("def");
319 : 3 : doc.add_term("ghi");
320 : 3 : const int N = 500;
321 [ + + ]: 1503 : for (int i = 0; i < N; ++i) {
322 : 1500 : db.add_document(doc);
323 : : }
324 : 3 : db.commit();
325 : :
326 : 3 : Xapian::Database rodb(get_writable_database_as_database());
327 : 3 : db.add_document(doc);
328 : 3 : db.commit();
329 : :
330 : 3 : db.add_document(doc);
331 : 3 : db.commit();
332 : :
333 : 3 : db.add_document(doc);
334 : : try {
335 [ # # ][ # # ]: 3 : TEST_EQUAL(*rodb.termlist_begin(N - 1), "abc");
336 : 0 : return false;
337 : 3 : } catch (const Xapian::DatabaseModifiedError &) {
338 : : }
339 : :
340 : : try {
341 : 3 : Xapian::Enquire enq(rodb);
342 : 3 : enq.set_query(Xapian::Query("abc"));
343 : 3 : Xapian::MSet mset = enq.get_mset(0, 10);
344 : 3 : return false;
345 : 3 : } catch (const Xapian::DatabaseModifiedError &) {
346 : : }
347 : :
348 : 3 : return true;
349 : : }
350 : :
351 : : /// Regression test for bug#462 fixed in 1.0.19 and 1.1.5.
352 : 9 : DEFINE_TESTCASE(qpmemoryleak1, writable && !inmemory) {
353 : : // Inmemory never throws DatabaseModifiedError.
354 : 9 : Xapian::WritableDatabase wdb(get_writable_database());
355 : 9 : Xapian::Document doc;
356 : :
357 : 9 : doc.add_term("foo");
358 [ + + ]: 189 : for (int i = 100; i < 120; ++i) {
359 : 180 : doc.add_term(str(i));
360 : : }
361 : :
362 [ + + ]: 459 : for (int j = 0; j < 50; ++j) {
363 : 450 : wdb.add_document(doc);
364 : : }
365 : 9 : wdb.commit();
366 : :
367 : 9 : Xapian::Database database(get_writable_database_as_database());
368 : 9 : Xapian::QueryParser queryparser;
369 : 9 : queryparser.set_database(database);
370 [ + - ][ - + ]: 60 : TEST_EXCEPTION(Xapian::DatabaseModifiedError,
[ + - ][ # # ]
[ - + ]
371 : : for (int k = 0; k < 3; ++k) {
372 : : wdb.add_document(doc);
373 : : wdb.commit();
374 : : (void)queryparser.parse_query("1", queryparser.FLAG_PARTIAL);
375 : : }
376 : : );
377 : :
378 : 9 : return true;
379 : : }
380 : :
381 : : static void
382 : 3 : make_msize1_db(Xapian::WritableDatabase &db, const string &)
383 : : {
384 : : const char * value0 =
385 : 3 : "ABBCDEFGHIJKLMMNOPQQRSTTUUVVWXYZZaabcdefghhijjkllmnopqrsttuvwxyz";
386 : : const char * value1 =
387 : 3 : "EMLEMMMMMMMNMMLMELEDNLEDMLMLDMLMLMLMEDGFHPOPBAHJIQJNGRKCGF";
388 [ + + ]: 195 : while (*value0) {
389 : 192 : Xapian::Document doc;
390 : 192 : doc.add_value(0, string(1, *value0++));
391 [ + + ]: 192 : if (*value1) {
392 : 174 : doc.add_value(1, string(1, *value1++));
393 : 174 : doc.add_term("K1");
394 : : }
395 : 192 : db.add_document(doc);
396 : : }
397 : 3 : }
398 : :
399 : : /// Regression test for ticket#464, fixed in 1.1.6 and 1.0.20.
400 : 3 : DEFINE_TESTCASE(msize1, generated) {
401 : 3 : Xapian::Database db = get_database("msize1", make_msize1_db);
402 : 3 : Xapian::Enquire enq(db);
403 : 3 : enq.set_sort_by_value(1, false);
404 : 3 : enq.set_collapse_key(0);
405 : 3 : enq.set_query(Xapian::Query("K1"));
406 : :
407 : 3 : Xapian::MSet mset = enq.get_mset(0, 10, 1000);
408 : 3 : Xapian::doccount lb = mset.get_matches_lower_bound();
409 : 3 : Xapian::doccount ub = mset.get_matches_upper_bound();
410 : 3 : Xapian::doccount est = mset.get_matches_estimated();
411 [ - + # # ]: 3 : TEST_EQUAL(lb, ub);
412 [ - + ][ # # ]: 3 : TEST_EQUAL(lb, est);
413 : :
414 : 3 : Xapian::MSet mset2 = enq.get_mset(50, 10, 1000);
415 : 3 : Xapian::doccount lb2 = mset2.get_matches_lower_bound();
416 : 3 : Xapian::doccount ub2 = mset2.get_matches_upper_bound();
417 : 3 : Xapian::doccount est2 = mset2.get_matches_estimated();
418 [ - + # # ]: 3 : TEST_EQUAL(lb2, ub2);
419 [ - + ][ # # ]: 3 : TEST_EQUAL(lb2, est2);
420 [ - + ][ # # ]: 3 : TEST_EQUAL(est, est2);
421 : :
422 : 3 : Xapian::MSet mset3 = enq.get_mset(0, 60);
423 : 3 : Xapian::doccount lb3 = mset3.get_matches_lower_bound();
424 : 3 : Xapian::doccount ub3 = mset3.get_matches_upper_bound();
425 : 3 : Xapian::doccount est3 = mset3.get_matches_estimated();
426 [ - + # # ]: 3 : TEST_EQUAL(lb3, ub3);
427 [ - + ][ # # ]: 3 : TEST_EQUAL(lb3, est3);
428 [ - + ][ # # ]: 3 : TEST_EQUAL(est, est3);
429 : :
430 : 3 : return true;
431 : : }
432 : :
433 : : static void
434 : 3 : make_msize2_db(Xapian::WritableDatabase &db, const string &)
435 : : {
436 : 3 : const char * value0 = "AAABCDEEFGHIIJJKLLMNNOOPPQQRSTTUVWXYZ";
437 : 3 : const char * value1 = "MLEMNMLMLMEDEDEMLEMLMLMLPOAHGF";
438 [ + + ]: 114 : while (*value0) {
439 : 111 : Xapian::Document doc;
440 : 111 : doc.add_value(0, string(1, *value0++));
441 [ + + ]: 111 : if (*value1) {
442 : 90 : doc.add_value(1, string(1, *value1++));
443 : 90 : doc.add_term("K1");
444 : : }
445 : 111 : db.add_document(doc);
446 : : }
447 : 3 : }
448 : :
449 : : /// Regression test for bug related to ticket#464, fixed in 1.1.6 and 1.0.20.
450 : 3 : DEFINE_TESTCASE(msize2, generated) {
451 : 3 : Xapian::Database db = get_database("msize2", make_msize2_db);
452 : 3 : Xapian::Enquire enq(db);
453 : 3 : enq.set_sort_by_value(1, false);
454 : 3 : enq.set_collapse_key(0);
455 : 3 : enq.set_query(Xapian::Query("K1"));
456 : :
457 : 3 : Xapian::MSet mset = enq.get_mset(0, 10, 1000);
458 : 3 : Xapian::doccount lb = mset.get_matches_lower_bound();
459 : 3 : Xapian::doccount ub = mset.get_matches_upper_bound();
460 : 3 : Xapian::doccount est = mset.get_matches_estimated();
461 [ - + # # ]: 3 : TEST_EQUAL(lb, ub);
462 [ - + ][ # # ]: 3 : TEST_EQUAL(lb, est);
463 : :
464 : 3 : Xapian::MSet mset2 = enq.get_mset(50, 10, 1000);
465 : 3 : Xapian::doccount lb2 = mset2.get_matches_lower_bound();
466 : 3 : Xapian::doccount ub2 = mset2.get_matches_upper_bound();
467 : 3 : Xapian::doccount est2 = mset2.get_matches_estimated();
468 [ - + # # ]: 3 : TEST_EQUAL(lb2, ub2);
469 [ - + ][ # # ]: 3 : TEST_EQUAL(lb2, est2);
470 [ - + ][ # # ]: 3 : TEST_EQUAL(est, est2);
471 : :
472 : 3 : Xapian::MSet mset3 = enq.get_mset(0, 60);
473 : 3 : Xapian::doccount lb3 = mset3.get_matches_lower_bound();
474 : 3 : Xapian::doccount ub3 = mset3.get_matches_upper_bound();
475 : 3 : Xapian::doccount est3 = mset3.get_matches_estimated();
476 [ - + # # ]: 3 : TEST_EQUAL(lb3, ub3);
477 [ - + ][ # # ]: 3 : TEST_EQUAL(lb3, est3);
478 [ - + ][ # # ]: 3 : TEST_EQUAL(est, est3);
479 : :
480 : 3 : return true;
481 : : }
482 : :
483 : : static void
484 : 3 : make_xordecay1_db(Xapian::WritableDatabase &db, const string &)
485 : : {
486 [ + + ]: 150 : for (int n = 1; n != 50; ++n) {
487 : 147 : Xapian::Document doc;
488 [ + + ]: 7350 : for (int i = 1; i != 50; ++i) {
489 [ + + ]: 7203 : if (n % i == 0)
490 : 603 : doc.add_term("N" + str(i));
491 : : }
492 : 147 : db.add_document(doc);
493 : : }
494 : 3 : }
495 : :
496 : : /// Regression test for bug in decay of XOR, fixed in 1.2.1 and 1.0.21.
497 : 3 : DEFINE_TESTCASE(xordecay1, generated) {
498 : 3 : Xapian::Database db = get_database("xordecay1", make_xordecay1_db);
499 : 3 : Xapian::Enquire enq(db);
500 : : enq.set_query(Xapian::Query(Xapian::Query::OP_XOR,
501 : : Xapian::Query("N10"),
502 : : Xapian::Query(Xapian::Query::OP_OR,
503 : : Xapian::Query("N2"),
504 : 3 : Xapian::Query("N3"))));
505 : 3 : Xapian::MSet mset1 = enq.get_mset(0, 1);
506 : 3 : Xapian::MSet msetall = enq.get_mset(0, db.get_doccount());
507 : :
508 [ - + # # ]: 3 : TEST(mset_range_is_same(mset1, 0, msetall, 0, mset1.size()));
509 : 3 : return true;
510 : : }
511 : :
512 : : static void
513 : 3 : make_ordecay_db(Xapian::WritableDatabase &db, const string &)
514 : : {
515 : 3 : const char * p = "VJ=QC]LUNTaARLI;715RR^];A4O=P4ZG<2CS4EM^^VS[A6QENR";
516 [ + + ]: 153 : for (int d = 0; p[d]; ++d) {
517 : 150 : int l = int(p[d] - '0');
518 : 150 : Xapian::Document doc;
519 [ + + ]: 3867 : for (int n = 1; n < l; ++n) {
520 : 3717 : doc.add_term("N" + str(n));
521 [ + + ]: 3717 : if (n % (d + 1) == 0) {
522 : 309 : doc.add_term("M" + str(n));
523 : : }
524 : : }
525 : 150 : db.add_document(doc);
526 : : }
527 : 3 : }
528 : :
529 : : /// Regression test for bug in decay of OR to AND, fixed in 1.2.1 and 1.0.21.
530 : 3 : DEFINE_TESTCASE(ordecay1, generated) {
531 : 3 : Xapian::Database db = get_database("ordecay", make_ordecay_db);
532 : 3 : Xapian::Enquire enq(db);
533 : : enq.set_query(Xapian::Query(Xapian::Query::OP_OR,
534 : : Xapian::Query("N20"),
535 : 3 : Xapian::Query("N21")));
536 : :
537 : 3 : Xapian::MSet msetall = enq.get_mset(0, db.get_doccount());
538 [ + + ]: 96 : for (unsigned int i = 1; i < msetall.size(); ++i) {
539 : 93 : Xapian::MSet submset = enq.get_mset(0, i);
540 [ - + # # ]: 93 : TEST(mset_range_is_same(submset, 0, msetall, 0, submset.size()));
541 : : }
542 : 3 : return true;
543 : : }
544 : :
545 : : /** Regression test for bug in decay of OR to AND_MAYBE, fixed in 1.2.1 and
546 : : * 1.0.21.
547 : : */
548 : 3 : DEFINE_TESTCASE(ordecay2, generated) {
549 : 3 : Xapian::Database db = get_database("ordecay", make_ordecay_db);
550 : 3 : Xapian::Enquire enq(db);
551 : 3 : std::vector<Xapian::Query> q;
552 : 3 : q.push_back(Xapian::Query("M20"));
553 : 3 : q.push_back(Xapian::Query("N21"));
554 : 3 : q.push_back(Xapian::Query("N22"));
555 : : enq.set_query(Xapian::Query(Xapian::Query::OP_OR,
556 : : Xapian::Query("N25"),
557 : : Xapian::Query(Xapian::Query::OP_AND,
558 : : q.begin(),
559 : 3 : q.end())));
560 : :
561 : 3 : Xapian::MSet msetall = enq.get_mset(0, db.get_doccount());
562 [ + + ]: 84 : for (unsigned int i = 1; i < msetall.size(); ++i) {
563 : 81 : Xapian::MSet submset = enq.get_mset(0, i);
564 [ - + # # ]: 81 : TEST(mset_range_is_same(submset, 0, msetall, 0, submset.size()));
565 : : }
566 : 3 : return true;
567 : : }
568 : :
569 : : static void
570 : 3 : make_orcheck_db(Xapian::WritableDatabase &db, const string &)
571 : : {
572 : : static const int t1[6] = {2, 4, 6, 8, 10, 0};
573 : : static const int t2[11] = {6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 0};
574 : : static const int t3[11] = {3, 7, 8, 11, 12, 13, 14, 15, 16, 17, 0};
575 : :
576 [ + + ]: 54 : for (unsigned i = 1; i <= 17; ++i) {
577 : 51 : Xapian::Document doc;
578 : 51 : db.replace_document(i, doc);
579 : : }
580 [ + + ]: 18 : for (const int * p = t1; *p != 0; ++p) {
581 : 15 : Xapian::Document doc(db.get_document(*p));
582 : 15 : doc.add_term("T1");
583 : 15 : db.replace_document(*p, doc);
584 : : }
585 [ + + ]: 33 : for (const int * p = t2; *p != 0; ++p) {
586 : 30 : Xapian::Document doc(db.get_document(*p));
587 : 30 : doc.add_term("T2");
588 [ + + ]: 30 : if (*p < 17) {
589 : 27 : doc.add_term("T2_lowfreq");
590 : : }
591 : 30 : doc.add_value(2, "1");
592 : 30 : db.replace_document(*p, doc);
593 : : }
594 [ + + ]: 33 : for (const int * p = t3; *p != 0; ++p) {
595 : 30 : Xapian::Document doc(db.get_document(*p));
596 : 30 : doc.add_term("T3");
597 [ + + ]: 30 : if (*p < 17) {
598 : 27 : doc.add_term("T3_lowfreq");
599 : : }
600 : 30 : doc.add_value(3, "1");
601 : 30 : db.replace_document(*p, doc);
602 : : }
603 : 3 : }
604 : :
605 : : /** Regression test for bugs in the check() method of OrPostList. (ticket #485)
606 : : * Bugs introduced and fixed between 1.2.0 and 1.2.1 (never in a release).
607 : : */
608 : 3 : DEFINE_TESTCASE(orcheck1, generated) {
609 : 3 : Xapian::Database db = get_database("orcheck1", make_orcheck_db);
610 : 3 : Xapian::Enquire enq(db);
611 : 3 : Xapian::Query q1("T1");
612 : 3 : Xapian::Query q2("T2");
613 : 3 : Xapian::Query q2l("T2_lowfreq");
614 : 3 : Xapian::Query q3("T3");
615 : 3 : Xapian::Query q3l("T3_lowfreq");
616 : 3 : Xapian::Query v2(Xapian::Query::OP_VALUE_RANGE, 2, "0", "2");
617 : 3 : Xapian::Query v3(Xapian::Query::OP_VALUE_RANGE, 3, "0", "2");
618 : :
619 : 3 : tout << "Checking q2 OR q3\n";
620 : : enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
621 : 3 : Xapian::Query(Xapian::Query::OP_OR, q2, q3)));
622 : 3 : mset_expect_order(enq.get_mset(0, db.get_doccount()), 8, 6);
623 : :
624 : 3 : tout << "Checking q2l OR q3\n";
625 : : enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
626 : 3 : Xapian::Query(Xapian::Query::OP_OR, q2l, q3)));
627 : 3 : mset_expect_order(enq.get_mset(0, db.get_doccount()), 8, 6);
628 : :
629 : 3 : tout << "Checking q2 OR q3l\n";
630 : : enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
631 : 3 : Xapian::Query(Xapian::Query::OP_OR, q2, q3l)));
632 : 3 : mset_expect_order(enq.get_mset(0, db.get_doccount()), 8, 6);
633 : :
634 : 3 : tout << "Checking v2 OR q3\n";
635 : : enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
636 : 3 : Xapian::Query(Xapian::Query::OP_OR, v2, q3)));
637 : 3 : mset_expect_order(enq.get_mset(0, db.get_doccount()), 8, 6);
638 : :
639 : 3 : tout << "Checking q2 OR v3\n";
640 : : enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
641 : 3 : Xapian::Query(Xapian::Query::OP_OR, q2, v3)));
642 : : // Order of results in this one is different, because v3 gives no weight,
643 : : // both documents are in q2, and document 8 has a higher length.
644 : 3 : mset_expect_order(enq.get_mset(0, db.get_doccount()), 6, 8);
645 : :
646 : 3 : return true;
647 : : }
648 : :
649 : : /** Regression test for bug fixed in 1.2.1 and 1.0.21.
650 : : *
651 : : * We failed to mark the Btree as unmodified after cancel().
652 : : */
653 : 3 : DEFINE_TESTCASE(failedreplace1, brass || chert || flint) {
654 : 3 : Xapian::WritableDatabase db(get_writable_database());
655 : 3 : Xapian::Document doc;
656 : 3 : doc.add_term("foo");
657 : 3 : db.add_document(doc);
658 : 3 : Xapian::docid did = db.add_document(doc);
659 : 3 : doc.add_term("abc");
660 : 3 : doc.add_term(string(1000, 'm'));
661 : 3 : doc.add_term("xyz");
662 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::InvalidArgumentError, db.replace_document(did, doc));
[ # # ][ - + ]
663 : 3 : db.commit();
664 [ - + # # ]: 3 : TEST_EQUAL(db.get_doccount(), 0);
665 [ - + ][ # # ]: 3 : TEST_EQUAL(db.get_termfreq("foo"), 0);
666 : 3 : return true;
667 : : }
668 : :
669 : 3 : DEFINE_TESTCASE(failedreplace2, brass || chert || flint) {
670 : 3 : Xapian::WritableDatabase db(get_writable_database("apitest_simpledata"));
671 : 3 : db.commit();
672 : 3 : Xapian::doccount db_size = db.get_doccount();
673 : 3 : Xapian::Document doc;
674 : 3 : doc.set_data("wibble");
675 : 3 : doc.add_term("foo");
676 : 3 : doc.add_value(0, "seven");
677 : 3 : db.add_document(doc);
678 : 3 : Xapian::docid did = db.add_document(doc);
679 : 3 : doc.add_term("abc");
680 : 3 : doc.add_term(string(1000, 'm'));
681 : 3 : doc.add_term("xyz");
682 : 3 : doc.add_value(0, "six");
683 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::InvalidArgumentError, db.replace_document(did, doc));
[ # # ][ - + ]
684 : 3 : db.commit();
685 [ - + # # ]: 3 : TEST_EQUAL(db.get_doccount(), db_size);
686 [ - + ][ # # ]: 3 : TEST_EQUAL(db.get_termfreq("foo"), 0);
687 : 3 : return true;
688 : : }
689 : :
690 : : /// Coverage for SelectPostList::skip_to().
691 : 13 : DEFINE_TESTCASE(phrase3, positional) {
692 : 13 : Xapian::Database db = get_database("apitest_phrase");
693 : :
694 : 13 : const char * phrase_words[] = { "phrase", "near" };
695 : 13 : Xapian::Query q(Xapian::Query::OP_NEAR, phrase_words, phrase_words + 2, 12);
696 : 13 : q = Xapian::Query(Xapian::Query::OP_AND_MAYBE, Xapian::Query("pad"), q);
697 : :
698 : 13 : Xapian::Enquire enquire(db);
699 : 13 : enquire.set_query(q);
700 : 13 : Xapian::MSet mset = enquire.get_mset(0, 5);
701 : :
702 : 13 : return true;
703 : : }
704 : :
705 : : /// Check that get_mset(<large number>, 10) doesn't exhaust memory needlessly.
706 : : // Regression test for fix in 1.2.4.
707 : 13 : DEFINE_TESTCASE(msetfirst2, backend) {
708 : 13 : Xapian::Database db(get_database("apitest_simpledata"));
709 : 13 : Xapian::Enquire enquire(db);
710 : 13 : enquire.set_query(Xapian::Query("paragraph"));
711 : 13 : Xapian::MSet mset;
712 : : // Before the fix, this tried to allocated too much memory.
713 : 13 : mset = enquire.get_mset(0xfffffff0, 1);
714 [ - + # # ]: 13 : TEST_EQUAL(mset.get_firstitem(), 0xfffffff0);
715 : : // Check that the number of documents gets clamped too.
716 : 13 : mset = enquire.get_mset(1, 0xfffffff0);
717 [ - + # # ]: 13 : TEST_EQUAL(mset.get_firstitem(), 1);
718 : : // Another regression test - MatchNothing used to give an MSet with
719 : : // get_firstitem() returning 0.
720 : 13 : enquire.set_query(Xapian::Query::MatchNothing);
721 : 13 : mset = enquire.get_mset(1, 1);
722 [ - + # # ]: 13 : TEST_EQUAL(mset.get_firstitem(), 1);
723 : 13 : return true;
724 : : }
725 : :
726 : 13 : DEFINE_TESTCASE(bm25weight2, backend) {
727 : 13 : Xapian::Database db(get_database("etext"));
728 : 13 : Xapian::Enquire enquire(db);
729 : 13 : enquire.set_query(Xapian::Query("the"));
730 : 13 : enquire.set_weighting_scheme(Xapian::BM25Weight(0, 0, 0, 0, 1));
731 : 13 : Xapian::MSet mset = enquire.get_mset(0, 100);
732 [ - + # # ]: 13 : TEST_REL(mset.size(),>=,2);
733 : 13 : Xapian::weight weight0 = mset[0].get_weight();
734 [ + + ]: 1300 : for (size_t i = 1; i != mset.size(); ++i) {
735 [ - + ][ # # ]: 1287 : TEST_EQUAL(weight0, mset[i].get_weight());
736 : : }
737 : 13 : return true;
738 : : }
739 : :
740 : 13 : DEFINE_TESTCASE(tradweight2, backend) {
741 : 13 : Xapian::Database db(get_database("etext"));
742 : 13 : Xapian::Enquire enquire(db);
743 : 13 : enquire.set_query(Xapian::Query("the"));
744 : 13 : enquire.set_weighting_scheme(Xapian::TradWeight(0));
745 : 13 : Xapian::MSet mset = enquire.get_mset(0, 100);
746 [ - + # # ]: 13 : TEST_REL(mset.size(),>=,2);
747 : 13 : Xapian::weight weight0 = mset[0].get_weight();
748 [ + + ]: 1300 : for (size_t i = 1; i != mset.size(); ++i) {
749 [ - + ][ # # ]: 1287 : TEST_EQUAL(weight0, mset[i].get_weight());
750 : : }
751 : 13 : return true;
752 : : }
|