Document xapian-compact --blocksize takes an argument
[xapian.git] / xapian-core / tests / api_none.cc
bloba81ee1bdd7fa45baea45dac0db391980901f28c9
1 /** @file api_none.cc
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 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 #include <xapian.h>
29 #include "apitest.h"
30 #include "str.h"
31 #include "testsuite.h"
32 #include "testutils.h"
34 using namespace std;
36 // Check the version functions give consistent results.
37 DEFINE_TESTCASE(version1, !backend) {
38 string version = str(Xapian::major_version());
39 version += '.';
40 version += str(Xapian::minor_version());
41 version += '.';
42 version += str(Xapian::revision());
43 TEST_EQUAL(Xapian::version_string(), version);
44 return true;
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 // 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));
64 return true;
67 /// Feature test for Document::add_boolean_term(), new in 1.0.18/1.1.4.
68 DEFINE_TESTCASE(document1, !backend) {
69 Xapian::Document doc;
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());
80 return true;
83 /// Regression test - the docid wasn't initialised prior to 1.0.22/1.2.4.
84 DEFINE_TESTCASE(document2, !backend) {
85 Xapian::Document doc;
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);
89 return true;
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.
95 Xapian::Query q;
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());
107 return true;
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(),\
115 "Query(test)")
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);
128 return true;
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(),\
135 "Query()")
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);
148 return true;
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);
168 return true;
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))");
179 return true;
182 class DestroyedFlag {
183 bool & destroyed;
185 public:
186 DestroyedFlag(bool & destroyed_) : destroyed(destroyed_) {
187 destroyed = false;
190 ~DestroyedFlag() {
191 destroyed = true;
195 class TestValueRangeProcessor : public Xapian::ValueRangeProcessor {
196 DestroyedFlag destroyed;
198 public:
199 TestValueRangeProcessor(bool & destroyed_) : destroyed(destroyed_) { }
201 Xapian::valueno operator()(std::string &, std::string &) {
202 return 42;
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);
213 TEST(!gone);
214 Xapian::QueryParser qp;
215 qp.add_valuerangeprocessor(vrp->release());
216 TEST(!gone);
218 TEST(gone);
220 // Check a second call to release() has no effect.
222 Xapian::ValueRangeProcessor * vrp = new TestValueRangeProcessor(gone);
223 TEST(!gone);
224 Xapian::QueryParser qp;
225 qp.add_valuerangeprocessor(vrp->release());
226 vrp->release();
227 TEST(!gone);
229 TEST(gone);
231 // Test reference counting works, and that a VRP with automatic storage
232 // works OK.
234 TestValueRangeProcessor vrp_auto(gone_auto);
235 TEST(!gone_auto);
237 Xapian::QueryParser qp1;
239 Xapian::QueryParser qp2;
240 Xapian::ValueRangeProcessor * vrp;
241 vrp = new TestValueRangeProcessor(gone);
242 TEST(!gone);
243 qp1.add_valuerangeprocessor(vrp->release());
244 TEST(!gone);
245 qp2.add_valuerangeprocessor(vrp);
246 TEST(!gone);
247 qp2.add_valuerangeprocessor(&vrp_auto);
248 TEST(!gone);
249 TEST(!gone_auto);
251 TEST(!gone);
253 TEST(gone);
254 TEST(!gone_auto);
256 TEST(gone_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);
266 TEST(!gone);
267 qp.add_valuerangeprocessor(vrp);
268 delete vrp;
269 TEST(gone);
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.
276 return true;
279 class TestFieldProcessor : public Xapian::FieldProcessor {
280 DestroyedFlag destroyed;
282 public:
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);
297 TEST(!gone);
298 Xapian::QueryParser qp;
299 qp.add_prefix("foo", proc->release());
300 TEST(!gone);
302 TEST(gone);
304 // Check a second call to release() has no effect.
306 Xapian::FieldProcessor * proc = new TestFieldProcessor(gone);
307 TEST(!gone);
308 Xapian::QueryParser qp;
309 qp.add_prefix("foo", proc->release());
310 proc->release();
311 TEST(!gone);
313 TEST(gone);
315 // Test reference counting works, and that a FieldProcessor with automatic
316 // storage works OK.
318 TestFieldProcessor proc_auto(gone_auto);
319 TEST(!gone_auto);
321 Xapian::QueryParser qp1;
323 Xapian::QueryParser qp2;
324 Xapian::FieldProcessor * proc;
325 proc = new TestFieldProcessor(gone);
326 TEST(!gone);
327 qp1.add_prefix("foo", proc->release());
328 TEST(!gone);
329 qp2.add_prefix("foo", proc);
330 TEST(!gone);
331 qp2.add_prefix("bar", &proc_auto);
332 TEST(!gone);
333 TEST(!gone_auto);
335 TEST(!gone);
337 TEST(gone);
338 TEST(!gone_auto);
340 TEST(gone_auto);
342 return true;
345 class TestMatchSpy : public Xapian::MatchSpy {
346 DestroyedFlag destroyed;
348 public:
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);
363 TEST(!gone);
364 Xapian::Enquire enquire(db);
365 enquire.add_matchspy(spy->release());
366 TEST(!gone);
368 TEST(gone);
370 // Check a second call to release() has no effect.
372 Xapian::MatchSpy * spy = new TestMatchSpy(gone);
373 TEST(!gone);
374 Xapian::Enquire enquire(db);
375 enquire.add_matchspy(spy->release());
376 spy->release();
377 TEST(!gone);
379 TEST(gone);
381 // Test reference counting works, and that a MatchSpy with automatic
382 // storage works OK.
384 TestMatchSpy spy_auto(gone_auto);
385 TEST(!gone_auto);
387 Xapian::Enquire enq1(db);
389 Xapian::Enquire enq2(db);
390 Xapian::MatchSpy * spy;
391 spy = new TestMatchSpy(gone);
392 TEST(!gone);
393 enq1.add_matchspy(spy->release());
394 TEST(!gone);
395 enq2.add_matchspy(spy);
396 TEST(!gone);
397 enq2.add_matchspy(&spy_auto);
398 TEST(!gone);
399 TEST(!gone_auto);
401 TEST(!gone);
403 TEST(gone);
404 TEST(!gone_auto);
406 TEST(gone_auto);
408 return true;
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')");
424 return true;