1 /** @file
2  * @brief tests which need a backend with positional information
3  */
4 /* Copyright 1999,2000,2001 BrightStation PLC
5  * Copyright 2002 Ananova Ltd
6  * Copyright 2002,2003,2004,2005,2006,2007,2009,2016 Olly Betts
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_posdb.h"
27 
28 #include <string>
29 #include <vector>
30 
31 using namespace std;
32 
33 #include <xapian.h>
34 #include "testsuite.h"
35 #include "testutils.h"
36 
37 #include "apitest.h"
38 
39 /// Simple test of NEAR
DEFINE_TESTCASE(near1,positional)40 DEFINE_TESTCASE(near1, positional) {
41     Xapian::Database mydb(get_database("apitest_phrase"));
42     Xapian::Enquire enquire(mydb);
43     Xapian::Stem stemmer("english");
44     enquire.set_weighting_scheme(Xapian::BoolWeight());
45 
46     // make a query
47     vector<Xapian::Query> subqs;
48     Xapian::Query q;
49     subqs.push_back(Xapian::Query(stemmer("phrase")));
50     subqs.push_back(Xapian::Query(stemmer("fridge")));
51     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
52     enquire.set_query(q);
53 
54     // retrieve the top ten results
55     Xapian::MSet mymset = enquire.get_mset(0, 10);
56     mset_expect_order(mymset);
57 
58     subqs.clear();
59     subqs.push_back(Xapian::Query(stemmer("phrase")));
60     subqs.push_back(Xapian::Query(stemmer("near")));
61     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 2);
62     enquire.set_query(q);
63 
64     // retrieve the top ten results
65     mymset = enquire.get_mset(0, 10);
66     mset_expect_order(mymset, 3);
67 
68     subqs.clear();
69     subqs.push_back(Xapian::Query(stemmer("phrase")));
70     subqs.push_back(Xapian::Query(stemmer("near")));
71     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 3);
72     enquire.set_query(q);
73 
74     // retrieve the top ten results
75     mymset = enquire.get_mset(0, 10);
76     mset_expect_order(mymset, 1, 3);
77 
78     subqs.clear();
79     subqs.push_back(Xapian::Query(stemmer("phrase")));
80     subqs.push_back(Xapian::Query(stemmer("near")));
81     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 5);
82     enquire.set_query(q);
83 
84     // retrieve the top ten results
85     mymset = enquire.get_mset(0, 10);
86     mset_expect_order(mymset, 1, 3);
87 
88     subqs.clear();
89     subqs.push_back(Xapian::Query(stemmer("phrase")));
90     subqs.push_back(Xapian::Query(stemmer("near")));
91     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 6);
92     enquire.set_query(q);
93 
94     // retrieve the top ten results
95     mymset = enquire.get_mset(0, 10);
96     mset_expect_order(mymset, 1, 2, 3);
97 
98     subqs.clear();
99     subqs.push_back(Xapian::Query(stemmer("leave")));
100     subqs.push_back(Xapian::Query(stemmer("fridge")));
101     subqs.push_back(Xapian::Query(stemmer("on")));
102     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 3);
103     enquire.set_query(q);
104 
105     // retrieve the top ten results
106     mymset = enquire.get_mset(0, 10);
107     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9);
108 
109     subqs.clear();
110     subqs.push_back(Xapian::Query(stemmer("leave")));
111     subqs.push_back(Xapian::Query(stemmer("fridge")));
112     subqs.push_back(Xapian::Query(stemmer("on")));
113     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 4);
114     enquire.set_query(q);
115 
116     // retrieve the top ten results
117     mymset = enquire.get_mset(0, 10);
118     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10);
119 
120     subqs.clear();
121     subqs.push_back(Xapian::Query(stemmer("leave")));
122     subqs.push_back(Xapian::Query(stemmer("fridge")));
123     subqs.push_back(Xapian::Query(stemmer("on")));
124     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 5);
125     enquire.set_query(q);
126 
127     // retrieve the top ten results
128     mymset = enquire.get_mset(0, 10);
129     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11);
130 
131     subqs.clear();
132     subqs.push_back(Xapian::Query(stemmer("leave")));
133     subqs.push_back(Xapian::Query(stemmer("fridge")));
134     subqs.push_back(Xapian::Query(stemmer("on")));
135     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 6);
136     enquire.set_query(q);
137 
138     // retrieve the top ten results
139     mymset = enquire.get_mset(0, 10);
140     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11, 12);
141 
142     subqs.clear();
143     subqs.push_back(Xapian::Query(stemmer("leave")));
144     subqs.push_back(Xapian::Query(stemmer("fridge")));
145     subqs.push_back(Xapian::Query(stemmer("on")));
146     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 7);
147     enquire.set_query(q);
148 
149     // retrieve the top twenty results
150     mymset = enquire.get_mset(0, 20);
151     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
152 
153     subqs.clear();
154     subqs.push_back(Xapian::Query(stemmer("leave")));
155     subqs.push_back(Xapian::Query(stemmer("fridge")));
156     subqs.push_back(Xapian::Query(stemmer("on")));
157     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 8);
158     enquire.set_query(q);
159 
160     // retrieve the top twenty results
161     mymset = enquire.get_mset(0, 20);
162     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
163 
164     subqs.clear();
165     subqs.push_back(Xapian::Query(stemmer("leave")));
166     subqs.push_back(Xapian::Query(stemmer("fridge")));
167     subqs.push_back(Xapian::Query(stemmer("on")));
168     // test really large window size
169     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 999999999);
170     enquire.set_query(q);
171 
172     // retrieve the top twenty results
173     mymset = enquire.get_mset(0, 20);
174     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
175 }
176 
177 /// Test NEAR over operators
DEFINE_TESTCASE(near2,positional)178 DEFINE_TESTCASE(near2, positional) {
179     Xapian::Database mydb(get_database("apitest_phrase"));
180     Xapian::Enquire enquire(mydb);
181     Xapian::Stem stemmer("english");
182     enquire.set_weighting_scheme(Xapian::BoolWeight());
183     Xapian::MSet mymset;
184 
185     // make a query
186     vector<Xapian::Query> subqs;
187     Xapian::Query q;
188     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
189 			    Xapian::Query(stemmer("phrase")),
190 			    Xapian::Query(stemmer("near"))));
191     subqs.push_back(Xapian::Query(stemmer("and")));
192     TEST_EXCEPTION(Xapian::UnimplementedError,
193 	q = Xapian::Query(q.OP_NEAR, subqs.begin(), subqs.end(), 2);
194 	enquire.set_query(q);
195 
196 	// retrieve the top ten results
197 	mymset = enquire.get_mset(0, 10)
198     );
199 #if 0 // Disable until we reimplement this.
200     mset_expect_order(mymset, 1);
201 
202     subqs.clear();
203     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
204 			    Xapian::Query(stemmer("phrase")),
205 			    Xapian::Query(stemmer("near"))));
206     subqs.push_back(Xapian::Query(stemmer("operator")));
207     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 2);
208     enquire.set_query(q);
209 
210     // retrieve the top ten results
211     mymset = enquire.get_mset(0, 10);
212     mset_expect_order(mymset, 2);
213 
214     subqs.clear();
215     subqs.push_back(Xapian::Query(stemmer("operator")));
216     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
217 			    Xapian::Query(stemmer("phrase")),
218 			    Xapian::Query(stemmer("near"))));
219     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 2);
220     enquire.set_query(q);
221 
222     // retrieve the top ten results
223     mymset = enquire.get_mset(0, 10);
224     mset_expect_order(mymset, 2);
225 #endif
226 }
227 
228 /// Simple test of PHRASE
DEFINE_TESTCASE(phrase1,positional)229 DEFINE_TESTCASE(phrase1, positional) {
230     Xapian::Database mydb(get_database("apitest_phrase"));
231     Xapian::Enquire enquire(mydb);
232     Xapian::Stem stemmer("english");
233     enquire.set_weighting_scheme(Xapian::BoolWeight());
234 
235     // make a query
236     vector<Xapian::Query> subqs;
237     Xapian::Query q;
238     subqs.push_back(Xapian::Query(stemmer("phrase")));
239     subqs.push_back(Xapian::Query(stemmer("fridge")));
240     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
241     enquire.set_query(q);
242 
243     // retrieve the top ten results
244     Xapian::MSet mymset = enquire.get_mset(0, 10);
245     mset_expect_order(mymset);
246 
247     subqs.clear();
248     subqs.push_back(Xapian::Query(stemmer("phrase")));
249     subqs.push_back(Xapian::Query(stemmer("near")));
250     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
251     enquire.set_query(q);
252 
253     // retrieve the top ten results
254     mymset = enquire.get_mset(0, 10);
255     mset_expect_order(mymset);
256 
257     subqs.clear();
258     subqs.push_back(Xapian::Query(stemmer("phrase")));
259     subqs.push_back(Xapian::Query(stemmer("near")));
260     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 3);
261     enquire.set_query(q);
262 
263     // retrieve the top ten results
264     mymset = enquire.get_mset(0, 10);
265     mset_expect_order(mymset, 1);
266 
267     subqs.clear();
268     subqs.push_back(Xapian::Query(stemmer("phrase")));
269     subqs.push_back(Xapian::Query(stemmer("near")));
270     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 5);
271     enquire.set_query(q);
272 
273     // retrieve the top ten results
274     mymset = enquire.get_mset(0, 10);
275     mset_expect_order(mymset, 1);
276 
277     subqs.clear();
278     subqs.push_back(Xapian::Query(stemmer("phrase")));
279     subqs.push_back(Xapian::Query(stemmer("near")));
280     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 6);
281     enquire.set_query(q);
282 
283     // retrieve the top ten results
284     mymset = enquire.get_mset(0, 10);
285     mset_expect_order(mymset, 1, 2);
286 
287     subqs.clear();
288     subqs.push_back(Xapian::Query(stemmer("leave")));
289     subqs.push_back(Xapian::Query(stemmer("fridge")));
290     subqs.push_back(Xapian::Query(stemmer("on")));
291     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 3);
292     enquire.set_query(q);
293 
294     // retrieve the top ten results
295     mymset = enquire.get_mset(0, 10);
296     mset_expect_order(mymset, 4);
297 
298     subqs.clear();
299     subqs.push_back(Xapian::Query(stemmer("leave")));
300     subqs.push_back(Xapian::Query(stemmer("fridge")));
301     subqs.push_back(Xapian::Query(stemmer("on")));
302     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 4);
303     enquire.set_query(q);
304 
305     // retrieve the top ten results
306     mymset = enquire.get_mset(0, 10);
307     mset_expect_order(mymset, 4);
308 
309     subqs.clear();
310     subqs.push_back(Xapian::Query(stemmer("leave")));
311     subqs.push_back(Xapian::Query(stemmer("fridge")));
312     subqs.push_back(Xapian::Query(stemmer("on")));
313     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 5);
314     enquire.set_query(q);
315 
316     // retrieve the top ten results
317     mymset = enquire.get_mset(0, 10);
318     mset_expect_order(mymset, 4);
319 
320     subqs.clear();
321     subqs.push_back(Xapian::Query(stemmer("leave")));
322     subqs.push_back(Xapian::Query(stemmer("fridge")));
323     subqs.push_back(Xapian::Query(stemmer("on")));
324     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 6);
325     enquire.set_query(q);
326 
327     // retrieve the top ten results
328     mymset = enquire.get_mset(0, 10);
329     mset_expect_order(mymset, 4);
330 
331     subqs.clear();
332     subqs.push_back(Xapian::Query(stemmer("leave")));
333     subqs.push_back(Xapian::Query(stemmer("fridge")));
334     subqs.push_back(Xapian::Query(stemmer("on")));
335     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 7);
336     enquire.set_query(q);
337 
338     // retrieve the top twenty results
339     mymset = enquire.get_mset(0, 20);
340     mset_expect_order(mymset, 4);
341 
342     subqs.clear();
343     subqs.push_back(Xapian::Query(stemmer("leave")));
344     subqs.push_back(Xapian::Query(stemmer("fridge")));
345     subqs.push_back(Xapian::Query(stemmer("on")));
346     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 8);
347     enquire.set_query(q);
348 
349     // retrieve the top 20 results
350     mymset = enquire.get_mset(0, 20);
351     mset_expect_order(mymset, 4);
352 
353     // test really large window size
354     subqs.clear();
355     subqs.push_back(Xapian::Query(stemmer("leave")));
356     subqs.push_back(Xapian::Query(stemmer("fridge")));
357     subqs.push_back(Xapian::Query(stemmer("on")));
358     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 999999999);
359     enquire.set_query(q);
360 
361     // retrieve the top 20 results
362     mymset = enquire.get_mset(0, 20);
363     mset_expect_order(mymset, 4);
364 
365     // regression test (was matching doc 15, should fail)
366     subqs.clear();
367     subqs.push_back(Xapian::Query(stemmer("first")));
368     subqs.push_back(Xapian::Query(stemmer("second")));
369     subqs.push_back(Xapian::Query(stemmer("third")));
370     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 9);
371     enquire.set_query(q);
372 
373     // retrieve the top ten results
374     mymset = enquire.get_mset(0, 10);
375     mset_expect_order(mymset);
376 
377     // regression test (should match doc 15, make sure still does with fix)
378     subqs.clear();
379     subqs.push_back(Xapian::Query(stemmer("first")));
380     subqs.push_back(Xapian::Query(stemmer("second")));
381     subqs.push_back(Xapian::Query(stemmer("third")));
382     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 10);
383     enquire.set_query(q);
384 
385     // retrieve the top ten results
386     mymset = enquire.get_mset(0, 10);
387     mset_expect_order(mymset, 15);
388 
389     // regression test (phrase matching was getting order wrong when
390     // build_and_tree reordered vector of PostLists)
391     subqs.clear();
392     subqs.push_back(Xapian::Query(stemmer("milk")));
393     subqs.push_back(Xapian::Query(stemmer("rare")));
394     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
395     enquire.set_query(q);
396 
397     // retrieve the top ten results
398     mymset = enquire.get_mset(0, 10);
399     mset_expect_order(mymset, 16);
400 
401     // regression test (phrase matching was getting order wrong when
402     // build_and_tree reordered vector of PostLists)
403     subqs.clear();
404     subqs.push_back(Xapian::Query(stemmer("rare")));
405     subqs.push_back(Xapian::Query(stemmer("milk")));
406     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
407     enquire.set_query(q);
408 
409     // retrieve the top ten results
410     mymset = enquire.get_mset(0, 10);
411     mset_expect_order(mymset, 17);
412 }
413 
414 /// Test PHRASE over operators
DEFINE_TESTCASE(phrase2,positional)415 DEFINE_TESTCASE(phrase2, positional) {
416     Xapian::Database mydb(get_database("apitest_phrase"));
417     Xapian::Enquire enquire(mydb);
418     Xapian::Stem stemmer("english");
419     enquire.set_weighting_scheme(Xapian::BoolWeight());
420     Xapian::MSet mymset;
421 
422     // make a query
423     vector<Xapian::Query> subqs;
424     Xapian::Query q;
425     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
426 			    Xapian::Query(stemmer("phrase")),
427 			    Xapian::Query(stemmer("near"))));
428     subqs.push_back(Xapian::Query(stemmer("and")));
429     TEST_EXCEPTION(Xapian::UnimplementedError,
430 	q = Xapian::Query(q.OP_PHRASE, subqs.begin(), subqs.end(), 2);
431 	enquire.set_query(q);
432 
433 	// retrieve the top ten results
434 	mymset = enquire.get_mset(0, 10)
435     );
436 #if 0 // Disable until we reimplement this.
437     mset_expect_order(mymset);
438 
439     subqs.clear();
440     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
441 			    Xapian::Query(stemmer("phrase")),
442 			    Xapian::Query(stemmer("near"))));
443     subqs.push_back(Xapian::Query(stemmer("operator")));
444     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
445     enquire.set_query(q);
446 
447     // retrieve the top ten results
448     mymset = enquire.get_mset(0, 10);
449     mset_expect_order(mymset, 2);
450 
451     subqs.clear();
452     subqs.push_back(Xapian::Query(stemmer("operator")));
453     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
454 			    Xapian::Query(stemmer("phrase")),
455 			    Xapian::Query(stemmer("near"))));
456     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
457     enquire.set_query(q);
458 
459     // retrieve the top ten results
460     mymset = enquire.get_mset(0, 10);
461     mset_expect_order(mymset);
462 #endif
463 }
464 
465 /// Test getting position lists from databases
DEFINE_TESTCASE(poslist1,positional)466 DEFINE_TESTCASE(poslist1, positional) {
467     Xapian::Database mydb(get_database("apitest_poslist"));
468 
469     Xapian::Stem stemmer("english");
470     string term = stemmer("sponge");
471 
472     Xapian::PositionIterator pli = mydb.positionlist_begin(2, term);
473 
474     TEST(pli != mydb.positionlist_end(2, term));
475     TEST_EQUAL(*pli, 1);
476     pli++;
477     TEST(pli != mydb.positionlist_end(2, term));
478     TEST_EQUAL(*pli, 2);
479     pli++;
480     TEST(pli != mydb.positionlist_end(2, term));
481     TEST_EQUAL(*pli, 3);
482     pli++;
483     TEST(pli != mydb.positionlist_end(2, term));
484     TEST_EQUAL(*pli, 5);
485     pli++;
486     TEST(pli != mydb.positionlist_end(2, term));
487     TEST_EQUAL(*pli, 8);
488     pli++;
489     TEST(pli != mydb.positionlist_end(2, term));
490     TEST_EQUAL(*pli, 13);
491     pli++;
492     TEST(pli != mydb.positionlist_end(2, term));
493     TEST_EQUAL(*pli, 21);
494     pli++;
495     TEST(pli != mydb.positionlist_end(2, term));
496     TEST_EQUAL(*pli, 34);
497     pli++;
498     TEST(pli == mydb.positionlist_end(2, term));
499 }
500 
DEFINE_TESTCASE(poslist2,positional && writable)501 DEFINE_TESTCASE(poslist2, positional && writable) {
502     Xapian::WritableDatabase db = get_writable_database();
503 
504     Xapian::Document doc;
505     doc.add_term("nopos");
506     Xapian::docid did = db.add_document(doc);
507 
508     // Check what happens when term doesn't exist - should give an empty list.
509     // Threw RangeError in Xapian < 1.1.0.
510     TEST_EQUAL(db.positionlist_begin(did, "nosuchterm"),
511 	       db.positionlist_end(did, "nosuchterm"));
512 
513     // Check what happens when the document doesn't even exist - should give
514     // an empty list.  Threw DocNotFoundError in Xapian < 1.1.0.
515     TEST_EQUAL(db.positionlist_begin(123, "nosuchterm"),
516 	       db.positionlist_end(123, "nosuchterm"));
517 
518     TEST_EQUAL(db.positionlist_begin(did, "nopos"),
519 	       db.positionlist_end(did, "nopos"));
520 
521     Xapian::Document doc2 = db.get_document(did);
522 
523     Xapian::TermIterator term = doc2.termlist_begin();
524 
525     {
526 	Xapian::PositionIterator i = term.positionlist_begin();
527 	TEST_EQUAL(i, term.positionlist_end());
528     }
529 
530     Xapian::Document doc3;
531     doc3.add_posting("hadpos", 1);
532     Xapian::docid did2 = db.add_document(doc3);
533 
534     Xapian::Document doc4 = db.get_document(did2);
535     doc4.remove_posting("hadpos", 1);
536     db.replace_document(did2, doc4);
537 
538     {
539 	Xapian::PositionIterator i = db.positionlist_begin(did2, "hadpos");
540 	TEST_EQUAL(i, db.positionlist_end(did2, "hadpos"));
541     }
542 
543     db.delete_document(did);
544     // Check what happens when the document doesn't exist (but once did).
545     TEST_EQUAL(db.positionlist_begin(did, "nosuchterm"),
546 	       db.positionlist_end(did, "nosuchterm"));
547 }
548 
549 /// Test playing with a positionlist, testing skip_to in particular.
550 /// (used to be quartztest's test_positionlist1).
DEFINE_TESTCASE(poslist3,positional && writable)551 DEFINE_TESTCASE(poslist3, positional && writable) {
552     Xapian::WritableDatabase db = get_writable_database();
553 
554     Xapian::Document document;
555     document.add_posting("foo", 5);
556     document.add_posting("foo", 8);
557     document.add_posting("foo", 10);
558     document.add_posting("foo", 12);
559     db.add_document(document);
560 
561     Xapian::PositionIterator pl = db.positionlist_begin(1, "foo");
562     Xapian::PositionIterator pl_end = db.positionlist_end(1, "foo");
563 
564     TEST(pl != pl_end);
565     TEST_EQUAL(*pl, 5);
566     ++pl;
567     TEST(pl != pl_end);
568     TEST_EQUAL(*pl, 8);
569     ++pl;
570     TEST(pl != pl_end);
571     TEST_EQUAL(*pl, 10);
572     ++pl;
573     TEST(pl != pl_end);
574     TEST_EQUAL(*pl, 12);
575     ++pl;
576     TEST(pl == pl_end);
577 
578     pl = db.positionlist_begin(1, "foo");
579     pl.skip_to(5);
580     TEST(pl != pl_end);
581     TEST_EQUAL(*pl, 5);
582 
583     pl.skip_to(9);
584     TEST(pl != pl_end);
585     TEST_EQUAL(*pl, 10);
586 
587     ++pl;
588     TEST(pl != pl_end);
589     TEST_EQUAL(*pl, 12);
590 
591     pl.skip_to(12);
592     TEST(pl != pl_end);
593     TEST_EQUAL(*pl, 12);
594 
595     pl.skip_to(13);
596     TEST(pl == pl_end);
597 }
598 
599 // Regression test - in 0.9.4 (and many previous versions) you couldn't get a
600 // PositionIterator from a TermIterator from Database::termlist_begin().
601 //
602 // Also test that positionlist_count() is implemented for this case, which it
603 // wasn't in 1.0.2 and earlier.
DEFINE_TESTCASE(positfromtermit1,positional)604 DEFINE_TESTCASE(positfromtermit1, positional) {
605     Xapian::Database db(get_database("apitest_phrase"));
606     Xapian::TermIterator t(db.termlist_begin(7));
607     TEST_NOT_EQUAL(t, db.termlist_end(7));
608     Xapian::PositionIterator p = t.positionlist_begin();
609     TEST_NOT_EQUAL(p, t.positionlist_end());
610 
611     try {
612 	TEST_EQUAL(t.positionlist_count(), 1);
613 	t.skip_to("on");
614 	TEST_NOT_EQUAL(t, db.termlist_end(7));
615 	TEST_EQUAL(t.positionlist_count(), 2);
616     } catch (const Xapian::UnimplementedError &) {
617 	SKIP_TEST("TermList::positionlist_count() not yet implemented for this backend");
618     }
619 }
620