2 * @brief Tests of closing databases.
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
24 #include "api_closedb.h"
28 #include "safeunistd.h"
31 #include "testutils.h"
35 #define COUNT_EXCEPTION(CODE, EXCEPTION) \
38 } catch (const Xapian::EXCEPTION&) { \
42 #define COUNT_CLOSED(CODE) COUNT_EXCEPTION(CODE, DatabaseClosedError)
44 // Iterators used by closedb1.
45 struct closedb1_iterators
{
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_
) {
62 // Set up the iterators for the test.
63 pl1
= db
.postlist_begin("paragraph");
64 pl2
= db
.postlist_begin("this");
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");
77 int exception_count
= 0;
79 // Getting a document may throw.
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
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.
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));
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));
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));
159 COUNT_CLOSED(TEST_EQUAL(*atl1
, "that"));
160 COUNT_CLOSED(TEST_EQUAL(atl1
.get_termfreq(), 2));
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.
179 int exception_count
= iters
.perform();
180 TEST_EQUAL(exception_count
, 0);
182 // Setup for the next test.
185 // Close the database.
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).
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
206 TEST(!db
.get_description().empty());
208 // Calling close repeatedly is okay.
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"),
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();
235 TEST_EQUAL(db
.get_uuid(), uuid
);
236 } catch (const Xapian::DatabaseClosedError
&) {
239 TEST(db
.has_positions());
240 } catch (const Xapian::DatabaseClosedError
&) {
243 TEST_EQUAL(db
.get_doccount(), 566);
244 } catch (const Xapian::DatabaseClosedError
&) {
247 TEST_EQUAL(db
.get_lastdocid(), 566);
248 } catch (const Xapian::DatabaseClosedError
&) {
251 TEST_REL(db
.get_doclength_lower_bound(), <, db
.get_avlength());
252 } catch (const Xapian::DatabaseClosedError
&) {
255 TEST_REL(db
.get_doclength_upper_bound(), >, db
.get_avlength());
256 } catch (const Xapian::DatabaseClosedError
&) {
259 TEST(db
.get_wdf_upper_bound("king"));
260 } catch (const Xapian::DatabaseClosedError
&) {
263 // For non-remote databases, keep_alive() is a no-op anyway.
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);
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);
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);
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();
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.
317 wdb
.begin_transaction();
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();
330 TEST_EXCEPTION(Xapian::InvalidOperationError
,
331 wdb
.cancel_transaction());
334 wdb
.begin_transaction();
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"));
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());
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.
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");
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");
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");
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
{
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_
) {
454 // Set up the iterators for the test.
455 pl1
= db
.postlist_begin("paragraph");
456 pl2
= db
.postlist_begin("this");
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");
469 int exception_count
= 0;
471 // Getting a document may throw.
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.
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));
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));
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));
550 COUNT_NETWORK(TEST_EQUAL(*atl1
, "that"));
551 COUNT_NETWORK(TEST_EQUAL(atl1
.get_termfreq(), 2));
556 COUNT_NETWORK(TEST_EQUAL(*pil1
, 28));
559 return exception_count
;
563 // Test for a remote server failing.
564 DEFINE_TESTCASE(remotefailure1
, remotetcp
) {
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");
572 Xapian::Database
db(get_database("apitest_simpledata"));
573 remotefailure1_iterators iters
;
575 // Run the test, checking that we get no exceptions.
577 int exception_count
= iters
.perform();
578 TEST_EQUAL(exception_count
, 0);
580 // Setup for the next test.
583 // Simulate remote server failure.
586 // Dup stdout to the fds which the database was using, to try to catch
587 // issues with lingering references to closed 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
600 TEST(!db
.get_description().empty());
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
) {
614 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
615 SKIP_TEST("This testcase doesn't work under Wine");
617 Xapian::Database
db(get_database("etext"));
618 const string
& uuid
= db
.get_uuid();
621 TEST_EQUAL(db
.get_uuid(), uuid
);
622 } catch (const Xapian::NetworkError
&) {
625 TEST(db
.has_positions());
626 } catch (const Xapian::NetworkError
&) {
629 TEST_EQUAL(db
.get_doccount(), 566);
630 } catch (const Xapian::NetworkError
&) {
633 TEST_EQUAL(db
.get_lastdocid(), 566);
634 } catch (const Xapian::NetworkError
&) {
637 TEST_REL(db
.get_doclength_lower_bound(), <, db
.get_avlength());
638 } catch (const Xapian::NetworkError
&) {
641 TEST_REL(db
.get_doclength_upper_bound(), >, db
.get_avlength());
642 } catch (const Xapian::NetworkError
&) {
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
) {
654 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
655 SKIP_TEST("This testcase doesn't work under Wine");
658 Xapian::WritableDatabase wdb
= get_writable_database();
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());
686 // With a transaction active, commit_transaction() should fail with
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());
697 // With a transaction active, cancel_transaction() should fail with
699 TEST_EXCEPTION(Xapian::NetworkError
,
700 wdb
.cancel_transaction());
704 // Test WritableDatabase methods.
705 DEFINE_TESTCASE(remotefailure7
, remotetcp
) {
707 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
708 SKIP_TEST("This testcase doesn't work under Wine");
710 Xapian::WritableDatabase
db(get_writable_database());
711 db
.add_document(Xapian::Document());
714 // We have a pending change from before the kill so this should fail.
715 TEST_EXCEPTION(Xapian::NetworkError
,
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
) {
732 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
733 SKIP_TEST("This testcase doesn't work under Wine");
735 Xapian::WritableDatabase
db(get_writable_database());
736 db
.add_spelling("pneumatic");
737 db
.add_spelling("pneumonia");
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
) {
756 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
757 SKIP_TEST("This testcase doesn't work under Wine");
759 Xapian::WritableDatabase
db(get_writable_database());
760 db
.add_synonym("color", "colour");
761 db
.add_synonym("honor", "honour");
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
) {
779 if (getenv("XAPIAN_TESTSUITE_RUNNING_UNDER_WINE"))
780 SKIP_TEST("This testcase doesn't work under Wine");
782 Xapian::WritableDatabase
db(get_writable_database());
783 db
.set_metadata("foo", "FOO");
784 db
.set_metadata("bar", "BAR");
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());