2 * @brief tests which don't need a backend
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
27 #define XAPIAN_DEPRECATED(D) D
32 #include "testsuite.h"
33 #include "testutils.h"
37 // Check the version functions give consistent results.
38 DEFINE_TESTCASE(version1
, !backend
) {
39 string version
= str(Xapian::major_version());
41 version
+= str(Xapian::minor_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
) {
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
) {
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
) {
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
) {
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());
99 TEST_EQUAL(doc
.termlist_count(), 0);
100 TEST(doc
.termlist_begin() == doc
.termlist_end());
101 // Test clear_terms() when there are no 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
;
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());
125 TEST_EQUAL(doc
.values_count(), 0);
126 TEST(doc
.values_begin() == doc
.values_end());
127 // Test clear_values() when there are no 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
;
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.
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(),\
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(),\
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
{
253 DestroyedFlag(bool & destroyed_
) : destroyed(destroyed_
) {
262 class TestRangeProcessor
: public Xapian::RangeProcessor
{
263 DestroyedFlag destroyed
;
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
);
283 Xapian::QueryParser qp
;
284 qp
.add_rangeprocessor(rp
->release());
289 // Check a second call to release() has no effect.
291 Xapian::RangeProcessor
* rp
= new TestRangeProcessor(gone
);
293 Xapian::QueryParser qp
;
294 qp
.add_rangeprocessor(rp
->release());
300 // Test reference counting works, and that a RangeProcessor with automatic
303 TestRangeProcessor
rp_auto(gone_auto
);
306 Xapian::QueryParser qp1
;
308 Xapian::QueryParser qp2
;
309 Xapian::RangeProcessor
* rp
;
310 rp
= new TestRangeProcessor(gone
);
312 qp1
.add_rangeprocessor(rp
->release());
314 qp2
.add_rangeprocessor(rp
);
316 qp2
.add_rangeprocessor(&rp_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
);
335 qp
.add_rangeprocessor(rp
);
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
;
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
);
364 Xapian::QueryParser qp
;
365 qp
.add_prefix("foo", proc
->release());
370 // Check a second call to release() has no effect.
372 Xapian::FieldProcessor
* proc
= new TestFieldProcessor(gone
);
374 Xapian::QueryParser qp
;
375 qp
.add_prefix("foo", proc
->release());
381 // Test reference counting works, and that a FieldProcessor with automatic
384 TestFieldProcessor
proc_auto(gone_auto
);
387 Xapian::QueryParser qp1
;
389 Xapian::QueryParser qp2
;
390 Xapian::FieldProcessor
* proc
;
391 proc
= new TestFieldProcessor(gone
);
393 qp1
.add_prefix("foo", proc
->release());
395 qp2
.add_prefix("foo", proc
);
397 qp2
.add_prefix("bar", &proc_auto
);
409 class TestMatchSpy
: public Xapian::MatchSpy
{
410 DestroyedFlag destroyed
;
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
);
428 Xapian::Enquire
enquire(db
);
429 enquire
.add_matchspy(spy
->release());
434 // Check a second call to release() has no effect.
436 Xapian::MatchSpy
* spy
= new TestMatchSpy(gone
);
438 Xapian::Enquire
enquire(db
);
439 enquire
.add_matchspy(spy
->release());
445 // Test reference counting works, and that a MatchSpy with automatic
448 TestMatchSpy
spy_auto(gone_auto
);
451 Xapian::Enquire
enq1(db
);
453 Xapian::Enquire
enq2(db
);
454 Xapian::MatchSpy
* spy
;
455 spy
= new TestMatchSpy(gone
);
457 enq1
.add_matchspy(spy
->release());
459 enq2
.add_matchspy(spy
);
461 enq2
.add_matchspy(&spy_auto
);
473 class TestStopper
: public Xapian::Stopper
{
474 DestroyedFlag destroyed
;
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
);
490 Xapian::QueryParser qp
;
491 qp
.set_stopper(stopper
->release());
496 // Test that setting a new stopper causes the previous one to be released.
499 Xapian::Stopper
* stopper0
= new TestStopper(gone0
);
501 Xapian::QueryParser qp
;
502 qp
.set_stopper(stopper0
->release());
505 Xapian::Stopper
* stopper
= new TestStopper(gone
);
507 qp
.set_stopper(stopper
->release());
513 // Check a second call to release() has no effect.
515 Xapian::Stopper
* stopper
= new TestStopper(gone
);
517 Xapian::QueryParser qp
;
518 qp
.set_stopper(stopper
->release());
524 // Test reference counting works, and that a Stopper with automatic
527 TestStopper
stopper_auto(gone_auto
);
530 Xapian::QueryParser qp1
;
532 Xapian::QueryParser qp2
;
533 Xapian::Stopper
* stopper
;
534 stopper
= new TestStopper(gone
);
536 qp1
.set_stopper(stopper
->release());
538 qp2
.set_stopper(stopper
);
540 qp2
.set_stopper(&stopper_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
);
560 Xapian::TermGenerator indexer
;
561 indexer
.set_stopper(stopper
->release());
566 // Test that setting a new stopper causes the previous one to be released.
569 Xapian::Stopper
* stopper0
= new TestStopper(gone0
);
571 Xapian::TermGenerator indexer
;
572 indexer
.set_stopper(stopper0
->release());
575 Xapian::Stopper
* stopper
= new TestStopper(gone
);
577 indexer
.set_stopper(stopper
->release());
583 // Check a second call to release() has no effect.
585 Xapian::Stopper
* stopper
= new TestStopper(gone
);
587 Xapian::TermGenerator indexer
;
588 indexer
.set_stopper(stopper
->release());
594 // Test reference counting works, and that a Stopper with automatic
597 TestStopper
stopper_auto(gone_auto
);
600 Xapian::TermGenerator indexer1
;
602 Xapian::TermGenerator indexer2
;
603 Xapian::Stopper
* stopper
;
604 stopper
= new TestStopper(gone
);
606 indexer1
.set_stopper(stopper
->release());
608 indexer2
.set_stopper(stopper
);
610 indexer2
.set_stopper(&stopper_auto
);
622 class TestKeyMaker
: public Xapian::KeyMaker
{
623 DestroyedFlag destroyed
;
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
);
641 Xapian::Enquire
enq(db
);
642 enq
.set_sort_by_key(keymaker
->release(), false);
647 // Test that setting a new keymaker causes the previous one to be released.
650 Xapian::KeyMaker
* keymaker0
= new TestKeyMaker(gone0
);
652 Xapian::Enquire
enq(db
);
653 enq
.set_sort_by_key(keymaker0
->release(), false);
656 Xapian::KeyMaker
* keymaker
= new TestKeyMaker(gone
);
658 enq
.set_sort_by_key_then_relevance(keymaker
->release(), false);
664 // Check a second call to release() has no effect.
666 Xapian::KeyMaker
* keymaker
= new TestKeyMaker(gone
);
668 Xapian::Enquire
enq(db
);
669 enq
.set_sort_by_key(keymaker
->release(), false);
675 // Test reference counting works, and that a KeyMaker with automatic
678 TestKeyMaker
keymaker_auto(gone_auto
);
681 Xapian::Enquire
enq1(db
);
683 Xapian::Enquire
enq2(db
);
684 Xapian::KeyMaker
* keymaker
;
685 keymaker
= new TestKeyMaker(gone
);
687 enq1
.set_sort_by_key(keymaker
->release(), false);
689 enq2
.set_sort_by_relevance_then_key(keymaker
, false);
691 enq2
.set_sort_by_key_then_relevance(&keymaker_auto
, false);
703 class TestExpandDecider
: public Xapian::ExpandDecider
{
704 DestroyedFlag destroyed
;
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
);
717 rset
.add_document(1);
719 bool gone_auto
, gone
;
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
);
728 (void)enq
.get_eset(5, rset
, 0, &edecider_auto
);
733 // Simple test of release().
735 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
737 (void)enq
.get_eset(5, rset
, 0, edecider
);
743 // Test that a released ExpandDecider gets cleaned up by get_eset().
745 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
747 (void)enq
.get_eset(5, rset
, 0, edecider
->release());
751 // Check a second call to release() has no effect.
753 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
757 (void)enq
.get_eset(5, rset
, 0, edecider
->release());
762 // Test combinations of released/non-released with ExpandDeciderAnd.
764 TestExpandDecider
edecider_auto(gone_auto
);
766 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
768 (void)enq
.get_eset(5, rset
, 0,
769 (new Xapian::ExpandDeciderAnd(
771 edecider
->release()))->release());
777 TestExpandDecider
edecider_auto(gone_auto
);
779 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
781 (void)enq
.get_eset(5, rset
, 0,
782 (new Xapian::ExpandDeciderAnd(
784 &edecider_auto
))->release());
791 class TestValueRangeProcessor
: public Xapian::ValueRangeProcessor
{
792 DestroyedFlag destroyed
;
795 TestValueRangeProcessor(bool & destroyed_
) : destroyed(destroyed_
) { }
797 Xapian::valueno
operator()(std::string
&, std::string
&) {
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
);
810 Xapian::QueryParser qp
;
811 qp
.add_valuerangeprocessor(vrp
->release());
816 // Check a second call to release() has no effect.
818 Xapian::ValueRangeProcessor
* vrp
= new TestValueRangeProcessor(gone
);
820 Xapian::QueryParser qp
;
821 qp
.add_valuerangeprocessor(vrp
->release());
827 // Test reference counting works, and that a VRP with automatic storage
830 TestValueRangeProcessor
vrp_auto(gone_auto
);
833 Xapian::QueryParser qp1
;
835 Xapian::QueryParser qp2
;
836 Xapian::ValueRangeProcessor
* vrp
;
837 vrp
= new TestValueRangeProcessor(gone
);
839 qp1
.add_valuerangeprocessor(vrp
->release());
841 qp2
.add_valuerangeprocessor(vrp
);
843 qp2
.add_valuerangeprocessor(&vrp_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
);
863 qp
.add_valuerangeprocessor(vrp
);
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
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")) {
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;
923 doc
.add_posting("foo", pos
);
924 auto new_pos
= prev_pos
+ 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
);
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).
990 /// Regression test for warning with GCC 9.
991 DEFINE_TESTCASE(errorcopyctor
, !backend
) {
992 Xapian::RangeError
e("test");
994 errorcopyctor_helper(e
);
995 FAIL_TEST("Expected exception to be thrown");
996 } catch (Xapian::Error
&) {
999 FAIL_TEST("Expected RangeError wasn't caught");