root / tags / 1.0.8 / xapian-core / tests / api_nodb.cc

Revision 11150, 13.5 kB (checked in by olly, 4 months ago)

Backport changes from trunk:
api/omenquire.cc: Simplify RSet::remove_document() and
RSet::contains() a little. Fix output of RSet::get_description().
tests/api_nodb.cc: Add regression test rset4 for
RSet::get_description() fix.
tests/api_nodb.cc: Clarify in comments that this affected 1.0.7.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* api_nodb.cc: tests which don't use any of the backends
2 *
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007 Olly Betts
6 * Copyright 2006 Lemur Consulting Ltd
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21 * USA
22 */
23
24#include <config.h>
25
26#include "api_nodb.h"
27
28#include <string>
29#include <vector>
30#include "autoptr.h"
31
32#include <xapian.h>
33
34#include "apitest.h"
35#include "testsuite.h"
36#include "testutils.h"
37#include "utils.h"
38
39#include <list>
40
41using namespace std;
42
43// always succeeds
44DEFINE_TESTCASE(trivial1, !backend) {
45    return true;
46}
47
48// tests that get_query_terms() returns the terms in the right order
49DEFINE_TESTCASE(getqterms1, !backend) {
50    list<string> answers_list;
51    answers_list.push_back("one");
52    answers_list.push_back("two");
53    answers_list.push_back("three");
54    answers_list.push_back("four");
55
56    Xapian::Query myquery(Xapian::Query::OP_OR,
57            Xapian::Query(Xapian::Query::OP_AND,
58                    Xapian::Query("one", 1, 1),
59                    Xapian::Query("three", 1, 3)),
60            Xapian::Query(Xapian::Query::OP_OR,
61                    Xapian::Query("four", 1, 4),
62                    Xapian::Query("two", 1, 2)));
63
64    list<string> list1;
65    {
66        Xapian::TermIterator t;
67        for (t = myquery.get_terms_begin(); t != myquery.get_terms_end(); ++t)
68            list1.push_back(*t);
69    }
70    TEST(list1 == answers_list);
71    list<string> list2(myquery.get_terms_begin(), myquery.get_terms_end());
72    TEST(list2 == answers_list);
73    return true;
74}
75
76// tests that get_query_terms() doesn't SEGV on an empty query
77// (regression test for bug in 0.9.0)
78DEFINE_TESTCASE(getqterms2, !backend) {
79    Xapian::Query empty_query;
80    TEST_EQUAL(empty_query.get_terms_begin(), empty_query.get_terms_end());
81    return true;
82}
83
84// tests that empty queries work correctly
85DEFINE_TESTCASE(emptyquery2, !backend) {
86    // test that Query::empty() is true for an empty query.
87    TEST(Xapian::Query().empty());
88    // test that an empty query has length 0
89    TEST(Xapian::Query().get_length() == 0);
90    vector<Xapian::Query> v;
91    TEST(Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end()).empty());
92    TEST(Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end()).get_length() == 0);
93    return true;
94}
95
96/// Regression test for behaviour for an empty query with AND_NOT.
97DEFINE_TESTCASE(emptyquery3, !backend) {
98    static const Xapian::Query::op ops[] = {
99        Xapian::Query::OP_AND,
100        Xapian::Query::OP_OR,
101        Xapian::Query::OP_XOR,
102        Xapian::Query::OP_AND_MAYBE,
103        Xapian::Query::OP_AND_NOT
104    };
105
106    for (size_t i = 0; i < sizeof(ops) / sizeof(ops[0]); ++i) {
107        tout << "Testing op #" << i << endl;
108        Xapian::Query empty;
109        Xapian::Query q("test");
110        Xapian::Query qcombine(ops[i], empty, q);
111        tout << qcombine.get_description() << endl;
112        Xapian::Query qcombine2(ops[i], q, empty);
113        tout << qcombine2.get_description() << endl;
114    }
115
116    return true;
117}
118
119// tests that query lengths are calculated correctly
120DEFINE_TESTCASE(querylen1, !backend) {
121    // test that a simple query has the right length
122    Xapian::Query myquery;
123    myquery = Xapian::Query(Xapian::Query::OP_OR,
124                      Xapian::Query("foo"),
125                      Xapian::Query("bar"));
126    myquery = Xapian::Query(Xapian::Query::OP_AND,
127                      myquery,
128                      Xapian::Query(Xapian::Query::OP_OR,
129                              Xapian::Query("wibble"),
130                              Xapian::Query("spoon")));
131
132    TEST_EQUAL(myquery.get_length(), 4);
133    TEST(!myquery.empty());
134    return true;
135}
136
137// tests that query lengths are calculated correctly
138DEFINE_TESTCASE(querylen2, !backend) {
139    // test with an even bigger and strange query
140    string terms[3] = {
141        "foo",
142        "bar",
143        "baz"
144    };
145    Xapian::Query queries[3] = {
146        Xapian::Query("wibble"),
147        Xapian::Query("wobble"),
148        Xapian::Query(Xapian::Query::OP_OR, string("jelly"), string("belly"))
149    };
150
151    Xapian::Query myquery;
152    vector<string> v1(terms, terms + 3);
153    vector<Xapian::Query> v2(queries, queries + 3);
154    vector<Xapian::Query *> v3;
155    AutoPtr<Xapian::Query> dynquery1(new Xapian::Query(Xapian::Query::OP_AND,
156                                           string("ball"),
157                                           string("club")));
158    AutoPtr<Xapian::Query> dynquery2(new Xapian::Query("ring"));
159    v3.push_back(dynquery1.get());
160    v3.push_back(dynquery2.get());
161
162    Xapian::Query myq1 = Xapian::Query(Xapian::Query::OP_AND, v1.begin(), v1.end());
163    tout << "myq1=" << myq1 << "\n";
164    TEST_EQUAL(myq1.get_length(), 3);
165
166    Xapian::Query myq2_1 = Xapian::Query(Xapian::Query::OP_OR, v2.begin(), v2.end());
167    tout << "myq2_1=" << myq2_1 << "\n";
168    TEST_EQUAL(myq2_1.get_length(), 4);
169
170    Xapian::Query myq2_2 = Xapian::Query(Xapian::Query::OP_AND, v3.begin(), v3.end());
171    tout << "myq2_2=" << myq2_2 << "\n";
172    TEST_EQUAL(myq2_2.get_length(), 3);
173
174    Xapian::Query myq2 = Xapian::Query(Xapian::Query::OP_OR, myq2_1, myq2_2);
175    tout << "myq2=" << myq2 << "\n";
176    TEST_EQUAL(myq2.get_length(), 7);
177
178    myquery = Xapian::Query(Xapian::Query::OP_OR, myq1, myq2);
179    tout << "myquery=" << myquery << "\n";
180    TEST_EQUAL(myquery.get_length(), 10);
181
182    return true;
183}
184
185// tests that queries validate correctly
186DEFINE_TESTCASE(queryvalid1, !backend) {
187    vector<Xapian::Query> v1;
188    // Need two arguments
189    TEST_EXCEPTION(Xapian::InvalidArgumentError,
190                   Xapian::Query(Xapian::Query::OP_AND_NOT, v1.begin(), v1.end()));
191    tout << "ANDNOT () checked" << endl;
192    v1.push_back(Xapian::Query("bad"));
193    TEST_EXCEPTION(Xapian::InvalidArgumentError,
194                   Xapian::Query(Xapian::Query::OP_AND_NOT, v1.begin(), v1.end()));
195    tout << "ANDNOT (\"bad\") checked" << endl;
196    v1.clear();
197    v1.push_back(Xapian::Query());
198    TEST_EXCEPTION(Xapian::InvalidArgumentError,
199                   Xapian::Query(Xapian::Query::OP_AND_NOT, v1.begin(), v1.end()));
200    tout << "ANDNOT (Xapian::Query()) checked" << endl;
201    Xapian::Query q2(Xapian::Query::OP_XOR, Xapian::Query("foo"), Xapian::Query("bar"));
202    tout << "XOR (\"foo\", \"bar\") checked" << endl;
203    return true;
204}
205
206// tests that collapsing of queries includes subqueries
207DEFINE_TESTCASE(subqcollapse1, !backend) {
208    Xapian::Query queries1[3] = {
209        Xapian::Query("wibble"),
210        Xapian::Query("wobble"),
211        Xapian::Query(Xapian::Query::OP_OR, string("jelly"), string("belly"))
212    };
213
214    Xapian::Query queries2[3] = {
215        Xapian::Query(Xapian::Query::OP_AND, string("jelly"), string("belly")),
216        Xapian::Query("wibble"),
217        Xapian::Query("wobble")
218    };
219
220    vector<Xapian::Query> vec1(queries1, queries1 + 3);
221    Xapian::Query myquery1(Xapian::Query::OP_OR, vec1.begin(), vec1.end());
222    TEST_EQUAL(myquery1.get_description(),
223               "Xapian::Query((wibble OR wobble OR jelly OR belly))");
224
225    vector<Xapian::Query> vec2(queries2, queries2 + 3);
226    Xapian::Query myquery2(Xapian::Query::OP_AND, vec2.begin(), vec2.end());
227    TEST_EQUAL(myquery2.get_description(),
228               "Xapian::Query((jelly AND belly AND wibble AND wobble))");
229
230    return true;
231}
232
233// test behaviour when creating a query from an empty vector
234DEFINE_TESTCASE(emptyquerypart1, !backend) {
235    vector<string> emptyterms;
236    Xapian::Query query(Xapian::Query::OP_OR, emptyterms.begin(), emptyterms.end());
237    TEST(Xapian::Query(Xapian::Query::OP_AND, query, Xapian::Query("x")).empty());
238    TEST(Xapian::Query(Xapian::Query::OP_AND, query, Xapian::Query("x")).get_length() == 0);
239    TEST(!Xapian::Query(Xapian::Query::OP_OR, query, Xapian::Query("x")).empty());
240    TEST(Xapian::Query(Xapian::Query::OP_OR, query, Xapian::Query("x")).get_length() == 1);
241    return true;
242}
243
244DEFINE_TESTCASE(singlesubq1, !backend) {
245    vector<string> oneterm;
246    oneterm.push_back("solo");
247    Xapian::Query q_eliteset(Xapian::Query::OP_ELITE_SET, oneterm.begin(), oneterm.end(), 1);
248    Xapian::Query q_near(Xapian::Query::OP_NEAR, oneterm.begin(), oneterm.end(), 1);
249    Xapian::Query q_phrase(Xapian::Query::OP_PHRASE, oneterm.begin(), oneterm.end(), 1);
250    return true;
251}
252
253DEFINE_TESTCASE(stemlangs1, !backend) {
254    string langs = Xapian::Stem::get_available_languages();
255    tout << "available languages '" << langs << "'" << endl;
256    TEST(!langs.empty());
257
258    // Also test the language codes.
259    langs += " da nl en fi fr de hu it no pt ro ru es sv tr";
260
261    string::size_type i = 0;
262    while (true) {
263        string::size_type spc = langs.find(' ', i);
264        // The only spaces in langs should be a single one between each pair
265        // of language names.
266        TEST_NOT_EQUAL(i, spc);
267
268        // Try making a stemmer for this language.  We should be able to create
269        // it without an exception being thrown.
270        string language = langs.substr(i, spc - i);
271        tout << "checking language code '" << language << "' works" << endl;
272        Xapian::Stem stemmer(language);
273
274        if (spc == string::npos) break;
275        i = spc + 1;
276    }
277
278    // Check that we get an exception for a bogus language name.
279    TEST_EXCEPTION(Xapian::InvalidArgumentError, Xapian::Stem stemmer("bogus"));
280
281    // Stem("") should give an object which doesn't change any input.
282    Xapian::Stem stem_nothing = Xapian::Stem("");
283
284    return true;
285}
286
287// Some simple tests of the built in weighting schemes.
288DEFINE_TESTCASE(weight1, !backend) {
289    Xapian::Weight * wt;
290
291    Xapian::BoolWeight boolweight;
292    TEST_EQUAL(boolweight.name(), "Bool");
293    wt = Xapian::BoolWeight().unserialise(boolweight.serialise());
294    TEST_EQUAL(boolweight.serialise(), wt->serialise());
295    delete wt;
296
297    Xapian::TradWeight tradweight_dflt;
298    Xapian::TradWeight tradweight(1.0);
299    TEST_EQUAL(tradweight.name(), "Trad");
300    TEST_EQUAL(tradweight_dflt.serialise(), tradweight.serialise());
301    wt = Xapian::TradWeight().unserialise(tradweight.serialise());
302    TEST_EQUAL(tradweight.serialise(), wt->serialise());
303    delete wt;
304
305    Xapian::TradWeight tradweight2(2.0);
306    TEST_NOT_EQUAL(tradweight.serialise(), tradweight2.serialise());
307
308    Xapian::BM25Weight bm25weight_dflt;
309    Xapian::BM25Weight bm25weight(1, 0, 1, 0.5, 0.5);
310    TEST_EQUAL(bm25weight.name(), "BM25");
311    TEST_EQUAL(bm25weight_dflt.serialise(), bm25weight.serialise());
312    wt = Xapian::BM25Weight().unserialise(bm25weight.serialise());
313    TEST_EQUAL(bm25weight.serialise(), wt->serialise());
314    delete wt;
315
316    Xapian::BM25Weight bm25weight2(1, 0.5, 1, 0.5, 0.5);
317    TEST_NOT_EQUAL(bm25weight.serialise(), bm25weight2.serialise());
318
319    return true;
320}
321
322// Regression test.
323DEFINE_TESTCASE(nosuchdb1, !backend) {
324    // This is a "nodb" test because it doesn't test a particular backend.
325    TEST_EXCEPTION(Xapian::DatabaseOpeningError,
326                   Xapian::Database db("NOsuChdaTabASe"));
327    return true;
328}
329
330// Feature tests for value manipulations.
331DEFINE_TESTCASE(addvalue1, !backend) {
332    // Regression test for add_value on an existing value (bug#82).
333    Xapian::Document doc;
334    doc.add_value(1, "original");
335    doc.add_value(1, "replacement");
336    TEST_EQUAL(doc.get_value(1), "replacement");
337
338    doc.add_value(2, "too");
339    doc.add_value(3, "free");
340    doc.add_value(4, "for");
341
342    doc.remove_value(2);
343    doc.remove_value(4);
344    TEST_EQUAL(doc.get_value(0), "");
345    TEST_EQUAL(doc.get_value(1), "replacement");
346    TEST_EQUAL(doc.get_value(2), "");
347    TEST_EQUAL(doc.get_value(3), "free");
348    TEST_EQUAL(doc.get_value(4), "");
349
350    return true;
351}
352
353// tests that the collapsing on termpos optimisation gives correct query length
354DEFINE_TESTCASE(poscollapse2, !backend) {
355    Xapian::Query q(Xapian::Query::OP_OR, Xapian::Query("this", 1, 1), Xapian::Query("this", 1, 1));
356    TEST_EQUAL(q.get_length(), 2);
357    return true;
358}
359
360// regression test of querying an uninitialised database: should report an
361// error; used to segfault with 1.0.0.
362DEFINE_TESTCASE(uninitdb1, !backend) {
363    Xapian::Database db;
364    TEST_EXCEPTION(Xapian::InvalidArgumentError,
365                   Xapian::Enquire enq(db));
366    return true;
367}
368
369// Test a scaleweight query applied to a match nothing query
370DEFINE_TESTCASE(scaleweight3, !backend) {
371    Xapian::Query matchnothing(Xapian::Query::MatchNothing);
372    Xapian::Query query(Xapian::Query::OP_SCALE_WEIGHT, matchnothing, 3.0);
373    TEST_EQUAL(query.get_description(), "Xapian::Query()");
374    return true;
375}
376
377// Test that scaling by a weight close to 1 is optimised away.
378DEFINE_TESTCASE(scaleweight4, !backend) {
379    // Factor is a double which, when multiplied by its reciprocal, doesn't
380    // give exactly 1.0
381    double factor = 179.76931348623157e306;
382    double nearly1 = factor * (1.0 / factor);
383
384    TEST_NOT_EQUAL(nearly1, 1.0);
385    Xapian::Query foo("foo");
386    Xapian::Query foo_nearly1(Xapian::Query::OP_SCALE_WEIGHT, foo, nearly1);
387    TEST_EQUAL(foo_nearly1.get_description(), "Xapian::Query(foo)");
388
389    return true;
390}
391
392// Regression test - RSet::get_description() gave a malformed answer in 1.0.7.
393DEFINE_TESTCASE(rset4, !backend) {
394    Xapian::RSet rset;
395    rset.add_document(1);
396    // In 1.0.7 this gave: RSet(RSet(RSet::Internal(, 1))
397    TEST_STRINGS_EQUAL(rset.get_description(), "RSet(RSet::Internal(1))");
398    return true;
399}
400
401// Check that Query(OP_VALUE_GE, 0, "") -> Query::MatchAll.
402DEFINE_TESTCASE(opvaluege1, !backend) {
403    Xapian::Query query(Xapian::Query::OP_VALUE_GE, 0, "");
404    TEST_STRINGS_EQUAL(query.get_description(), Xapian::Query::MatchAll.get_description());
405    return true;
406}
Note: See TracBrowser for help on using the browser.