2 * @brief tests which don't need a backend
4 /* Copyright (C) 2009 Richard Boulton
5 * Copyright (C) 2009,2010,2011,2013,2014,2015 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
31 #include "testsuite.h"
32 #include "testutils.h"
36 // Check the version functions give consistent results.
37 DEFINE_TESTCASE(version1
, !backend
) {
38 string version
= str(Xapian::major_version());
40 version
+= str(Xapian::minor_version());
42 version
+= str(Xapian::revision());
43 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 // Fails to compile with g++ 3.3.5 on OpenBSD (ticket#458):
52 // TEST_EQUAL(db.get_metadata("foo"), std::string());
53 TEST(db
.get_metadata("foo").empty());
54 TEST_EQUAL(db
.metadata_keys_begin(), db
.metadata_keys_end());
55 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.termlist_begin(1));
56 TEST_EQUAL(db
.allterms_begin(), db
.allterms_end());
57 TEST_EQUAL(db
.allterms_begin("foo"), db
.allterms_end("foo"));
58 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.positionlist_begin(1, "foo"));
59 TEST_EQUAL(db
.get_lastdocid(), 0);
60 TEST_EQUAL(db
.valuestream_begin(7), db
.valuestream_end(7));
61 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.get_doclength(1));
62 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.get_unique_terms(1));
63 TEST_EXCEPTION(Xapian::InvalidOperationError
, db
.get_document(1));
67 /// Feature test for Document::add_boolean_term(), new in 1.0.18/1.1.4.
68 DEFINE_TESTCASE(document1
, !backend
) {
70 doc
.add_boolean_term("Hxapian.org");
71 TEST_EQUAL(doc
.termlist_count(), 1);
72 Xapian::TermIterator t
= doc
.termlist_begin();
73 TEST(t
!= doc
.termlist_end());
74 TEST_EQUAL(*t
, "Hxapian.org");
75 TEST_EQUAL(t
.get_wdf(), 0);
76 TEST(++t
== doc
.termlist_end());
77 doc
.remove_term("Hxapian.org");
78 TEST_EQUAL(doc
.termlist_count(), 0);
79 TEST(doc
.termlist_begin() == doc
.termlist_end());
83 /// Regression test - the docid wasn't initialised prior to 1.0.22/1.2.4.
84 DEFINE_TESTCASE(document2
, !backend
) {
86 // The return value is uninitialised, so running under valgrind this
87 // will fail reliably prior to the fix.
88 TEST_EQUAL(doc
.get_docid(), 0);
92 DEFINE_TESTCASE(emptyquery4
, !backend
) {
93 // Test we get an empty query from applying any of the following ops to
94 // an empty list of subqueries.
96 TEST(Xapian::Query(q
.OP_AND
, &q
, &q
).empty());
97 TEST(Xapian::Query(q
.OP_OR
, &q
, &q
).empty());
98 TEST(Xapian::Query(q
.OP_AND_NOT
, &q
, &q
).empty());
99 TEST(Xapian::Query(q
.OP_XOR
, &q
, &q
).empty());
100 TEST(Xapian::Query(q
.OP_AND_MAYBE
, &q
, &q
).empty());
101 TEST(Xapian::Query(q
.OP_FILTER
, &q
, &q
).empty());
102 TEST(Xapian::Query(q
.OP_NEAR
, &q
, &q
).empty());
103 TEST(Xapian::Query(q
.OP_PHRASE
, &q
, &q
).empty());
104 TEST(Xapian::Query(q
.OP_ELITE_SET
, &q
, &q
).empty());
105 TEST(Xapian::Query(q
.OP_SYNONYM
, &q
, &q
).empty());
106 TEST(Xapian::Query(q
.OP_MAX
, &q
, &q
).empty());
110 DEFINE_TESTCASE(singlesubquery1
, !backend
) {
111 // Test that we get just the subquery if we apply any of the following
112 // ops to just that subquery.
113 #define singlesubquery1_(OP) \
114 TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
116 Xapian::Query q
[1] = { Xapian::Query("test") };
117 singlesubquery1_(OP_AND
);
118 singlesubquery1_(OP_OR
);
119 singlesubquery1_(OP_AND_NOT
);
120 singlesubquery1_(OP_XOR
);
121 singlesubquery1_(OP_AND_MAYBE
);
122 singlesubquery1_(OP_FILTER
);
123 singlesubquery1_(OP_NEAR
);
124 singlesubquery1_(OP_PHRASE
);
125 singlesubquery1_(OP_ELITE_SET
);
126 singlesubquery1_(OP_SYNONYM
);
127 singlesubquery1_(OP_MAX
);
131 DEFINE_TESTCASE(singlesubquery2
, !backend
) {
132 // Like the previous test, but using MatchNothing as the subquery.
133 #define singlesubquery2_(OP) \
134 TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
136 Xapian::Query q
[1] = { Xapian::Query::MatchNothing
};
137 singlesubquery2_(OP_AND
);
138 singlesubquery2_(OP_OR
);
139 singlesubquery2_(OP_AND_NOT
);
140 singlesubquery2_(OP_XOR
);
141 singlesubquery2_(OP_AND_MAYBE
);
142 singlesubquery2_(OP_FILTER
);
143 singlesubquery2_(OP_NEAR
);
144 singlesubquery2_(OP_PHRASE
);
145 singlesubquery2_(OP_ELITE_SET
);
146 singlesubquery2_(OP_SYNONYM
);
147 singlesubquery2_(OP_MAX
);
151 DEFINE_TESTCASE(singlesubquery3
, !backend
) {
152 // Like the previous test, but using MatchNothing as the subquery.
153 #define singlesubquery3_(OP) \
154 TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
155 "Query(<alldocuments>)")
156 Xapian::Query q
[1] = { Xapian::Query::MatchAll
};
157 singlesubquery3_(OP_AND
);
158 singlesubquery3_(OP_OR
);
159 singlesubquery3_(OP_AND_NOT
);
160 singlesubquery3_(OP_XOR
);
161 singlesubquery3_(OP_AND_MAYBE
);
162 singlesubquery3_(OP_FILTER
);
163 singlesubquery3_(OP_NEAR
);
164 singlesubquery3_(OP_PHRASE
);
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 TestValueRangeProcessor
: public Xapian::ValueRangeProcessor
{
196 DestroyedFlag destroyed
;
199 TestValueRangeProcessor(bool & destroyed_
) : destroyed(destroyed_
) { }
201 Xapian::valueno
operator()(std::string
&, std::string
&) {
206 /// Check reference counting of user-subclassable classes.
207 DEFINE_TESTCASE(subclassablerefcount1
, !backend
) {
208 bool gone_auto
, gone
;
210 // Simple test of release().
212 Xapian::ValueRangeProcessor
* vrp
= new TestValueRangeProcessor(gone
);
214 Xapian::QueryParser qp
;
215 qp
.add_valuerangeprocessor(vrp
->release());
220 // Check a second call to release() has no effect.
222 Xapian::ValueRangeProcessor
* vrp
= new TestValueRangeProcessor(gone
);
224 Xapian::QueryParser qp
;
225 qp
.add_valuerangeprocessor(vrp
->release());
231 // Test reference counting works, and that a VRP with automatic storage
234 TestValueRangeProcessor
vrp_auto(gone_auto
);
237 Xapian::QueryParser qp1
;
239 Xapian::QueryParser qp2
;
240 Xapian::ValueRangeProcessor
* vrp
;
241 vrp
= new TestValueRangeProcessor(gone
);
243 qp1
.add_valuerangeprocessor(vrp
->release());
245 qp2
.add_valuerangeprocessor(vrp
);
247 qp2
.add_valuerangeprocessor(&vrp_auto
);
258 // Regression test for initial implementation, where ~opt_instrusive_ptr()
259 // checked the reference of the object, which may have already been deleted
260 // if it wasn't been reference counted.
262 Xapian::QueryParser qp
;
264 Xapian::ValueRangeProcessor
* vrp
=
265 new TestValueRangeProcessor(gone
);
267 qp
.add_valuerangeprocessor(vrp
);
271 // At the end of this block, qp is destroyed, but mustn't dereference
272 // the pointer it has to vrp. If it does, that should get caught
273 // when tests are run under valgrind.
279 class TestFieldProcessor
: public Xapian::FieldProcessor
{
280 DestroyedFlag destroyed
;
283 TestFieldProcessor(bool & destroyed_
) : destroyed(destroyed_
) { }
285 Xapian::Query
operator()(const string
&str
) {
286 return Xapian::Query(str
);
290 /// Check reference counting of user-subclassable classes.
291 DEFINE_TESTCASE(subclassablerefcount2
, !backend
) {
292 bool gone_auto
, gone
;
294 // Simple test of release().
296 Xapian::FieldProcessor
* proc
= new TestFieldProcessor(gone
);
298 Xapian::QueryParser qp
;
299 qp
.add_prefix("foo", proc
->release());
304 // Check a second call to release() has no effect.
306 Xapian::FieldProcessor
* proc
= new TestFieldProcessor(gone
);
308 Xapian::QueryParser qp
;
309 qp
.add_prefix("foo", proc
->release());
315 // Test reference counting works, and that a FieldProcessor with automatic
318 TestFieldProcessor
proc_auto(gone_auto
);
321 Xapian::QueryParser qp1
;
323 Xapian::QueryParser qp2
;
324 Xapian::FieldProcessor
* proc
;
325 proc
= new TestFieldProcessor(gone
);
327 qp1
.add_prefix("foo", proc
->release());
329 qp2
.add_prefix("foo", proc
);
331 qp2
.add_prefix("bar", &proc_auto
);
345 class TestMatchSpy
: public Xapian::MatchSpy
{
346 DestroyedFlag destroyed
;
349 TestMatchSpy(bool & destroyed_
) : destroyed(destroyed_
) { }
351 void operator()(const Xapian::Document
&, double) { }
354 /// Check reference counting of MatchSpy.
355 DEFINE_TESTCASE(subclassablerefcount3
, backend
) {
356 Xapian::Database db
= get_database("apitest_simpledata");
358 bool gone_auto
, gone
;
360 // Simple test of release().
362 Xapian::MatchSpy
* spy
= new TestMatchSpy(gone
);
364 Xapian::Enquire
enquire(db
);
365 enquire
.add_matchspy(spy
->release());
370 // Check a second call to release() has no effect.
372 Xapian::MatchSpy
* spy
= new TestMatchSpy(gone
);
374 Xapian::Enquire
enquire(db
);
375 enquire
.add_matchspy(spy
->release());
381 // Test reference counting works, and that a MatchSpy with automatic
384 TestMatchSpy
spy_auto(gone_auto
);
387 Xapian::Enquire
enq1(db
);
389 Xapian::Enquire
enq2(db
);
390 Xapian::MatchSpy
* spy
;
391 spy
= new TestMatchSpy(gone
);
393 enq1
.add_matchspy(spy
->release());
395 enq2
.add_matchspy(spy
);
397 enq2
.add_matchspy(&spy_auto
);
411 /// Check encoding of non-UTF8 document data.
412 DEFINE_TESTCASE(nonutf8docdesc1
, !backend
) {
413 Xapian::Document doc
;
414 doc
.set_data("\xc0\x80\xf5\x80\x80\x80\xfe\xff");
415 TEST_EQUAL(doc
.get_description(),
416 "Document(data='\\xc0\\x80\\xf5\\x80\\x80\\x80\\xfe\\xff')");
417 doc
.set_data(string("\x00\x1f", 2));
418 TEST_EQUAL(doc
.get_description(),
419 "Document(data='\\x00\\x1f')");
420 // Check that backslashes are encoded so output isn't ambiguous.
421 doc
.set_data("back\\slash");
422 TEST_EQUAL(doc
.get_description(),
423 "Document(data='back\\x5cslash')");