omegatest: Use test_scriptindex_error in another case
[xapian.git] / xapian-core / tests / api_none.cc
blobfc25141d9d0907f84ca2fd37a6ebb0a53556f279
1 /** @file
2 * @brief tests which don't need a backend
3 */
4 /* Copyright (C) 2009 Richard Boulton
5 * Copyright (C) 2009,2010,2011,2013,2014,2015,2016,2017,2018 Olly Betts
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
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
20 * USA
23 #include <config.h>
25 #include "api_none.h"
27 #define XAPIAN_DEPRECATED(D) D
28 #include <xapian.h>
30 #include "apitest.h"
31 #include "str.h"
32 #include "testsuite.h"
33 #include "testutils.h"
35 using namespace std;
37 // Check the version functions give consistent results.
38 DEFINE_TESTCASE(version1, !backend) {
39 string version = str(Xapian::major_version());
40 version += '.';
41 version += str(Xapian::minor_version());
42 version += '.';
43 version += str(Xapian::revision());
44 TEST_EQUAL(Xapian::version_string(), version);
47 // Regression test: various methods on Database() used to segfault or cause
48 // division by 0. Fixed in 1.1.4 and 1.0.18. Ticket#415.
49 DEFINE_TESTCASE(nosubdatabases1, !backend) {
50 Xapian::Database db;
51 TEST(db.get_metadata("foo").empty());
52 TEST_EQUAL(db.metadata_keys_begin(), db.metadata_keys_end());
53 TEST_EXCEPTION(Xapian::InvalidOperationError, db.termlist_begin(1));
54 TEST_EQUAL(db.allterms_begin(), db.allterms_end());
55 TEST_EQUAL(db.allterms_begin("foo"), db.allterms_end("foo"));
56 TEST_EXCEPTION(Xapian::InvalidOperationError, db.positionlist_begin(1, "foo"));
57 TEST_EQUAL(db.get_lastdocid(), 0);
58 TEST_EQUAL(db.valuestream_begin(7), db.valuestream_end(7));
59 TEST_EXCEPTION(Xapian::InvalidOperationError, db.get_doclength(1));
60 TEST_EXCEPTION(Xapian::InvalidOperationError, db.get_unique_terms(1));
61 TEST_EXCEPTION(Xapian::InvalidOperationError, db.get_document(1));
64 /// Feature test for Document::add_boolean_term(), new in 1.0.18/1.1.4.
65 DEFINE_TESTCASE(document1, !backend) {
66 Xapian::Document doc;
67 doc.add_boolean_term("Hxapian.org");
68 TEST_EQUAL(doc.termlist_count(), 1);
69 Xapian::TermIterator t = doc.termlist_begin();
70 TEST(t != doc.termlist_end());
71 TEST_EQUAL(*t, "Hxapian.org");
72 TEST_EQUAL(t.get_wdf(), 0);
73 TEST(++t == doc.termlist_end());
74 doc.remove_term("Hxapian.org");
75 TEST_EQUAL(doc.termlist_count(), 0);
76 TEST(doc.termlist_begin() == doc.termlist_end());
79 /// Regression test - the docid wasn't initialised prior to 1.0.22/1.2.4.
80 DEFINE_TESTCASE(document2, !backend) {
81 Xapian::Document doc;
82 // The return value is uninitialised, so running under valgrind this
83 // will fail reliably prior to the fix.
84 TEST_EQUAL(doc.get_docid(), 0);
87 /// Feature tests for Document::clear_terms().
88 DEFINE_TESTCASE(documentclearterms1, !backend) {
90 Xapian::Document doc;
91 doc.add_boolean_term("Hlocalhost");
92 doc.add_term("hello");
93 doc.add_term("there", 2);
94 doc.add_posting("positional", 1);
95 doc.add_posting("information", 2, 3);
96 TEST_EQUAL(doc.termlist_count(), 5);
97 TEST(doc.termlist_begin() != doc.termlist_end());
98 doc.clear_terms();
99 TEST_EQUAL(doc.termlist_count(), 0);
100 TEST(doc.termlist_begin() == doc.termlist_end());
101 // Test clear_terms() when there are no terms.
102 doc.clear_terms();
103 TEST_EQUAL(doc.termlist_count(), 0);
104 TEST(doc.termlist_begin() == doc.termlist_end());
108 // Test clear_terms() when there have never been any terms.
109 Xapian::Document doc;
110 doc.clear_terms();
111 TEST_EQUAL(doc.termlist_count(), 0);
112 TEST(doc.termlist_begin() == doc.termlist_end());
116 /// Feature tests for Document::clear_values().
117 DEFINE_TESTCASE(documentclearvalues1, !backend) {
119 Xapian::Document doc;
120 doc.add_value(37, "hello");
121 doc.add_value(42, "world");
122 TEST_EQUAL(doc.values_count(), 2);
123 TEST(doc.values_begin() != doc.values_end());
124 doc.clear_values();
125 TEST_EQUAL(doc.values_count(), 0);
126 TEST(doc.values_begin() == doc.values_end());
127 // Test clear_values() when there are no values.
128 doc.clear_values();
129 TEST_EQUAL(doc.values_count(), 0);
130 TEST(doc.values_begin() == doc.values_end());
134 // Test clear_values() when there have never been any values.
135 Xapian::Document doc;
136 doc.clear_values();
137 TEST_EQUAL(doc.values_count(), 0);
138 TEST(doc.termlist_begin() == doc.termlist_end());
142 /// Feature tests for errors for empty terms.
143 DEFINE_TESTCASE(documentemptyterm1, !backend) {
144 Xapian::Document doc;
145 TEST_EXCEPTION(Xapian::InvalidArgumentError,
146 doc.add_boolean_term(string()));
147 TEST_EXCEPTION(Xapian::InvalidArgumentError,
148 doc.add_term(string()));
149 TEST_EXCEPTION(Xapian::InvalidArgumentError,
150 doc.add_posting(string(), 1));
151 TEST_EXCEPTION(Xapian::InvalidArgumentError,
152 doc.add_posting(string(), 2, 3));
153 TEST_EXCEPTION(Xapian::InvalidArgumentError,
154 doc.remove_term(string()));
155 TEST_EXCEPTION(Xapian::InvalidArgumentError,
156 doc.remove_posting(string(), 1));
157 TEST_EXCEPTION(Xapian::InvalidArgumentError,
158 doc.remove_posting(string(), 2, 3));
159 TEST_EXCEPTION(Xapian::InvalidArgumentError,
160 doc.remove_postings(string(), 2, 3));
161 TEST_EXCEPTION(Xapian::InvalidArgumentError,
162 doc.remove_postings(string(), 2, 3, 4));
165 DEFINE_TESTCASE(emptyquery4, !backend) {
166 // Test we get an empty query from applying any of the following ops to
167 // an empty list of subqueries.
168 Xapian::Query q;
169 TEST(Xapian::Query(q.OP_AND, &q, &q).empty());
170 TEST(Xapian::Query(q.OP_OR, &q, &q).empty());
171 TEST(Xapian::Query(q.OP_AND_NOT, &q, &q).empty());
172 TEST(Xapian::Query(q.OP_XOR, &q, &q).empty());
173 TEST(Xapian::Query(q.OP_AND_MAYBE, &q, &q).empty());
174 TEST(Xapian::Query(q.OP_FILTER, &q, &q).empty());
175 TEST(Xapian::Query(q.OP_NEAR, &q, &q).empty());
176 TEST(Xapian::Query(q.OP_PHRASE, &q, &q).empty());
177 TEST(Xapian::Query(q.OP_ELITE_SET, &q, &q).empty());
178 TEST(Xapian::Query(q.OP_SYNONYM, &q, &q).empty());
179 TEST(Xapian::Query(q.OP_MAX, &q, &q).empty());
182 DEFINE_TESTCASE(singlesubquery1, !backend) {
183 // Test that we get just the subquery if we apply any of the following
184 // ops to just that subquery.
185 #define singlesubquery1_(OP) \
186 TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
187 "Query(test)")
188 Xapian::Query q[1] = { Xapian::Query("test") };
189 singlesubquery1_(OP_AND);
190 singlesubquery1_(OP_OR);
191 singlesubquery1_(OP_AND_NOT);
192 singlesubquery1_(OP_XOR);
193 singlesubquery1_(OP_AND_MAYBE);
194 singlesubquery1_(OP_FILTER);
195 singlesubquery1_(OP_NEAR);
196 singlesubquery1_(OP_PHRASE);
197 singlesubquery1_(OP_ELITE_SET);
198 singlesubquery1_(OP_SYNONYM);
199 singlesubquery1_(OP_MAX);
202 DEFINE_TESTCASE(singlesubquery2, !backend) {
203 // Like the previous test, but using MatchNothing as the subquery.
204 #define singlesubquery2_(OP) \
205 TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
206 "Query()")
207 Xapian::Query q[1] = { Xapian::Query::MatchNothing };
208 singlesubquery2_(OP_AND);
209 singlesubquery2_(OP_OR);
210 singlesubquery2_(OP_AND_NOT);
211 singlesubquery2_(OP_XOR);
212 singlesubquery2_(OP_AND_MAYBE);
213 singlesubquery2_(OP_FILTER);
214 singlesubquery2_(OP_NEAR);
215 singlesubquery2_(OP_PHRASE);
216 singlesubquery2_(OP_ELITE_SET);
217 singlesubquery2_(OP_SYNONYM);
218 singlesubquery2_(OP_MAX);
221 DEFINE_TESTCASE(singlesubquery3, !backend) {
222 // Like the previous test, but using MatchAll as the subquery.
223 #define singlesubquery3_(OP) \
224 TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
225 "Query(<alldocuments>)")
226 Xapian::Query q[1] = { Xapian::Query::MatchAll };
227 singlesubquery3_(OP_AND);
228 singlesubquery3_(OP_OR);
229 singlesubquery3_(OP_AND_NOT);
230 singlesubquery3_(OP_XOR);
231 singlesubquery3_(OP_AND_MAYBE);
232 singlesubquery3_(OP_FILTER);
233 // OP_NEAR and OP_PHRASE over MatchAll doesn't really make sense.
234 singlesubquery3_(OP_ELITE_SET);
235 singlesubquery3_(OP_SYNONYM);
236 singlesubquery3_(OP_MAX);
239 /// Check we no longer combine wqf for same term at the same position.
240 DEFINE_TESTCASE(combinewqfnomore1, !backend) {
241 Xapian::Query q(Xapian::Query::OP_OR,
242 Xapian::Query("beer", 1, 1),
243 Xapian::Query("beer", 1, 1));
244 // Prior to 1.3.0, we would have given beer@2, but we decided that wasn't
245 // really useful or helpful.
246 TEST_EQUAL(q.get_description(), "Query((beer@1 OR beer@1))");
249 class DestroyedFlag {
250 bool & destroyed;
252 public:
253 DestroyedFlag(bool & destroyed_) : destroyed(destroyed_) {
254 destroyed = false;
257 ~DestroyedFlag() {
258 destroyed = true;
262 class TestRangeProcessor : public Xapian::RangeProcessor {
263 DestroyedFlag destroyed;
265 public:
266 TestRangeProcessor(bool & destroyed_)
267 : Xapian::RangeProcessor(0), destroyed(destroyed_) { }
269 Xapian::Query operator()(const std::string&, const std::string&)
271 return Xapian::Query::MatchAll;
275 /// Check reference counting of user-subclassable classes.
276 DEFINE_TESTCASE(subclassablerefcount1, !backend) {
277 bool gone_auto, gone;
279 // Simple test of release().
281 Xapian::RangeProcessor * rp = new TestRangeProcessor(gone);
282 TEST(!gone);
283 Xapian::QueryParser qp;
284 qp.add_rangeprocessor(rp->release());
285 TEST(!gone);
287 TEST(gone);
289 // Check a second call to release() has no effect.
291 Xapian::RangeProcessor * rp = new TestRangeProcessor(gone);
292 TEST(!gone);
293 Xapian::QueryParser qp;
294 qp.add_rangeprocessor(rp->release());
295 rp->release();
296 TEST(!gone);
298 TEST(gone);
300 // Test reference counting works, and that a RangeProcessor with automatic
301 // storage works OK.
303 TestRangeProcessor rp_auto(gone_auto);
304 TEST(!gone_auto);
306 Xapian::QueryParser qp1;
308 Xapian::QueryParser qp2;
309 Xapian::RangeProcessor * rp;
310 rp = new TestRangeProcessor(gone);
311 TEST(!gone);
312 qp1.add_rangeprocessor(rp->release());
313 TEST(!gone);
314 qp2.add_rangeprocessor(rp);
315 TEST(!gone);
316 qp2.add_rangeprocessor(&rp_auto);
317 TEST(!gone);
318 TEST(!gone_auto);
320 TEST(!gone);
322 TEST(gone);
323 TEST(!gone_auto);
325 TEST(gone_auto);
327 // Regression test for initial implementation, where ~opt_intrusive_ptr()
328 // checked the reference of the object, which may have already been deleted
329 // if it wasn't been reference counted.
331 Xapian::QueryParser qp;
333 Xapian::RangeProcessor * rp = new TestRangeProcessor(gone);
334 TEST(!gone);
335 qp.add_rangeprocessor(rp);
336 delete rp;
337 TEST(gone);
339 // At the end of this block, qp is destroyed, but mustn't dereference
340 // the pointer it has to rp. If it does, that should get caught
341 // when tests are run under valgrind.
345 class TestFieldProcessor : public Xapian::FieldProcessor {
346 DestroyedFlag destroyed;
348 public:
349 TestFieldProcessor(bool & destroyed_) : destroyed(destroyed_) { }
351 Xapian::Query operator()(const string &str) {
352 return Xapian::Query(str);
356 /// Check reference counting of user-subclassable classes.
357 DEFINE_TESTCASE(subclassablerefcount2, !backend) {
358 bool gone_auto, gone;
360 // Simple test of release().
362 Xapian::FieldProcessor * proc = new TestFieldProcessor(gone);
363 TEST(!gone);
364 Xapian::QueryParser qp;
365 qp.add_prefix("foo", proc->release());
366 TEST(!gone);
368 TEST(gone);
370 // Check a second call to release() has no effect.
372 Xapian::FieldProcessor * proc = new TestFieldProcessor(gone);
373 TEST(!gone);
374 Xapian::QueryParser qp;
375 qp.add_prefix("foo", proc->release());
376 proc->release();
377 TEST(!gone);
379 TEST(gone);
381 // Test reference counting works, and that a FieldProcessor with automatic
382 // storage works OK.
384 TestFieldProcessor proc_auto(gone_auto);
385 TEST(!gone_auto);
387 Xapian::QueryParser qp1;
389 Xapian::QueryParser qp2;
390 Xapian::FieldProcessor * proc;
391 proc = new TestFieldProcessor(gone);
392 TEST(!gone);
393 qp1.add_prefix("foo", proc->release());
394 TEST(!gone);
395 qp2.add_prefix("foo", proc);
396 TEST(!gone);
397 qp2.add_prefix("bar", &proc_auto);
398 TEST(!gone);
399 TEST(!gone_auto);
401 TEST(!gone);
403 TEST(gone);
404 TEST(!gone_auto);
406 TEST(gone_auto);
409 class TestMatchSpy : public Xapian::MatchSpy {
410 DestroyedFlag destroyed;
412 public:
413 TestMatchSpy(bool & destroyed_) : destroyed(destroyed_) { }
415 void operator()(const Xapian::Document &, double) { }
418 /// Check reference counting of MatchSpy.
419 DEFINE_TESTCASE(subclassablerefcount3, backend) {
420 Xapian::Database db = get_database("apitest_simpledata");
422 bool gone_auto, gone;
424 // Simple test of release().
426 Xapian::MatchSpy * spy = new TestMatchSpy(gone);
427 TEST(!gone);
428 Xapian::Enquire enquire(db);
429 enquire.add_matchspy(spy->release());
430 TEST(!gone);
432 TEST(gone);
434 // Check a second call to release() has no effect.
436 Xapian::MatchSpy * spy = new TestMatchSpy(gone);
437 TEST(!gone);
438 Xapian::Enquire enquire(db);
439 enquire.add_matchspy(spy->release());
440 spy->release();
441 TEST(!gone);
443 TEST(gone);
445 // Test reference counting works, and that a MatchSpy with automatic
446 // storage works OK.
448 TestMatchSpy spy_auto(gone_auto);
449 TEST(!gone_auto);
451 Xapian::Enquire enq1(db);
453 Xapian::Enquire enq2(db);
454 Xapian::MatchSpy * spy;
455 spy = new TestMatchSpy(gone);
456 TEST(!gone);
457 enq1.add_matchspy(spy->release());
458 TEST(!gone);
459 enq2.add_matchspy(spy);
460 TEST(!gone);
461 enq2.add_matchspy(&spy_auto);
462 TEST(!gone);
463 TEST(!gone_auto);
465 TEST(!gone);
467 TEST(gone);
468 TEST(!gone_auto);
470 TEST(gone_auto);
473 class TestStopper : public Xapian::Stopper {
474 DestroyedFlag destroyed;
476 public:
477 TestStopper(bool & destroyed_) : destroyed(destroyed_) { }
479 bool operator()(const std::string&) const { return true; }
482 /// Check reference counting of Stopper with QueryParser.
483 DEFINE_TESTCASE(subclassablerefcount4, !backend) {
484 bool gone_auto, gone;
486 // Simple test of release().
488 Xapian::Stopper * stopper = new TestStopper(gone);
489 TEST(!gone);
490 Xapian::QueryParser qp;
491 qp.set_stopper(stopper->release());
492 TEST(!gone);
494 TEST(gone);
496 // Test that setting a new stopper causes the previous one to be released.
498 bool gone0;
499 Xapian::Stopper * stopper0 = new TestStopper(gone0);
500 TEST(!gone0);
501 Xapian::QueryParser qp;
502 qp.set_stopper(stopper0->release());
503 TEST(!gone0);
505 Xapian::Stopper * stopper = new TestStopper(gone);
506 TEST(!gone);
507 qp.set_stopper(stopper->release());
508 TEST(gone0);
509 TEST(!gone);
511 TEST(gone);
513 // Check a second call to release() has no effect.
515 Xapian::Stopper * stopper = new TestStopper(gone);
516 TEST(!gone);
517 Xapian::QueryParser qp;
518 qp.set_stopper(stopper->release());
519 stopper->release();
520 TEST(!gone);
522 TEST(gone);
524 // Test reference counting works, and that a Stopper with automatic
525 // storage works OK.
527 TestStopper stopper_auto(gone_auto);
528 TEST(!gone_auto);
530 Xapian::QueryParser qp1;
532 Xapian::QueryParser qp2;
533 Xapian::Stopper * stopper;
534 stopper = new TestStopper(gone);
535 TEST(!gone);
536 qp1.set_stopper(stopper->release());
537 TEST(!gone);
538 qp2.set_stopper(stopper);
539 TEST(!gone);
540 qp2.set_stopper(&stopper_auto);
541 TEST(!gone);
542 TEST(!gone_auto);
544 TEST(!gone);
546 TEST(gone);
547 TEST(!gone_auto);
549 TEST(gone_auto);
552 /// Check reference counting of Stopper with TermGenerator.
553 DEFINE_TESTCASE(subclassablerefcount5, !backend) {
554 bool gone_auto, gone;
556 // Simple test of release().
558 Xapian::Stopper * stopper = new TestStopper(gone);
559 TEST(!gone);
560 Xapian::TermGenerator indexer;
561 indexer.set_stopper(stopper->release());
562 TEST(!gone);
564 TEST(gone);
566 // Test that setting a new stopper causes the previous one to be released.
568 bool gone0;
569 Xapian::Stopper * stopper0 = new TestStopper(gone0);
570 TEST(!gone0);
571 Xapian::TermGenerator indexer;
572 indexer.set_stopper(stopper0->release());
573 TEST(!gone0);
575 Xapian::Stopper * stopper = new TestStopper(gone);
576 TEST(!gone);
577 indexer.set_stopper(stopper->release());
578 TEST(gone0);
579 TEST(!gone);
581 TEST(gone);
583 // Check a second call to release() has no effect.
585 Xapian::Stopper * stopper = new TestStopper(gone);
586 TEST(!gone);
587 Xapian::TermGenerator indexer;
588 indexer.set_stopper(stopper->release());
589 stopper->release();
590 TEST(!gone);
592 TEST(gone);
594 // Test reference counting works, and that a Stopper with automatic
595 // storage works OK.
597 TestStopper stopper_auto(gone_auto);
598 TEST(!gone_auto);
600 Xapian::TermGenerator indexer1;
602 Xapian::TermGenerator indexer2;
603 Xapian::Stopper * stopper;
604 stopper = new TestStopper(gone);
605 TEST(!gone);
606 indexer1.set_stopper(stopper->release());
607 TEST(!gone);
608 indexer2.set_stopper(stopper);
609 TEST(!gone);
610 indexer2.set_stopper(&stopper_auto);
611 TEST(!gone);
612 TEST(!gone_auto);
614 TEST(!gone);
616 TEST(gone);
617 TEST(!gone_auto);
619 TEST(gone_auto);
622 class TestKeyMaker : public Xapian::KeyMaker {
623 DestroyedFlag destroyed;
625 public:
626 TestKeyMaker(bool & destroyed_) : destroyed(destroyed_) { }
628 string operator()(const Xapian::Document&) const { return string(); }
631 /// Check reference counting of KeyMaker.
632 DEFINE_TESTCASE(subclassablerefcount6, backend) {
633 Xapian::Database db = get_database("apitest_simpledata");
635 bool gone_auto, gone;
637 // Simple test of release().
639 Xapian::KeyMaker * keymaker = new TestKeyMaker(gone);
640 TEST(!gone);
641 Xapian::Enquire enq(db);
642 enq.set_sort_by_key(keymaker->release(), false);
643 TEST(!gone);
645 TEST(gone);
647 // Test that setting a new keymaker causes the previous one to be released.
649 bool gone0;
650 Xapian::KeyMaker * keymaker0 = new TestKeyMaker(gone0);
651 TEST(!gone0);
652 Xapian::Enquire enq(db);
653 enq.set_sort_by_key(keymaker0->release(), false);
654 TEST(!gone0);
656 Xapian::KeyMaker * keymaker = new TestKeyMaker(gone);
657 TEST(!gone);
658 enq.set_sort_by_key_then_relevance(keymaker->release(), false);
659 TEST(gone0);
660 TEST(!gone);
662 TEST(gone);
664 // Check a second call to release() has no effect.
666 Xapian::KeyMaker * keymaker = new TestKeyMaker(gone);
667 TEST(!gone);
668 Xapian::Enquire enq(db);
669 enq.set_sort_by_key(keymaker->release(), false);
670 keymaker->release();
671 TEST(!gone);
673 TEST(gone);
675 // Test reference counting works, and that a KeyMaker with automatic
676 // storage works OK.
678 TestKeyMaker keymaker_auto(gone_auto);
679 TEST(!gone_auto);
681 Xapian::Enquire enq1(db);
683 Xapian::Enquire enq2(db);
684 Xapian::KeyMaker * keymaker;
685 keymaker = new TestKeyMaker(gone);
686 TEST(!gone);
687 enq1.set_sort_by_key(keymaker->release(), false);
688 TEST(!gone);
689 enq2.set_sort_by_relevance_then_key(keymaker, false);
690 TEST(!gone);
691 enq2.set_sort_by_key_then_relevance(&keymaker_auto, false);
692 TEST(!gone);
693 TEST(!gone_auto);
695 TEST(!gone);
697 TEST(gone);
698 TEST(!gone_auto);
700 TEST(gone_auto);
703 class TestExpandDecider : public Xapian::ExpandDecider {
704 DestroyedFlag destroyed;
706 public:
707 TestExpandDecider(bool & destroyed_) : destroyed(destroyed_) { }
709 bool operator()(const string&) const { return true; }
712 /// Check reference counting of ExpandDecider.
713 DEFINE_TESTCASE(subclassablerefcount7, backend) {
714 Xapian::Database db = get_database("apitest_simpledata");
715 Xapian::Enquire enq(db);
716 Xapian::RSet rset;
717 rset.add_document(1);
719 bool gone_auto, gone;
721 for (int flags = 0;
722 flags <= Xapian::Enquire::INCLUDE_QUERY_TERMS;
723 flags += Xapian::Enquire::INCLUDE_QUERY_TERMS) {
724 // Test of auto lifetime ExpandDecider.
726 TestExpandDecider edecider_auto(gone_auto);
727 TEST(!gone_auto);
728 (void)enq.get_eset(5, rset, 0, &edecider_auto);
729 TEST(!gone_auto);
731 TEST(gone_auto);
733 // Simple test of release().
735 Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
736 TEST(!gone);
737 (void)enq.get_eset(5, rset, 0, edecider);
738 TEST(!gone);
739 delete edecider;
740 TEST(gone);
743 // Test that a released ExpandDecider gets cleaned up by get_eset().
745 Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
746 TEST(!gone);
747 (void)enq.get_eset(5, rset, 0, edecider->release());
748 TEST(gone);
751 // Check a second call to release() has no effect.
753 Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
754 TEST(!gone);
755 edecider->release();
756 TEST(!gone);
757 (void)enq.get_eset(5, rset, 0, edecider->release());
758 TEST(gone);
762 // Test combinations of released/non-released with ExpandDeciderAnd.
764 TestExpandDecider edecider_auto(gone_auto);
765 TEST(!gone_auto);
766 Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
767 TEST(!gone);
768 (void)enq.get_eset(5, rset, 0,
769 (new Xapian::ExpandDeciderAnd(
770 &edecider_auto,
771 edecider->release()))->release());
772 TEST(!gone_auto);
773 TEST(gone);
775 TEST(gone_auto);
777 TestExpandDecider edecider_auto(gone_auto);
778 TEST(!gone_auto);
779 Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
780 TEST(!gone);
781 (void)enq.get_eset(5, rset, 0,
782 (new Xapian::ExpandDeciderAnd(
783 edecider->release(),
784 &edecider_auto))->release());
785 TEST(!gone_auto);
786 TEST(gone);
788 TEST(gone_auto);
791 class TestValueRangeProcessor : public Xapian::ValueRangeProcessor {
792 DestroyedFlag destroyed;
794 public:
795 TestValueRangeProcessor(bool & destroyed_) : destroyed(destroyed_) { }
797 Xapian::valueno operator()(std::string &, std::string &) {
798 return 42;
802 /// Check reference counting of user-subclassable classes.
803 DEFINE_TESTCASE(subclassablerefcount8, !backend) {
804 bool gone_auto, gone;
806 // Simple test of release().
808 Xapian::ValueRangeProcessor * vrp = new TestValueRangeProcessor(gone);
809 TEST(!gone);
810 Xapian::QueryParser qp;
811 qp.add_valuerangeprocessor(vrp->release());
812 TEST(!gone);
814 TEST(gone);
816 // Check a second call to release() has no effect.
818 Xapian::ValueRangeProcessor * vrp = new TestValueRangeProcessor(gone);
819 TEST(!gone);
820 Xapian::QueryParser qp;
821 qp.add_valuerangeprocessor(vrp->release());
822 vrp->release();
823 TEST(!gone);
825 TEST(gone);
827 // Test reference counting works, and that a VRP with automatic storage
828 // works OK.
830 TestValueRangeProcessor vrp_auto(gone_auto);
831 TEST(!gone_auto);
833 Xapian::QueryParser qp1;
835 Xapian::QueryParser qp2;
836 Xapian::ValueRangeProcessor * vrp;
837 vrp = new TestValueRangeProcessor(gone);
838 TEST(!gone);
839 qp1.add_valuerangeprocessor(vrp->release());
840 TEST(!gone);
841 qp2.add_valuerangeprocessor(vrp);
842 TEST(!gone);
843 qp2.add_valuerangeprocessor(&vrp_auto);
844 TEST(!gone);
845 TEST(!gone_auto);
847 TEST(!gone);
849 TEST(gone);
850 TEST(!gone_auto);
852 TEST(gone_auto);
854 // Regression test for initial implementation, where ~opt_intrusive_ptr()
855 // checked the reference of the object, which may have already been deleted
856 // if it wasn't been reference counted.
858 Xapian::QueryParser qp;
860 Xapian::ValueRangeProcessor * vrp =
861 new TestValueRangeProcessor(gone);
862 TEST(!gone);
863 qp.add_valuerangeprocessor(vrp);
864 delete vrp;
865 TEST(gone);
867 // At the end of this block, qp is destroyed, but mustn't dereference
868 // the pointer it has to vrp. If it does, that should get caught
869 // when tests are run under valgrind.
873 /// Check encoding of non-UTF8 document data.
874 DEFINE_TESTCASE(nonutf8docdesc1, !backend) {
875 Xapian::Document doc;
876 doc.set_data("\xc0\x80\xf5\x80\x80\x80\xfe\xff");
877 TEST_EQUAL(doc.get_description(),
878 "Document(data='\\xc0\\x80\\xf5\\x80\\x80\\x80\\xfe\\xff')");
879 doc.set_data(string("\x00\x1f", 2));
880 TEST_EQUAL(doc.get_description(),
881 "Document(data='\\x00\\x1f')");
882 // Check that backslashes are encoded so output isn't ambiguous.
883 doc.set_data("back\\slash");
884 TEST_EQUAL(doc.get_description(),
885 "Document(data='back\\x5cslash')");
888 /** Test removal of terms from a document while iterating over them.
890 * Prior to 1.5.0 and 1.4.6 the underlying iterator was invalidated when
891 * preinc == false, leading to undefined behaviour (typically a segmentation
892 * fault).
894 DEFINE_TESTCASE(deletewhileiterating1, !backend) {
895 for (bool preinc : { false, true }) {
896 Xapian::Document doc;
897 Xapian::TermGenerator indexer;
898 indexer.set_document(doc);
899 indexer.index_text("Pull the rug out from under ourselves", 1, "S");
900 Xapian::TermIterator term_iterator = doc.termlist_begin();
901 term_iterator.skip_to("S");
902 while (term_iterator != doc.termlist_end()) {
903 const string& term = *term_iterator;
904 if (!startswith(term, "S")) {
905 break;
907 if (preinc) ++term_iterator;
908 doc.remove_term(term);
909 if (!preinc) ++term_iterator;
911 TEST_EQUAL(doc.termlist_count(), 0);
912 TEST(doc.termlist_begin() == doc.termlist_end());
916 /// Feature test for Document::remove_postings().
917 DEFINE_TESTCASE(removepostings, !backend) {
918 Xapian::Document doc;
919 // Add Fibonacci sequence as positions.
920 Xapian::termpos prev_pos = 1;
921 Xapian::termpos pos = 1;
922 while (pos < 1000) {
923 doc.add_posting("foo", pos);
924 auto new_pos = prev_pos + pos;
925 prev_pos = pos;
926 pos = new_pos;
929 // Check we added exactly one term.
930 TEST_EQUAL(doc.termlist_count(), 1);
932 Xapian::TermIterator t = doc.termlist_begin();
933 auto num_pos = t.positionlist_count();
934 TEST_EQUAL(t.get_wdf(), num_pos);
936 // Out of order is a no-op.
937 TEST_EQUAL(doc.remove_postings("foo", 2, 1), 0);
938 t = doc.termlist_begin();
939 TEST_EQUAL(t.positionlist_count(), num_pos);
940 TEST_EQUAL(t.get_wdf(), num_pos);
942 // 6 and 7 aren't in the sequence.
943 TEST_EQUAL(doc.remove_postings("foo", 6, 7), 0);
944 t = doc.termlist_begin();
945 TEST_EQUAL(t.positionlist_count(), num_pos);
946 TEST_EQUAL(t.get_wdf(), num_pos);
948 // Beyond the end of the positions.
949 TEST_EQUAL(doc.remove_postings("foo", 1000, 2000), 0);
950 t = doc.termlist_begin();
951 TEST_EQUAL(t.positionlist_count(), num_pos);
952 TEST_EQUAL(t.get_wdf(), num_pos);
954 // 1, 2, 3 are in the sequence, 4 isn't.
955 TEST_EQUAL(doc.remove_postings("foo", 1, 4), 3);
956 t = doc.termlist_begin();
957 TEST_EQUAL(t.positionlist_count(), num_pos - 3);
958 TEST_EQUAL(t.get_wdf(), num_pos - 3);
960 // Remove the end position.
961 TEST_EQUAL(doc.remove_postings("foo", 876, 987), 1);
962 t = doc.termlist_begin();
963 TEST_EQUAL(t.positionlist_count(), num_pos - 4);
964 TEST_EQUAL(t.get_wdf(), num_pos - 4);
966 // Remove a range in the middle.
967 TEST_EQUAL(doc.remove_postings("foo", 33, 233), 5);
968 t = doc.termlist_begin();
969 TEST_EQUAL(t.positionlist_count(), num_pos - 9);
970 TEST_EQUAL(t.get_wdf(), num_pos - 9);
972 // Check the expected positions are left.
973 t = doc.termlist_begin();
974 static const Xapian::termpos expected[] = { 5, 8, 13, 21, 377, 610, 9999 };
975 const Xapian::termpos* expect = expected;
976 for (auto p = t.positionlist_begin(); p != t.positionlist_end(); ++p) {
977 TEST_EQUAL(*p, *expect);
978 ++expect;
982 static void
983 errorcopyctor_helper(Xapian::Error& error)
985 // GCC 9 was giving a warning on the next line with -Wdeprecated-copy
986 // (which is enabled by -Wextra).
987 throw error;
990 /// Regression test for warning with GCC 9.
991 DEFINE_TESTCASE(errorcopyctor, !backend) {
992 Xapian::RangeError e("test");
993 try {
994 errorcopyctor_helper(e);
995 FAIL_TEST("Expected exception to be thrown");
996 } catch (Xapian::Error&) {
997 return;
999 FAIL_TEST("Expected RangeError wasn't caught");