[ci] Fix clang-santisers job for GHA change
[xapian.git] / xapian-core / tests / api_closedb.cc
blobe9262dafb8866fb80501f73b2c71ab8bdb1ea907
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 __WIN32__
566 // Calling GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid) to kill
567 // a whole process group fails under Wine with ERROR_INVALID_HANDLE.
568 // We haven't managed to reduce a testcase for this.
569 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
570 SKIP_TEST("This testcase doesn't work under Wine");
571 #endif
572 Xapian::Database db(get_database("apitest_simpledata"));
573 remotefailure1_iterators iters;
575 // Run the test, checking that we get no exceptions.
576 iters.setup(db);
577 int exception_count = iters.perform();
578 TEST_EQUAL(exception_count, 0);
580 // Setup for the next test.
581 iters.setup(db);
583 // Simulate remote server failure.
584 kill_remote(db);
586 // Dup stdout to the fds which the database was using, to try to catch
587 // issues with lingering references to closed fds.
588 vector<int> fds;
589 for (int i = 0; i != 6; ++i) {
590 fds.push_back(dup(1));
593 // Run the test again, checking that we get some "NetworkError" exceptions.
594 exception_count = iters.perform();
595 TEST_NOT_EQUAL(exception_count, 0);
597 // get_description() shouldn't throw an exception. Actually do something
598 // with the description, in case this method is marked as "pure" in the
599 // future.
600 TEST(!db.get_description().empty());
602 for (int fd : fds) {
603 close(fd);
607 // There's no remotefailure2 plus other gaps in the numbering - these testcases
608 // are adapted versions of the closedb testcases, but some closedb testcases
609 // don't make sense to convert.
611 /// Check API methods which might either work or throw an exception.
612 DEFINE_TESTCASE(remotefailure3, remotetcp) {
613 #ifdef __WIN32__
614 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
615 SKIP_TEST("This testcase doesn't work under Wine");
616 #endif
617 Xapian::Database db(get_database("etext"));
618 const string & uuid = db.get_uuid();
619 kill_remote(db);
620 try {
621 TEST_EQUAL(db.get_uuid(), uuid);
622 } catch (const Xapian::NetworkError&) {
624 try {
625 TEST(db.has_positions());
626 } catch (const Xapian::NetworkError&) {
628 try {
629 TEST_EQUAL(db.get_doccount(), 566);
630 } catch (const Xapian::NetworkError&) {
632 try {
633 TEST_EQUAL(db.get_lastdocid(), 566);
634 } catch (const Xapian::NetworkError&) {
636 try {
637 TEST_REL(db.get_doclength_lower_bound(), <, db.get_avlength());
638 } catch (const Xapian::NetworkError&) {
640 try {
641 TEST_REL(db.get_doclength_upper_bound(), >, db.get_avlength());
642 } catch (const Xapian::NetworkError&) {
644 try {
645 TEST(db.get_wdf_upper_bound("king"));
646 } catch (const Xapian::NetworkError&) {
648 TEST_EXCEPTION(Xapian::NetworkError, db.keep_alive());
651 /// Test the effects of remote server failure on transactions
652 DEFINE_TESTCASE(remotefailure5, remotetcp) {
653 #ifdef __WIN32__
654 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
655 SKIP_TEST("This testcase doesn't work under Wine");
656 #endif
658 Xapian::WritableDatabase wdb = get_writable_database();
659 kill_remote(wdb);
661 // commit_transaction() and cancel_transaction() should throw
662 // InvalidOperationError because we aren't in a transaction.
663 TEST_EXCEPTION(Xapian::InvalidOperationError,
664 wdb.commit_transaction());
666 TEST_EXCEPTION(Xapian::InvalidOperationError,
667 wdb.cancel_transaction());
669 // begin_transaction() only sets state locally so works.
670 wdb.begin_transaction();
671 // commit_transaction() should only communicate with the server if
672 // there are changes in the transaction.
673 wdb.commit_transaction();
675 wdb.begin_transaction();
676 // cancel_transaction() should only communicate with the server if
677 // there are changes in the transaction.
678 wdb.cancel_transaction();
682 Xapian::WritableDatabase wdb = get_writable_database();
683 wdb.begin_transaction();
684 wdb.add_document(Xapian::Document());
685 kill_remote(wdb);
686 // With a transaction active, commit_transaction() should fail with
687 // NetworkError.
688 TEST_EXCEPTION(Xapian::NetworkError,
689 wdb.commit_transaction());
693 Xapian::WritableDatabase wdb = get_writable_database();
694 wdb.begin_transaction();
695 wdb.add_document(Xapian::Document());
696 kill_remote(wdb);
697 // With a transaction active, cancel_transaction() should fail with
698 // NetworkError.
699 TEST_EXCEPTION(Xapian::NetworkError,
700 wdb.cancel_transaction());
704 // Test WritableDatabase methods.
705 DEFINE_TESTCASE(remotefailure7, remotetcp) {
706 #ifdef __WIN32__
707 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
708 SKIP_TEST("This testcase doesn't work under Wine");
709 #endif
710 Xapian::WritableDatabase db(get_writable_database());
711 db.add_document(Xapian::Document());
712 kill_remote(db);
714 // We have a pending change from before the kill so this should fail.
715 TEST_EXCEPTION(Xapian::NetworkError,
716 db.commit());
717 TEST_EXCEPTION(Xapian::NetworkError,
718 db.add_document(Xapian::Document()));
719 TEST_EXCEPTION(Xapian::NetworkError,
720 db.delete_document(1));
721 TEST_EXCEPTION(Xapian::NetworkError,
722 db.replace_document(1, Xapian::Document()));
723 TEST_EXCEPTION(Xapian::NetworkError,
724 db.replace_document(2, Xapian::Document()));
725 TEST_EXCEPTION(Xapian::NetworkError,
726 db.replace_document("Qi", Xapian::Document()));
729 // Test spelling related methods.
730 DEFINE_TESTCASE(remotefailure8, remotetcp) {
731 #ifdef __WIN32__
732 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
733 SKIP_TEST("This testcase doesn't work under Wine");
734 #endif
735 Xapian::WritableDatabase db(get_writable_database());
736 db.add_spelling("pneumatic");
737 db.add_spelling("pneumonia");
738 kill_remote(db);
740 TEST_EXCEPTION(Xapian::NetworkError,
741 db.add_spelling("penmanship"));
742 TEST_EXCEPTION(Xapian::NetworkError,
743 db.remove_spelling("pneumatic"));
744 // These methods aren't implemented for remote databases - they're no-ops
745 // which don't fail even when we kill the remote server. Once remote
746 // spelling suggestions are working we can uncomment them.
747 // TEST_EXCEPTION(Xapian::NetworkError,
748 // db.get_spelling_suggestion("newmonia"));
749 // TEST_EXCEPTION(Xapian::NetworkError,
750 // db.spellings_begin());
753 // Test synonym related methods.
754 DEFINE_TESTCASE(remotefailure9, remotetcp) {
755 #ifdef __WIN32__
756 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
757 SKIP_TEST("This testcase doesn't work under Wine");
758 #endif
759 Xapian::WritableDatabase db(get_writable_database());
760 db.add_synonym("color", "colour");
761 db.add_synonym("honor", "honour");
762 kill_remote(db);
764 TEST_EXCEPTION(Xapian::NetworkError,
765 db.add_synonym("behavior", "behaviour"));
766 TEST_EXCEPTION(Xapian::NetworkError,
767 db.remove_synonym("honor", "honour"));
768 TEST_EXCEPTION(Xapian::NetworkError,
769 db.clear_synonyms("honor"));
770 TEST_EXCEPTION(Xapian::NetworkError,
771 db.synonyms_begin("color"));
772 TEST_EXCEPTION(Xapian::NetworkError,
773 db.synonym_keys_begin());
776 // Test metadata related methods.
777 DEFINE_TESTCASE(remotefailure10, remotetcp) {
778 #ifdef __WIN32__
779 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
780 SKIP_TEST("This testcase doesn't work under Wine");
781 #endif
782 Xapian::WritableDatabase db(get_writable_database());
783 db.set_metadata("foo", "FOO");
784 db.set_metadata("bar", "BAR");
785 kill_remote(db);
787 TEST_EXCEPTION(Xapian::NetworkError,
788 db.set_metadata("test", "TEST"));
789 TEST_EXCEPTION(Xapian::NetworkError,
790 db.get_metadata("foo"));
791 TEST_EXCEPTION(Xapian::NetworkError,
792 db.get_metadata("bar"));
793 TEST_EXCEPTION(Xapian::NetworkError,
794 db.metadata_keys_begin());