1use strict;
2# Before 'make install' is performed this script should be runnable with
3# 'make test'. After 'make install' it should work as 'perl test.pl'
4
5#########################
6
7# Make warnings fatal
8use warnings;
9BEGIN {$SIG{__WARN__} = sub { die "Terminating test due to warning: $_[0]" } };
10
11use Test::More;
12use Devel::Peek;
13BEGIN { plan tests => 110 };
14use Xapian qw(:standard);
15ok(1); # If we made it this far, we're ok.
16
17#########################
18
19# Insert your test code below, the Test module is use()ed here so read
20# its man page ( perldoc Test ) for help writing this test script.
21
22sub mset_expect_order (\@@) {
23    my ($m, @a) = @_;
24    my @m = map { $_->get_docid() } @{$m};
25    is( scalar @m, scalar @a );
26    for my $j (0 .. (scalar @a - 1)) {
27	is( $m[$j], $a[$j] );
28    }
29}
30
31# first create database dir, if it doesn't exist;
32my $db_dir = 'testdb';
33
34my $database;
35ok( $database = Xapian::Database->new( $db_dir ) );
36
37my $qp = new Xapian::QueryParser( $database );
38$qp = new Xapian::QueryParser();
39
40$qp->set_stemmer( Xapian::Stem->new('english') );
41$qp->set_stemming_strategy( STEM_ALL );
42$qp->set_default_op( OP_AND );
43
44my $query;
45ok( $query = $qp->parse_query( 'one or two', FLAG_BOOLEAN|FLAG_BOOLEAN_ANY_CASE|FLAG_SPELLING_CORRECTION ) );
46ok( not $qp->get_corrected_query_string());
47is( $query->get_description(), 'Query((one@1 OR two@2))' );
48
49ok( $query = $qp->parse_query( 'one OR (two AND three)' ) );
50is( $query->get_description(), 'Query((one@1 OR (two@2 AND three@3)))' );
51
52ok( my $enq = $database->enquire( $query ) );
53
54{
55  is( $qp->set_stopper(sub { $_[0] eq 'bad' }), undef );
56  is( $qp->parse_query("bad news")->get_description(), 'Query(news@2)' );
57}
58
59{
60  my @stopwords = qw(a the in on and);
61  my $stopper;
62  ok( $stopper = new Xapian::SimpleStopper(@stopwords) );
63  foreach (@stopwords) {
64    ok( $stopper->stop_word($_) );
65  }
66  foreach (qw(one two three four five)) {
67    ok( !$stopper->stop_word($_) );
68  }
69  is( $qp->set_stopper($stopper), undef );
70}
71ok( $qp->parse_query("one two many") );
72
73$qp = new Xapian::QueryParser();
74my $rp;
75ok( $rp = new Xapian::RangeProcessor(1) );
76$qp->add_rangeprocessor($rp);
77$qp->add_boolean_prefix("test", "XTEST");
78my $qpo = new Xapian::QueryParser();
79my $vrp;
80ok( $vrp = new Xapian::StringValueRangeProcessor(1) );
81$qpo->add_valuerangeprocessor($vrp);
82$qpo->add_boolean_prefix("test", "XTEST");
83
84my $pair;
85foreach $pair (
86    [ 'a..b', 'VALUE_RANGE 1 a b' ],
87    [ '$50..100', 'VALUE_RANGE 1 $50 100' ],
88    [ '$50..$99', 'VALUE_RANGE 1 $50 $99' ],
89    [ '$50..$100', '' ],
90    [ '02/03/1979..10/12/1980', 'VALUE_RANGE 1 02/03/1979 10/12/1980' ],
91    [ 'a..b hello', '(hello@1 FILTER VALUE_RANGE 1 a b)' ],
92    [ 'hello a..b', '(hello@1 FILTER VALUE_RANGE 1 a b)' ],
93    [ 'hello a..b world', '((hello@1 OR world@2) FILTER VALUE_RANGE 1 a b)' ],
94    [ 'hello a..b test:foo', '(hello@1 FILTER (VALUE_RANGE 1 a b AND XTESTfoo))' ],
95    [ '-5..7', 'VALUE_RANGE 1 -5 7' ],
96    [ 'hello -5..7', '(hello@1 FILTER VALUE_RANGE 1 -5 7)' ],
97    [ '-5..7 hello', '(hello@1 FILTER VALUE_RANGE 1 -5 7)' ],
98    [ '"time flies" 09:00..12:30', '((time@1 PHRASE 2 flies@2) FILTER VALUE_RANGE 1 09:00 12:30)' ]
99    ) {
100    my ($str, $res) = @{$pair};
101    my $query = $qp->parse_query($str);
102    is( $query->get_description(), "Query($res)" );
103    $query = $qpo->parse_query($str);
104    is( $query->get_description(), "Query($res)" );
105}
106
107$qp = new Xapian::QueryParser();
108
109my $rp1 = new Xapian::DateRangeProcessor(1);
110my $rp2 = new Xapian::NumberRangeProcessor(2);
111my $rp3 = new Xapian::RangeProcessor(3);
112my $rp4 = new Xapian::NumberRangeProcessor(4, '$', Xapian::RP_REPEATED);
113my $rp5 = new Xapian::NumberRangeProcessor(5, 'kg', Xapian::RP_REPEATED|Xapian::RP_SUFFIX);
114my $rp6 = new Xapian::RangeProcessor(6, 'country:');
115my $rp7 = new Xapian::RangeProcessor(7, ':name', Xapian::RP_SUFFIX);
116$qp->add_rangeprocessor( $rp1 );
117$qp->add_rangeprocessor( $rp2 );
118$qp->add_rangeprocessor( $rp4 );
119$qp->add_rangeprocessor( $rp5 );
120$qp->add_rangeprocessor( $rp6 );
121$qp->add_rangeprocessor( $rp7 );
122$qp->add_rangeprocessor( $rp3 );
123
124$qp->add_boolean_prefix("test", "XTEST");
125
126$qpo = new Xapian::QueryParser();
127
128my $vrp1 = new Xapian::DateValueRangeProcessor(1);
129my $vrp2 = new Xapian::NumberValueRangeProcessor(2);
130my $vrp3 = new Xapian::StringValueRangeProcessor(3);
131my $vrp4 = new Xapian::NumberValueRangeProcessor(4, '$');
132my $vrp5 = new Xapian::NumberValueRangeProcessor(5, 'kg', 0);
133my $vrp6 = new Xapian::StringValueRangeProcessor(6, 'country:');
134my $vrp7 = new Xapian::StringValueRangeProcessor(7, ':name', 0);
135$qpo->add_valuerangeprocessor( $vrp1 );
136$qpo->add_valuerangeprocessor( $vrp2 );
137$qpo->add_valuerangeprocessor( $vrp4 );
138$qpo->add_valuerangeprocessor( $vrp5 );
139$qpo->add_valuerangeprocessor( $vrp6 );
140$qpo->add_valuerangeprocessor( $vrp7 );
141$qpo->add_valuerangeprocessor( $vrp3 );
142
143$qpo->add_boolean_prefix("test", "XTEST");
144
145foreach $pair (
146    [ 'a..b', 'VALUE_RANGE 3 a b' ],
147    [ '1..12', "VALUE_RANGE 2 \\xa0 \\xae" ],
148    [ '20070201..20070228', 'VALUE_RANGE 1 20070201 20070228' ],
149    [ '$10..20', "VALUE_RANGE 4 \\xad \\xb1" ],
150    [ '$10..$20', "VALUE_RANGE 4 \\xad \\xb1" ],
151    [ '12..42kg', "VALUE_RANGE 5 \\xae \\xb5\@" ],
152    [ '12kg..42kg', "VALUE_RANGE 5 \\xae \\xb5\@" ],
153    [ '12kg..42', 'VALUE_RANGE 3 12kg 42' ],
154    [ '10..$20', '' ],
155    [ '1999-03-12..2020-12-30', 'VALUE_RANGE 1 19990312 20201230' ],
156    [ '1999/03/12..2020/12/30', 'VALUE_RANGE 1 19990312 20201230' ],
157    [ '1999.03.12..2020.12.30', 'VALUE_RANGE 1 19990312 20201230' ],
158    [ '12/03/99..12/04/01', 'VALUE_RANGE 1 19990312 20010412' ],
159    [ '03-12-99..04-14-01', 'VALUE_RANGE 1 19990312 20010414' ],
160    [ '(test:a..test:b hello)', '(hello@1 FILTER VALUE_RANGE 3 test:a test:b)' ],
161    [ 'country:chile..denmark', 'VALUE_RANGE 6 chile denmark' ],
162    [ 'albert..xeni:name', 'VALUE_RANGE 7 albert xeni' ],
163    ) {
164    my ($str, $res) = @{$pair};
165    my $query = $qp->parse_query($str);
166    is( $query->get_description(), "Query($res)" );
167    $query = $qpo->parse_query($str);
168    is( $query->get_description(), "Query($res)" );
169}
170
171$qp = new Xapian::QueryParser();
172$qp->add_rangeprocessor( sub { Xapian::Query->new("spam") } );
173foreach $pair (
174    [ '123..345', '0 * spam' ],
175    ) {
176    my ($str, $res) = @{$pair};
177    my $query = $qp->parse_query($str);
178    is( $query->get_description(), "Query($res)" );
179}
180
181$qp = new Xapian::QueryParser();
182{
183  my $rpdate = new Xapian::DateRangeProcessor(1, Xapian::RP_DATE_PREFER_MDY, 1960);
184  $qp->add_rangeprocessor( $rpdate );
185}
186
187$qpo = new Xapian::QueryParser();
188
189{
190  my $vrpdate = new Xapian::DateValueRangeProcessor(1, 1, 1960);
191  $qpo->add_valuerangeprocessor( $vrpdate );
192}
193
194foreach $pair (
195    [ '12/03/99..12/04/01', 'VALUE_RANGE 1 19991203 20011204' ],
196    [ '03-12-99..04-14-01', 'VALUE_RANGE 1 19990312 20010414' ],
197    [ '01/30/60..02/02/59', 'VALUE_RANGE 1 19600130 20590202' ],
198    ) {
199    my ($str, $res) = @{$pair};
200    my $query = $qp->parse_query($str);
201    is( $query->get_description(), "Query($res)" );
202    $query = $qpo->parse_query($str);
203    is( $query->get_description(), "Query($res)" );
204}
205
206$qp = new Xapian::QueryParser();
207$qp->add_prefix("foo", sub {return new Xapian::Query('foo')});
208is( $qp->parse_query("foo:test")->get_description(), 'Query(foo)' );
209
210# Regression test for Search::Xapian bug fixed in 1.0.5.0.  In 1.0.0.0-1.0.4.0
211# we tried to catch const char * not Xapian::Error, so std::terminate got
212# called.
213$qp = Xapian::QueryParser->new;
214eval {
215    $qp->parse_query('other* AND', FLAG_BOOLEAN|FLAG_WILDCARD);
216};
217ok($@);
218is(ref($@), "Xapian::QueryParserError", "correct class for exception");
219ok($@->isa('Xapian::Error'));
220is($@->get_msg, "Syntax: <expression> AND <expression>", "get_msg works");
221
222# Check FLAG_DEFAULT is wrapped (new in 1.0.11.0).
223ok( $qp->parse_query('hello world', FLAG_DEFAULT|FLAG_BOOLEAN_ANY_CASE) );
224
225# Test OP_WILDCARD with limits.
226my ($q, @matches);
227ok( $enq = Xapian::Enquire->new($database) );
228
229$qp->set_max_expansion(1, Xapian::WILDCARD_LIMIT_FIRST);
230ok( $q = $qp->parse_query('t*', FLAG_WILDCARD) );
231$enq->set_query($q);
232@matches = $enq->matches(0, 10);
233mset_expect_order(@matches, (1, 2));
234
235$qp->set_max_expansion(1, Xapian::WILDCARD_LIMIT_MOST_FREQUENT);
236ok( $q = $qp->parse_query('t*', FLAG_WILDCARD) );
237$enq->set_query($q);
238@matches = $enq->matches(0, 10);
239mset_expect_order(@matches, (1, 2));
240
241$qp->set_max_expansion(1, Xapian::WILDCARD_LIMIT_ERROR);
242ok( $q = $qp->parse_query('t*', FLAG_WILDCARD) );
243$enq->set_query($q);
244eval {
245    @matches = $enq->matches(0, 10);
246};
247ok( $@ );
248is(ref($@), "Xapian::WildcardError", "correct class for exception");
249
2501;
251