Support: quest -f cjk_ngram
[xapian.git] / xapian-core / tests / api_wrdb.cc
blobdba719678b16ed77c38cbbd9ae95ad3722e00788
1 /* api_wrdb.cc: tests which need a writable backend
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2001 Hein Ragas
5 * Copyright 2002 Ananova Ltd
6 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2014,2015 Olly Betts
7 * Copyright 2006 Richard Boulton
8 * Copyright 2007 Lemur Consulting Ltd
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 * USA
26 #include <config.h>
28 #include "api_wrdb.h"
30 #include <xapian.h>
32 #include "filetests.h"
33 #include "omassert.h"
34 #include "str.h"
35 #include "stringutils.h"
36 #include "testsuite.h"
37 #include "testutils.h"
38 #include "unixcmds.h"
40 #include "apitest.h"
42 #include "safeunistd.h"
43 #include <cmath>
44 #include <cstdlib>
45 #include <map>
46 #include <string>
48 using namespace std;
50 // #######################################################################
51 // # Tests start here
53 // test that indexing a term more than once at the same position increases
54 // the wdf
55 DEFINE_TESTCASE(adddoc1, writable) {
56 Xapian::WritableDatabase db = get_writable_database();
58 Xapian::Document doc1, doc2, doc3;
60 // doc1 should come top, but if term "foo" gets wdf of 1, doc2 will beat it
61 // doc3 should beat both
62 // Note: all docs have same length
63 doc1.set_data(string("tom"));
64 doc1.add_posting("foo", 1);
65 doc1.add_posting("foo", 1);
66 doc1.add_posting("foo", 1);
67 doc1.add_posting("bar", 3);
68 doc1.add_posting("bar", 4);
69 db.add_document(doc1);
71 doc2.set_data(string("dick"));
72 doc2.add_posting("foo", 1);
73 doc2.add_posting("foo", 2);
74 doc2.add_posting("bar", 3);
75 doc2.add_posting("bar", 3);
76 doc2.add_posting("bar", 3);
77 db.add_document(doc2);
79 doc3.set_data(string("harry"));
80 doc3.add_posting("foo", 1);
81 doc3.add_posting("foo", 1);
82 doc3.add_posting("foo", 2);
83 doc3.add_posting("foo", 2);
84 doc3.add_posting("bar", 3);
85 db.add_document(doc3);
87 Xapian::Query query("foo");
89 Xapian::Enquire enq(db);
90 enq.set_query(query);
92 Xapian::MSet mset = enq.get_mset(0, 10);
94 mset_expect_order(mset, 3, 1, 2);
96 return true;
99 // test that removing a posting and removing a term works
100 DEFINE_TESTCASE(adddoc2, writable) {
101 Xapian::WritableDatabase db = get_writable_database();
103 Xapian::Document doc1;
105 doc1.add_posting("foo", 1);
106 doc1.add_posting("foo", 1);
107 doc1.add_posting("foo", 2);
108 doc1.add_posting("foo", 2);
109 doc1.add_posting("bar", 3);
110 doc1.add_posting("gone", 1);
111 // Quartz had a bug handling a term >= 128 characters longer than the
112 // preceding term in the sort order - this is "foo" + 130 "X"s
113 doc1.add_posting("fooXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 1);
114 Xapian::docid did;
116 Xapian::Document doc2 = db.get_document(did = db.add_document(doc1));
117 TEST_EQUAL(did, 1);
119 Xapian::TermIterator iter1 = doc1.termlist_begin();
120 Xapian::TermIterator iter2 = doc2.termlist_begin();
121 TEST(iter1 != doc1.termlist_end());
122 TEST(iter2 != doc2.termlist_end());
123 TEST_EQUAL(*iter1, "bar");
124 TEST_EQUAL(*iter2, *iter1);
125 TEST_EQUAL(iter1.get_wdf(), 1);
126 TEST_EQUAL(iter2.get_wdf(), 1);
127 //TEST_EQUAL(iter1.get_termfreq(), 0);
128 TEST_EQUAL(iter2.get_termfreq(), 1);
130 iter1++;
131 iter2++;
132 TEST(iter1 != doc1.termlist_end());
133 TEST(iter2 != doc2.termlist_end());
134 TEST_EQUAL(*iter1, "foo");
135 TEST_EQUAL(*iter2, *iter1);
136 TEST_EQUAL(iter1.get_wdf(), 4);
137 TEST_EQUAL(iter2.get_wdf(), 4);
138 //TEST_EQUAL(iter1.get_termfreq(), 0);
139 TEST_EQUAL(iter2.get_termfreq(), 1);
141 iter1++;
142 iter2++;
143 TEST(iter1 != doc1.termlist_end());
144 TEST(iter2 != doc2.termlist_end());
145 TEST_EQUAL(*iter1, "fooXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
146 TEST_EQUAL(*iter2, *iter1);
147 TEST_EQUAL(iter1.get_wdf(), 1);
148 TEST_EQUAL(iter2.get_wdf(), 1);
149 // assertion fails in debug build! TEST_EQUAL(iter1.get_termfreq(), 0);
150 TEST_EQUAL(iter2.get_termfreq(), 1);
152 iter1++;
153 iter2++;
154 TEST(iter1 != doc1.termlist_end());
155 TEST(iter2 != doc2.termlist_end());
156 TEST_EQUAL(*iter1, "gone");
157 TEST_EQUAL(*iter2, *iter1);
158 TEST_EQUAL(iter1.get_wdf(), 1);
159 TEST_EQUAL(iter2.get_wdf(), 1);
160 // assertion fails in debug build! TEST_EQUAL(iter1.get_termfreq(), 0);
161 TEST_EQUAL(iter2.get_termfreq(), 1);
163 iter1++;
164 iter2++;
165 TEST(iter1 == doc1.termlist_end());
166 TEST(iter2 == doc2.termlist_end());
168 doc2.remove_posting("foo", 1, 5);
169 doc2.add_term("bat", 0);
170 doc2.add_term("bar", 8);
171 doc2.add_term("bag", 0);
172 doc2.remove_term("gone");
173 doc2.remove_term("fooXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
175 // Should have (doc,wdf) pairs: (bag,0)(bar,9)(bat,0)(foo,0)
176 // positionlists (bag,none)(bar,3)(bat,none)(foo,2)
178 iter2 = doc2.termlist_begin();
179 TEST(iter2 != doc2.termlist_end());
180 TEST_EQUAL(*iter2, "bag");
181 //TEST_EQUAL(iter2.get_termfreq(), 0);
182 iter2++;
183 TEST(iter2 != doc2.termlist_end());
184 TEST_EQUAL(*iter2, "bar");
185 //TEST_EQUAL(iter2.get_termfreq(), 0);
186 iter2++;
187 TEST(iter2 != doc2.termlist_end());
188 TEST_EQUAL(*iter2, "bat");
189 //TEST_EQUAL(iter2.get_termfreq(), 0);
190 iter2++;
191 TEST(iter2 != doc2.termlist_end());
192 TEST_EQUAL(*iter2, "foo");
193 //TEST_EQUAL(iter2.get_termfreq(), 0);
194 iter2++;
195 TEST(iter2 == doc2.termlist_end());
197 doc1 = db.get_document(did = db.add_document(doc2));
198 TEST_EQUAL(did, 2);
200 iter1 = doc1.termlist_begin();
201 iter2 = doc2.termlist_begin();
202 TEST(iter1 != doc1.termlist_end());
203 TEST(iter2 != doc2.termlist_end());
204 TEST_EQUAL(*iter1, "bag");
205 TEST_EQUAL(*iter2, *iter1);
206 TEST_EQUAL(iter1.get_wdf(), 0);
207 TEST_EQUAL(iter2.get_wdf(), 0);
208 TEST_EQUAL(iter1.get_termfreq(), 1);
209 //TEST_EQUAL(iter2.get_termfreq(), 0);
210 TEST(iter1.positionlist_begin() == iter1.positionlist_end());
211 TEST(iter2.positionlist_begin() == iter2.positionlist_end());
213 iter1++;
214 iter2++;
215 TEST(iter1 != doc1.termlist_end());
216 TEST(iter2 != doc2.termlist_end());
217 TEST_EQUAL(*iter1, "bar");
218 TEST_EQUAL(*iter2, *iter1);
219 TEST_EQUAL(iter1.get_wdf(), 9);
220 TEST_EQUAL(iter2.get_wdf(), 9);
221 TEST_EQUAL(iter1.get_termfreq(), 2);
222 //TEST_EQUAL(iter2.get_termfreq(), 0);
224 Xapian::PositionIterator pi1;
225 pi1 = iter1.positionlist_begin();
226 Xapian::PositionIterator pi2 = iter2.positionlist_begin();
227 TEST_EQUAL(*pi1, 3); pi1++;
228 TEST_EQUAL(*pi2, 3); pi2++;
229 TEST(pi1 == iter1.positionlist_end());
230 TEST(pi2 == iter2.positionlist_end());
232 iter1++;
233 iter2++;
234 TEST(iter1 != doc1.termlist_end());
235 TEST(iter2 != doc2.termlist_end());
236 TEST_EQUAL(*iter1, "bat");
237 TEST_EQUAL(*iter2, *iter1);
238 TEST_EQUAL(iter1.get_wdf(), 0);
239 TEST_EQUAL(iter2.get_wdf(), 0);
240 TEST_EQUAL(iter1.get_termfreq(), 1);
241 //TEST_EQUAL(iter2.get_termfreq(), 0);
242 TEST(iter1.positionlist_begin() == iter1.positionlist_end());
243 TEST(iter2.positionlist_begin() == iter2.positionlist_end());
245 iter1++;
246 iter2++;
247 TEST(iter1 != doc1.termlist_end());
248 TEST(iter2 != doc2.termlist_end());
249 TEST_EQUAL(*iter1, "foo");
250 TEST_EQUAL(*iter2, *iter1);
251 TEST_EQUAL(iter1.get_wdf(), 0);
252 TEST_EQUAL(iter2.get_wdf(), 0);
253 TEST_EQUAL(iter1.get_termfreq(), 2);
254 //TEST_EQUAL(iter2.get_termfreq(), 0);
256 Xapian::PositionIterator temp1 = iter1.positionlist_begin();
257 pi1 = temp1;
258 Xapian::PositionIterator temp2 = iter2.positionlist_begin();
259 pi2 = temp2;
260 TEST_EQUAL(*pi1, 2); pi1++;
261 TEST_EQUAL(*pi2, 2); pi2++;
262 TEST(pi1 == iter1.positionlist_end());
263 TEST(pi2 == iter2.positionlist_end());
265 iter1++;
266 iter2++;
267 TEST(iter1 == doc1.termlist_end());
268 TEST(iter2 == doc2.termlist_end());
270 return true;
273 // test that adding lots of documents works, and doesn't leak memory
274 // REGRESSION FIXED:2003-09-07
275 DEFINE_TESTCASE(adddoc3, writable) {
276 Xapian::WritableDatabase db = get_writable_database();
278 for (Xapian::doccount i = 0; i < 2100; ++i) {
279 Xapian::Document doc;
280 for (Xapian::termcount t = 0; t < 100; ++t) {
281 string term("foo");
282 term += char(t ^ 70 ^ i);
283 doc.add_posting(term, t);
285 db.add_document(doc);
287 return true;
290 // We originally wanted to test that a termlist starting with a 48 character
291 // long term worked since that required special handling in flint for
292 // historical reasons. That's no longer relevant, but it seems useful to
293 // continue to test term lists starting with various term lengths work.
294 DEFINE_TESTCASE(adddoc4, writable) {
295 Xapian::WritableDatabase db = get_writable_database();
297 for (Xapian::doccount i = 1; i <= 240; ++i) {
298 Xapian::Document doc;
299 string term(i, 'X');
300 doc.add_term(term);
301 db.add_document(doc);
303 db.add_document(Xapian::Document());
304 db.commit();
306 for (Xapian::doccount i = 1; i <= 240; ++i) {
307 Xapian::Document doc = db.get_document(i);
308 Xapian::TermIterator t = doc.termlist_begin();
309 TEST(t != doc.termlist_end());
310 TEST_EQUAL((*t).size(), i);
311 ++t;
312 TEST(t == doc.termlist_end());
315 // And test a document with no terms.
316 Xapian::Document doc = db.get_document(241);
317 TEST(doc.termlist_begin() == doc.termlist_end());
319 return true;
322 // Test adding a document, and checking that it got added correctly.
323 // This testcase used to be adddoc2 in quartztest.
324 DEFINE_TESTCASE(adddoc5, writable) {
325 // Inmemory doesn't support get_writable_database_as_database().
326 SKIP_TEST_FOR_BACKEND("inmemory");
328 Xapian::docid did;
329 Xapian::Document document_in;
330 document_in.set_data("Foobar rising");
331 document_in.add_value(7, "Value7");
332 document_in.add_value(13, "Value13");
333 document_in.add_posting("foobar", 1);
334 document_in.add_posting("rising", 2);
335 document_in.add_posting("foobar", 3);
337 Xapian::Document document_in2;
338 document_in2.set_data("Foobar falling");
339 document_in2.add_posting("foobar", 1);
340 document_in2.add_posting("falling", 2);
342 Xapian::WritableDatabase database(get_writable_database());
344 TEST_EQUAL(database.get_doccount(), 0);
345 TEST_EQUAL(database.get_avlength(), 0);
347 did = database.add_document(document_in);
348 TEST_EQUAL(database.get_doccount(), 1);
349 TEST_EQUAL(database.get_avlength(), 3);
351 TEST_EQUAL(database.get_termfreq("foobar"), 1);
352 TEST_EQUAL(database.get_collection_freq("foobar"), 2);
353 TEST_EQUAL(database.get_termfreq("rising"), 1);
354 TEST_EQUAL(database.get_collection_freq("rising"), 1);
355 TEST_EQUAL(database.get_termfreq("falling"), 0);
356 TEST_EQUAL(database.get_collection_freq("falling"), 0);
358 Xapian::docid did2 = database.add_document(document_in2);
359 TEST_EQUAL(database.get_doccount(), 2);
360 TEST_NOT_EQUAL(did, did2);
361 TEST_EQUAL(database.get_avlength(), 5.0/2.0);
363 TEST_EQUAL(database.get_termfreq("foobar"), 2);
364 TEST_EQUAL(database.get_collection_freq("foobar"), 3);
365 TEST_EQUAL(database.get_termfreq("rising"), 1);
366 TEST_EQUAL(database.get_collection_freq("rising"), 1);
367 TEST_EQUAL(database.get_termfreq("falling"), 1);
368 TEST_EQUAL(database.get_collection_freq("falling"), 1);
370 database.delete_document(did);
371 TEST_EQUAL(database.get_doccount(), 1);
372 TEST_EQUAL(database.get_avlength(), 2);
374 TEST_EQUAL(database.get_termfreq("foobar"), 1);
375 TEST_EQUAL(database.get_collection_freq("foobar"), 1);
376 TEST_EQUAL(database.get_termfreq("rising"), 0);
377 TEST_EQUAL(database.get_collection_freq("rising"), 0);
378 TEST_EQUAL(database.get_termfreq("falling"), 1);
379 TEST_EQUAL(database.get_collection_freq("falling"), 1);
381 did = database.add_document(document_in);
382 TEST_EQUAL(database.get_doccount(), 2);
383 TEST_EQUAL(database.get_avlength(), 5.0/2.0);
385 TEST_EQUAL(database.get_termfreq("foobar"), 2);
386 TEST_EQUAL(database.get_collection_freq("foobar"), 3);
387 TEST_EQUAL(database.get_termfreq("rising"), 1);
388 TEST_EQUAL(database.get_collection_freq("rising"), 1);
389 TEST_EQUAL(database.get_termfreq("falling"), 1);
390 TEST_EQUAL(database.get_collection_freq("falling"), 1);
394 Xapian::Database database(get_writable_database_as_database());
395 Xapian::Document document_out = database.get_document(did);
397 TEST_EQUAL(document_in.get_data(), document_out.get_data());
400 Xapian::ValueIterator i(document_in.values_begin());
401 Xapian::ValueIterator j(document_out.values_begin());
402 for (; i != document_in.values_end(); i++, j++) {
403 TEST_NOT_EQUAL(j, document_out.values_end());
404 TEST_EQUAL(*i, *j);
405 TEST_EQUAL(i.get_valueno(), j.get_valueno());
407 TEST_EQUAL(j, document_out.values_end());
411 // Regression test for bug fixed in 1.0.5 - values_begin() didn't
412 // ensure that values had been read. However, values_end() did
413 // (and so did values_count()) so this wasn't generally an issue
414 // but it shouldn't happen anyway.
415 Xapian::Document doc_tmp = database.get_document(did);
416 Xapian::ValueIterator i = document_in.values_begin();
417 Xapian::ValueIterator j = doc_tmp.values_begin();
418 TEST_EQUAL(*i, *j);
422 Xapian::TermIterator i(document_in.termlist_begin());
423 Xapian::TermIterator j(document_out.termlist_begin());
424 for (; i != document_in.termlist_end(); i++, j++) {
425 TEST_NOT_EQUAL(j, document_out.termlist_end());
426 TEST_EQUAL(*i, *j);
427 TEST_EQUAL(i.get_wdf(), j.get_wdf());
428 // Actually use termfreq to stop compiler optimising away the
429 // call to get_termfreq().
430 TEST_EXCEPTION(Xapian::InvalidOperationError,
431 if (i.get_termfreq()) return false);
432 TEST_NOT_EQUAL(0, j.get_termfreq());
433 if (*i == "foobar") {
434 // termfreq of foobar is 2
435 TEST_EQUAL(2, j.get_termfreq());
436 } else {
437 // termfreq of rising is 1
438 TEST_EQUAL(*i, "rising");
439 TEST_EQUAL(1, j.get_termfreq());
441 Xapian::PositionIterator k(i.positionlist_begin());
442 Xapian::PositionIterator l(j.positionlist_begin());
443 for (; k != i.positionlist_end(); k++, l++) {
444 TEST_NOT_EQUAL(l, j.positionlist_end());
445 TEST_EQUAL(*k, *l);
447 TEST_EQUAL(l, j.positionlist_end());
449 TEST_EQUAL(j, document_out.termlist_end());
453 return true;
456 // Test adding a document, and checking that it got added correctly.
457 // This testcase used to be adddoc3 in quartztest.
458 DEFINE_TESTCASE(adddoc6, writable) {
459 // Inmemory doesn't support get_writable_database_again().
460 SKIP_TEST_FOR_BACKEND("inmemory");
462 Xapian::docid did;
463 Xapian::Document document_in;
464 document_in.set_data("Foobar rising");
465 document_in.add_value(7, "Value7");
466 document_in.add_value(13, "Value13");
467 document_in.add_posting("foo", 1);
468 document_in.add_posting("bar", 2);
471 Xapian::WritableDatabase database(get_writable_database());
473 did = database.add_document(document_in);
474 TEST_EQUAL(did, 1);
475 TEST_EQUAL(database.get_doccount(), 1);
476 TEST_EQUAL(database.get_avlength(), 2);
480 Xapian::WritableDatabase database(get_writable_database_again());
482 document_in.remove_term("foo");
483 document_in.add_posting("baz", 1);
485 database.replace_document(1, document_in);
487 database.delete_document(1);
489 TEST_EQUAL(database.get_doccount(), 0);
490 TEST_EQUAL(database.get_avlength(), 0);
491 TEST_EQUAL(database.get_termfreq("foo"), 0);
492 TEST_EQUAL(database.get_collection_freq("foo"), 0);
493 TEST_EQUAL(database.get_termfreq("bar"), 0);
494 TEST_EQUAL(database.get_collection_freq("bar"), 0);
495 TEST_EQUAL(database.get_termfreq("baz"), 0);
496 TEST_EQUAL(database.get_collection_freq("baz"), 0);
499 return true;
502 // tests that database destructors commit if it isn't done explicitly
503 DEFINE_TESTCASE(implicitendsession1, writable) {
504 Xapian::WritableDatabase db = get_writable_database();
506 Xapian::Document doc;
508 doc.set_data(string("top secret"));
509 doc.add_posting("cia", 1);
510 doc.add_posting("nsa", 2);
511 doc.add_posting("fbi", 3);
512 db.add_document(doc);
514 return true;
517 // tests that assignment of Xapian::Database and Xapian::WritableDatabase works
518 // as expected
519 DEFINE_TESTCASE(databaseassign1, writable) {
520 Xapian::WritableDatabase wdb = get_writable_database();
521 Xapian::Database db = get_database("");
522 Xapian::Database actually_wdb = wdb;
523 Xapian::WritableDatabase w1(wdb);
524 w1 = wdb;
525 Xapian::Database d1(wdb);
526 Xapian::Database d2(actually_wdb);
527 d2 = wdb;
528 d2 = actually_wdb;
529 wdb = wdb; // check assign to itself works
530 db = db; // check assign to itself works
531 return true;
534 // tests that deletion and updating of documents works as expected
535 DEFINE_TESTCASE(deldoc1, writable) {
536 Xapian::WritableDatabase db = get_writable_database();
538 Xapian::Document doc1;
540 doc1.add_posting("foo", 1);
541 doc1.add_posting("foo", 1);
542 doc1.add_posting("foo", 2);
543 doc1.add_posting("foo", 2);
544 doc1.add_posting("bar", 3);
545 doc1.add_posting("gone", 1);
547 Xapian::docid did = db.add_document(doc1);
548 TEST_EQUAL(did, 1);
550 doc1.remove_term("gone");
552 did = db.add_document(doc1);
553 TEST_EQUAL(did, 2);
555 doc1.add_term("new", 1);
556 did = db.add_document(doc1);
557 TEST_EQUAL(did, 3);
559 db.delete_document(1);
561 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(1));
563 doc1 = db.get_document(2);
564 doc1.remove_term("foo");
565 doc1.add_term("fwing");
566 db.replace_document(2, doc1);
568 Xapian::Document doc2 = db.get_document(2);
569 Xapian::TermIterator tit = doc2.termlist_begin();
570 TEST_NOT_EQUAL(tit, doc2.termlist_end());
571 TEST_EQUAL(*tit, "bar");
572 tit++;
573 TEST_NOT_EQUAL(tit, doc2.termlist_end());
574 TEST_EQUAL(*tit, "fwing");
575 tit++;
576 TEST_EQUAL(tit, doc2.termlist_end());
578 return true;
581 // tests that deletion and updating of documents works as expected
582 DEFINE_TESTCASE(deldoc2, writable) {
583 Xapian::WritableDatabase db = get_writable_database();
585 Xapian::Document doc1;
587 doc1.add_posting("one", 1);
588 doc1.add_posting("two", 2);
589 doc1.add_posting("two", 3);
590 Xapian::docid did;
592 did = db.add_document(doc1);
593 TEST_EQUAL(did, 1);
595 doc1.remove_term("one");
596 doc1.add_posting("three", 4);
598 did = db.add_document(doc1);
599 TEST_EQUAL(did, 2);
601 doc1.add_posting("one", 7);
602 doc1.remove_term("two");
604 did = db.add_document(doc1);
605 TEST_EQUAL(did, 3);
607 db.commit();
609 // reopen() on a writable database shouldn't do anything.
610 TEST(!db.reopen());
612 db.delete_document(1);
613 db.delete_document(2);
614 db.delete_document(3);
616 db.commit();
618 // reopen() on a writable database shouldn't do anything.
619 TEST(!db.reopen());
621 TEST_EQUAL(db.postlist_begin("one"), db.postlist_end("one"));
622 TEST_EQUAL(db.postlist_begin("two"), db.postlist_end("two"));
623 TEST_EQUAL(db.postlist_begin("three"), db.postlist_end("three"));
625 TEST_EXCEPTION(Xapian::DocNotFoundError, db.termlist_begin(1));
626 TEST_EXCEPTION(Xapian::DocNotFoundError, db.termlist_begin(2));
627 TEST_EXCEPTION(Xapian::DocNotFoundError, db.termlist_begin(3));
628 TEST_EXCEPTION(Xapian::DocNotFoundError, db.termlist_begin(4));
630 // test positionlist_{begin,end}?
632 TEST_EQUAL(db.get_doccount(), 0);
633 TEST_EQUAL(db.get_avlength(), 0);
634 TEST_EQUAL(db.get_termfreq("one"), 0);
635 TEST_EQUAL(db.get_termfreq("two"), 0);
636 TEST_EQUAL(db.get_termfreq("three"), 0);
638 TEST(!db.term_exists("one"));
639 TEST(!db.term_exists("two"));
640 TEST(!db.term_exists("three"));
642 TEST_EQUAL(db.get_collection_freq("one"), 0);
643 TEST_EQUAL(db.get_collection_freq("two"), 0);
644 TEST_EQUAL(db.get_collection_freq("three"), 0);
646 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_doclength(1));
647 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_doclength(2));
648 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_doclength(3));
650 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_unique_terms(1));
651 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_unique_terms(2));
652 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_unique_terms(3));
654 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(1));
655 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(2));
656 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(3));
658 TEST_EQUAL(db.allterms_begin(), db.allterms_end());
660 return true;
663 // another test of deletion of documents, a cut-down version of deldoc2
664 DEFINE_TESTCASE(deldoc3, writable) {
665 Xapian::WritableDatabase db = get_writable_database();
667 Xapian::Document doc1;
669 doc1.add_posting("one", 1);
671 Xapian::docid did = db.add_document(doc1);
672 TEST_EQUAL(did, 1);
674 db.commit();
676 // reopen() on a writable database shouldn't do anything.
677 TEST(!db.reopen());
679 db.delete_document(1);
681 db.commit();
683 // reopen() on a writable database shouldn't do anything.
684 TEST(!db.reopen());
686 TEST_EQUAL(db.postlist_begin("one"), db.postlist_end("one"));
688 TEST_EXCEPTION(Xapian::DocNotFoundError, db.termlist_begin(1));
689 (void)&db; // gcc 2.95 seems to miscompile without this!!! - Olly
690 TEST_EXCEPTION(Xapian::DocNotFoundError, db.termlist_begin(2));
692 // test positionlist_{begin,end}?
694 TEST_EQUAL(db.get_doccount(), 0);
695 TEST_EQUAL(db.get_avlength(), 0);
696 TEST_EQUAL(db.get_termfreq("one"), 0);
698 TEST(!db.term_exists("one"));
700 TEST_EQUAL(db.get_collection_freq("one"), 0);
702 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_doclength(1));
703 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_doclength(2));
705 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_unique_terms(1));
706 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_unique_terms(2));
708 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(1));
709 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(2));
711 TEST_EQUAL(db.allterms_begin(), db.allterms_end());
713 return true;
716 // tests that deletion and updating of (lots of) documents works as expected
717 DEFINE_TESTCASE(deldoc4, writable) {
718 Xapian::WritableDatabase db = get_writable_database();
720 Xapian::Document doc1;
722 doc1.add_posting("one", 1);
723 doc1.add_posting("two", 2);
724 doc1.add_posting("two", 3);
726 Xapian::Document doc2 = doc1;
727 doc2.remove_term("one");
728 doc2.add_posting("three", 4);
730 Xapian::Document doc3 = doc2;
731 doc3.add_posting("one", 7);
732 doc3.remove_term("two");
734 const Xapian::docid maxdoc = 1000 * 3;
735 Xapian::docid did;
736 for (Xapian::docid i = 0; i < maxdoc / 3; ++i) {
737 did = db.add_document(doc1);
738 TEST_EQUAL(did, i * 3 + 1);
739 did = db.add_document(doc2);
740 TEST_EQUAL(did, i * 3 + 2);
741 did = db.add_document(doc3);
742 TEST_EQUAL(did, i * 3 + 3);
744 bool is_power_of_two = ((i & (i - 1)) == 0);
745 if (is_power_of_two) {
746 db.commit();
747 // reopen() on a writable database shouldn't do anything.
748 TEST(!db.reopen());
751 db.commit();
752 // reopen() on a writable database shouldn't do anything.
753 TEST(!db.reopen());
755 /* delete the documents in a peculiar order */
756 for (Xapian::docid i = 0; i < maxdoc / 3; ++i) {
757 db.delete_document(maxdoc - i);
758 db.delete_document(maxdoc / 3 + i + 1);
759 db.delete_document(i + 1);
762 db.commit();
763 // reopen() on a writable database shouldn't do anything.
764 TEST(!db.reopen());
766 TEST_EQUAL(db.postlist_begin("one"), db.postlist_end("one"));
767 TEST_EQUAL(db.postlist_begin("two"), db.postlist_end("two"));
768 TEST_EQUAL(db.postlist_begin("three"), db.postlist_end("three"));
770 for (Xapian::docid i = 1; i <= maxdoc; ++i) {
771 // TEST_EXCEPTION writes to tout each time if the test is run
772 // in verbose mode and some string stream implementations get
773 // very inefficient with large strings, so clear tout on each pass of
774 // the loop to speed up the test since the older information isn't
775 // interesting anyway.
776 tout.str(string());
777 TEST_EXCEPTION(Xapian::DocNotFoundError, db.termlist_begin(i));
778 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_doclength(i));
779 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_unique_terms(i));
780 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(i));
783 // test positionlist_{begin,end}?
785 TEST_EQUAL(db.get_doccount(), 0);
786 TEST_EQUAL(db.get_avlength(), 0);
787 TEST_EQUAL(db.get_termfreq("one"), 0);
788 TEST_EQUAL(db.get_termfreq("two"), 0);
789 TEST_EQUAL(db.get_termfreq("three"), 0);
791 TEST(!db.term_exists("one"));
792 TEST(!db.term_exists("two"));
793 TEST(!db.term_exists("three"));
795 TEST_EQUAL(db.get_collection_freq("one"), 0);
796 TEST_EQUAL(db.get_collection_freq("two"), 0);
797 TEST_EQUAL(db.get_collection_freq("three"), 0);
799 TEST_EQUAL(db.allterms_begin(), db.allterms_end());
801 return true;
804 // Test deleting a document which was added in the same batch.
805 DEFINE_TESTCASE(deldoc5, writable) {
806 Xapian::WritableDatabase db = get_writable_database();
808 Xapian::Document doc1;
810 doc1.add_posting("foo", 1);
811 doc1.add_posting("bar", 2);
812 doc1.add_posting("aardvark", 3);
814 Xapian::docid did = db.add_document(doc1);
815 TEST_EQUAL(did, 1);
817 doc1.remove_term("bar");
818 doc1.add_term("hello");
820 did = db.add_document(doc1);
821 TEST_EQUAL(did, 2);
823 doc1.add_term("world", 1);
824 did = db.add_document(doc1);
825 TEST_EQUAL(did, 3);
827 db.delete_document(2);
829 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(2));
831 db.commit();
833 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(2));
835 TEST_EQUAL(db.get_termfreq("foo"), 2);
836 TEST_EQUAL(db.get_termfreq("aardvark"), 2);
837 TEST_EQUAL(db.get_termfreq("hello"), 1);
839 Xapian::PostingIterator p = db.postlist_begin("foo");
840 TEST_NOT_EQUAL(p, db.postlist_end("foo"));
841 TEST_EQUAL(*p, 1);
842 ++p;
843 TEST_NOT_EQUAL(p, db.postlist_end("foo"));
844 TEST_EQUAL(*p, 3);
845 ++p;
846 TEST_EQUAL(p, db.postlist_end("foo"));
848 return true;
851 // Regression test for bug in quartz and flint, fixed in 1.0.2.
852 DEFINE_TESTCASE(deldoc6, writable) {
853 Xapian::WritableDatabase db = get_writable_database();
855 Xapian::Document doc1;
857 doc1.add_posting("foo", 1);
858 doc1.add_posting("bar", 2);
859 doc1.add_posting("aardvark", 3);
861 Xapian::docid did = db.add_document(doc1);
862 TEST_EQUAL(did, 1);
864 doc1.remove_term("bar");
865 doc1.add_term("hello");
867 did = db.add_document(doc1);
868 TEST_EQUAL(did, 2);
870 db.commit();
872 db.delete_document(2);
873 TEST_EXCEPTION(Xapian::DocNotFoundError, db.delete_document(3));
875 db.commit();
877 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(2));
879 return true;
882 DEFINE_TESTCASE(replacedoc1, writable) {
883 Xapian::WritableDatabase db = get_writable_database();
885 Xapian::Document doc1;
887 doc1.add_posting("foo", 1);
888 doc1.add_posting("foo", 2);
889 doc1.add_posting("gone",3);
890 doc1.add_posting("bar", 4);
891 doc1.add_posting("foo", 5);
892 Xapian::docid did;
894 did = db.add_document(doc1);
895 TEST_EQUAL(did, 1);
897 Xapian::Document doc2;
899 doc2.add_posting("foo", 1);
900 doc2.add_posting("pipco", 2);
901 doc2.add_posting("bar", 4);
902 doc2.add_posting("foo", 5);
904 db.replace_document(did, doc2);
906 Xapian::Document doc3 = db.get_document(did);
907 Xapian::TermIterator tIter = doc3.termlist_begin();
908 TEST_EQUAL(*tIter, "bar");
909 Xapian::PositionIterator pIter = tIter.positionlist_begin();
910 TEST_EQUAL(*pIter, 4);
911 ++tIter;
912 TEST_EQUAL(*tIter, "foo");
913 Xapian::PositionIterator qIter = tIter.positionlist_begin();
914 TEST_EQUAL(*qIter, 1);
915 ++qIter;
916 TEST_EQUAL(*qIter, 5);
917 ++tIter;
918 TEST_EQUAL(*tIter, "pipco");
919 Xapian::PositionIterator rIter = tIter.positionlist_begin();
920 TEST_EQUAL(*rIter, 2);
921 ++tIter;
922 TEST_EQUAL(tIter, doc3.termlist_end());
923 return true;
926 // Test of new feature: WritableDatabase::replace_document accepts a docid
927 // which doesn't yet exist as of Xapian 0.8.2.
928 DEFINE_TESTCASE(replacedoc2, writable) {
929 Xapian::WritableDatabase db = get_writable_database();
931 Xapian::Document doc1;
933 doc1.add_posting("foo", 1);
934 doc1.add_posting("foo", 2);
935 doc1.add_posting("gone",3);
936 doc1.add_posting("bar", 4);
937 doc1.add_posting("foo", 5);
938 Xapian::docid did = 31770;
940 db.replace_document(did, doc1);
942 // Regression tests for bug in the InMemory backend - fixed in 1.0.2.
943 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(1));
944 Xapian::PostingIterator postit = db.postlist_begin("");
945 TEST(postit != db.postlist_end(""));
946 TEST_EQUAL(*postit, 31770);
948 Xapian::Document doc2;
950 doc2.add_posting("foo", 1);
951 doc2.add_posting("pipco", 2);
952 doc2.add_posting("bar", 4);
953 doc2.add_posting("foo", 5);
955 db.replace_document(did, doc2);
956 TEST_EQUAL(db.get_doccount(), 1);
958 Xapian::Document doc3 = db.get_document(did);
959 Xapian::TermIterator tIter = doc3.termlist_begin();
960 TEST_EQUAL(*tIter, "bar");
961 Xapian::PositionIterator pIter = tIter.positionlist_begin();
962 TEST_EQUAL(*pIter, 4);
963 ++tIter;
964 TEST_EQUAL(*tIter, "foo");
965 Xapian::PositionIterator qIter = tIter.positionlist_begin();
966 TEST_EQUAL(*qIter, 1);
967 ++qIter;
968 TEST_EQUAL(*qIter, 5);
969 ++tIter;
970 TEST_EQUAL(*tIter, "pipco");
971 Xapian::PositionIterator rIter = tIter.positionlist_begin();
972 TEST_EQUAL(*rIter, 2);
973 ++tIter;
974 TEST_EQUAL(tIter, doc3.termlist_end());
976 did = db.add_document(doc1);
977 TEST_EQUAL(did, 31771);
978 TEST_EQUAL(db.get_doccount(), 2);
980 TEST_EXCEPTION(Xapian::InvalidArgumentError, db.replace_document(0, doc2));
982 return true;
985 // Test replacing a document which was added in the same batch.
986 DEFINE_TESTCASE(replacedoc3, writable) {
987 Xapian::WritableDatabase db = get_writable_database();
989 Xapian::Document doc1;
991 doc1.add_posting("foo", 1);
992 doc1.add_posting("bar", 2);
993 doc1.add_posting("aardvark", 3);
995 Xapian::docid did = db.add_document(doc1);
996 TEST_EQUAL(did, 1);
998 doc1.remove_term("bar");
999 doc1.add_term("hello");
1001 did = db.add_document(doc1);
1002 TEST_EQUAL(did, 2);
1004 doc1.add_term("world", 1);
1005 did = db.add_document(doc1);
1006 TEST_EQUAL(did, 3);
1008 Xapian::Document doc2;
1009 doc2.add_term("world");
1010 db.replace_document(2, doc2);
1012 db.commit();
1014 // Check that the document exists (no DocNotFoundError).
1015 doc2 = db.get_document(2);
1017 TEST_EQUAL(db.get_termfreq("foo"), 2);
1018 TEST_EQUAL(db.get_termfreq("aardvark"), 2);
1019 TEST_EQUAL(db.get_termfreq("hello"), 1);
1020 TEST_EQUAL(db.get_termfreq("world"), 2);
1022 TEST_EQUAL(db.get_doclength(1), 3);
1023 TEST_EQUAL(db.get_doclength(2), 1);
1024 TEST_EQUAL(db.get_doclength(3), 4);
1026 TEST_EQUAL(db.get_unique_terms(1), 3);
1027 TEST_EQUAL(db.get_unique_terms(2), 1);
1028 TEST_EQUAL(db.get_unique_terms(3), 4);
1030 Xapian::PostingIterator p = db.postlist_begin("foo");
1031 TEST_NOT_EQUAL(p, db.postlist_end("foo"));
1032 TEST_EQUAL(*p, 1);
1033 TEST_EQUAL(p.get_doclength(), 3);
1034 TEST_EQUAL(p.get_unique_terms(), 3);
1035 ++p;
1036 TEST_NOT_EQUAL(p, db.postlist_end("foo"));
1037 TEST_EQUAL(*p, 3);
1038 TEST_EQUAL(p.get_doclength(), 4);
1039 TEST_EQUAL(p.get_unique_terms(), 4);
1040 ++p;
1041 TEST_EQUAL(p, db.postlist_end("foo"));
1043 p = db.postlist_begin("world");
1044 TEST_NOT_EQUAL(p, db.postlist_end("world"));
1045 TEST_EQUAL(*p, 2);
1046 TEST_EQUAL(p.get_doclength(), 1);
1047 TEST_EQUAL(p.get_unique_terms(), 1);
1048 ++p;
1049 TEST_NOT_EQUAL(p, db.postlist_end("world"));
1050 TEST_EQUAL(*p, 3);
1051 TEST_EQUAL(p.get_doclength(), 4);
1052 TEST_EQUAL(p.get_unique_terms(), 4);
1053 ++p;
1054 TEST_EQUAL(p, db.postlist_end("world"));
1056 return true;
1059 // Test replacing a document which was deleted in the same batch.
1060 DEFINE_TESTCASE(replacedoc4, writable) {
1061 Xapian::WritableDatabase db = get_writable_database();
1063 Xapian::Document doc1;
1065 doc1.add_posting("foo", 1);
1066 doc1.add_posting("bar", 2);
1067 doc1.add_posting("aardvark", 3);
1069 Xapian::docid did = db.add_document(doc1);
1070 TEST_EQUAL(did, 1);
1072 doc1.remove_term("bar");
1073 doc1.add_term("hello");
1075 did = db.add_document(doc1);
1076 TEST_EQUAL(did, 2);
1078 doc1.add_term("world", 1);
1079 did = db.add_document(doc1);
1080 TEST_EQUAL(did, 3);
1082 db.delete_document(2);
1084 Xapian::Document doc2;
1085 doc2.add_term("world");
1086 db.replace_document(2, doc2);
1088 db.commit();
1090 // Check that the document exists (no DocNotFoundError).
1091 doc2 = db.get_document(2);
1093 TEST_EQUAL(db.get_termfreq("foo"), 2);
1094 TEST_EQUAL(db.get_termfreq("aardvark"), 2);
1095 TEST_EQUAL(db.get_termfreq("hello"), 1);
1096 TEST_EQUAL(db.get_termfreq("world"), 2);
1098 Xapian::PostingIterator p = db.postlist_begin("foo");
1099 TEST_NOT_EQUAL(p, db.postlist_end("foo"));
1100 TEST_EQUAL(*p, 1);
1101 ++p;
1102 TEST_NOT_EQUAL(p, db.postlist_end("foo"));
1103 TEST_EQUAL(*p, 3);
1104 ++p;
1105 TEST_EQUAL(p, db.postlist_end("foo"));
1107 p = db.postlist_begin("world");
1108 TEST_NOT_EQUAL(p, db.postlist_end("world"));
1109 TEST_EQUAL(*p, 2);
1110 ++p;
1111 TEST_NOT_EQUAL(p, db.postlist_end("world"));
1112 TEST_EQUAL(*p, 3);
1113 ++p;
1114 TEST_EQUAL(p, db.postlist_end("world"));
1116 return true;
1119 // Test replacing a document with itself without modifying postings.
1120 // Regression test for bug in 0.9.9 and earlier - there flint and quartz
1121 // lost all positional information for the document when you did this.
1122 DEFINE_TESTCASE(replacedoc5, writable) {
1123 Xapian::WritableDatabase db = get_writable_database();
1126 Xapian::Document doc;
1127 doc.add_posting("hello", 1);
1128 doc.add_posting("world", 2);
1130 Xapian::docid did = db.add_document(doc);
1131 TEST_EQUAL(did, 1);
1132 db.commit();
1136 Xapian::Document doc = db.get_document(1);
1137 TEST(db.has_positions());
1138 TEST(db.positionlist_begin(1, "hello") != db.positionlist_end(1, "hello"));
1139 TEST(db.positionlist_begin(1, "world") != db.positionlist_end(1, "world"));
1140 db.replace_document(1, doc);
1141 db.commit();
1143 TEST(db.has_positions());
1144 TEST(db.positionlist_begin(1, "hello") != db.positionlist_end(1, "hello"));
1145 TEST(db.positionlist_begin(1, "world") != db.positionlist_end(1, "world"));
1148 // The backends now spot simple cases of replacing the same document and
1149 // don't do needless work. Force them to actually do the replacement to
1150 // make sure that case works.
1153 Xapian::Document doc;
1154 doc.add_term("Q2");
1155 db.add_document(doc);
1156 db.commit();
1160 Xapian::Document doc = db.get_document(1);
1161 TEST(db.has_positions());
1162 TEST(db.positionlist_begin(1, "hello") != db.positionlist_end(1, "hello"));
1163 TEST(db.positionlist_begin(1, "world") != db.positionlist_end(1, "world"));
1164 (void)db.get_document(2);
1165 db.replace_document(1, doc);
1166 db.commit();
1168 TEST(db.has_positions());
1169 TEST(db.positionlist_begin(1, "hello") != db.positionlist_end(1, "hello"));
1170 TEST(db.positionlist_begin(1, "world") != db.positionlist_end(1, "world"));
1173 return true;
1176 // Test replacing a document while adding values, without changing anything
1177 // else. Regression test for a bug introduced while implementing lazy update,
1178 // and also covers a few other code paths.
1179 DEFINE_TESTCASE(replacedoc6, writable) {
1180 Xapian::WritableDatabase db = get_writable_database();
1182 Xapian::Document doc;
1183 Xapian::docid did = db.add_document(doc);
1184 TEST_EQUAL(did, 1);
1185 db.commit();
1187 // Add document
1188 doc = db.get_document(1);
1189 TEST_EQUAL(doc.get_value(1), "");
1190 TEST_EQUAL(doc.get_value(2), "");
1191 doc.add_value(1, "banana1");
1192 db.replace_document(1, doc);
1194 doc = db.get_document(1);
1195 TEST_EQUAL(doc.get_value(1), "banana1");
1196 TEST_EQUAL(doc.get_value(2), "");
1197 db.commit();
1199 doc = db.get_document(1);
1200 TEST_EQUAL(doc.get_value(1), "banana1");
1201 TEST_EQUAL(doc.get_value(2), "");
1202 doc.add_value(2, "banana2");
1203 db.replace_document(1, doc);
1205 TEST_EQUAL(doc.get_value(1), "banana1");
1206 TEST_EQUAL(doc.get_value(2), "banana2");
1207 db.commit();
1209 doc = db.get_document(1);
1210 TEST_EQUAL(doc.get_value(1), "banana1");
1211 TEST_EQUAL(doc.get_value(2), "banana2");
1213 return true;
1216 // Test of new feature: WritableDatabase::replace_document and delete_document
1217 // can take a unique termname instead of a document id as of Xapian 0.8.2.
1218 DEFINE_TESTCASE(uniqueterm1, writable) {
1219 Xapian::WritableDatabase db = get_writable_database();
1221 for (int n = 1; n <= 20; ++n) {
1222 Xapian::Document doc;
1223 string uterm = "U" + str(n % 16);
1224 doc.add_term(uterm);
1225 doc.add_term(str(n));
1226 doc.add_term(str(n ^ 9));
1227 doc.add_term("all");
1228 doc.set_data("pass1");
1229 db.add_document(doc);
1232 TEST_EQUAL(db.get_doccount(), 20);
1234 static const Xapian::doccount sizes[20] = {
1235 19, 17, 16, 15,
1236 15, 15, 15, 15,
1237 15, 15, 15, 15,
1238 15, 15, 15, 15,
1239 15, 15, 15, 15
1241 for (int n = 1; n <= 20; ++n) {
1242 string uterm = "U" + str(n % 16);
1243 if (uterm == "U2") {
1244 db.delete_document(uterm);
1245 } else {
1246 Xapian::Document doc;
1247 doc.add_term(uterm);
1248 doc.add_term(str(n));
1249 doc.add_term(str(n ^ 9));
1250 doc.add_term("all");
1251 doc.set_data("pass2");
1252 db.replace_document(uterm, doc);
1254 TEST_EQUAL(db.get_doccount(), sizes[n - 1]);
1257 string uterm = "U571";
1258 Xapian::Document doc;
1259 doc.add_term(uterm);
1260 doc.set_data("pass3");
1261 db.replace_document(uterm, doc);
1263 TEST_EQUAL(db.get_doccount(), 16);
1265 db.delete_document("U2");
1267 TEST_EQUAL(db.get_doccount(), 16);
1269 return true;
1272 // tests all document postlists
1273 DEFINE_TESTCASE(allpostlist2, writable) {
1274 Xapian::WritableDatabase db(get_writable_database("apitest_manydocs"));
1275 Xapian::PostingIterator i = db.postlist_begin("");
1276 unsigned int j = 1;
1277 while (i != db.postlist_end("")) {
1278 TEST_EQUAL(*i, j);
1279 i++;
1280 j++;
1282 TEST_EQUAL(j, 513);
1284 db.delete_document(1);
1285 db.delete_document(50);
1286 db.delete_document(512);
1288 i = db.postlist_begin("");
1289 j = 2;
1290 while (i != db.postlist_end("")) {
1291 TEST_EQUAL(*i, j);
1292 i++;
1293 j++;
1294 if (j == 50) j++;
1296 TEST_EQUAL(j, 512);
1298 i = db.postlist_begin("");
1299 j = 2;
1300 while (i != db.postlist_end("")) {
1301 TEST_EQUAL(*i, j);
1302 i++;
1303 j++;
1304 if (j == 40) {
1305 j += 10;
1306 i.skip_to(j);
1307 j++;
1310 TEST_EQUAL(j, 512);
1312 return true;
1315 static void test_emptyterm2_helper(Xapian::WritableDatabase & db)
1317 // Don't bother with postlist_begin() because allpostlist tests cover that.
1318 TEST_EXCEPTION(Xapian::InvalidArgumentError, db.positionlist_begin(1, ""));
1319 TEST_EQUAL(db.get_doccount(), db.get_termfreq(""));
1320 TEST_EQUAL(db.get_doccount() != 0, db.term_exists(""));
1321 TEST_EQUAL(db.get_doccount(), db.get_collection_freq(""));
1324 // tests results of passing an empty term to various methods
1325 // equivalent of emptyterm1 for a writable database
1326 DEFINE_TESTCASE(emptyterm2, writable) {
1328 Xapian::WritableDatabase db(get_writable_database("apitest_manydocs"));
1329 TEST_EQUAL(db.get_doccount(), 512);
1330 test_emptyterm2_helper(db);
1331 db.delete_document(1);
1332 TEST_EQUAL(db.get_doccount(), 511);
1333 test_emptyterm2_helper(db);
1334 db.delete_document(50);
1335 TEST_EQUAL(db.get_doccount(), 510);
1336 test_emptyterm2_helper(db);
1337 db.delete_document(512);
1338 TEST_EQUAL(db.get_doccount(), 509);
1339 test_emptyterm2_helper(db);
1343 Xapian::WritableDatabase db(get_writable_database("apitest_onedoc"));
1344 TEST_EQUAL(db.get_doccount(), 1);
1345 test_emptyterm2_helper(db);
1346 db.delete_document(1);
1347 TEST_EQUAL(db.get_doccount(), 0);
1348 test_emptyterm2_helper(db);
1352 Xapian::WritableDatabase db(get_writable_database());
1353 TEST_EQUAL(db.get_doccount(), 0);
1354 test_emptyterm2_helper(db);
1357 return true;
1360 // Check that PHRASE/NEAR becomes AND if there's no positional info in the
1361 // database.
1362 DEFINE_TESTCASE(phraseorneartoand1, writable) {
1363 Xapian::WritableDatabase db = get_writable_database();
1365 for (int n = 1; n <= 20; ++n) {
1366 Xapian::Document doc;
1367 doc.add_term(str(n));
1368 doc.add_term(str(n ^ 9));
1369 doc.add_term("all");
1370 doc.set_data("pass1");
1371 db.add_document(doc);
1373 db.commit();
1375 Xapian::Enquire enquire(db);
1376 Xapian::MSet mymset;
1378 const char * q1[] = { "all", "1" };
1379 enquire.set_query(Xapian::Query(Xapian::Query::OP_PHRASE, q1, q1 + 2));
1380 mymset = enquire.get_mset(0, 10);
1381 TEST_EQUAL(2, mymset.size());
1383 enquire.set_query(Xapian::Query(Xapian::Query::OP_NEAR, q1, q1 + 2));
1384 mymset = enquire.get_mset(0, 10);
1385 TEST_EQUAL(2, mymset.size());
1387 const char * q2[] = { "1", "2" };
1388 enquire.set_query(Xapian::Query(Xapian::Query::OP_PHRASE, q2, q2 + 2));
1389 mymset = enquire.get_mset(0, 10);
1390 TEST_EQUAL(0, mymset.size());
1392 enquire.set_query(Xapian::Query(Xapian::Query::OP_NEAR, q2, q2 + 2));
1393 mymset = enquire.get_mset(0, 10);
1394 TEST_EQUAL(0, mymset.size());
1396 return true;
1399 // Check that a large number of position list entries for a particular term
1400 // works - regression test for flint.
1401 DEFINE_TESTCASE(longpositionlist1, writable) {
1402 Xapian::WritableDatabase db = get_writable_database();
1404 Xapian::Document doc;
1405 Xapian::termpos n;
1406 for (n = 1; n <= 2000; ++n) {
1407 doc.add_posting("fork", n * 3);
1408 doc.add_posting("knife", n * unsigned(log(double(n + 2))));
1409 doc.add_posting("spoon", n * n);
1411 doc.set_data("cutlery");
1412 Xapian::docid did = db.add_document(doc);
1413 db.commit();
1415 doc = db.get_document(did);
1417 Xapian::TermIterator t, tend;
1418 Xapian::PositionIterator p, pend;
1420 t = doc.termlist_begin();
1421 tend = doc.termlist_end();
1423 TEST(t != tend);
1424 TEST_EQUAL(*t, "fork");
1425 p = t.positionlist_begin();
1426 pend = t.positionlist_end();
1427 for (n = 1; n <= 2000; ++n) {
1428 TEST(p != pend);
1429 TEST_EQUAL(*p, n * 3);
1430 ++p;
1432 TEST(p == pend);
1434 ++t;
1435 TEST(t != tend);
1436 TEST_EQUAL(*t, "knife");
1437 p = t.positionlist_begin();
1438 pend = t.positionlist_end();
1439 for (n = 1; n <= 2000; ++n) {
1440 TEST(p != pend);
1441 TEST_EQUAL(*p, n * unsigned(log(double(n + 2))));
1442 ++p;
1444 TEST(p == pend);
1446 ++t;
1447 TEST(t != tend);
1448 TEST_EQUAL(*t, "spoon");
1449 p = t.positionlist_begin();
1450 pend = t.positionlist_end();
1451 for (n = 1; n <= 2000; ++n) {
1452 TEST(p != pend);
1453 TEST_EQUAL(*p, n * n);
1454 ++p;
1456 TEST(p == pend);
1458 ++t;
1459 TEST(t == tend);
1461 return true;
1464 // Regression test for bug#110: Inconsistent sort order between pages with
1465 // set_sort_by_value_then_relevance.
1466 DEFINE_TESTCASE(consistency2, writable) {
1467 Xapian::WritableDatabase db = get_writable_database();
1468 char buf[2] = "X";
1469 int i = 0;
1471 // Add 5 documents indexed by "test" with wdf 1.
1472 for (i = 0; i < 5; ++i) {
1473 Xapian::Document doc;
1474 *buf = '0' + i;
1475 doc.add_value(0, buf);
1476 doc.add_term("test");
1477 db.add_document(doc);
1480 // Add 5 documents indexed by "test" with wdf 2.
1481 for (i = 0; i < 5; ++i) {
1482 Xapian::Document doc;
1483 *buf = '0' + i;
1484 doc.add_value(0, buf);
1485 doc.add_term("test", 2);
1486 db.add_document(doc);
1489 db.commit();
1491 Xapian::Enquire enq(db);
1492 enq.set_query(Xapian::Query("test"));
1494 enq.set_sort_by_value_then_relevance(0, true);
1496 // 10 results, unpaged.
1497 Xapian::MSet mset1 = enq.get_mset(0, 10);
1498 TEST_EQUAL(mset1.size(), 10);
1500 // 10 results, split.
1501 Xapian::MSet mset2a = enq.get_mset(0, 1);
1502 TEST_EQUAL(mset2a.size(), 1);
1503 Xapian::MSet mset2b = enq.get_mset(1, 1);
1504 TEST_EQUAL(mset2b.size(), 1);
1505 Xapian::MSet mset2c = enq.get_mset(2, 8);
1506 TEST_EQUAL(mset2c.size(), 8);
1508 TEST_EQUAL(*mset1[0], *mset2a[0]);
1509 TEST_EQUAL(*mset1[1], *mset2b[0]);
1510 for (i = 0; i < 8; ++i) {
1511 TEST_EQUAL(*mset1[i + 2], *mset2c[i]);
1514 return true;
1517 DEFINE_TESTCASE(crashrecovery1, chert) {
1518 // Glass has a single version file per revision, rather than multiple base
1519 // files, so it simply can't get into the situations we are testing
1520 // recovery from.
1521 const string & dbtype = get_dbtype();
1522 string path = ".";
1523 path += dbtype;
1524 path += "/dbw";
1525 const char * base_ext = ".baseB";
1527 Xapian::Document doc;
1529 Xapian::WritableDatabase db = get_writable_database();
1530 Xapian::Database dbr(get_writable_database_as_database());
1531 TEST_EQUAL(dbr.get_doccount(), 0);
1533 // Xapian::Database has full set of baseA, no baseB
1534 TEST(file_exists(path + "/postlist.baseA"));
1535 TEST(file_exists(path + "/record.baseA"));
1536 TEST(file_exists(path + "/termlist.baseA"));
1537 TEST(!file_exists(path + "/postlist.baseB"));
1538 TEST(!file_exists(path + "/record.baseB"));
1539 TEST(!file_exists(path + "/termlist.baseB"));
1541 db.add_document(doc);
1542 db.commit();
1543 TEST(dbr.reopen());
1544 TEST_EQUAL(dbr.get_doccount(), 1);
1546 // Xapian::Database has full set of baseB, old baseA
1547 TEST(file_exists(path + "/postlist.baseA"));
1548 TEST(file_exists(path + "/record.baseA"));
1549 TEST(file_exists(path + "/termlist.baseA"));
1550 TEST(file_exists(path + "/postlist.baseB"));
1551 TEST(file_exists(path + "/record.baseB"));
1552 TEST(file_exists(path + "/termlist.baseB"));
1554 db.add_document(doc);
1555 db.commit();
1556 TEST(dbr.reopen());
1557 TEST_EQUAL(dbr.get_doccount(), 2);
1559 // Xapian::Database has full set of baseA, old baseB
1560 TEST(file_exists(path + "/postlist.baseA"));
1561 TEST(file_exists(path + "/record.baseA"));
1562 TEST(file_exists(path + "/termlist.baseA"));
1563 TEST(file_exists(path + "/postlist.baseB"));
1564 TEST(file_exists(path + "/record.baseB"));
1565 TEST(file_exists(path + "/termlist.baseB"));
1567 // Simulate a transaction starting, some of the baseB getting removed,
1568 // but then the transaction fails.
1569 unlink((path + "/record" + base_ext).c_str());
1570 unlink((path + "/termlist" + base_ext).c_str());
1572 TEST(!dbr.reopen());
1573 TEST_EQUAL(dbr.get_doccount(), 2);
1576 Xapian::WritableDatabase db(path, Xapian::DB_OPEN);
1577 // Xapian::Database has full set of baseA, some old baseB
1578 TEST(file_exists(path + "/postlist.baseA"));
1579 TEST(file_exists(path + "/record.baseA"));
1580 TEST(file_exists(path + "/termlist.baseA"));
1581 TEST(file_exists(path + "/postlist.baseB"));
1582 TEST(!file_exists(path + "/record.baseB"));
1583 TEST(!file_exists(path + "/termlist.baseB"));
1584 Xapian::Database dbr = Xapian::Database(path);
1586 db.add_document(doc);
1587 db.commit();
1588 TEST(dbr.reopen());
1589 TEST_EQUAL(dbr.get_doccount(), 3);
1591 // Xapian::Database has full set of baseB, old baseA
1592 TEST(file_exists(path + "/postlist.baseA"));
1593 TEST(file_exists(path + "/record.baseA"));
1594 TEST(file_exists(path + "/termlist.baseA"));
1595 TEST(file_exists(path + "/postlist.baseB"));
1596 TEST(file_exists(path + "/record.baseB"));
1597 TEST(file_exists(path + "/termlist.baseB"));
1599 db.add_document(doc);
1600 db.commit();
1601 TEST(dbr.reopen());
1602 TEST_EQUAL(dbr.get_doccount(), 4);
1604 return true;
1607 // Check that DatabaseError is thrown if the docid counter would wrap.
1608 // Regression test for bug#152.
1609 DEFINE_TESTCASE(nomoredocids1, writable) {
1610 // The InMemory backend uses a vector for the documents, so trying to add
1611 // document "-1" will fail because we can't allocate enough memory!
1612 SKIP_TEST_FOR_BACKEND("inmemory");
1614 Xapian::WritableDatabase db = get_writable_database();
1615 Xapian::Document doc;
1616 doc.set_data("prose");
1617 doc.add_term("word");
1619 // FIXME: This probably should use the _MAX_DOCID values
1620 Xapian::docid max_id = 0xffffffff;
1622 db.replace_document(max_id, doc);
1624 TEST_EXCEPTION(Xapian::DatabaseError, db.add_document(doc));
1626 return true;
1629 // Test synonym iterators.
1630 DEFINE_TESTCASE(synonymitor1, writable && synonyms) {
1631 Xapian::WritableDatabase db = get_writable_database();
1633 // Test iterators for terms which aren't there.
1634 TEST(db.synonyms_begin("abc") == db.synonyms_end("abc"));
1636 // Test iterating the synonym keys when there aren't any.
1637 TEST(db.synonym_keys_begin() == db.synonym_keys_end());
1639 db.add_synonym("hello", "howdy");
1640 db.add_synonym("hello", "hi");
1641 db.add_synonym("goodbye", "bye");
1642 db.add_synonym("goodbye", "farewell");
1644 Xapian::TermIterator t;
1645 string s;
1647 // Try these tests twice - once before committing and once after.
1648 for (int times = 1; times <= 2; ++times) {
1649 // Test iterators for terms which aren't there.
1650 TEST(db.synonyms_begin("abc") == db.synonyms_end("abc"));
1651 TEST(db.synonyms_begin("ghi") == db.synonyms_end("ghi"));
1652 TEST(db.synonyms_begin("zzzzz") == db.synonyms_end("zzzzz"));
1654 s = "|";
1655 t = db.synonyms_begin("hello");
1656 while (t != db.synonyms_end("hello")) {
1657 s += *t++;
1658 s += '|';
1660 TEST_STRINGS_EQUAL(s, "|hi|howdy|");
1662 s = "|";
1663 t = db.synonyms_begin("goodbye");
1664 while (t != db.synonyms_end("goodbye")) {
1665 s += *t++;
1666 s += '|';
1668 TEST_STRINGS_EQUAL(s, "|bye|farewell|");
1670 s = "|";
1671 t = db.synonym_keys_begin();
1672 while (t != db.synonym_keys_end()) {
1673 s += *t++;
1674 s += '|';
1676 TEST_STRINGS_EQUAL(s, "|goodbye|hello|");
1678 db.commit();
1681 // Delete a synonym for "hello" and all synonyms for "goodbye".
1682 db.remove_synonym("hello", "hi");
1683 db.clear_synonyms("goodbye");
1685 // Try these tests twice - once before committing and once after.
1686 for (int times = 1; times <= 2; ++times) {
1687 // Test iterators for terms which aren't there.
1688 TEST(db.synonyms_begin("abc") == db.synonyms_end("abc"));
1689 TEST(db.synonyms_begin("ghi") == db.synonyms_end("ghi"));
1690 TEST(db.synonyms_begin("zzzzz") == db.synonyms_end("zzzzz"));
1692 s = "|";
1693 t = db.synonyms_begin("hello");
1694 while (t != db.synonyms_end("hello")) {
1695 s += *t++;
1696 s += '|';
1698 TEST_STRINGS_EQUAL(s, "|howdy|");
1700 TEST(db.synonyms_begin("goodbye") == db.synonyms_end("goodbye"));
1702 s = "|";
1703 t = db.synonym_keys_begin();
1704 while (t != db.synonym_keys_end()) {
1705 s += *t++;
1706 s += '|';
1708 TEST_STRINGS_EQUAL(s, "|hello|");
1710 db.commit();
1713 Xapian::Database db_multi;
1714 db_multi.add_database(db);
1715 db_multi.add_database(get_database("apitest_simpledata"));
1717 // Test iterators for terms which aren't there.
1718 TEST(db_multi.synonyms_begin("abc") == db_multi.synonyms_end("abc"));
1719 TEST(db_multi.synonyms_begin("ghi") == db_multi.synonyms_end("ghi"));
1720 TEST(db_multi.synonyms_begin("zzzzz") == db_multi.synonyms_end("zzzzz"));
1722 s = "|";
1723 t = db_multi.synonyms_begin("hello");
1724 while (t != db_multi.synonyms_end("hello")) {
1725 s += *t++;
1726 s += '|';
1728 TEST_STRINGS_EQUAL(s, "|howdy|");
1730 TEST(db_multi.synonyms_begin("goodbye") == db_multi.synonyms_end("goodbye"));
1732 s = "|";
1733 t = db_multi.synonym_keys_begin();
1734 while (t != db_multi.synonym_keys_end()) {
1735 s += *t++;
1736 s += '|';
1738 TEST_STRINGS_EQUAL(s, "|hello|");
1740 return true;
1743 // Test that adding a document with a really long term gives an error on
1744 // add_document() rather than on commit().
1745 DEFINE_TESTCASE(termtoolong1, writable) {
1746 // Inmemory doesn't impose a limit.
1747 SKIP_TEST_FOR_BACKEND("inmemory");
1749 Xapian::WritableDatabase db = get_writable_database();
1751 for (Xapian::doccount i = 246; i <= 290; ++i) {
1752 tout.str(string());
1753 tout << "Term length " << i << endl;
1754 Xapian::Document doc;
1755 string term(i, 'X');
1756 doc.add_term(term);
1757 try {
1758 db.add_document(doc);
1759 TEST_AND_EXPLAIN(false, "Expecting exception InvalidArgumentError");
1760 } catch (const Xapian::InvalidArgumentError &e) {
1761 // Check that the max length is correctly expressed in the
1762 // exception message - we've got this wrong in two different ways
1763 // in the past!
1764 tout << e.get_msg() << endl;
1765 TEST(e.get_msg().find("Term too long (> 245)") != string::npos);
1769 for (Xapian::doccount j = 240; j <= 245; ++j) {
1770 tout.str(string());
1771 tout << "Term length " << j << endl;
1772 Xapian::Document doc;
1773 string term(j, 'X');
1774 doc.add_term(term);
1775 db.add_document(doc);
1778 db.commit();
1780 size_t limit = endswith(get_dbtype(), "glass") ? 255 : 252;
1782 // Currently chert and glass escape zero bytes from terms in keys for
1783 // some tables, so a term with 127 zero bytes won't work for chert, and
1784 // with 128 zero bytes won't work for glass.
1785 Xapian::Document doc;
1786 doc.add_term(string(limit / 2 + 1, '\0'));
1787 db.add_document(doc);
1788 try {
1789 db.commit();
1790 TEST_AND_EXPLAIN(false, "Expecting exception InvalidArgumentError");
1791 } catch (const Xapian::InvalidArgumentError &e) {
1792 // Check that the max length is correctly expressed in the
1793 // exception message - we've got this wrong in two different ways
1794 // in the past!
1795 tout << e.get_msg() << endl;
1796 string target = " is ";
1797 target += str(limit);
1798 target += " bytes";
1799 TEST(e.get_msg().find(target) != string::npos);
1803 return true;
1806 /// Test playing with a postlist
1807 DEFINE_TESTCASE(postlist7, writable) {
1808 Xapian::WritableDatabase db_w = get_writable_database();
1811 Xapian::Document doc;
1812 doc.add_term("foo", 3);
1813 doc.add_term("zz", 4);
1814 db_w.replace_document(5, doc);
1817 Xapian::PostingIterator p;
1818 p = db_w.postlist_begin("foo");
1819 TEST(p != db_w.postlist_end("foo"));
1820 TEST_EQUAL(*p, 5);
1821 TEST_EQUAL(p.get_wdf(), 3);
1822 TEST_EQUAL(p.get_doclength(), 7);
1823 TEST_EQUAL(p.get_unique_terms(), 2);
1824 ++p;
1825 TEST(p == db_w.postlist_end("foo"));
1828 Xapian::Document doc;
1829 doc.add_term("foo", 1);
1830 doc.add_term("zz", 1);
1831 db_w.replace_document(6, doc);
1834 p = db_w.postlist_begin("foo");
1835 TEST(p != db_w.postlist_end("foo"));
1836 TEST_EQUAL(*p, 5);
1837 TEST_EQUAL(p.get_wdf(), 3);
1838 TEST_EQUAL(p.get_doclength(), 7);
1839 TEST_EQUAL(p.get_unique_terms(), 2);
1840 ++p;
1841 TEST(p != db_w.postlist_end("foo"));
1842 TEST_EQUAL(*p, 6);
1843 TEST_EQUAL(p.get_wdf(), 1);
1844 TEST_EQUAL(p.get_doclength(), 2);
1845 TEST_EQUAL(p.get_unique_terms(), 2);
1846 ++p;
1847 TEST(p == db_w.postlist_end("foo"));
1849 return true;
1852 DEFINE_TESTCASE(lazytablebug1, chert || glass) {
1854 Xapian::WritableDatabase db = get_named_writable_database("lazytablebug1", string());
1856 Xapian::Document doc;
1857 doc.add_term("foo");
1858 db.add_document(doc);
1859 db.commit();
1861 string synonym(255, 'x');
1862 char buf[] = " iamafish!!!!!!!!!!";
1863 for (int i = 33; i < 120; ++i) {
1864 db.add_synonym(buf, synonym);
1865 ++buf[0];
1868 db.commit();
1871 Xapian::Database db = get_writable_database_as_database();
1872 for (Xapian::TermIterator t = db.synonym_keys_begin(); t != db.synonym_keys_end(); ++t) {
1873 tout << *t << endl;
1876 return true;
1879 /** Regression test for bug #287 for flint.
1881 * Chert also has the same duff code but this testcase doesn't actually
1882 * tickle the bug there.
1884 DEFINE_TESTCASE(cursordelbug1, chert || glass) {
1885 static const int terms[] = { 219, 221, 222, 223, 224, 225, 226 };
1886 static const int copies[] = { 74, 116, 199, 21, 45, 155, 189 };
1888 Xapian::WritableDatabase db;
1889 db = get_named_writable_database("cursordelbug1", string());
1891 for (size_t i = 0; i < sizeof(terms) / sizeof(terms[0]); ++i) {
1892 Xapian::Document doc;
1893 doc.add_term("XC" + str(terms[i]));
1894 doc.add_term("XTabc");
1895 doc.add_term("XAdef");
1896 doc.add_term("XRghi");
1897 doc.add_term("XYabc");
1898 size_t c = copies[i];
1899 while (c--) db.add_document(doc);
1902 db.commit();
1904 for (size_t i = 0; i < sizeof(terms) / sizeof(terms[0]); ++i) {
1905 db.delete_document("XC" + str(terms[i]));
1908 db.commit();
1910 const string & db_path = get_named_writable_database_path("cursordelbug1");
1911 return Xapian::Database::check(db_path) == 0;
1914 /** Helper function for modifyvalues1.
1916 * Check that the values stored in the database match */
1917 static void
1918 check_vals(const Xapian::Database & db, const map<Xapian::docid, string> & vals)
1920 TEST_EQUAL(db.get_doccount(), vals.size());
1921 if (vals.empty()) return;
1922 TEST_REL(vals.rbegin()->first,<=,db.get_lastdocid());
1923 map<Xapian::docid, string>::const_iterator i;
1924 for (i = vals.begin(); i != vals.end(); ++i) {
1925 tout.str(string());
1926 tout << "Checking value in doc " << i->first << " - should be '" << i->second << "'\n";
1927 Xapian::Document doc = db.get_document(i->first);
1928 string dbval = doc.get_value(1);
1929 TEST_EQUAL(dbval, i->second);
1930 if (dbval.empty()) {
1931 TEST_EQUAL(0, doc.values_count());
1932 TEST_EQUAL(doc.values_begin(), doc.values_end());
1933 } else {
1934 TEST_EQUAL(1, doc.values_count());
1935 Xapian::ValueIterator valit = doc.values_begin();
1936 TEST_NOT_EQUAL(valit, doc.values_end());
1937 TEST_EQUAL(dbval, *valit);
1938 TEST_EQUAL(1, valit.get_valueno());
1939 ++valit;
1940 TEST_EQUAL(valit, doc.values_end());
1945 /** Regression test for bug in initial streaming values implementation in
1946 * chert.
1948 DEFINE_TESTCASE(modifyvalues1, writable) {
1949 unsigned int seed = 7;
1950 Xapian::WritableDatabase db = get_writable_database();
1951 // Note: doccount must be coprime with 13
1952 const Xapian::doccount doccount = 1000;
1953 static_assert(doccount % 13 != 0, "doccount divisible by 13");
1955 map<Xapian::docid, string> vals;
1957 for (Xapian::doccount num = 1; num <= doccount; ++num) {
1958 tout.str(string());
1959 Xapian::Document doc;
1960 string val = "val" + str(num);
1961 tout << "Setting val '" << val << "' in doc " << num << "\n";
1962 doc.add_value(1, val);
1963 db.add_document(doc);
1964 vals[num] = val;
1966 check_vals(db, vals);
1967 db.commit();
1968 check_vals(db, vals);
1970 // Modify one of the values (this is a regression test which failed with
1971 // the initial implementation of streaming values).
1973 Xapian::Document doc;
1974 string val = "newval0";
1975 tout << "Setting val '" << val << "' in doc 2\n";
1976 doc.add_value(1, val);
1977 db.replace_document(2, doc);
1978 vals[2] = val;
1979 check_vals(db, vals);
1980 db.commit();
1981 check_vals(db, vals);
1984 // Check that value doesn't get lost when replacing a document with itself.
1986 tout << "Replacing document 1 with itself\n";
1987 Xapian::Document doc = db.get_document(1);
1988 db.replace_document(1, doc);
1989 check_vals(db, vals);
1990 db.commit();
1991 check_vals(db, vals);
1994 // Check that value doesn't get lost when replacing a document with itself,
1995 // accessing another document in the meantime. This is a regression test
1996 // for a bug in the code which implements lazy updates - this used to
1997 // forget the values in the document in this situation.
1999 tout << "Replacing document 1 with itself, after reading doc 2.\n";
2000 Xapian::Document doc = db.get_document(1);
2001 db.get_document(2);
2002 db.replace_document(1, doc);
2003 check_vals(db, vals);
2004 db.commit();
2005 check_vals(db, vals);
2008 // Do some random modifications: seed random generator, for repeatable
2009 // results.
2010 tout << "Setting seed to " << seed << "\n";
2011 srand(seed);
2012 for (Xapian::doccount num = 1; num <= doccount * 2; ++num) {
2013 tout.str(string());
2014 Xapian::docid did = ((rand() >> 8) % doccount) + 1;
2015 Xapian::Document doc;
2016 string val;
2018 if (num % 5 != 0) {
2019 val = "newval" + str(num);
2020 tout << "Setting val '" << val << "' in doc " << did << "\n";
2021 doc.add_value(1, val);
2022 } else {
2023 tout << "Adding/replacing empty document " << did << "\n";
2025 db.replace_document(did, doc);
2026 vals[did] = val;
2028 check_vals(db, vals);
2029 db.commit();
2030 check_vals(db, vals);
2032 // Delete all the remaining values, in a slightly shuffled order.
2033 // This is where it's important that doccount is coprime with 13.
2034 for (Xapian::doccount num = 0; num < doccount * 13; num += 13) {
2035 tout.str(string());
2036 Xapian::docid did = (num % doccount) + 1;
2037 tout << "Clearing val in doc " << did << "\n";
2038 Xapian::Document doc;
2039 db.replace_document(did, doc);
2040 vals[did] = string();
2042 check_vals(db, vals);
2043 db.commit();
2044 check_vals(db, vals);
2046 return true;