2 // Run this PHP script using 'make check' in the build tree.
4 /* Simple test to ensure that we can load the xapian module and exercise basic
5 * functionality successfully.
7 * Copyright (C) 2004,2005,2006,2007,2009,2011,2012,2013,2014,2015 Olly Betts
8 * Copyright (C) 2010 Richard Boulton
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
26 # Die on any error, warning, notice, etc.
27 function die_on_error($errno, $errstr, $file, $line) {
30 if ($line !== Null) print ":$line";
36 set_error_handler("die_on_error", -1);
40 # Test the version number reporting functions give plausible results.
41 $v = Xapian
::major_version().'.'.Xapian
::minor_version().'.'.Xapian
::revision();
42 $v2 = Xapian
::version_string();
44 print "Unexpected version output ($v != $v2)\n";
48 $db = Xapian
::inmemory_open();
49 $db2 = Xapian
::inmemory_open();
51 # Check PHP5 handling of Xapian::DocNotFoundError
53 $doc2 = $db->get_document(2);
54 print "Retrieved non-existent document\n";
56 } catch (Exception
$e) {
57 if ($e->getMessage() !== "DocNotFoundError: Docid 2 not found") {
58 print "DocNotFoundError Exception string not as expected, got: '{$e->getMessage()}'\n";
63 # Check QueryParser parsing error.
65 $qp = new XapianQueryParser
;
66 $qp->set_stemmer(new XapianStem("en"));
67 $qp->parse_query("test AND");
68 print "Successfully parsed bad query\n";
70 } catch (Exception
$e) {
71 if ($e->getMessage() !== "QueryParserError: Syntax: <expression> AND <expression>") {
72 print "QueryParserError Exception string not as expected, got: '$e->getMessage()'\n";
77 # Check that open_stub() is wrapped as expected.
79 $db = Xapian
::auto_open_stub("nosuchdir/nosuchdb");
80 print "Opened non-existent stub database\n";
82 } catch (Exception
$e) {
83 if ($e->getMessage() !== "DatabaseOpeningError: Couldn't open stub database file: nosuchdir/nosuchdb (No such file or directory)") {
84 print "DatabaseOpeningError Exception string not as expected, got: '{$e->getMessage()}'\n";
89 # Check that DB_BACKEND_STUB works as expected.
91 $db = new XapianDatabase("nosuchdir/nosuchdb", Xapian
::DB_BACKEND_STUB
);
92 print "Opened non-existent stub database\n";
94 } catch (Exception
$e) {
95 if ($e->getMessage() !== "DatabaseOpeningError: Couldn't open stub database file: nosuchdir/nosuchdb (No such file or directory)") {
96 print "DatabaseOpeningError Exception string not as expected, got: '{$e->getMessage()}'\n";
101 # Check that open_stub() writable form is wrapped as expected.
103 $db = Xapian
::auto_open_stub("nosuchdir/nosuchdb", Xapian
::DB_OPEN
);
104 print "Opened non-existent stub database\n";
106 } catch (Exception
$e) {
107 if ($e->getMessage() !== "DatabaseOpeningError: Couldn't open stub database file: nosuchdir/nosuchdb (No such file or directory)") {
108 print "DatabaseOpeningError Exception string not as expected, got: '{$e->getMessage()}'\n";
113 # Check that DB_BACKEND_STUB works as expected.
115 $db = new XapianWritableDatabase("nosuchdir/nosuchdb",
116 Xapian
::DB_OPEN|Xapian
::DB_BACKEND_STUB
);
117 print "Opened non-existent stub database\n";
119 } catch (Exception
$e) {
120 if ($e->getMessage() !== "DatabaseOpeningError: Couldn't open stub database file: nosuchdir/nosuchdb (No such file or directory)") {
121 print "DatabaseOpeningError Exception string not as expected, got: '{$e->getMessage()}'\n";
126 # Regression test for bug#193, fixed in 1.0.3.
127 $vrp = new XapianNumberValueRangeProcessor(0, '$', true);
131 if (Xapian
::sortable_unserialise($a) != 10) {
132 print Xapian
::sortable_unserialise($a)." != 10\n";
135 if (Xapian
::sortable_unserialise($b) != 20) {
136 print Xapian
::sortable_unserialise($b)." != 20\n";
140 $stem = new XapianStem("english");
141 if ($stem->get_description() != "Xapian::Stem(english)") {
142 print "Unexpected \$stem->get_description()\n";
146 $doc = new XapianDocument();
147 $doc->set_data("a\x00b");
148 if ($doc->get_data() === "a") {
149 print "get_data+set_data truncates at a zero byte\n";
152 if ($doc->get_data() !== "a\x00b") {
153 print "get_data+set_data doesn't transparently handle a zero byte\n";
156 $doc->set_data("is there anybody out there?");
157 $doc->add_term("XYzzy");
158 $doc->add_posting($stem->apply("is"), 1);
159 $doc->add_posting($stem->apply("there"), 2);
160 $doc->add_posting($stem->apply("anybody"), 3);
161 $doc->add_posting($stem->apply("out"), 4);
162 $doc->add_posting($stem->apply("there"), 5);
164 // Check virtual function dispatch.
165 if (substr($db->get_description(), 0, 17) !== "WritableDatabase(") {
166 print "Unexpected \$db->get_description()\n";
169 $db->add_document($doc);
170 if ($db->get_doccount() != 1) {
171 print "Unexpected \$db->get_doccount()\n";
175 $terms = array("smoke", "test", "terms");
176 $query = new XapianQuery(XapianQuery
::OP_OR
, $terms);
177 if ($query->get_description() != "Query((smoke OR test OR terms))") {
178 print "Unexpected \$query->get_description()\n";
181 $query1 = new XapianQuery(XapianQuery
::OP_PHRASE
, array("smoke", "test", "tuple"));
182 if ($query1->get_description() != "Query((smoke PHRASE 3 test PHRASE 3 tuple))") {
183 print "Unexpected \$query1->get_description()\n";
186 $query1b = new XapianQuery(XapianQuery
::OP_NEAR
, array("smoke", "test", "tuple"), 4);
187 if ($query1b->get_description() != "Query((smoke NEAR 4 test NEAR 4 tuple))") {
188 print "Unexpected \$query1b->get_description()\n";
191 $query2 = new XapianQuery(XapianQuery
::OP_XOR
, array(new XapianQuery("smoke"), $query1, "string"));
192 if ($query2->get_description() != "Query((smoke XOR (smoke PHRASE 3 test PHRASE 3 tuple) XOR string))") {
193 print "Unexpected \$query2->get_description()\n";
196 $subqs = array("a", "b");
197 $query3 = new XapianQuery(XapianQuery
::OP_OR
, $subqs);
198 if ($query3->get_description() != "Query((a OR b))") {
199 print "Unexpected \$query3->get_description()\n";
202 $enq = new XapianEnquire($db);
203 $enq->set_query(new XapianQuery(XapianQuery
::OP_OR
, "there", "is"));
204 $mset = $enq->get_mset(0, 10);
205 if ($mset->size() != 1) {
206 print "Unexpected \$mset->size()\n";
209 $terms = join(" ", $enq->get_matching_terms($mset->get_hit(0)));
210 if ($terms != "is there") {
211 print "Unexpected matching terms: $terms\n";
215 # Feature test for MatchDecider
216 $doc = new XapianDocument();
217 $doc->set_data("Two");
218 $doc->add_posting($stem->apply("out"), 1);
219 $doc->add_posting($stem->apply("outside"), 1);
220 $doc->add_posting($stem->apply("source"), 2);
221 $doc->add_value(0, "yes");
222 $db->add_document($doc);
224 class testmatchdecider
extends XapianMatchDecider
{
225 function apply($doc) {
226 return ($doc->get_value(0) == "yes");
230 $query = new XapianQuery($stem->apply("out"));
231 $enquire = new XapianEnquire($db);
232 $enquire->set_query($query);
233 $mdecider = new testmatchdecider();
234 $mset = $enquire->get_mset(0, 10, null, $mdecider);
235 if ($mset->size() != 1) {
236 print "Unexpected number of documents returned by match decider (".$mset->size().")\n";
239 if ($mset->get_docid(0) != 2) {
240 print "MatchDecider mset has wrong docid in\n";
244 class testexpanddecider
extends XapianExpandDecider
{
245 function apply($term) {
246 return ($term[0] !== 'a');
250 $enquire = new XapianEnquire($db);
251 $rset = new XapianRSet();
252 $rset->add_document(1);
253 $eset = $enquire->get_eset(10, $rset, XapianEnquire
::USE_EXACT_TERMFREQ
, 1.0, new testexpanddecider());
254 foreach ($eset->begin() as $t) {
256 print "XapianExpandDecider was not used\n";
261 # Check min_wt argument to get_eset() works (new in 1.2.5).
262 $eset = $enquire->get_eset(100, $rset, XapianEnquire
::USE_EXACT_TERMFREQ
);
264 foreach ($eset->begin() as $i => $dummy) {
265 $min_wt = $i->get_weight();
267 if ($min_wt >= 1.9) {
268 print "ESet min weight too high for testcase\n";
271 $eset = $enquire->get_eset(100, $rset, XapianEnquire
::USE_EXACT_TERMFREQ
, 1.0, NULL, 1.9);
273 foreach ($eset->begin() as $i => $dummy) {
274 $min_wt = $i->get_weight();
277 print "ESet min_wt threshold not applied\n";
281 if (XapianQuery
::OP_ELITE_SET
!= 10) {
282 print "OP_ELITE_SET is XapianQuery::OP_ELITE_SET not 10\n";
286 # Regression test - overload resolution involving boolean types failed.
287 $enq->set_sort_by_value(1, TRUE);
289 # Regression test - fixed in 0.9.10.1.
290 $oqparser = new XapianQueryParser();
291 $oquery = $oqparser->parse_query("I like tea");
293 # Regression test for bug#192 - fixed in 1.0.3.
294 $enq->set_cutoff(100);
296 # Check DateValueRangeProcessor works.
297 function add_vrp_date(&$qp) {
298 $vrpdate = new XapianDateValueRangeProcessor(1, 1, 1960);
299 $qp->add_valuerangeprocessor($vrpdate);
301 $qp = new XapianQueryParser();
303 $query = $qp->parse_query('12/03/99..12/04/01');
304 if ($query->get_description() !== 'Query(0 * VALUE_RANGE 1 19991203 20011204)') {
305 print "XapianDateValueRangeProcessor didn't work - result was ".$query->get_description()."\n";
309 # Feature test for XapianFieldProcessor
310 class testfieldprocessor
extends XapianFieldProcessor
{
311 function apply($str) {
312 if ($str === 'spam') throw new Exception('already spam');
313 return new XapianQuery("spam");
317 $tfp = new testfieldprocessor();
318 $qp->add_prefix('spam', $tfp);
319 $query = $qp->parse_query('spam:ignored');
320 if ($query->get_description() !== 'Query(spam)') {
321 print "testfieldprocessor didn't work - result was ".$query->get_description()."\n";
326 $query = $qp->parse_query('spam:spam');
327 print "testfieldprocessor exception not rethrown\n";
329 } catch (Exception
$e) {
330 if ($e->getMessage() !== 'already spam') {
331 print "Exception has wrong message\n";
336 # Test setting and getting metadata
337 if ($db->get_metadata('Foo') !== '') {
338 print "Unexpected value for metadata associated with 'Foo' (expected ''): '".$db->get_metadata('Foo')."'\n";
341 $db->set_metadata('Foo', 'Foo');
342 if ($db->get_metadata('Foo') !== 'Foo') {
343 print "Unexpected value for metadata associated with 'Foo' (expected 'Foo'): '".$db->get_metadata('Foo')."'\n";
347 # Test OP_SCALE_WEIGHT and corresponding constructor
348 $query4 = new XapianQuery(XapianQuery
::OP_SCALE_WEIGHT
, new XapianQuery('foo'), 5.0);
349 if ($query4->get_description() != "Query(5 * foo)") {
350 print "Unexpected \$query4->get_description()\n";
354 # Test MultiValueKeyMaker.
356 $doc = new XapianDocument();
357 $doc->add_term("foo");
358 $doc->add_value(0, "ABB");
359 $db2->add_document($doc);
360 $doc->add_value(0, "ABC");
361 $db2->add_document($doc);
362 $doc->add_value(0, "ABC\0");
363 $db2->add_document($doc);
364 $doc->add_value(0, "ABCD");
365 $db2->add_document($doc);
366 $doc->add_value(0, "ABC\xff");
367 $db2->add_document($doc);
369 $enquire = new XapianEnquire($db2);
370 $enquire->set_query(new XapianQuery("foo"));
373 $sorter = new XapianMultiValueKeyMaker();
374 $sorter->add_value(0);
375 $enquire->set_sort_by_key($sorter, true);
376 $mset = $enquire->get_mset(0, 10);
377 mset_expect_order($mset, array(5, 4, 3, 2, 1));
381 $sorter = new XapianMultiValueKeyMaker();
382 $sorter->add_value(0, true);
383 $enquire->set_sort_by_key($sorter, true);
384 $mset = $enquire->get_mset(0, 10);
385 mset_expect_order($mset, array(1, 2, 3, 4, 5));
389 $sorter = new XapianMultiValueKeyMaker();
390 $sorter->add_value(0);
391 $sorter->add_value(1);
392 $enquire->set_sort_by_key($sorter, true);
393 $mset = $enquire->get_mset(0, 10);
394 mset_expect_order($mset, array(5, 4, 3, 2, 1));
398 $sorter = new XapianMultiValueKeyMaker();
399 $sorter->add_value(0, true);
400 $sorter->add_value(1);
401 $enquire->set_sort_by_key($sorter, true);
402 $mset = $enquire->get_mset(0, 10);
403 mset_expect_order($mset, array(1, 2, 3, 4, 5));
407 $sorter = new XapianMultiValueKeyMaker();
408 $sorter->add_value(0);
409 $sorter->add_value(1, true);
410 $enquire->set_sort_by_key($sorter, true);
411 $mset = $enquire->get_mset(0, 10);
412 mset_expect_order($mset, array(5, 4, 3, 2, 1));
416 $sorter = new XapianMultiValueKeyMaker();
417 $sorter->add_value(0, true);
418 $sorter->add_value(1, true);
419 $enquire->set_sort_by_key($sorter, true);
420 $mset = $enquire->get_mset(0, 10);
421 mset_expect_order($mset, array(1, 2, 3, 4, 5));
424 # Feature test for ValueSetMatchDecider:
426 $md = new XapianValueSetMatchDecider(0, true);
427 $md->add_value("ABC");
428 $doc = new XapianDocument();
429 $doc->add_value(0, "ABCD");
430 if ($md->apply($doc)) {
431 print "Unexpected result from ValueSetMatchDecider->apply(); expected false\n";
435 $doc = new XapianDocument();
436 $doc->add_value(0, "ABC");
437 if (!$md->apply($doc)) {
438 print "Unexpected result from ValueSetMatchDecider->apply(); expected true\n";
442 $mset = $enquire->get_mset(0, 10, 0, null, $md, null);
443 mset_expect_order($mset, array(2));
445 $md = new XapianValueSetMatchDecider(0, false);
446 $md->add_value("ABC");
447 $mset = $enquire->get_mset(0, 10, 0, null, $md, null);
448 mset_expect_order($mset, array(1, 3, 4, 5));
451 function mset_expect_order($mset, $a) {
452 if ($mset->size() != sizeof($a)) {
453 print "MSet has ".$mset->size()." entries, expected ".sizeof($a)."\n";
456 for ($j = 0; $j < sizeof($a); ++
$j) {
457 # PHP4 doesn't cope with: $mset->get_hit($j)->get_docid();
458 $hit = $mset->get_hit($j);
459 if ($hit->get_docid() != $a[$j]) {
460 print "Expected MSet[$j] to be $a[$j], got ".$hit->get_docid()."\n";
466 # Feature tests for Query "term" constructor optional arguments:
467 $query_wqf = new XapianQuery('wqf', 3);
468 if ($query_wqf->get_description() != 'Query(wqf#3)') {
469 print "Unexpected \$query_wqf->get_description():\n";
470 print $query_wqf->get_description() . "\n";
474 $query = new XapianQuery(XapianQuery
::OP_VALUE_GE
, 0, "100");
475 if ($query->get_description() != 'Query(VALUE_GE 0 100)') {
476 print "Unexpected \$query->get_description():\n";
477 print $query->get_description() . "\n";
481 $query = XapianQuery
::MatchAll();
482 if ($query->get_description() != 'Query(<alldocuments>)') {
483 print "Unexpected \$query->get_description():\n";
484 print $query->get_description() . "\n";
488 $query = XapianQuery
::MatchNothing();
489 if ($query->get_description() != 'Query()') {
490 print "Unexpected \$query->get_description():\n";
491 print $query->get_description() . "\n";
495 # Test access to matchspy values:
497 $matchspy = new XapianValueCountMatchSpy(0);
498 $enquire->add_matchspy($matchspy);
499 $enquire->get_mset(0, 10);
501 foreach ($matchspy->values_begin() as $k => $term) {
502 $values[$term] = $k->get_termfreq();
511 if ($values != $expected) {
512 print "Unexpected matchspy values():\n";
520 # Regression test for SWIG bug - it was generating "return $r;" in wrapper
521 # functions which didn't set $r.
522 $indexer = new XapianTermGenerator();
523 $doc = new XapianDocument();
525 $indexer->set_document($doc);
526 $indexer->index_text("I ask nothing in return");
527 $indexer->index_text_without_positions("Tea time");
528 $indexer->index_text("Return in time");
531 foreach ($doc->termlist_begin() as $term) {
534 if ($s !== 'ask i in nothing return tea time ') {
535 print "PHP Iterator wrapping of TermIterator doesn't work ($s)\n";
540 foreach ($doc->termlist_begin() as $k => $term) {
541 $s .= $term . ':' . $k->get_wdf() . ' ';
543 if ($s !== 'ask:1 i:1 in:2 nothing:1 return:2 tea:1 time:2 ') {
544 print "PHP Iterator wrapping of TermIterator keys doesn't work ($s)\n";
548 # Test GeoSpatial API
549 $coord = new XapianLatLongCoord();
550 $coord = new XapianLatLongCoord(-41.288889, 174.777222);
552 define('COORD_SLOT', 2);
553 $metric = new XapianGreatCircleMetric();
556 $centre = new XapianLatLongCoords($coord);
557 $query = new XapianQuery(new XapianLatLongDistancePostingSource(COORD_SLOT
, $centre, $metric, $range));
559 $db = Xapian
::inmemory_open();
560 $coords = new XapianLatLongCoords();
561 $coords->append(new XapianLatLongCoord(40.6048, -74.4427));
562 $doc = new XapianDocument();
563 $doc->add_term("coffee");
564 $doc->add_value(COORD_SLOT
, $coords->serialise());
565 $db->add_document($doc);
567 $centre = new XapianLatLongCoords();
568 $centre->append(new XapianLatLongCoord(40.6048, -74.4427));
570 $ps = new XapianLatLongDistancePostingSource(COORD_SLOT
, $centre, $metric, $range);
571 $q = new XapianQuery("coffee");
572 $q = new XapianQuery(XapianQuery
::OP_AND
, $q, new XapianQuery($ps));
574 $enq = new XapianEnquire($db);
576 $mset = $enq->get_mset(0, 10);
577 if ($mset->size() != 1) {
578 print "Expected one result with XapianLatLongDistancePostingSource, got ";
579 print $mset->size() . "\n";
584 foreach ($db->allterms_begin() as $k => $term) {
585 $s .= "($term:{$k->get_termfreq()})";
587 if ($s !== '(coffee:1)') {
588 print "PHP Iterator iteration of allterms doesn't work ($s)\n";
592 # Test reference tracking and regression test for #659.
593 $qp = new XapianQueryParser();
595 $stop = new XapianSimpleStopper();
597 $qp->set_stopper($stop);
599 $query = $qp->parse_query('a b');
600 if ($query->get_description() !== 'Query(b@2)') {
601 print "XapianQueryParser::set_stopper() didn't work as expected - result was ".$query->get_description()."\n";