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 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
);
48 // Regression test: various methods on Database() used to segfault or cause
49 // division by 0. Fixed in 1.1.4 and 1.0.18. Ticket#415.
50 DEFINE_TESTCASE(nosubdatabases1
, !backend
) {
52 // Fails to compile with g++ 3.3.5 on OpenBSD (ticket#458):
53 // TEST_EQUAL(db.get_metadata("foo"), std::string());
54 TEST(db
.get_metadata("foo").empty());
55 TEST_EQUAL(db
.metadata_keys_begin(), db
.metadata_keys_end());
56 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.termlist_begin(1));
57 TEST_EQUAL(db
.allterms_begin(), db
.allterms_end());
58 TEST_EQUAL(db
.allterms_begin("foo"), db
.allterms_end("foo"));
59 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.positionlist_begin(1, "foo"));
60 TEST_EQUAL(db
.get_lastdocid(), 0);
61 TEST_EQUAL(db
.valuestream_begin(7), db
.valuestream_end(7));
62 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.get_doclength(1));
63 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.get_unique_terms(1));
64 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.get_document(1));
68 /// Feature test for Document::add_boolean_term(), new in 1.0.18/1.1.4.
69 DEFINE_TESTCASE(document1
, !backend
) {
71 doc
.add_boolean_term("Hxapian.org");
72 TEST_EQUAL(doc
.termlist_count(), 1);
73 Xapian::TermIterator t
= doc
.termlist_begin();
74 TEST(t
!= doc
.termlist_end());
75 TEST_EQUAL(*t
, "Hxapian.org");
76 TEST_EQUAL(t
.get_wdf(), 0);
77 TEST(++t
== doc
.termlist_end());
78 doc
.remove_term("Hxapian.org");
79 TEST_EQUAL(doc
.termlist_count(), 0);
80 TEST(doc
.termlist_begin() == doc
.termlist_end());
84 /// Regression test - the docid wasn't initialised prior to 1.0.22/1.2.4.
85 DEFINE_TESTCASE(document2
, !backend
) {
87 // The return value is uninitialised, so running under valgrind this
88 // will fail reliably prior to the fix.
89 TEST_EQUAL(doc
.get_docid(), 0);
93 DEFINE_TESTCASE(emptyquery4
, !backend
) {
94 // Test we get an empty query from applying any of the following ops to
95 // an empty list of subqueries.
97 TEST(Xapian::Query(q
.OP_AND
, &q
, &q
).empty());
98 TEST(Xapian::Query(q
.OP_OR
, &q
, &q
).empty());
99 TEST(Xapian::Query(q
.OP_AND_NOT
, &q
, &q
).empty());
100 TEST(Xapian::Query(q
.OP_XOR
, &q
, &q
).empty());
101 TEST(Xapian::Query(q
.OP_AND_MAYBE
, &q
, &q
).empty());
102 TEST(Xapian::Query(q
.OP_FILTER
, &q
, &q
).empty());
103 TEST(Xapian::Query(q
.OP_NEAR
, &q
, &q
).empty());
104 TEST(Xapian::Query(q
.OP_PHRASE
, &q
, &q
).empty());
105 TEST(Xapian::Query(q
.OP_ELITE_SET
, &q
, &q
).empty());
106 TEST(Xapian::Query(q
.OP_SYNONYM
, &q
, &q
).empty());
107 TEST(Xapian::Query(q
.OP_MAX
, &q
, &q
).empty());
111 DEFINE_TESTCASE(singlesubquery1
, !backend
) {
112 // Test that we get just the subquery if we apply any of the following
113 // ops to just that subquery.
114 #define singlesubquery1_(OP) \
115 TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
117 Xapian::Query q
[1] = { Xapian::Query("test") };
118 singlesubquery1_(OP_AND
);
119 singlesubquery1_(OP_OR
);
120 singlesubquery1_(OP_AND_NOT
);
121 singlesubquery1_(OP_XOR
);
122 singlesubquery1_(OP_AND_MAYBE
);
123 singlesubquery1_(OP_FILTER
);
124 singlesubquery1_(OP_NEAR
);
125 singlesubquery1_(OP_PHRASE
);
126 singlesubquery1_(OP_ELITE_SET
);
127 singlesubquery1_(OP_SYNONYM
);
128 singlesubquery1_(OP_MAX
);
132 DEFINE_TESTCASE(singlesubquery2
, !backend
) {
133 // Like the previous test, but using MatchNothing as the subquery.
134 #define singlesubquery2_(OP) \
135 TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
137 Xapian::Query q
[1] = { Xapian::Query::MatchNothing
};
138 singlesubquery2_(OP_AND
);
139 singlesubquery2_(OP_OR
);
140 singlesubquery2_(OP_AND_NOT
);
141 singlesubquery2_(OP_XOR
);
142 singlesubquery2_(OP_AND_MAYBE
);
143 singlesubquery2_(OP_FILTER
);
144 singlesubquery2_(OP_NEAR
);
145 singlesubquery2_(OP_PHRASE
);
146 singlesubquery2_(OP_ELITE_SET
);
147 singlesubquery2_(OP_SYNONYM
);
148 singlesubquery2_(OP_MAX
);
152 DEFINE_TESTCASE(singlesubquery3
, !backend
) {
153 // Like the previous test, but using MatchAll as the subquery.
154 #define singlesubquery3_(OP) \
155 TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
156 "Query(<alldocuments>)")
157 Xapian::Query q
[1] = { Xapian::Query::MatchAll
};
158 singlesubquery3_(OP_AND
);
159 singlesubquery3_(OP_OR
);
160 singlesubquery3_(OP_AND_NOT
);
161 singlesubquery3_(OP_XOR
);
162 singlesubquery3_(OP_AND_MAYBE
);
163 singlesubquery3_(OP_FILTER
);
164 // OP_NEAR and OP_PHRASE over MatchAll doesn't really make sense.
165 singlesubquery3_(OP_ELITE_SET
);
166 singlesubquery3_(OP_SYNONYM
);
167 singlesubquery3_(OP_MAX
);
171 /// Check we no longer combine wqf for same term at the same position.
172 DEFINE_TESTCASE(combinewqfnomore1
, !backend
) {
173 Xapian::Query
q(Xapian::Query::OP_OR
,
174 Xapian::Query("beer", 1, 1),
175 Xapian::Query("beer", 1, 1));
176 // Prior to 1.3.0, we would have given beer@2, but we decided that wasn't
177 // really useful or helpful.
178 TEST_EQUAL(q
.get_description(), "Query((beer@1 OR beer@1))");
182 class DestroyedFlag
{
186 DestroyedFlag(bool & destroyed_
) : destroyed(destroyed_
) {
195 class TestRangeProcessor
: public Xapian::RangeProcessor
{
196 DestroyedFlag destroyed
;
199 TestRangeProcessor(bool & destroyed_
)
200 : Xapian::RangeProcessor(0), destroyed(destroyed_
) { }
202 Xapian::Query
operator()(const std::string
&, const std::string
&)
204 return Xapian::Query::MatchAll
;
208 /// Check reference counting of user-subclassable classes.
209 DEFINE_TESTCASE(subclassablerefcount1
, !backend
) {
210 bool gone_auto
, gone
;
212 // Simple test of release().
214 Xapian::RangeProcessor
* rp
= new TestRangeProcessor(gone
);
216 Xapian::QueryParser qp
;
217 qp
.add_rangeprocessor(rp
->release());
222 // Check a second call to release() has no effect.
224 Xapian::RangeProcessor
* rp
= new TestRangeProcessor(gone
);
226 Xapian::QueryParser qp
;
227 qp
.add_rangeprocessor(rp
->release());
233 // Test reference counting works, and that a VRP with automatic storage
236 TestRangeProcessor
rp_auto(gone_auto
);
239 Xapian::QueryParser qp1
;
241 Xapian::QueryParser qp2
;
242 Xapian::RangeProcessor
* rp
;
243 rp
= new TestRangeProcessor(gone
);
245 qp1
.add_rangeprocessor(rp
->release());
247 qp2
.add_rangeprocessor(rp
);
249 qp2
.add_rangeprocessor(&rp_auto
);
260 // Regression test for initial implementation, where ~opt_instrusive_ptr()
261 // checked the reference of the object, which may have already been deleted
262 // if it wasn't been reference counted.
264 Xapian::QueryParser qp
;
266 Xapian::RangeProcessor
* rp
= new TestRangeProcessor(gone
);
268 qp
.add_rangeprocessor(rp
);
272 // At the end of this block, qp is destroyed, but mustn't dereference
273 // the pointer it has to rp. If it does, that should get caught
274 // when tests are run under valgrind.
280 class TestFieldProcessor
: public Xapian::FieldProcessor
{
281 DestroyedFlag destroyed
;
284 TestFieldProcessor(bool & destroyed_
) : destroyed(destroyed_
) { }
286 Xapian::Query
operator()(const string
&str
) {
287 return Xapian::Query(str
);
291 /// Check reference counting of user-subclassable classes.
292 DEFINE_TESTCASE(subclassablerefcount2
, !backend
) {
293 bool gone_auto
, gone
;
295 // Simple test of release().
297 Xapian::FieldProcessor
* proc
= new TestFieldProcessor(gone
);
299 Xapian::QueryParser qp
;
300 qp
.add_prefix("foo", proc
->release());
305 // Check a second call to release() has no effect.
307 Xapian::FieldProcessor
* proc
= new TestFieldProcessor(gone
);
309 Xapian::QueryParser qp
;
310 qp
.add_prefix("foo", proc
->release());
316 // Test reference counting works, and that a FieldProcessor with automatic
319 TestFieldProcessor
proc_auto(gone_auto
);
322 Xapian::QueryParser qp1
;
324 Xapian::QueryParser qp2
;
325 Xapian::FieldProcessor
* proc
;
326 proc
= new TestFieldProcessor(gone
);
328 qp1
.add_prefix("foo", proc
->release());
330 qp2
.add_prefix("foo", proc
);
332 qp2
.add_prefix("bar", &proc_auto
);
346 class TestMatchSpy
: public Xapian::MatchSpy
{
347 DestroyedFlag destroyed
;
350 TestMatchSpy(bool & destroyed_
) : destroyed(destroyed_
) { }
352 void operator()(const Xapian::Document
&, double) { }
355 /// Check reference counting of MatchSpy.
356 DEFINE_TESTCASE(subclassablerefcount3
, backend
) {
357 Xapian::Database db
= get_database("apitest_simpledata");
359 bool gone_auto
, gone
;
361 // Simple test of release().
363 Xapian::MatchSpy
* spy
= new TestMatchSpy(gone
);
365 Xapian::Enquire
enquire(db
);
366 enquire
.add_matchspy(spy
->release());
371 // Check a second call to release() has no effect.
373 Xapian::MatchSpy
* spy
= new TestMatchSpy(gone
);
375 Xapian::Enquire
enquire(db
);
376 enquire
.add_matchspy(spy
->release());
382 // Test reference counting works, and that a MatchSpy with automatic
385 TestMatchSpy
spy_auto(gone_auto
);
388 Xapian::Enquire
enq1(db
);
390 Xapian::Enquire
enq2(db
);
391 Xapian::MatchSpy
* spy
;
392 spy
= new TestMatchSpy(gone
);
394 enq1
.add_matchspy(spy
->release());
396 enq2
.add_matchspy(spy
);
398 enq2
.add_matchspy(&spy_auto
);
412 class TestStopper
: public Xapian::Stopper
{
413 DestroyedFlag destroyed
;
416 TestStopper(bool & destroyed_
) : destroyed(destroyed_
) { }
418 bool operator()(const std::string
&) const { return true; }
421 /// Check reference counting of Stopper with QueryParser.
422 DEFINE_TESTCASE(subclassablerefcount4
, !backend
) {
423 bool gone_auto
, gone
;
425 // Simple test of release().
427 Xapian::Stopper
* stopper
= new TestStopper(gone
);
429 Xapian::QueryParser qp
;
430 qp
.set_stopper(stopper
->release());
435 // Test that setting a new stopper causes the previous one to be released.
438 Xapian::Stopper
* stopper0
= new TestStopper(gone0
);
440 Xapian::QueryParser qp
;
441 qp
.set_stopper(stopper0
->release());
444 Xapian::Stopper
* stopper
= new TestStopper(gone
);
446 qp
.set_stopper(stopper
->release());
452 // Check a second call to release() has no effect.
454 Xapian::Stopper
* stopper
= new TestStopper(gone
);
456 Xapian::QueryParser qp
;
457 qp
.set_stopper(stopper
->release());
463 // Test reference counting works, and that a Stopper with automatic
466 TestStopper
stopper_auto(gone_auto
);
469 Xapian::QueryParser qp1
;
471 Xapian::QueryParser qp2
;
472 Xapian::Stopper
* stopper
;
473 stopper
= new TestStopper(gone
);
475 qp1
.set_stopper(stopper
->release());
477 qp2
.set_stopper(stopper
);
479 qp2
.set_stopper(&stopper_auto
);
493 /// Check reference counting of Stopper with TermGenerator.
494 DEFINE_TESTCASE(subclassablerefcount5
, !backend
) {
495 bool gone_auto
, gone
;
497 // Simple test of release().
499 Xapian::Stopper
* stopper
= new TestStopper(gone
);
501 Xapian::TermGenerator indexer
;
502 indexer
.set_stopper(stopper
->release());
507 // Test that setting a new stopper causes the previous one to be released.
510 Xapian::Stopper
* stopper0
= new TestStopper(gone0
);
512 Xapian::TermGenerator indexer
;
513 indexer
.set_stopper(stopper0
->release());
516 Xapian::Stopper
* stopper
= new TestStopper(gone
);
518 indexer
.set_stopper(stopper
->release());
524 // Check a second call to release() has no effect.
526 Xapian::Stopper
* stopper
= new TestStopper(gone
);
528 Xapian::TermGenerator indexer
;
529 indexer
.set_stopper(stopper
->release());
535 // Test reference counting works, and that a Stopper with automatic
538 TestStopper
stopper_auto(gone_auto
);
541 Xapian::TermGenerator indexer1
;
543 Xapian::TermGenerator indexer2
;
544 Xapian::Stopper
* stopper
;
545 stopper
= new TestStopper(gone
);
547 indexer1
.set_stopper(stopper
->release());
549 indexer2
.set_stopper(stopper
);
551 indexer2
.set_stopper(&stopper_auto
);
565 class TestKeyMaker
: public Xapian::KeyMaker
{
566 DestroyedFlag destroyed
;
569 TestKeyMaker(bool & destroyed_
) : destroyed(destroyed_
) { }
571 string
operator()(const Xapian::Document
&) const { return string(); }
574 /// Check reference counting of KeyMaker.
575 DEFINE_TESTCASE(subclassablerefcount6
, backend
) {
576 Xapian::Database db
= get_database("apitest_simpledata");
578 bool gone_auto
, gone
;
580 // Simple test of release().
582 Xapian::KeyMaker
* keymaker
= new TestKeyMaker(gone
);
584 Xapian::Enquire
enq(db
);
585 enq
.set_sort_by_key(keymaker
->release(), false);
590 // Test that setting a new keymaker causes the previous one to be released.
593 Xapian::KeyMaker
* keymaker0
= new TestKeyMaker(gone0
);
595 Xapian::Enquire
enq(db
);
596 enq
.set_sort_by_key(keymaker0
->release(), false);
599 Xapian::KeyMaker
* keymaker
= new TestKeyMaker(gone
);
601 enq
.set_sort_by_key_then_relevance(keymaker
->release(), false);
607 // Check a second call to release() has no effect.
609 Xapian::KeyMaker
* keymaker
= new TestKeyMaker(gone
);
611 Xapian::Enquire
enq(db
);
612 enq
.set_sort_by_key(keymaker
->release(), false);
618 // Test reference counting works, and that a KeyMaker with automatic
621 TestKeyMaker
keymaker_auto(gone_auto
);
624 Xapian::Enquire
enq1(db
);
626 Xapian::Enquire
enq2(db
);
627 Xapian::KeyMaker
* keymaker
;
628 keymaker
= new TestKeyMaker(gone
);
630 enq1
.set_sort_by_key(keymaker
->release(), false);
632 enq2
.set_sort_by_relevance_then_key(keymaker
, false);
634 enq2
.set_sort_by_key_then_relevance(&keymaker_auto
, false);
648 class TestExpandDecider
: public Xapian::ExpandDecider
{
649 DestroyedFlag destroyed
;
652 TestExpandDecider(bool & destroyed_
) : destroyed(destroyed_
) { }
654 bool operator()(const string
&) const { return true; }
657 /// Check reference counting of ExpandDecider.
658 DEFINE_TESTCASE(subclassablerefcount7
, backend
) {
659 Xapian::Database db
= get_database("apitest_simpledata");
660 Xapian::Enquire
enq(db
);
662 rset
.add_document(1);
664 bool gone_auto
, gone
;
667 flags
<= Xapian::Enquire::INCLUDE_QUERY_TERMS
;
668 flags
+= Xapian::Enquire::INCLUDE_QUERY_TERMS
) {
669 // Test of auto lifetime ExpandDecider.
671 TestExpandDecider
edecider_auto(gone_auto
);
673 (void)enq
.get_eset(5, rset
, 0, &edecider_auto
);
678 // Simple test of release().
680 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
682 (void)enq
.get_eset(5, rset
, 0, edecider
);
688 // Test that a released ExpandDecider gets cleaned up by get_eset().
690 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
692 (void)enq
.get_eset(5, rset
, 0, edecider
->release());
696 // Check a second call to release() has no effect.
698 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
702 (void)enq
.get_eset(5, rset
, 0, edecider
->release());
707 // Test combinations of released/non-released with ExpandDeciderAnd.
709 TestExpandDecider
edecider_auto(gone_auto
);
711 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
713 (void)enq
.get_eset(5, rset
, 0,
714 (new Xapian::ExpandDeciderAnd(
716 edecider
->release()))->release());
722 TestExpandDecider
edecider_auto(gone_auto
);
724 Xapian::ExpandDecider
* edecider
= new TestExpandDecider(gone
);
726 (void)enq
.get_eset(5, rset
, 0,
727 (new Xapian::ExpandDeciderAnd(
729 &edecider_auto
))->release());
738 class TestValueRangeProcessor
: public Xapian::ValueRangeProcessor
{
739 DestroyedFlag destroyed
;
742 TestValueRangeProcessor(bool & destroyed_
) : destroyed(destroyed_
) { }
744 Xapian::valueno
operator()(std::string
&, std::string
&) {
749 /// Check reference counting of user-subclassable classes.
750 DEFINE_TESTCASE(subclassablerefcount8
, !backend
) {
751 bool gone_auto
, gone
;
753 // Simple test of release().
755 Xapian::ValueRangeProcessor
* vrp
= new TestValueRangeProcessor(gone
);
757 Xapian::QueryParser qp
;
758 qp
.add_valuerangeprocessor(vrp
->release());
763 // Check a second call to release() has no effect.
765 Xapian::ValueRangeProcessor
* vrp
= new TestValueRangeProcessor(gone
);
767 Xapian::QueryParser qp
;
768 qp
.add_valuerangeprocessor(vrp
->release());
774 // Test reference counting works, and that a VRP with automatic storage
777 TestValueRangeProcessor
vrp_auto(gone_auto
);
780 Xapian::QueryParser qp1
;
782 Xapian::QueryParser qp2
;
783 Xapian::ValueRangeProcessor
* vrp
;
784 vrp
= new TestValueRangeProcessor(gone
);
786 qp1
.add_valuerangeprocessor(vrp
->release());
788 qp2
.add_valuerangeprocessor(vrp
);
790 qp2
.add_valuerangeprocessor(&vrp_auto
);
801 // Regression test for initial implementation, where ~opt_instrusive_ptr()
802 // checked the reference of the object, which may have already been deleted
803 // if it wasn't been reference counted.
805 Xapian::QueryParser qp
;
807 Xapian::ValueRangeProcessor
* vrp
=
808 new TestValueRangeProcessor(gone
);
810 qp
.add_valuerangeprocessor(vrp
);
814 // At the end of this block, qp is destroyed, but mustn't dereference
815 // the pointer it has to vrp. If it does, that should get caught
816 // when tests are run under valgrind.
822 /// Check encoding of non-UTF8 document data.
823 DEFINE_TESTCASE(nonutf8docdesc1
, !backend
) {
824 Xapian::Document doc
;
825 doc
.set_data("\xc0\x80\xf5\x80\x80\x80\xfe\xff");
826 TEST_EQUAL(doc
.get_description(),
827 "Document(data='\\xc0\\x80\\xf5\\x80\\x80\\x80\\xfe\\xff')");
828 doc
.set_data(string("\x00\x1f", 2));
829 TEST_EQUAL(doc
.get_description(),
830 "Document(data='\\x00\\x1f')");
831 // Check that backslashes are encoded so output isn't ambiguous.
832 doc
.set_data("back\\slash");
833 TEST_EQUAL(doc
.get_description(),
834 "Document(data='back\\x5cslash')");