Make TradWeight a simple subclass and deprecate
[xapian.git] / xapian-core / tests / api_closedb.cc
blob7af5f5751889ae618ba8ad52ef56001eb99d5b4a
1 /** @file
2 * @brief Tests of closing databases.
3 */
4 /* Copyright 2008,2009 Lemur Consulting Ltd
5 * Copyright 2009,2012,2015,2023 Olly Betts
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
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.
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 USA
22 #include <config.h>
24 #include "api_closedb.h"
26 #include <xapian.h>
28 #include "safeunistd.h"
30 #include "apitest.h"
31 #include "testutils.h"
33 using namespace std;
35 #define COUNT_EXCEPTION(CODE, EXCEPTION) \
36 try { \
37 CODE; \
38 } catch (const Xapian::EXCEPTION&) { \
39 ++exception_count; \
42 #define COUNT_CLOSED(CODE) COUNT_EXCEPTION(CODE, DatabaseClosedError)
44 // Iterators used by closedb1.
45 struct closedb1_iterators {
46 Xapian::Database db;
47 Xapian::Document doc1;
48 Xapian::PostingIterator pl1;
49 Xapian::PostingIterator pl2;
50 Xapian::PostingIterator pl1end;
51 Xapian::PostingIterator pl2end;
52 Xapian::TermIterator tl1;
53 Xapian::TermIterator tlend;
54 Xapian::TermIterator atl1;
55 Xapian::TermIterator atlend;
56 Xapian::PositionIterator pil1;
57 Xapian::PositionIterator pilend;
59 void setup(Xapian::Database db_) {
60 db = db_;
62 // Set up the iterators for the test.
63 pl1 = db.postlist_begin("paragraph");
64 pl2 = db.postlist_begin("this");
65 ++pl2;
66 pl1end = db.postlist_end("paragraph");
67 pl2end = db.postlist_end("this");
68 tl1 = db.termlist_begin(1);
69 tlend = db.termlist_end(1);
70 atl1 = db.allterms_begin("t");
71 atlend = db.allterms_end("t");
72 pil1 = db.positionlist_begin(1, "paragraph");
73 pilend = db.positionlist_end(1, "paragraph");
76 int perform() {
77 int exception_count = 0;
79 // Getting a document may throw.
80 COUNT_CLOSED(
81 doc1 = db.get_document(1);
82 // Only do these if get_document() succeeded.
83 COUNT_CLOSED(TEST_EQUAL(doc1.get_data().substr(0, 33),
84 "This is a test document used with"));
85 COUNT_CLOSED(doc1.termlist_begin());
88 // Causing the database to access its files raises the "database
89 // closed" error.
90 COUNT_CLOSED(db.postlist_begin("paragraph"));
91 COUNT_CLOSED(db.get_document(1).get_value(1));
92 COUNT_CLOSED(db.termlist_begin(1));
93 COUNT_CLOSED(db.positionlist_begin(1, "paragraph"));
94 COUNT_CLOSED(db.allterms_begin());
95 COUNT_CLOSED(db.allterms_begin("p"));
96 COUNT_CLOSED(db.get_termfreq("paragraph"));
97 COUNT_CLOSED(db.get_collection_freq("paragraph"));
98 COUNT_CLOSED(db.term_exists("paragraph"));
99 COUNT_CLOSED(db.get_value_freq(1));
100 COUNT_CLOSED(db.get_value_lower_bound(1));
101 COUNT_CLOSED(db.get_value_upper_bound(1));
102 COUNT_CLOSED(db.valuestream_begin(1));
103 COUNT_CLOSED(db.get_doclength(1));
104 COUNT_CLOSED(db.get_unique_terms(1));
106 // Reopen raises the "database closed" error.
107 COUNT_CLOSED(db.reopen());
109 TEST_NOT_EQUAL(pl1, pl1end);
110 TEST_NOT_EQUAL(pl2, pl2end);
111 TEST_NOT_EQUAL(tl1, tlend);
112 TEST_NOT_EQUAL(atl1, atlend);
113 TEST_NOT_EQUAL(pil1, pilend);
115 COUNT_CLOSED(db.postlist_begin("paragraph"));
117 COUNT_CLOSED(TEST_EQUAL(*pl1, 1));
118 COUNT_CLOSED(TEST_EQUAL(pl1.get_doclength(), 28));
119 COUNT_CLOSED(TEST_EQUAL(pl1.get_unique_terms(), 21));
121 COUNT_CLOSED(TEST_EQUAL(*pl2, 2));
122 COUNT_CLOSED(TEST_EQUAL(pl2.get_doclength(), 81));
123 COUNT_CLOSED(TEST_EQUAL(pl2.get_unique_terms(), 56));
125 COUNT_CLOSED(TEST_EQUAL(*tl1, "a"));
126 COUNT_CLOSED(TEST_EQUAL(tl1.get_wdf(), 2));
127 COUNT_CLOSED(TEST_EQUAL(tl1.get_termfreq(), 3));
129 COUNT_CLOSED(TEST_EQUAL(*atl1, "test"));
130 COUNT_CLOSED(TEST_EQUAL(atl1.get_termfreq(), 1));
132 COUNT_CLOSED(TEST_EQUAL(*pil1, 12));
134 // Advancing the iterator may or may not raise an error, but if it
135 // doesn't it must return the correct answers.
136 COUNT_CLOSED(
137 ++pl1;
138 COUNT_CLOSED(TEST_EQUAL(*pl1, 2));
139 COUNT_CLOSED(TEST_EQUAL(pl1.get_doclength(), 81));
140 COUNT_CLOSED(TEST_EQUAL(pl1.get_unique_terms(), 56));
143 COUNT_CLOSED(
144 ++pl2;
145 COUNT_CLOSED(TEST_EQUAL(*pl2, 3));
146 COUNT_CLOSED(TEST_EQUAL(pl2.get_doclength(), 15));
147 COUNT_CLOSED(TEST_EQUAL(pl2.get_unique_terms(), 14));
150 COUNT_CLOSED(
151 ++tl1;
152 COUNT_CLOSED(TEST_EQUAL(*tl1, "api"));
153 COUNT_CLOSED(TEST_EQUAL(tl1.get_wdf(), 1));
154 COUNT_CLOSED(TEST_EQUAL(tl1.get_termfreq(), 1));
157 COUNT_CLOSED(
158 ++atl1;
159 COUNT_CLOSED(TEST_EQUAL(*atl1, "that"));
160 COUNT_CLOSED(TEST_EQUAL(atl1.get_termfreq(), 2));
163 COUNT_CLOSED(
164 ++pil1;
165 COUNT_CLOSED(TEST_EQUAL(*pil1, 28));
168 return exception_count;
172 // Test for closing a database
173 DEFINE_TESTCASE(closedb1, backend) {
174 Xapian::Database db(get_database("apitest_simpledata"));
175 closedb1_iterators iters;
177 // Run the test, checking that we get no "closed" exceptions.
178 iters.setup(db);
179 int exception_count = iters.perform();
180 TEST_EQUAL(exception_count, 0);
182 // Setup for the next test.
183 iters.setup(db);
185 // Close the database.
186 db.close();
188 // Dup stdout to the fds which the database was using, to try to catch
189 // issues with lingering references to closed fds (regression test for
190 // early development versions of honey).
191 vector<int> fds;
192 for (int i = 0; i != 6; ++i) {
193 fds.push_back(dup(1));
196 // Reopening a closed database should always raise DatabaseClosedError.
197 TEST_EXCEPTION(Xapian::DatabaseClosedError, db.reopen());
199 // Run the test again, checking that we get some "closed" exceptions.
200 exception_count = iters.perform();
201 TEST_NOT_EQUAL(exception_count, 0);
203 // get_description() shouldn't throw an exception. Actually do something
204 // with the description, in case this method is marked as "pure" in the
205 // future.
206 TEST(!db.get_description().empty());
208 // Calling close repeatedly is okay.
209 db.close();
211 for (int fd : fds) {
212 close(fd);
216 // Test closing a writable database, and that it drops the lock.
217 DEFINE_TESTCASE(closedb2, writable && path) {
218 Xapian::WritableDatabase dbw1(get_named_writable_database("apitest_closedb2"));
219 TEST_EXCEPTION(Xapian::DatabaseLockError,
220 Xapian::WritableDatabase db(get_named_writable_database_path("apitest_closedb2"),
221 Xapian::DB_OPEN));
222 dbw1.close();
223 Xapian::WritableDatabase dbw2 = get_named_writable_database("apitest_closedb2");
224 TEST_EXCEPTION(Xapian::DatabaseClosedError,
225 dbw1.postlist_begin("paragraph"));
226 TEST_EQUAL(dbw2.postlist_begin("paragraph"), dbw2.postlist_end("paragraph"));
229 /// Check API methods which might either work or throw an exception.
230 DEFINE_TESTCASE(closedb3, backend) {
231 Xapian::Database db(get_database("etext"));
232 const string & uuid = db.get_uuid();
233 db.close();
234 try {
235 TEST_EQUAL(db.get_uuid(), uuid);
236 } catch (const Xapian::DatabaseClosedError &) {
238 try {
239 TEST(db.has_positions());
240 } catch (const Xapian::DatabaseClosedError &) {
242 try {
243 TEST_EQUAL(db.get_doccount(), 566);
244 } catch (const Xapian::DatabaseClosedError &) {
246 try {
247 TEST_EQUAL(db.get_lastdocid(), 566);
248 } catch (const Xapian::DatabaseClosedError &) {
250 try {
251 TEST_REL(db.get_doclength_lower_bound(), <, db.get_avlength());
252 } catch (const Xapian::DatabaseClosedError &) {
254 try {
255 TEST_REL(db.get_doclength_upper_bound(), >, db.get_avlength());
256 } catch (const Xapian::DatabaseClosedError &) {
258 try {
259 TEST(db.get_wdf_upper_bound("king"));
260 } catch (const Xapian::DatabaseClosedError &) {
262 try {
263 // For non-remote databases, keep_alive() is a no-op anyway.
264 db.keep_alive();
265 } catch (const Xapian::DatabaseClosedError &) {
269 /// Regression test for bug fixed in 1.1.4 - close() should implicitly commit().
270 DEFINE_TESTCASE(closedb4, writable && !inmemory) {
271 Xapian::WritableDatabase wdb(get_writable_database());
272 wdb.add_document(Xapian::Document());
273 TEST_EQUAL(wdb.get_doccount(), 1);
274 wdb.close();
275 Xapian::Database db(get_writable_database_as_database());
276 TEST_EQUAL(db.get_doccount(), 1);
279 /// Test the effects of close() on transactions
280 DEFINE_TESTCASE(closedb5, transactions) {
282 // If a transaction is active, close() shouldn't implicitly commit().
283 Xapian::WritableDatabase wdb = get_writable_database();
284 wdb.begin_transaction();
285 wdb.add_document(Xapian::Document());
286 TEST_EQUAL(wdb.get_doccount(), 1);
287 wdb.close();
288 Xapian::Database db = get_writable_database_as_database();
289 TEST_EQUAL(db.get_doccount(), 0);
293 // Same test but for an unflushed transaction.
294 Xapian::WritableDatabase wdb = get_writable_database();
295 wdb.begin_transaction(false);
296 wdb.add_document(Xapian::Document());
297 TEST_EQUAL(wdb.get_doccount(), 1);
298 wdb.close();
299 Xapian::Database db = get_writable_database_as_database();
300 TEST_EQUAL(db.get_doccount(), 0);
304 // commit_transaction() throws InvalidOperationError when
305 // not in a transaction.
306 Xapian::WritableDatabase wdb = get_writable_database();
307 wdb.close();
308 TEST_EXCEPTION(Xapian::InvalidOperationError,
309 wdb.commit_transaction());
311 // begin_transaction() is no-op or throws DatabaseClosedError. We may be
312 // able to call db.begin_transaction(), but we can't make any changes
313 // inside that transaction. If begin_transaction() succeeds, then
314 // commit_transaction() either end the transaction or throw
315 // DatabaseClosedError.
316 try {
317 wdb.begin_transaction();
318 try {
319 wdb.commit_transaction();
320 } catch (const Xapian::DatabaseClosedError &) {
322 } catch (const Xapian::DatabaseClosedError &) {
327 // Same test but for cancel_transaction().
328 Xapian::WritableDatabase wdb = get_writable_database();
329 wdb.close();
330 TEST_EXCEPTION(Xapian::InvalidOperationError,
331 wdb.cancel_transaction());
333 try {
334 wdb.begin_transaction();
335 try {
336 wdb.cancel_transaction();
337 } catch (const Xapian::DatabaseClosedError &) {
339 } catch (const Xapian::DatabaseClosedError &) {
344 /// Database::keep_alive() should fail after close() for a remote database.
345 DEFINE_TESTCASE(closedb6, remote) {
346 Xapian::Database db(get_database("etext"));
347 db.close();
349 try {
350 db.keep_alive();
351 FAIL_TEST("Expected DatabaseClosedError wasn't thrown");
352 } catch (const Xapian::DatabaseClosedError &) {
356 // Test WritableDatabase methods.
357 DEFINE_TESTCASE(closedb7, writable) {
358 Xapian::WritableDatabase db(get_writable_database());
359 db.add_document(Xapian::Document());
360 db.close();
362 // Since we can't make any changes which need to be committed,
363 // db.commit() is a no-op, and so doesn't have to fail.
364 try {
365 db.commit();
366 } catch (const Xapian::DatabaseClosedError &) {
369 TEST_EXCEPTION(Xapian::DatabaseClosedError,
370 db.add_document(Xapian::Document()));
371 TEST_EXCEPTION(Xapian::DatabaseClosedError,
372 db.delete_document(1));
373 TEST_EXCEPTION(Xapian::DatabaseClosedError,
374 db.replace_document(1, Xapian::Document()));
375 TEST_EXCEPTION(Xapian::DatabaseClosedError,
376 db.replace_document(2, Xapian::Document()));
377 TEST_EXCEPTION(Xapian::DatabaseClosedError,
378 db.replace_document("Qi", Xapian::Document()));
381 // Test spelling related methods.
382 DEFINE_TESTCASE(closedb8, writable && spelling) {
383 Xapian::WritableDatabase db(get_writable_database());
384 db.add_spelling("pneumatic");
385 db.add_spelling("pneumonia");
386 db.close();
388 TEST_EXCEPTION(Xapian::DatabaseClosedError,
389 db.add_spelling("penmanship"));
390 TEST_EXCEPTION(Xapian::DatabaseClosedError,
391 db.remove_spelling("pneumatic"));
392 TEST_EXCEPTION(Xapian::DatabaseClosedError,
393 db.get_spelling_suggestion("newmonia"));
394 TEST_EXCEPTION(Xapian::DatabaseClosedError,
395 db.spellings_begin());
398 // Test synonym related methods.
399 DEFINE_TESTCASE(closedb9, writable && synonyms) {
400 Xapian::WritableDatabase db(get_writable_database());
401 db.add_synonym("color", "colour");
402 db.add_synonym("honor", "honour");
403 db.close();
405 TEST_EXCEPTION(Xapian::DatabaseClosedError,
406 db.add_synonym("behavior", "behaviour"));
407 TEST_EXCEPTION(Xapian::DatabaseClosedError,
408 db.remove_synonym("honor", "honour"));
409 TEST_EXCEPTION(Xapian::DatabaseClosedError,
410 db.clear_synonyms("honor"));
411 TEST_EXCEPTION(Xapian::DatabaseClosedError,
412 db.synonyms_begin("color"));
413 TEST_EXCEPTION(Xapian::DatabaseClosedError,
414 db.synonym_keys_begin());
417 // Test metadata related methods.
418 DEFINE_TESTCASE(closedb10, writable && metadata) {
419 Xapian::WritableDatabase db(get_writable_database());
420 db.set_metadata("foo", "FOO");
421 db.set_metadata("bar", "BAR");
422 db.close();
424 TEST_EXCEPTION(Xapian::DatabaseClosedError,
425 db.set_metadata("test", "TEST"));
426 TEST_EXCEPTION(Xapian::DatabaseClosedError,
427 db.get_metadata("foo"));
428 TEST_EXCEPTION(Xapian::DatabaseClosedError,
429 db.get_metadata("bar"));
430 TEST_EXCEPTION(Xapian::DatabaseClosedError,
431 db.metadata_keys_begin());
434 #define COUNT_NETWORK(CODE) COUNT_EXCEPTION(CODE, NetworkError)
436 // Iterators used by remotefailure1.
437 struct remotefailure1_iterators {
438 Xapian::Database db;
439 Xapian::Document doc1;
440 Xapian::PostingIterator pl1;
441 Xapian::PostingIterator pl2;
442 Xapian::PostingIterator pl1end;
443 Xapian::PostingIterator pl2end;
444 Xapian::TermIterator tl1;
445 Xapian::TermIterator tlend;
446 Xapian::TermIterator atl1;
447 Xapian::TermIterator atlend;
448 Xapian::PositionIterator pil1;
449 Xapian::PositionIterator pilend;
451 void setup(Xapian::Database db_) {
452 db = db_;
454 // Set up the iterators for the test.
455 pl1 = db.postlist_begin("paragraph");
456 pl2 = db.postlist_begin("this");
457 ++pl2;
458 pl1end = db.postlist_end("paragraph");
459 pl2end = db.postlist_end("this");
460 tl1 = db.termlist_begin(1);
461 tlend = db.termlist_end(1);
462 atl1 = db.allterms_begin("t");
463 atlend = db.allterms_end("t");
464 pil1 = db.positionlist_begin(1, "paragraph");
465 pilend = db.positionlist_end(1, "paragraph");
468 int perform() {
469 int exception_count = 0;
471 // Getting a document may throw.
472 COUNT_NETWORK(
473 doc1 = db.get_document(1);
474 // Only do these if get_document() succeeded.
475 COUNT_NETWORK(TEST_EQUAL(doc1.get_data().substr(0, 33),
476 "This is a test document used with"));
477 COUNT_NETWORK(doc1.termlist_begin());
480 // These should always fail.
481 COUNT_NETWORK(db.postlist_begin("paragraph"));
482 COUNT_NETWORK(db.get_document(1).get_value(1));
483 COUNT_NETWORK(db.termlist_begin(1));
484 COUNT_NETWORK(db.positionlist_begin(1, "paragraph"));
485 COUNT_NETWORK(db.allterms_begin());
486 COUNT_NETWORK(db.allterms_begin("p"));
487 COUNT_NETWORK(db.get_termfreq("paragraph"));
488 COUNT_NETWORK(db.get_collection_freq("paragraph"));
489 COUNT_NETWORK(db.term_exists("paragraph"));
490 COUNT_NETWORK(db.get_value_freq(1));
491 COUNT_NETWORK(db.get_value_lower_bound(1));
492 COUNT_NETWORK(db.get_value_upper_bound(1));
493 COUNT_NETWORK(db.valuestream_begin(1));
494 COUNT_NETWORK(db.get_doclength(1));
495 COUNT_NETWORK(db.get_unique_terms(1));
497 // Should always fail.
498 COUNT_NETWORK(db.reopen());
500 TEST_NOT_EQUAL(pl1, pl1end);
501 TEST_NOT_EQUAL(pl2, pl2end);
502 TEST_NOT_EQUAL(tl1, tlend);
503 TEST_NOT_EQUAL(atl1, atlend);
504 TEST_NOT_EQUAL(pil1, pilend);
506 COUNT_NETWORK(db.postlist_begin("paragraph"));
508 COUNT_NETWORK(TEST_EQUAL(*pl1, 1));
509 COUNT_NETWORK(TEST_EQUAL(pl1.get_doclength(), 28));
510 COUNT_NETWORK(TEST_EQUAL(pl1.get_unique_terms(), 21));
512 COUNT_NETWORK(TEST_EQUAL(*pl2, 2));
513 COUNT_NETWORK(TEST_EQUAL(pl2.get_doclength(), 81));
514 COUNT_NETWORK(TEST_EQUAL(pl2.get_unique_terms(), 56));
516 COUNT_NETWORK(TEST_EQUAL(*tl1, "a"));
517 COUNT_NETWORK(TEST_EQUAL(tl1.get_wdf(), 2));
518 COUNT_NETWORK(TEST_EQUAL(tl1.get_termfreq(), 3));
520 COUNT_NETWORK(TEST_EQUAL(*atl1, "test"));
521 COUNT_NETWORK(TEST_EQUAL(atl1.get_termfreq(), 1));
523 COUNT_NETWORK(TEST_EQUAL(*pil1, 12));
525 // Advancing the iterator may or may not raise an error, but if it
526 // doesn't it must return the correct answers.
527 COUNT_NETWORK(
528 ++pl1;
529 COUNT_NETWORK(TEST_EQUAL(*pl1, 2));
530 COUNT_NETWORK(TEST_EQUAL(pl1.get_doclength(), 81));
531 COUNT_NETWORK(TEST_EQUAL(pl1.get_unique_terms(), 56));
534 COUNT_NETWORK(
535 ++pl2;
536 COUNT_NETWORK(TEST_EQUAL(*pl2, 3));
537 COUNT_NETWORK(TEST_EQUAL(pl2.get_doclength(), 15));
538 COUNT_NETWORK(TEST_EQUAL(pl2.get_unique_terms(), 14));
541 COUNT_NETWORK(
542 ++tl1;
543 COUNT_NETWORK(TEST_EQUAL(*tl1, "api"));
544 COUNT_NETWORK(TEST_EQUAL(tl1.get_wdf(), 1));
545 COUNT_NETWORK(TEST_EQUAL(tl1.get_termfreq(), 1));
548 COUNT_NETWORK(
549 ++atl1;
550 COUNT_NETWORK(TEST_EQUAL(*atl1, "that"));
551 COUNT_NETWORK(TEST_EQUAL(atl1.get_termfreq(), 2));
554 COUNT_NETWORK(
555 ++pil1;
556 COUNT_NETWORK(TEST_EQUAL(*pil1, 28));
559 return exception_count;
563 // Test for a remote server failing.
564 DEFINE_TESTCASE(remotefailure1, remotetcp) {
565 #ifdef __MINGW32__
566 XFAIL("Remote failure handling or testing is buggy on __MINGW32__");
567 #endif
568 Xapian::Database db(get_database("apitest_simpledata"));
569 remotefailure1_iterators iters;
571 // Run the test, checking that we get no exceptions.
572 iters.setup(db);
573 int exception_count = iters.perform();
574 TEST_EQUAL(exception_count, 0);
576 // Setup for the next test.
577 iters.setup(db);
579 // Simulate remote server failure.
580 kill_remote(db);
582 // Dup stdout to the fds which the database was using, to try to catch
583 // issues with lingering references to closed fds.
584 vector<int> fds;
585 for (int i = 0; i != 6; ++i) {
586 fds.push_back(dup(1));
589 // Run the test again, checking that we get some "NetworkError" exceptions.
590 exception_count = iters.perform();
591 TEST_NOT_EQUAL(exception_count, 0);
593 // get_description() shouldn't throw an exception. Actually do something
594 // with the description, in case this method is marked as "pure" in the
595 // future.
596 TEST(!db.get_description().empty());
598 for (int fd : fds) {
599 close(fd);
603 // There's no remotefailure2 plus other gaps in the numbering - these testcases
604 // are adapted versions of the closedb testcases, but some closedb testcases
605 // don't make sense to convert.
607 /// Check API methods which might either work or throw an exception.
608 DEFINE_TESTCASE(remotefailure3, remotetcp) {
609 #ifdef __MINGW32__
610 XFAIL("Remote failure handling or testing is buggy on __MINGW32__");
611 #endif
612 Xapian::Database db(get_database("etext"));
613 const string & uuid = db.get_uuid();
614 kill_remote(db);
615 try {
616 TEST_EQUAL(db.get_uuid(), uuid);
617 } catch (const Xapian::NetworkError&) {
619 try {
620 TEST(db.has_positions());
621 } catch (const Xapian::NetworkError&) {
623 try {
624 TEST_EQUAL(db.get_doccount(), 566);
625 } catch (const Xapian::NetworkError&) {
627 try {
628 TEST_EQUAL(db.get_lastdocid(), 566);
629 } catch (const Xapian::NetworkError&) {
631 try {
632 TEST_REL(db.get_doclength_lower_bound(), <, db.get_avlength());
633 } catch (const Xapian::NetworkError&) {
635 try {
636 TEST_REL(db.get_doclength_upper_bound(), >, db.get_avlength());
637 } catch (const Xapian::NetworkError&) {
639 try {
640 TEST(db.get_wdf_upper_bound("king"));
641 } catch (const Xapian::NetworkError&) {
643 TEST_EXCEPTION(Xapian::NetworkError, db.keep_alive());
646 /// Test the effects of remote server failure on transactions
647 DEFINE_TESTCASE(remotefailure5, remotetcp) {
648 #ifdef __MINGW32__
649 XFAIL("Remote failure handling or testing is buggy on __MINGW32__");
650 #endif
652 Xapian::WritableDatabase wdb = get_writable_database();
653 kill_remote(wdb);
655 // commit_transaction() and cancel_transaction() should throw
656 // InvalidOperationError because we aren't in a transaction.
657 TEST_EXCEPTION(Xapian::InvalidOperationError,
658 wdb.commit_transaction());
660 TEST_EXCEPTION(Xapian::InvalidOperationError,
661 wdb.cancel_transaction());
663 // begin_transaction() only sets state locally so works.
664 wdb.begin_transaction();
665 // commit_transaction() should only communicate with the server if
666 // there are changes in the transaction.
667 wdb.commit_transaction();
669 wdb.begin_transaction();
670 // cancel_transaction() should only communicate with the server if
671 // there are changes in the transaction.
672 wdb.cancel_transaction();
676 Xapian::WritableDatabase wdb = get_writable_database();
677 wdb.begin_transaction();
678 wdb.add_document(Xapian::Document());
679 kill_remote(wdb);
680 // With a transaction active, commit_transaction() should fail with
681 // NetworkError.
682 TEST_EXCEPTION(Xapian::NetworkError,
683 wdb.commit_transaction());
687 Xapian::WritableDatabase wdb = get_writable_database();
688 wdb.begin_transaction();
689 wdb.add_document(Xapian::Document());
690 kill_remote(wdb);
691 // With a transaction active, cancel_transaction() should fail with
692 // NetworkError.
693 TEST_EXCEPTION(Xapian::NetworkError,
694 wdb.cancel_transaction());
698 // Test WritableDatabase methods.
699 DEFINE_TESTCASE(remotefailure7, remotetcp) {
700 #ifdef __MINGW32__
701 XFAIL("Remote failure handling or testing is buggy on __MINGW32__");
702 #endif
703 Xapian::WritableDatabase db(get_writable_database());
704 db.add_document(Xapian::Document());
705 kill_remote(db);
707 // We have a pending change from before the kill so this should fail.
708 TEST_EXCEPTION(Xapian::NetworkError,
709 db.commit());
710 TEST_EXCEPTION(Xapian::NetworkError,
711 db.add_document(Xapian::Document()));
712 TEST_EXCEPTION(Xapian::NetworkError,
713 db.delete_document(1));
714 TEST_EXCEPTION(Xapian::NetworkError,
715 db.replace_document(1, Xapian::Document()));
716 TEST_EXCEPTION(Xapian::NetworkError,
717 db.replace_document(2, Xapian::Document()));
718 TEST_EXCEPTION(Xapian::NetworkError,
719 db.replace_document("Qi", Xapian::Document()));
722 // Test spelling related methods.
723 DEFINE_TESTCASE(remotefailure8, remotetcp) {
724 #ifdef __MINGW32__
725 XFAIL("Remote failure handling or testing is buggy on __MINGW32__");
726 #endif
727 Xapian::WritableDatabase db(get_writable_database());
728 db.add_spelling("pneumatic");
729 db.add_spelling("pneumonia");
730 kill_remote(db);
732 TEST_EXCEPTION(Xapian::NetworkError,
733 db.add_spelling("penmanship"));
734 TEST_EXCEPTION(Xapian::NetworkError,
735 db.remove_spelling("pneumatic"));
736 // These methods aren't implemented for remote databases - they're no-ops
737 // which don't fail even when we kill the remote server. Once remote
738 // spelling suggestions are working we can uncomment them.
739 // TEST_EXCEPTION(Xapian::NetworkError,
740 // db.get_spelling_suggestion("newmonia"));
741 // TEST_EXCEPTION(Xapian::NetworkError,
742 // db.spellings_begin());
745 // Test synonym related methods.
746 DEFINE_TESTCASE(remotefailure9, remotetcp) {
747 #ifdef __MINGW32__
748 XFAIL("Remote failure handling or testing is buggy on __MINGW32__");
749 #endif
750 Xapian::WritableDatabase db(get_writable_database());
751 db.add_synonym("color", "colour");
752 db.add_synonym("honor", "honour");
753 kill_remote(db);
755 TEST_EXCEPTION(Xapian::NetworkError,
756 db.add_synonym("behavior", "behaviour"));
757 TEST_EXCEPTION(Xapian::NetworkError,
758 db.remove_synonym("honor", "honour"));
759 TEST_EXCEPTION(Xapian::NetworkError,
760 db.clear_synonyms("honor"));
761 TEST_EXCEPTION(Xapian::NetworkError,
762 db.synonyms_begin("color"));
763 TEST_EXCEPTION(Xapian::NetworkError,
764 db.synonym_keys_begin());
767 // Test metadata related methods.
768 DEFINE_TESTCASE(remotefailure10, remotetcp) {
769 #ifdef __MINGW32__
770 XFAIL("Remote failure handling or testing is buggy on __MINGW32__");
771 #endif
772 Xapian::WritableDatabase db(get_writable_database());
773 db.set_metadata("foo", "FOO");
774 db.set_metadata("bar", "BAR");
775 kill_remote(db);
777 TEST_EXCEPTION(Xapian::NetworkError,
778 db.set_metadata("test", "TEST"));
779 TEST_EXCEPTION(Xapian::NetworkError,
780 db.get_metadata("foo"));
781 TEST_EXCEPTION(Xapian::NetworkError,
782 db.get_metadata("bar"));
783 TEST_EXCEPTION(Xapian::NetworkError,
784 db.metadata_keys_begin());