[ci] Fix netbsd job to upgrade existing packages
[xapian.git] / xapian-applications / omega / omegatest.pl
blob5bc060a409c81b13d165a227ea4d06108409172a
1 #!/usr/bin/perl
2 # omegatest: Test omega CGI
4 # Copyright (C) 2015-2023 Olly Betts
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License as
8 # published by the Free Software Foundation; either version 2 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 # USA
21 use strict;
22 use warnings;
24 use File::Path qw(make_path remove_tree);
25 use IPC::Open2;
27 sub print_to_file {
28 my ($filename, $contents) = @_;
29 open my $fh, '>', $filename or die $!;
30 print $fh $contents;
31 close $fh or die $!;
34 my $omega = $ENV{OMEGA} // './omega';
35 my $scriptindex = $ENV{SCRIPTINDEX} // './scriptindex';
36 my $faketime = $ENV{FAKETIME} // 'faketime';
37 my $srcdir = ($0 =~ m,(.*)/,)[0] // '.';
39 # Suppress HTTP Content-Type header.
40 $ENV{SERVER_PROTOCOL} = 'INCLUDED';
42 # Don't complain about memory left allocated on exit - the OS will reclaim
43 # all memory at that point.
44 $ENV{LSAN_OPTIONS} = 'leak_check_at_exit=0';
46 # Turn off msys2's argument conversion as we shouldn't need it and it breaks
47 # some testcases.
48 $ENV{MSYS2_ARG_CONV_EXCL} = '*';
50 # Enable suppressions for UBSan (these are for defined-but-dubious behaviours
51 # such as unsigned overflow which UBSan can also catch - code in system library
52 # headers may trigger these checks). If UBSan isn't in use, setting this is
53 # harmless.
54 $ENV{UBSAN_OPTIONS} = "suppressions=$srcdir/ubsan.supp";
56 # Set up an empty database.
57 my $test_db = 'test-db';
58 remove_tree($test_db);
60 # Simple template which just shows the parsed query.
61 my $test_template = 'test-template';
62 print_to_file $test_template, "\$querydescription\n";
64 my $test_indexscript = 'test-indexscript';
66 my $test_log = 'test-log';
68 open my $devnull, '>', ($^O eq 'MSWin32' ? 'nul' : '/dev/null') or die $!;
70 my $OMEGA_CONFIG_FILE = 'test-omega.conf';
71 $ENV{OMEGA_CONFIG_FILE} = $OMEGA_CONFIG_FILE;
72 print_to_file $OMEGA_CONFIG_FILE, <<__END__ ;
73 database_dir .
74 template_dir .
75 log_dir .
76 default_template $test_template
77 default_db $test_db
78 __END__
80 # Glob pattern matching scriptindex summary line.
81 my $summary_re =
82 qr/records \(added, replaced, deleted, skipped\) = \(\d+, \d+, \d+, \d+\)$/;
84 # Some testcases don't work with 32-bit time_t.
85 my $have_32bit_time_t = 0;
87 open my $fh, '<', 'config.h' or die $!;
88 while (<$fh>) {
89 if (/^#define SIZEOF_TIME_T\s*(\d+)/) {
90 if ($1 eq 4) {
91 $have_32bit_time_t = 1;
92 print "Skipping testcases which need > 32-bit time_t\n";
94 last;
97 close $fh;
100 my $failed = 0;
102 sub run_scriptindex {
103 my $input = shift;
104 remove_tree($test_db);
105 my $pid = open2($devnull, my $in, $scriptindex, $test_db, $test_indexscript) or die $!;
106 print $in $input;
107 close $in or die $!;
108 waitpid($pid, 0);
109 if ($?) {
110 print "scriptindex gave non-zero exit status $?\n";
111 ++$failed;
115 sub testcase {
116 my $expected = shift;
118 my $output;
120 # If there are no positional parameters, pass one as otherwise omega will
121 # wait for parameters on stdin.
122 my $args = @_ > 0 ? \@_ : ['dummy'];
123 my $pid = open my $out, '-|', ref $omega ? @$omega : $omega, @$args
124 or die $!;
125 local $/ = undef;
126 $output = <$out>;
127 close $out;
129 $output =~ s/\r\n/\n/g;
130 chomp($output);
132 if ($output ne $expected) {
133 print "$omega @_:\n";
134 print " expected: «${expected}»\n";
135 print " received: «${output}»\n";
136 ++$failed;
140 sub qtestcase {
141 my $query_desc = shift;
142 return testcase("Query($query_desc)", @_);
145 # Test scriptindex gives expected error.
147 # Parameters:
148 # * what: text to report to describe situation
149 # * expect: expected text or regexp stdout+stderr should match
150 # * (optional) input: input to feed scriptindex (default: /dev/null)
152 # Uses database $test_db and index script $test_indexscript.
153 sub test_scriptindex_error {
154 my ($what, $expect, $input) = @_;
155 remove_tree($test_db);
156 my $out;
157 if (!defined $input) {
158 $out = `$scriptindex '$test_db' '$test_indexscript' < /dev/null 2>&1`;
159 } else {
160 $out = `$scriptindex '$test_db' '$test_indexscript' <<'__END__' 2>&1
161 $input
162 __END__`;
164 my $rc = $? >> 8;
165 $out =~ s/\r\n/\n/g;
166 chomp($out);
167 if ($rc == 0) {
168 print "scriptindex didn't exit with non-zero return code for $what\n";
169 print "Output: $out\n";
170 ++$failed;
171 } elsif ($out ne $expect) {
172 print "scriptindex didn't give expected error for $what\n";
173 print "Expect: $expect\n";
174 print "Output: $out\n";
175 ++$failed;
179 # Test scriptindex either works or gives an expected error.
181 # Parameters:
182 # * what: text to report to describe situation
183 # * expect: expected text or regexp stdout+stderr should match
184 # * (optional) input: input to feed scriptindex (default: /dev/null)
186 # Return value is the exit code from scriptindex.
188 # Uses database $test_db and index script $test_indexscript.
189 sub test_scriptindex_optional_error {
190 my ($what, $expect, $input) = @_;
191 remove_tree($test_db);
192 my $out;
193 if (!defined $input) {
194 $out = `$scriptindex '$test_db' '$test_indexscript' < /dev/null 2>&1`;
195 } else {
196 $out = `$scriptindex '$test_db' '$test_indexscript' <<'__END__' 2>&1
197 $input
198 __END__`;
200 my $rc = $? >> 8;
201 $out =~ s/\r\n/\n/g;
202 chomp($out);
203 if ($rc == 0) {
204 if ($out !~ $summary_re) {
205 print "scriptindex gave unexpected output for $what\n";
206 print "Output: $out\n";
207 ++$failed;
209 } else {
210 if ($out ne $expect) {
211 print "scriptindex didn't give expected error for $what\n";
212 print "Expect: $expect\n";
213 print "Output: $out\n";
214 ++$failed;
217 return $rc;
220 # Test scriptindex gives expected warning.
222 # Parameters:
223 # * what: text to report to describe situation
224 # * expect: expected text or regexp stdout+stderr should match
225 # * (optional) input: input to feed scriptindex (default: /dev/null)
227 # Uses database $test_db and index script $test_indexscript.
228 sub test_scriptindex_warning {
229 my ($what, $expect, $input) = @_;
230 remove_tree($test_db);
231 my $out;
232 if (!defined $input) {
233 $out = `$scriptindex '$test_db' '$test_indexscript' < /dev/null 2>&1`;
234 } else {
235 $out = `$scriptindex '$test_db' '$test_indexscript' <<'__END__' 2>&1
236 $input
237 __END__`;
239 my $rc = $? >> 8;
240 $out =~ s/\r\n/\n/g;
241 chomp($out);
242 if ($out !~ s/$summary_re//) {
243 print "scriptindex output lacked summary line for $what\n";
244 print "Output: $out\n";
245 ++$failed;
246 return;
248 chomp($out);
249 if ($rc != 0) {
250 print "scriptindex gave an error rather than the expected warning for $what\n";
251 print "Output: $out\n";
252 ++$failed;
253 } elsif ($out ne $expect) {
254 print "scriptindex didn't give expected warning for $what\n";
255 print "Expect: $expect\n";
256 print "Output: $out\n";
257 ++$failed;
261 # Test scriptindex runs cleanly
263 # Parameters:
264 # * what: text to report to describe situation
265 # * (optional) input: input to feed scriptindex (default: /dev/null)
267 # Uses database $test_db and index script $test_indexscript.
268 sub test_scriptindex {
269 my ($what, $input) = @_;
270 remove_tree($test_db);
271 my $out;
272 if (!defined $input) {
273 $out = `$scriptindex '$test_db' '$test_indexscript' < /dev/null 2>&1`;
274 } else {
275 $out = `$scriptindex '$test_db' '$test_indexscript' <<'__END__' 2>&1
276 $input
277 __END__`;
279 my $rc = $? >> 8;
280 $out =~ s/\r\n/\n/g;
281 chomp($out);
282 if ($rc != 0) {
283 print "scriptindex gave an error for $what\n";
284 print "Output: $out\n";
285 ++$failed;
286 } elsif ($out !~ $summary_re) {
287 print "scriptindex gave unexpected output for $what\n";
288 print "Output: $out\n";
289 ++$failed;
293 # Test a few simple things.
294 qtestcase('Zsimpl@1', 'P=simple');
295 qtestcase('(chocolate@1 FILTER Tconfectionary/fudge)', 'P=Chocolate', 'B=Tconfectionary/fudge');
297 # Test date value ranges.
298 qtestcase('VALUE_RANGE 0 2 ~', 'DATEVALUE=0', 'START=2000');
299 qtestcase('VALUE_RANGE 0 2 ~', 'DATEVALUE=0', 'START=200001');
300 qtestcase('VALUE_RANGE 0 2 ~', 'DATEVALUE=0', 'START=20000101');
301 qtestcase('VALUE_LE 1 1~', 'DATEVALUE=1', 'END=1999');
302 qtestcase('VALUE_LE 1 1~', 'DATEVALUE=1', 'END=199912');
303 qtestcase('VALUE_LE 1 1~', 'DATEVALUE=1', 'END=19991231');
304 qtestcase('VALUE_RANGE 2 201 ~', 'DATEVALUE=2', 'START=2010');
305 qtestcase('VALUE_RANGE 2 201 ~', 'DATEVALUE=2', 'START=201001');
306 qtestcase('VALUE_RANGE 2 201 ~', 'DATEVALUE=2', 'START=20100101');
307 qtestcase('VALUE_LE 3 198~', 'DATEVALUE=3', 'END=1989');
308 qtestcase('VALUE_LE 3 198~', 'DATEVALUE=3', 'END=198912');
309 qtestcase('VALUE_LE 3 198~', 'DATEVALUE=3', 'END=19891231');
310 qtestcase('VALUE_RANGE 4 1974 ~', 'DATEVALUE=4', 'START=1974');
311 qtestcase('VALUE_RANGE 4 1974 ~', 'DATEVALUE=4', 'START=197401');
312 qtestcase('VALUE_RANGE 4 1974 ~', 'DATEVALUE=4', 'START=19740101');
313 qtestcase('VALUE_LE 5 1974~', 'DATEVALUE=5', 'END=1974');
314 qtestcase('VALUE_LE 5 1974~', 'DATEVALUE=5', 'END=197412');
315 qtestcase('VALUE_LE 5 1974~', 'DATEVALUE=5', 'END=19741231');
316 qtestcase('VALUE_RANGE 6 20151 ~', 'DATEVALUE=6', 'START=201510');
317 qtestcase('VALUE_RANGE 6 20151 ~', 'DATEVALUE=6', 'START=20151001');
318 qtestcase('VALUE_LE 7 19870~', 'DATEVALUE=7', 'END=198709');
319 qtestcase('VALUE_LE 7 19870~', 'DATEVALUE=7', 'END=19870930');
320 qtestcase('VALUE_RANGE 8 201512 ~', 'DATEVALUE=8', 'START=201512');
321 qtestcase('VALUE_RANGE 8 201512 ~', 'DATEVALUE=8', 'START=20151201');
322 qtestcase('VALUE_LE 9 201511~', 'DATEVALUE=9', 'END=201511');
323 qtestcase('VALUE_LE 9 201511~', 'DATEVALUE=9', 'END=20151130');
324 qtestcase('VALUE_RANGE 10 2015021 ~', 'DATEVALUE=10', 'START=20150210');
325 qtestcase('VALUE_RANGE 10 2000022 ~', 'DATEVALUE=10', 'START=20000220');
326 qtestcase('VALUE_LE 11 19840401~', 'DATEVALUE=11', 'END=19840401');
327 qtestcase('VALUE_LE 11 19881128~', 'DATEVALUE=11', 'END=19881128');
329 # Leap year tests:
330 qtestcase('VALUE_LE 1 201502~', 'DATEVALUE=1', 'END=20150228');
331 qtestcase('VALUE_LE 1 198802~', 'DATEVALUE=1', 'END=19880229');
332 qtestcase('VALUE_LE 1 19880228~', 'DATEVALUE=1', 'END=19880228');
333 qtestcase('VALUE_LE 1 200002~', 'DATEVALUE=1', 'END=20000229');
334 qtestcase('VALUE_LE 1 20000228~', 'DATEVALUE=1', 'END=20000228');
335 if (!$have_32bit_time_t) {
336 qtestcase('VALUE_LE 1 190002~', 'DATEVALUE=1', 'END=19000228');
337 qtestcase('VALUE_LE 1 210002~', 'DATEVALUE=1', 'END=21000228');
340 # Month starts and ends:
341 qtestcase('VALUE_RANGE 0 2015 201501~', 'DATEVALUE=0', 'START=20150101', 'END=20150131');
342 qtestcase('VALUE_RANGE 0 2015 20150130~', 'DATEVALUE=0', 'START=20150101', 'END=20150130');
343 qtestcase('VALUE_RANGE 0 201502 201502~', 'DATEVALUE=0', 'START=20150201', 'END=20150228');
344 qtestcase('VALUE_RANGE 0 201502 20150227~', 'DATEVALUE=0', 'START=20150201', 'END=20150227');
345 qtestcase('VALUE_RANGE 0 201503 201503~', 'DATEVALUE=0', 'START=20150301', 'END=20150331');
346 qtestcase('VALUE_RANGE 0 201503 20150330~', 'DATEVALUE=0', 'START=20150301', 'END=20150330');
347 qtestcase('VALUE_RANGE 0 201504 201504~', 'DATEVALUE=0', 'START=20150401', 'END=20150430');
348 qtestcase('VALUE_RANGE 0 201504 2015042~', 'DATEVALUE=0', 'START=20150401', 'END=20150429');
349 qtestcase('VALUE_RANGE 0 201505 201505~', 'DATEVALUE=0', 'START=20150501', 'END=20150531');
350 qtestcase('VALUE_RANGE 0 201505 20150530~', 'DATEVALUE=0', 'START=20150501', 'END=20150530');
351 qtestcase('VALUE_RANGE 0 201506 201506~', 'DATEVALUE=0', 'START=20150601', 'END=20150630');
352 qtestcase('VALUE_RANGE 0 201506 2015062~', 'DATEVALUE=0', 'START=20150601', 'END=20150629');
353 qtestcase('VALUE_RANGE 0 201507 201507~', 'DATEVALUE=0', 'START=20150701', 'END=20150731');
354 qtestcase('VALUE_RANGE 0 201507 20150730~', 'DATEVALUE=0', 'START=20150701', 'END=20150730');
355 qtestcase('VALUE_RANGE 0 201508 201508~', 'DATEVALUE=0', 'START=20150801', 'END=20150831');
356 qtestcase('VALUE_RANGE 0 201508 20150830~', 'DATEVALUE=0', 'START=20150801', 'END=20150830');
357 qtestcase('VALUE_RANGE 0 201509 20150~', 'DATEVALUE=0', 'START=20150901', 'END=20150930');
358 qtestcase('VALUE_RANGE 0 201509 2015092~', 'DATEVALUE=0', 'START=20150901', 'END=20150929');
359 qtestcase('VALUE_RANGE 0 20151 201510~', 'DATEVALUE=0', 'START=20151001', 'END=20151031');
360 qtestcase('VALUE_RANGE 0 20151 20151030~', 'DATEVALUE=0', 'START=20151001', 'END=20151030');
361 qtestcase('VALUE_RANGE 0 201511 201511~', 'DATEVALUE=0', 'START=20151101', 'END=20151130');
362 qtestcase('VALUE_RANGE 0 201511 2015112~', 'DATEVALUE=0', 'START=20151101', 'END=20151129');
363 qtestcase('VALUE_RANGE 0 201512 2015~', 'DATEVALUE=0', 'START=20151201', 'END=20151231');
364 qtestcase('VALUE_RANGE 0 201512 20151230~', 'DATEVALUE=0', 'START=20151201', 'END=20151230');
366 # Forward spans:
367 qtestcase('VALUE_RANGE 0 20151104 20151106~', 'DATEVALUE=0', 'START=20151104', 'SPAN=3');
368 qtestcase('VALUE_RANGE 0 20141104 20151103~', 'DATEVALUE=0', 'START=20141104', 'SPAN=365');
370 # Backward spans:
371 qtestcase('VALUE_RANGE 0 20151104 20151106~', 'DATEVALUE=0', 'END=20151106', 'SPAN=3');
372 qtestcase('VALUE_RANGE 0 20141104 20151103~', 'DATEVALUE=0', 'END=20151103', 'SPAN=365');
374 # Check that if START, END and SPAN are all passed, START is ignored:
375 qtestcase('VALUE_RANGE 0 20151104 20151106~', 'DATEVALUE=0', 'START=19700101', 'END=20151106', 'SPAN=3');
377 # Test date value ranges using newer ".SLOT" parameters.
378 qtestcase('VALUE_RANGE 0 2 ~', 'START.0=2000');
379 qtestcase('VALUE_RANGE 0 2 ~', 'START.0=200001');
380 qtestcase('VALUE_RANGE 0 2 ~', 'START.0=20000101');
381 qtestcase('VALUE_LE 1 1~', 'END.1=1999');
382 qtestcase('VALUE_LE 1 1~', 'END.1=199912');
383 qtestcase('VALUE_LE 1 1~', 'END.1=19991231');
384 qtestcase('VALUE_RANGE 2 201 ~', 'START.2=2010');
385 qtestcase('VALUE_RANGE 2 201 ~', 'START.2=201001');
386 qtestcase('VALUE_RANGE 2 201 ~', 'START.2=20100101');
387 qtestcase('VALUE_LE 3 198~', 'END.3=1989');
388 qtestcase('VALUE_LE 3 198~', 'END.3=198912');
389 qtestcase('VALUE_LE 3 198~', 'END.3=19891231');
390 qtestcase('VALUE_RANGE 4 1974 ~', 'START.4=1974');
391 qtestcase('VALUE_RANGE 4 1974 ~', 'START.4=197401');
392 qtestcase('VALUE_RANGE 4 1974 ~', 'START.4=19740101');
393 qtestcase('VALUE_LE 5 1974~', 'END.5=1974');
394 qtestcase('VALUE_LE 5 1974~', 'END.5=197412');
395 qtestcase('VALUE_LE 5 1974~', 'END.5=19741231');
396 qtestcase('VALUE_RANGE 6 20151 ~', 'START.6=201510');
397 qtestcase('VALUE_RANGE 6 20151 ~', 'START.6=20151001');
398 qtestcase('VALUE_LE 7 19870~', 'END.7=198709');
399 qtestcase('VALUE_LE 7 19870~', 'END.7=19870930');
400 qtestcase('VALUE_RANGE 8 201512 ~', 'START.8=201512');
401 qtestcase('VALUE_RANGE 8 201512 ~', 'START.8=20151201');
402 qtestcase('VALUE_LE 9 201511~', 'END.9=201511');
403 qtestcase('VALUE_LE 9 201511~', 'END.9=20151130');
404 qtestcase('VALUE_RANGE 10 2015021 ~', 'START.10=20150210');
405 qtestcase('VALUE_RANGE 10 2000022 ~', 'START.10=20000220');
406 qtestcase('VALUE_LE 11 19840401~', 'END.11=19840401');
407 qtestcase('VALUE_LE 11 19881128~', 'END.11=19881128');
409 # Leap year tests:
410 qtestcase('VALUE_LE 1 201502~', 'END.1=20150228');
411 qtestcase('VALUE_LE 1 198802~', 'END.1=19880229');
412 qtestcase('VALUE_LE 1 19880228~', 'END.1=19880228');
413 qtestcase('VALUE_LE 1 200002~', 'END.1=20000229');
414 qtestcase('VALUE_LE 1 20000228~', 'END.1=20000228');
415 if (!$have_32bit_time_t) {
416 qtestcase('VALUE_LE 1 190002~', 'END.1=19000228');
417 qtestcase('VALUE_LE 1 210002~', 'END.1=21000228');
420 # Month starts and ends:
421 qtestcase('VALUE_RANGE 0 2015 201501~', 'START.0=20150101', 'END.0=20150131');
422 qtestcase('VALUE_RANGE 0 2015 20150130~', 'START.0=20150101', 'END.0=20150130');
423 qtestcase('VALUE_RANGE 0 201502 201502~', 'START.0=20150201', 'END.0=20150228');
424 qtestcase('VALUE_RANGE 0 201502 20150227~', 'START.0=20150201', 'END.0=20150227');
425 qtestcase('VALUE_RANGE 0 201503 201503~', 'START.0=20150301', 'END.0=20150331');
426 qtestcase('VALUE_RANGE 0 201503 20150330~', 'START.0=20150301', 'END.0=20150330');
427 qtestcase('VALUE_RANGE 0 201504 201504~', 'START.0=20150401', 'END.0=20150430');
428 qtestcase('VALUE_RANGE 0 201504 2015042~', 'START.0=20150401', 'END.0=20150429');
429 qtestcase('VALUE_RANGE 0 201505 201505~', 'START.0=20150501', 'END.0=20150531');
430 qtestcase('VALUE_RANGE 0 201505 20150530~', 'START.0=20150501', 'END.0=20150530');
431 qtestcase('VALUE_RANGE 0 201506 201506~', 'START.0=20150601', 'END.0=20150630');
432 qtestcase('VALUE_RANGE 0 201506 2015062~', 'START.0=20150601', 'END.0=20150629');
433 qtestcase('VALUE_RANGE 0 201507 201507~', 'START.0=20150701', 'END.0=20150731');
434 qtestcase('VALUE_RANGE 0 201507 20150730~', 'START.0=20150701', 'END.0=20150730');
435 qtestcase('VALUE_RANGE 0 201508 201508~', 'START.0=20150801', 'END.0=20150831');
436 qtestcase('VALUE_RANGE 0 201508 20150830~', 'START.0=20150801', 'END.0=20150830');
437 qtestcase('VALUE_RANGE 0 201509 20150~', 'START.0=20150901', 'END.0=20150930');
438 qtestcase('VALUE_RANGE 0 201509 2015092~', 'START.0=20150901', 'END.0=20150929');
439 qtestcase('VALUE_RANGE 0 20151 201510~', 'START.0=20151001', 'END.0=20151031');
440 qtestcase('VALUE_RANGE 0 20151 20151030~', 'START.0=20151001', 'END.0=20151030');
441 qtestcase('VALUE_RANGE 0 201511 201511~', 'START.0=20151101', 'END.0=20151130');
442 qtestcase('VALUE_RANGE 0 201511 2015112~', 'START.0=20151101', 'END.0=20151129');
443 qtestcase('VALUE_RANGE 0 201512 2015~', 'START.0=20151201', 'END.0=20151231');
444 qtestcase('VALUE_RANGE 0 201512 20151230~', 'START.0=20151201', 'END.0=20151230');
446 # Forward spans:
447 qtestcase('VALUE_RANGE 0 20151104 20151106~', 'START.0=20151104', 'SPAN.0=3');
448 qtestcase('VALUE_RANGE 0 20141104 20151103~', 'START.0=20141104', 'SPAN.0=365');
450 # Backward spans:
451 qtestcase('VALUE_RANGE 0 20151104 20151106~', 'END.0=20151106', 'SPAN.0=3');
452 qtestcase('VALUE_RANGE 0 20141104 20151103~', 'END.0=20151103', 'SPAN.0=365');
454 # Empty spans:
455 qtestcase('', 'START.0=20180919', 'END.0=19991225');
456 # Check that the empty span dominates other spans:
457 qtestcase('', 'START.0=20180919', 'END.0=19991225', 'START.1=20180901');
458 # Check that the empty span dominates a filter term:
459 qtestcase('', 'START.0=20180919', 'END.0=19991225', 'B=Ktag');
460 # Check that the empty span dominates a negated filter term:
461 qtestcase('', 'START.0=20180919', 'END.0=19991225', 'N=Kneg');
462 # Check that the empty span dominates a term-based date range filter:
463 qtestcase('', 'START.0=20180919', 'END.0=19991225', 'START=20000101', 'END=20001231');
465 # Check that if START, END and SPAN are all passed, START is ignored:
466 qtestcase('VALUE_RANGE 0 20151104 20151106~', 'START.0=19700101', 'END.0=20151106', 'SPAN.0=3');
468 # Check multiple .SLOT filters:
469 qtestcase('(VALUE_RANGE 0 201512 2015~ AND VALUE_LE 5 1974~)', 'START.0=20151201', 'END.0=20151231', 'END.5=1974');
470 qtestcase('(VALUE_RANGE 0 201512 20151207~ AND VALUE_RANGE 5 19740102 1974~)', 'START.0=20151201', 'SPAN.0=7', 'END.5=1974', 'SPAN.5=364');
472 # Check .SLOT and old-style value date range filter:
473 qtestcase('(VALUE_RANGE 0 201512 2015~ AND VALUE_LE 5 1974~)', 'START.0=20151201', 'END.0=20151231', 'DATEVALUE=5', 'END=1974');
475 # Check .SLOT and old-style value date range filter on same slot.
476 # (We don't promise anything for this case, but it shouldn't crash).
477 qtestcase('VALUE_RANGE 0 201512 2015~', 'START.0=20151201', 'END.0=20151231', 'DATEVALUE=0', 'END=1974');
479 # Tests of term-based date range filtering:
481 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR Y1988 OR Y1989 OR Y1990 OR Y1991 OR Y1992 OR Y1993 OR Y1994 OR Y1995 OR Y1996 OR Y1997 OR Y1998 OR Y1999 OR Dlatest)', 'END=19991231');
482 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR Y1988 OR Y1989 OR Dlatest)', 'END=19891231');
483 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Dlatest)', 'END=19741231');
484 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR M198701 OR M198702 OR M198703 OR M198704 OR M198705 OR M198706 OR M198707 OR M198708 OR M198709 OR Dlatest)', 'END=19870930');
485 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR Y1988 OR Y1989 OR Y1990 OR Y1991 OR Y1992 OR Y1993 OR Y1994 OR Y1995 OR Y1996 OR Y1997 OR Y1998 OR Y1999 OR Y2000 OR Y2001 OR Y2002 OR Y2003 OR Y2004 OR Y2005 OR Y2006 OR Y2007 OR Y2008 OR Y2009 OR Y2010 OR Y2011 OR Y2012 OR Y2013 OR Y2014 OR M201501 OR M201502 OR M201503 OR M201504 OR M201505 OR M201506 OR M201507 OR M201508 OR M201509 OR M201510 OR M201511 OR Dlatest)', 'END=20151130');
486 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR M198401 OR M198402 OR M198403 OR D19840401 OR Dlatest)', 'END=19840401');
487 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR M198801 OR M198802 OR M198803 OR M198804 OR M198805 OR M198806 OR M198807 OR M198808 OR M198809 OR M198810 OR D19881101 OR D19881102 OR D19881103 OR D19881104 OR D19881105 OR D19881106 OR D19881107 OR D19881108 OR D19881109 OR D19881110 OR D19881111 OR D19881112 OR D19881113 OR D19881114 OR D19881115 OR D19881116 OR D19881117 OR D19881118 OR D19881119 OR D19881120 OR D19881121 OR D19881122 OR D19881123 OR D19881124 OR D19881125 OR D19881126 OR D19881127 OR D19881128 OR Dlatest)', 'END=19881128');
489 # Leap year tests:
490 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR Y1988 OR Y1989 OR Y1990 OR Y1991 OR Y1992 OR Y1993 OR Y1994 OR Y1995 OR Y1996 OR Y1997 OR Y1998 OR Y1999 OR Y2000 OR Y2001 OR Y2002 OR Y2003 OR Y2004 OR Y2005 OR Y2006 OR Y2007 OR Y2008 OR Y2009 OR Y2010 OR Y2011 OR Y2012 OR Y2013 OR Y2014 OR M201501 OR M201502 OR Dlatest)', 'END=20150228');
491 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR M198801 OR M198802 OR Dlatest)', 'END=19880229');
492 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR M198801 OR D19880201 OR D19880202 OR D19880203 OR D19880204 OR D19880205 OR D19880206 OR D19880207 OR D19880208 OR D19880209 OR D19880210 OR D19880211 OR D19880212 OR D19880213 OR D19880214 OR D19880215 OR D19880216 OR D19880217 OR D19880218 OR D19880219 OR D19880220 OR D19880221 OR D19880222 OR D19880223 OR D19880224 OR D19880225 OR D19880226 OR D19880227 OR D19880228 OR Dlatest)', 'END=19880228');
493 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR Y1988 OR Y1989 OR Y1990 OR Y1991 OR Y1992 OR Y1993 OR Y1994 OR Y1995 OR Y1996 OR Y1997 OR Y1998 OR Y1999 OR M200001 OR M200002 OR Dlatest)', 'END=20000229');
494 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR Y1988 OR Y1989 OR Y1990 OR Y1991 OR Y1992 OR Y1993 OR Y1994 OR Y1995 OR Y1996 OR Y1997 OR Y1998 OR Y1999 OR M200001 OR D20000201 OR D20000202 OR D20000203 OR D20000204 OR D20000205 OR D20000206 OR D20000207 OR D20000208 OR D20000209 OR D20000210 OR D20000211 OR D20000212 OR D20000213 OR D20000214 OR D20000215 OR D20000216 OR D20000217 OR D20000218 OR D20000219 OR D20000220 OR D20000221 OR D20000222 OR D20000223 OR D20000224 OR D20000225 OR D20000226 OR D20000227 OR D20000228 OR Dlatest)', 'END=20000228');
495 if (!$have_32bit_time_t) {
496 qtestcase('Dlatest', 'END=19000228'); # Assumed start is 19700101
497 qtestcase('(Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR Y1988 OR Y1989 OR Y1990 OR Y1991 OR Y1992 OR Y1993 OR Y1994 OR Y1995 OR Y1996 OR Y1997 OR Y1998 OR Y1999 OR Y2000 OR Y2001 OR Y2002 OR Y2003 OR Y2004 OR Y2005 OR Y2006 OR Y2007 OR Y2008 OR Y2009 OR Y2010 OR Y2011 OR Y2012 OR Y2013 OR Y2014 OR Y2015 OR Y2016 OR Y2017 OR Y2018 OR Y2019 OR Y2020 OR Y2021 OR Y2022 OR Y2023 OR Y2024 OR Y2025 OR Y2026 OR Y2027 OR Y2028 OR Y2029 OR Y2030 OR Y2031 OR Y2032 OR Y2033 OR Y2034 OR Y2035 OR Y2036 OR Y2037 OR Y2038 OR Y2039 OR Y2040 OR Y2041 OR Y2042 OR Y2043 OR Y2044 OR Y2045 OR Y2046 OR Y2047 OR Y2048 OR Y2049 OR Y2050 OR Y2051 OR Y2052 OR Y2053 OR Y2054 OR Y2055 OR Y2056 OR Y2057 OR Y2058 OR Y2059 OR Y2060 OR Y2061 OR Y2062 OR Y2063 OR Y2064 OR Y2065 OR Y2066 OR Y2067 OR Y2068 OR Y2069 OR Y2070 OR Y2071 OR Y2072 OR Y2073 OR Y2074 OR Y2075 OR Y2076 OR Y2077 OR Y2078 OR Y2079 OR Y2080 OR Y2081 OR Y2082 OR Y2083 OR Y2084 OR Y2085 OR Y2086 OR Y2087 OR Y2088 OR Y2089 OR Y2090 OR Y2091 OR Y2092 OR Y2093 OR Y2094 OR Y2095 OR Y2096 OR Y2097 OR Y2098 OR Y2099 OR M210001 OR M210002 OR Dlatest)', 'END=21000228');
500 # Month starts and ends:
501 qtestcase('(M201501 OR Dlatest)', 'START=20150101', 'END=20150131');
502 qtestcase('(D20150101 OR D20150102 OR D20150103 OR D20150104 OR D20150105 OR D20150106 OR D20150107 OR D20150108 OR D20150109 OR D20150110 OR D20150111 OR D20150112 OR D20150113 OR D20150114 OR D20150115 OR D20150116 OR D20150117 OR D20150118 OR D20150119 OR D20150120 OR D20150121 OR D20150122 OR D20150123 OR D20150124 OR D20150125 OR D20150126 OR D20150127 OR D20150128 OR D20150129 OR D20150130 OR Dlatest)', 'START=20150101', 'END=20150130');
503 qtestcase('(M201502 OR Dlatest)', 'START=20150201', 'END=20150228');
504 qtestcase('(D20150201 OR D20150202 OR D20150203 OR D20150204 OR D20150205 OR D20150206 OR D20150207 OR D20150208 OR D20150209 OR D20150210 OR D20150211 OR D20150212 OR D20150213 OR D20150214 OR D20150215 OR D20150216 OR D20150217 OR D20150218 OR D20150219 OR D20150220 OR D20150221 OR D20150222 OR D20150223 OR D20150224 OR D20150225 OR D20150226 OR D20150227 OR Dlatest)', 'START=20150201', 'END=20150227');
505 qtestcase('(M201503 OR Dlatest)', 'START=20150301', 'END=20150331');
506 qtestcase('(D20150301 OR D20150302 OR D20150303 OR D20150304 OR D20150305 OR D20150306 OR D20150307 OR D20150308 OR D20150309 OR D20150310 OR D20150311 OR D20150312 OR D20150313 OR D20150314 OR D20150315 OR D20150316 OR D20150317 OR D20150318 OR D20150319 OR D20150320 OR D20150321 OR D20150322 OR D20150323 OR D20150324 OR D20150325 OR D20150326 OR D20150327 OR D20150328 OR D20150329 OR D20150330 OR Dlatest)', 'START=20150301', 'END=20150330');
507 qtestcase('(M201504 OR Dlatest)', 'START=20150401', 'END=20150430');
508 qtestcase('(D20150401 OR D20150402 OR D20150403 OR D20150404 OR D20150405 OR D20150406 OR D20150407 OR D20150408 OR D20150409 OR D20150410 OR D20150411 OR D20150412 OR D20150413 OR D20150414 OR D20150415 OR D20150416 OR D20150417 OR D20150418 OR D20150419 OR D20150420 OR D20150421 OR D20150422 OR D20150423 OR D20150424 OR D20150425 OR D20150426 OR D20150427 OR D20150428 OR D20150429 OR Dlatest)', 'START=20150401', 'END=20150429');
509 qtestcase('(M201505 OR Dlatest)', 'START=20150501', 'END=20150531');
510 qtestcase('(D20150501 OR D20150502 OR D20150503 OR D20150504 OR D20150505 OR D20150506 OR D20150507 OR D20150508 OR D20150509 OR D20150510 OR D20150511 OR D20150512 OR D20150513 OR D20150514 OR D20150515 OR D20150516 OR D20150517 OR D20150518 OR D20150519 OR D20150520 OR D20150521 OR D20150522 OR D20150523 OR D20150524 OR D20150525 OR D20150526 OR D20150527 OR D20150528 OR D20150529 OR D20150530 OR Dlatest)', 'START=20150501', 'END=20150530');
511 qtestcase('(M201506 OR Dlatest)', 'START=20150601', 'END=20150630');
512 qtestcase('(D20150601 OR D20150602 OR D20150603 OR D20150604 OR D20150605 OR D20150606 OR D20150607 OR D20150608 OR D20150609 OR D20150610 OR D20150611 OR D20150612 OR D20150613 OR D20150614 OR D20150615 OR D20150616 OR D20150617 OR D20150618 OR D20150619 OR D20150620 OR D20150621 OR D20150622 OR D20150623 OR D20150624 OR D20150625 OR D20150626 OR D20150627 OR D20150628 OR D20150629 OR Dlatest)', 'START=20150601', 'END=20150629');
513 qtestcase('(M201507 OR Dlatest)', 'START=20150701', 'END=20150731');
514 qtestcase('(D20150701 OR D20150702 OR D20150703 OR D20150704 OR D20150705 OR D20150706 OR D20150707 OR D20150708 OR D20150709 OR D20150710 OR D20150711 OR D20150712 OR D20150713 OR D20150714 OR D20150715 OR D20150716 OR D20150717 OR D20150718 OR D20150719 OR D20150720 OR D20150721 OR D20150722 OR D20150723 OR D20150724 OR D20150725 OR D20150726 OR D20150727 OR D20150728 OR D20150729 OR D20150730 OR Dlatest)', 'START=20150701', 'END=20150730');
515 qtestcase('(M201508 OR Dlatest)', 'START=20150801', 'END=20150831');
516 qtestcase('(D20150801 OR D20150802 OR D20150803 OR D20150804 OR D20150805 OR D20150806 OR D20150807 OR D20150808 OR D20150809 OR D20150810 OR D20150811 OR D20150812 OR D20150813 OR D20150814 OR D20150815 OR D20150816 OR D20150817 OR D20150818 OR D20150819 OR D20150820 OR D20150821 OR D20150822 OR D20150823 OR D20150824 OR D20150825 OR D20150826 OR D20150827 OR D20150828 OR D20150829 OR D20150830 OR Dlatest)', 'START=20150801', 'END=20150830');
517 qtestcase('(M201509 OR Dlatest)', 'START=20150901', 'END=20150930');
518 qtestcase('(D20150901 OR D20150902 OR D20150903 OR D20150904 OR D20150905 OR D20150906 OR D20150907 OR D20150908 OR D20150909 OR D20150910 OR D20150911 OR D20150912 OR D20150913 OR D20150914 OR D20150915 OR D20150916 OR D20150917 OR D20150918 OR D20150919 OR D20150920 OR D20150921 OR D20150922 OR D20150923 OR D20150924 OR D20150925 OR D20150926 OR D20150927 OR D20150928 OR D20150929 OR Dlatest)', 'START=20150901', 'END=20150929');
519 qtestcase('(M201510 OR Dlatest)', 'START=20151001', 'END=20151031');
520 qtestcase('(D20151001 OR D20151002 OR D20151003 OR D20151004 OR D20151005 OR D20151006 OR D20151007 OR D20151008 OR D20151009 OR D20151010 OR D20151011 OR D20151012 OR D20151013 OR D20151014 OR D20151015 OR D20151016 OR D20151017 OR D20151018 OR D20151019 OR D20151020 OR D20151021 OR D20151022 OR D20151023 OR D20151024 OR D20151025 OR D20151026 OR D20151027 OR D20151028 OR D20151029 OR D20151030 OR Dlatest)', 'START=20151001', 'END=20151030');
521 qtestcase('(M201511 OR Dlatest)', 'START=20151101', 'END=20151130');
522 qtestcase('(D20151101 OR D20151102 OR D20151103 OR D20151104 OR D20151105 OR D20151106 OR D20151107 OR D20151108 OR D20151109 OR D20151110 OR D20151111 OR D20151112 OR D20151113 OR D20151114 OR D20151115 OR D20151116 OR D20151117 OR D20151118 OR D20151119 OR D20151120 OR D20151121 OR D20151122 OR D20151123 OR D20151124 OR D20151125 OR D20151126 OR D20151127 OR D20151128 OR D20151129 OR Dlatest)', 'START=20151101', 'END=20151129');
523 qtestcase('(M201512 OR Dlatest)', 'START=20151201', 'END=20151231');
524 qtestcase('(D20151201 OR D20151202 OR D20151203 OR D20151204 OR D20151205 OR D20151206 OR D20151207 OR D20151208 OR D20151209 OR D20151210 OR D20151211 OR D20151212 OR D20151213 OR D20151214 OR D20151215 OR D20151216 OR D20151217 OR D20151218 OR D20151219 OR D20151220 OR D20151221 OR D20151222 OR D20151223 OR D20151224 OR D20151225 OR D20151226 OR D20151227 OR D20151228 OR D20151229 OR D20151230 OR Dlatest)', 'START=20151201', 'END=20151230');
526 # Forward spans:
527 qtestcase('(D20151104 OR D20151105 OR D20151106 OR D20151107 OR Dlatest)', 'START=20151104', 'SPAN=3');
528 qtestcase('(D20141104 OR D20141105 OR D20141106 OR D20141107 OR D20141108 OR D20141109 OR D20141110 OR D20141111 OR D20141112 OR D20141113 OR D20141114 OR D20141115 OR D20141116 OR D20141117 OR D20141118 OR D20141119 OR D20141120 OR D20141121 OR D20141122 OR D20141123 OR D20141124 OR D20141125 OR D20141126 OR D20141127 OR D20141128 OR D20141129 OR D20141130 OR M201412 OR M201501 OR M201502 OR M201503 OR M201504 OR M201505 OR M201506 OR M201507 OR M201508 OR M201509 OR M201510 OR D20151101 OR D20151102 OR D20151103 OR D20151104 OR Dlatest)', 'START=20141104', 'SPAN=365');
530 # Backward spans:
531 qtestcase('(D20151103 OR D20151104 OR D20151105 OR D20151106 OR Dlatest)', 'END=20151106', 'SPAN=3');
532 qtestcase('(D20141103 OR D20141104 OR D20141105 OR D20141106 OR D20141107 OR D20141108 OR D20141109 OR D20141110 OR D20141111 OR D20141112 OR D20141113 OR D20141114 OR D20141115 OR D20141116 OR D20141117 OR D20141118 OR D20141119 OR D20141120 OR D20141121 OR D20141122 OR D20141123 OR D20141124 OR D20141125 OR D20141126 OR D20141127 OR D20141128 OR D20141129 OR D20141130 OR M201412 OR M201501 OR M201502 OR M201503 OR M201504 OR M201505 OR M201506 OR M201507 OR M201508 OR M201509 OR M201510 OR D20151101 OR D20151102 OR D20151103 OR Dlatest)', 'END=20151103', 'SPAN=365');
534 # Check that if START, END and SPAN are all passed, START is ignored:
535 qtestcase('(D20151103 OR D20151104 OR D20151105 OR D20151106 OR Dlatest)', 'START=19700101', 'END=20151106', 'SPAN=3');
537 # Check that YYYYMM and YYYY are accepted and handled appropriately:
538 qtestcase('(Y1980 OR Y1981 OR Dlatest)', 'START=1980', 'END=1981');
539 qtestcase('(M198012 OR M198101 OR M198102 OR Dlatest)', 'START=198012', 'END=198102');
541 # Check .SLOT combined with term based date range filter:
542 qtestcase('(VALUE_RANGE 0 201512 2015~ AND (Y1970 OR Y1971 OR Y1972 OR Y1973 OR Y1974 OR Dlatest))', 'START.0=20151201', 'END.0=20151231', 'END=19741231');
544 # Check combining of filter terms:
545 qtestcase('(Horg AND Len)', 'B=Len', 'B=Horg');
546 qtestcase('(Len OR Lde)', 'B=Len', 'B=Lde');
547 qtestcase('((Horg OR Hcom) AND (Len OR Lfr))', 'B=Len', 'B=Lfr', 'B=Horg', 'B=Hcom');
549 # Check combining of filter terms with filter_op set:
550 print_to_file $test_template,('$setmap{nonexclusiveprefix,L,1,XAND,1}$setmap{boolprefix,lang,L,and,XAND,host,H,year,Y}$querydescription');
551 qtestcase('Len', 'B=Len');
552 qtestcase('0 * Len', 'P=lang:en');
553 qtestcase('XANDtest', 'B=XANDtest');
554 qtestcase('0 * XANDtest', 'P=and:test');
555 qtestcase('(Len AND XANDtest)', 'B=Len', 'B=XANDtest');
556 qtestcase('0 * (Len AND XANDtest)', 'P=lang:en and:test');
557 qtestcase('(Len AND Lde)', 'B=Len', 'B=Lde');
558 qtestcase('0 * (Len AND Lde)', 'P=lang:en lang:de');
559 qtestcase('((Horg OR Hcom) AND (Len AND Lfr))', 'B=Len', 'B=Lfr', 'B=Horg', 'B=Hcom');
560 qtestcase('0 * ((Len AND Lfr) AND (Horg OR Hcom))', 'P=lang:en lang:fr host:org host:com');
561 qtestcase('((XANDa AND XANDb AND XANDc) AND (Y1998 OR Y2001))', 'B=Y1998', 'B=Y2001', 'B=XANDa', 'B=XANDb', 'B=XANDc');
562 qtestcase('0 * ((XANDa AND XANDb AND XANDc) AND (Y1998 OR Y2001))', 'P=year:1998 year:2001 and:a and:b and:c');
564 # Check combining of filters around CGI parameter 'N':
565 qtestcase('(Ztruth@1 AND_NOT Epdf)', 'P=truth', 'N=Epdf');
566 qtestcase('(Ztruth@1 AND_NOT (Ehtm OR Epdf))', 'P=truth', 'N=Epdf', 'N=Ehtm');
567 qtestcase('(Ztruth@1 AND_NOT (Ehtm OR Epdf OR Lde OR Lfr))', 'P=truth', 'N=Lfr', 'N=Epdf', 'N=Ehtm', 'N=Lde');
568 qtestcase('((Ztruth@1 FILTER (Lfr AND Lzh)) AND_NOT (Lde OR Len))', 'P=truth', 'N=Lde', 'N=Len', 'B=Lfr', 'B=Lzh');
569 qtestcase('((Ztruth@1 FILTER Lfr) AND_NOT (Ehtm OR Epdf))', 'P=truth', 'N=Epdf', 'N=Ehtm', 'B=Lfr');
570 qtestcase('(<alldocuments> AND_NOT (Len OR Lfr))', 'N=Lfr', 'N=Len');
571 qtestcase('(VALUE_RANGE 0 2015 201501~ AND_NOT Len)', 'DATEVALUE=0', 'START=20150101', 'END=20150131', 'N=Len');
573 if ($faketime ne '') {
574 my $out = `$faketime -f '1980-12-08 00:00:00' date +%Y 2>&1`;
575 chomp($out);
576 my $rc = $? >> 8;
577 if ($rc == 127) {
578 print "Skipping testcases which need 'faketime' tool installed\n";
579 } elsif ($rc != 0) {
580 print "Skipping testcases which need 'faketime' tool - it's installed but doesn't work (test exited with return code $rc)\n";
581 } elsif ($out ne '1980') {
582 print "Skipping testcases which need 'faketime' tool - it's installed but doesn't work (got year '$out' instead of '1980')\n";
583 } else {
584 # We have faketime and it seems to work so use it to run test cases where
585 # the output depends on the current time.
586 my $save_omega = $omega;
587 $omega = [$faketime, '-f', '2015-11-28 06:07:08', $omega];
589 my $save_TZ = $ENV{TZ};
590 $ENV{TZ} = 'UTC';
591 qtestcase('VALUE_RANGE 0 20151127060709 20151128060708', 'DATEVALUE=0', 'SPAN=1');
592 if (defined $save_TZ) {
593 $ENV{TZ} = $save_TZ;
594 } else {
595 delete $ENV{TZ};
598 # Tests of term-based date range filtering:
599 qtestcase('(Y2000 OR Y2001 OR Y2002 OR Y2003 OR Y2004 OR Y2005 OR Y2006 OR Y2007 OR Y2008 OR Y2009 OR Y2010 OR Y2011 OR Y2012 OR Y2013 OR Y2014 OR M201501 OR M201502 OR M201503 OR M201504 OR M201505 OR M201506 OR M201507 OR M201508 OR M201509 OR M201510 OR D20151101 OR D20151102 OR D20151103 OR D20151104 OR D20151105 OR D20151106 OR D20151107 OR D20151108 OR D20151109 OR D20151110 OR D20151111 OR D20151112 OR D20151113 OR D20151114 OR D20151115 OR D20151116 OR D20151117 OR D20151118 OR D20151119 OR D20151120 OR D20151121 OR D20151122 OR D20151123 OR D20151124 OR D20151125 OR D20151126 OR D20151127 OR D20151128 OR Dlatest)', 'START=20000101');
600 qtestcase('(Y2010 OR Y2011 OR Y2012 OR Y2013 OR Y2014 OR M201501 OR M201502 OR M201503 OR M201504 OR M201505 OR M201506 OR M201507 OR M201508 OR M201509 OR M201510 OR D20151101 OR D20151102 OR D20151103 OR D20151104 OR D20151105 OR D20151106 OR D20151107 OR D20151108 OR D20151109 OR D20151110 OR D20151111 OR D20151112 OR D20151113 OR D20151114 OR D20151115 OR D20151116 OR D20151117 OR D20151118 OR D20151119 OR D20151120 OR D20151121 OR D20151122 OR D20151123 OR D20151124 OR D20151125 OR D20151126 OR D20151127 OR D20151128 OR Dlatest)', 'START=20100101');
601 qtestcase('(Y1974 OR Y1975 OR Y1976 OR Y1977 OR Y1978 OR Y1979 OR Y1980 OR Y1981 OR Y1982 OR Y1983 OR Y1984 OR Y1985 OR Y1986 OR Y1987 OR Y1988 OR Y1989 OR Y1990 OR Y1991 OR Y1992 OR Y1993 OR Y1994 OR Y1995 OR Y1996 OR Y1997 OR Y1998 OR Y1999 OR Y2000 OR Y2001 OR Y2002 OR Y2003 OR Y2004 OR Y2005 OR Y2006 OR Y2007 OR Y2008 OR Y2009 OR Y2010 OR Y2011 OR Y2012 OR Y2013 OR Y2014 OR M201501 OR M201502 OR M201503 OR M201504 OR M201505 OR M201506 OR M201507 OR M201508 OR M201509 OR M201510 OR D20151101 OR D20151102 OR D20151103 OR D20151104 OR D20151105 OR D20151106 OR D20151107 OR D20151108 OR D20151109 OR D20151110 OR D20151111 OR D20151112 OR D20151113 OR D20151114 OR D20151115 OR D20151116 OR D20151117 OR D20151118 OR D20151119 OR D20151120 OR D20151121 OR D20151122 OR D20151123 OR D20151124 OR D20151125 OR D20151126 OR D20151127 OR D20151128 OR Dlatest)', 'START=19740101');
602 qtestcase('(M201510 OR D20151101 OR D20151102 OR D20151103 OR D20151104 OR D20151105 OR D20151106 OR D20151107 OR D20151108 OR D20151109 OR D20151110 OR D20151111 OR D20151112 OR D20151113 OR D20151114 OR D20151115 OR D20151116 OR D20151117 OR D20151118 OR D20151119 OR D20151120 OR D20151121 OR D20151122 OR D20151123 OR D20151124 OR D20151125 OR D20151126 OR D20151127 OR D20151128 OR Dlatest)', 'START=20151001');
603 # Date range with start after end:
604 qtestcase('Dlatest', 'START=201512');
605 qtestcase('Dlatest', 'START=20151201');
606 qtestcase('(D20150210 OR D20150211 OR D20150212 OR D20150213 OR D20150214 OR D20150215 OR D20150216 OR D20150217 OR D20150218 OR D20150219 OR D20150220 OR D20150221 OR D20150222 OR D20150223 OR D20150224 OR D20150225 OR D20150226 OR D20150227 OR D20150228 OR M201503 OR M201504 OR M201505 OR M201506 OR M201507 OR M201508 OR M201509 OR M201510 OR D20151101 OR D20151102 OR D20151103 OR D20151104 OR D20151105 OR D20151106 OR D20151107 OR D20151108 OR D20151109 OR D20151110 OR D20151111 OR D20151112 OR D20151113 OR D20151114 OR D20151115 OR D20151116 OR D20151117 OR D20151118 OR D20151119 OR D20151120 OR D20151121 OR D20151122 OR D20151123 OR D20151124 OR D20151125 OR D20151126 OR D20151127 OR D20151128 OR Dlatest)', 'START=20150210');
607 qtestcase('(D20000220 OR D20000221 OR D20000222 OR D20000223 OR D20000224 OR D20000225 OR D20000226 OR D20000227 OR D20000228 OR D20000229 OR M200003 OR M200004 OR M200005 OR M200006 OR M200007 OR M200008 OR M200009 OR M200010 OR M200011 OR M200012 OR Y2001 OR Y2002 OR Y2003 OR Y2004 OR Y2005 OR Y2006 OR Y2007 OR Y2008 OR Y2009 OR Y2010 OR Y2011 OR Y2012 OR Y2013 OR Y2014 OR M201501 OR M201502 OR M201503 OR M201504 OR M201505 OR M201506 OR M201507 OR M201508 OR M201509 OR M201510 OR D20151101 OR D20151102 OR D20151103 OR D20151104 OR D20151105 OR D20151106 OR D20151107 OR D20151108 OR D20151109 OR D20151110 OR D20151111 OR D20151112 OR D20151113 OR D20151114 OR D20151115 OR D20151116 OR D20151117 OR D20151118 OR D20151119 OR D20151120 OR D20151121 OR D20151122 OR D20151123 OR D20151124 OR D20151125 OR D20151126 OR D20151127 OR D20151128 OR Dlatest)', 'START=20000220');
608 $omega = $save_omega;
610 } else {
611 print "Skipping testcases which need 'faketime' tool - \$FAKETIME is empty\n";
614 # Check clamping of out of range dates:
615 print_to_file $test_indexscript, "DATE : field valuepacked=0\n";
616 run_scriptindex(<<'END');
617 DATE=0
619 DATE=1617069804
621 qtestcase('VALUE_LE 0 V\x84o\xff', 'START.0=10010101', 'END.0=20151230');
622 if (!$have_32bit_time_t) {
623 qtestcase('VALUE_RANGE 0 V\x83\x1e\x80 \xff\xff\xff\xff', 'START.0=20151230', 'END.0=21060301');
624 qtestcase('VALUE_LE 0 \xff\xff\xff\xff', 'START.0=19691230', 'END.0=21060301');
627 # Test stem_all and stem_strategy.
628 print_to_file $test_template, '$if{$cgi{stem_all},$set{stem_all,$cgi{stem_all}}}$if{$cgi{stem_strategy},$set{stem_strategy,$cgi{stem_strategy}}}$querydescription';
630 qtestcase('(capitalised@1 AND tests@2 AND Zstem@3)', 'P=Capitalised "tests" stemmed');
631 qtestcase('(nearing@1 NEAR 11 distances@2)', 'P=nearing NEAR distances');
632 qtestcase('(capitalis@1 AND test@2 AND stem@3)', 'P=Capitalised "tests" stemmed', 'stem_all=true');
633 qtestcase('(near@1 NEAR 11 distanc@2)', 'P=nearing NEAR distances', 'stem_all=true');
634 qtestcase('(capitalis@1 AND test@2 AND stem@3)', 'P=Capitalised "tests" stemmed', 'stem_strategy=all');
635 qtestcase('(near@1 NEAR 11 distanc@2)', 'P=nearing NEAR distances', 'stem_strategy=all');
636 qtestcase('(Zcapitalis@1 AND Ztest@2 AND Zstem@3)', 'P=Capitalised "tests" stemmed', 'stem_strategy=all_z');
637 qtestcase('(Znear@1 NEAR 11 Zdistanc@2)', 'P=nearing NEAR distances', 'stem_strategy=all_z');
638 qtestcase('(capitalised@1 AND tests@2 AND stemmed@3)', 'P=Capitalised "tests" stemmed', 'stem_strategy=none');
639 qtestcase('(nearing@1 NEAR 11 distances@2)', 'P=nearing NEAR distances', 'stem_strategy=none');
640 qtestcase('(capitalised@1 AND tests@2 AND Zstem@3)', 'P=Capitalised "tests" stemmed', 'stem_strategy=some');
641 qtestcase('(nearing@1 NEAR 11 distances@2)', 'P=nearing NEAR distances', 'stem_strategy=some');
642 qtestcase('(capitalised@1 AND tests@2 AND Zstem@3)', 'P=Capitalised "tests" stemmed', 'stem_strategy=some_full_pos');
643 qtestcase('(Znear@1 NEAR 11 Zdistanc@2)', 'P=nearing NEAR distances', 'stem_strategy=some_full_pos');
644 qtestcase('(capitalised@1 AND tests@2 AND Zstem@3)', 'P=Capitalised "tests" stemmed', 'stem_strategy=some', 'stem_all=true');
645 qtestcase('(nearing@1 NEAR 11 distances@2)', 'P=nearing NEAR distances', 'stem_strategy=some', 'stem_all=true');
647 # Feature tests for $contains.
648 print_to_file $test_template, '$contains{$cgi{a},$cgi{b}}';
649 testcase('6', 'P=text', 'a=fish', 'b=Hello fish');
650 testcase('', 'P=text', 'a=Example', 'b=random');
652 # Feature tests for boolprefix and prefix maps.
653 print_to_file $test_template, '$set{stemmer,}$setmap{boolprefix,lang,L,host,H}$setmap{prefix,,XDEFAULT}$querydescription';
654 qtestcase('((XDEFAULTfoo@1 AND XDEFAULTbar@2) FILTER (Hexample.org AND Lzh))', 'P=host:example.org foo bar lang:zh');
656 # Feature tests for $addfilter.
657 print_to_file $test_template, '$addfilter{Hexample.org}$addfilter{Hexample.com,}$addfilter{XFOObar,B}$querydescription';
658 qtestcase('((Hexample.org OR Hexample.com) AND XFOObar)', 'P=');
659 print_to_file $test_template, '$addfilter{Hexample.org}$addfilter{Hexample.com,N}$addfilter{Hexample.net,N}$querydescription';
660 qtestcase('(Hexample.org AND_NOT (Hexample.com OR Hexample.net))', 'P=');
662 # Feature tests for $if.
663 print_to_file $test_template, '$if{$set{w,1}}$if{a,$set{x,1}}$if{b,$set{y,1},$set{y,2}}$if{,$set{x,0}}$if{,$set{z,2},$set{z,1}}$opt{w}$opt{x}$opt{y}$opt{z}';
664 testcase('1111');
666 # Feature tests for $cond.
667 print_to_file $test_template, '$cond{$cgi{one},1,$cgi{two},2,$cgi{three},3}';
668 testcase('1', 'one=true');
669 testcase('2', 'two=true');
670 testcase('3', 'three=true');
671 testcase('', 'nothing=true');
672 testcase('1', 'one=true', 'two=true', 'three=true');
673 testcase('2', 'two=true', 'three=true');
674 print_to_file $test_template, '$cond{$cgi{one},1,$cgi{two},2,$cgi{alt}}';
675 testcase('1', 'one=true');
676 testcase('2', 'two=true');
677 testcase('none', 'alt=none');
678 # Check evaluation is lazy.
679 print_to_file $test_template, '$cond{$cgi{one},$seterror{err1},$cgi{two},2,$cgi{alt}}$error';
680 testcase('err1', 'one=true');
681 testcase('2', 'two=true');
682 testcase('none', 'alt=none');
683 print_to_file $test_template, '$cond{$cgi{one},1,$cgi{two},$seterror{err2},$cgi{alt}}$error';
684 testcase('1', 'one=true');
685 testcase('err2', 'two=true');
686 testcase('none', 'alt=none');
687 print_to_file $test_template, '$cond{$cgi{one},1,$cgi{two},2,$seterror{erralt}}$error';
688 testcase('1', 'one=true');
689 testcase('2', 'two=true');
690 testcase('erralt', 'alt=none');
692 # Feature tests for $switch.
693 print_to_file $test_template, '$switch{$cgi{x},1,one,2,two}';
694 testcase('one', 'x=1');
695 testcase('two', 'x=2');
696 testcase('', 'x=3');
697 print_to_file $test_template, '$switch{$cgi{x},1,one,2,two,default}';
698 testcase('one', 'x=1');
699 testcase('two', 'x=2');
700 testcase('default', 'x=3');
701 # Check evaluation is lazy.
702 print_to_file $test_template, '$switch{$cgi{x},1,$seterror{err1},2,two}$error';
703 testcase('err1', 'x=1');
704 testcase('two', 'x=2');
705 testcase('', 'x=3');
706 print_to_file $test_template, '$switch{$cgi{x},1,one,2,$seterror{err2},default}$error';
707 testcase('one', 'x=1');
708 testcase('err2', 'x=2');
709 testcase('default', 'x=3');
710 print_to_file $test_template, '$switch{$cgi{x},1,one,2,two,$seterror{errdefault}}$error';
711 testcase('one', 'x=1');
712 testcase('two', 'x=2');
713 testcase('errdefault', 'x=3');
715 # Feature tests for $include.
716 # Test inclusion works and evaluates the included file.
717 print_to_file $test_template, '$set{c,$add{$or{$opt{c},0},1}}$opt{c},$if{$lt{$opt{c},10},$include{' . $test_template . '}}X';
718 testcase('1,2,3,4,5,6,7,8,9,10,XXXXXXXXXX');
719 # Test fallback action when trying to $include a file which doesn't exist or
720 # with a prohibited name.
721 print_to_file $test_template, '$include{$cgi{template},foo$set{data,bar}}-$opt{data}';
722 testcase('foo-bar', 'template=non_existent.template');
723 testcase('foo-bar', 'template=../secret/file');
724 # Test exception is thrown when there's no fallback action when trying to
725 # $include a file which doesn't exist or with a prohibited name.
726 print_to_file $test_template, '$include{$cgi{template}}-$opt{data}';
727 testcase("Exception: Couldn't read format template 'non_existent.template' (No such file or directory)", 'template=non_existent.template');
728 testcase("Exception: Couldn't read format template '../secret/file' (name contains '..')", 'template=../secret/file');
730 # Feature tests for $foreach.
731 print_to_file $test_template, '$foreach{$split{.,$cgi{a}},$chr{$add{$_,64}}}';
732 testcase('OMEGA', 'a=15.13.5.7.1');
733 # Check that the outer $_ is restored after the inner $foreach.
734 print_to_file $test_template, '$foreach{$split{.,$cgi{a}},$foreach{$split{.,$cgi{$_}},$chr{$add{$_,64}}}$_}';
735 testcase('OMbEGcAd', 'a=b.c.d', 'b=15.13', 'c=5.7', 'd=1');
737 # Feature tests for $map.
738 print_to_file $test_template, '$list{$map{$split{-,$cgi{a}},$list{$map{$split{,$cgi{b}},$_},-}},|}';
739 testcase('1-2-3|1-2-3', 'P=text', 'a=ab-cd', 'b=123');
740 print_to_file $test_template, '$list{$map{$split{-,$cgi{a}},$set{__,$_}$list{$map{$split{,$cgi{b}},$opt{__}$_},-}},|}';
741 testcase('a1-a2-a3|b1-b2-b3', 'P=text', 'a=a-b', 'b=123');
742 print_to_file $test_template, '$list{$map{$split{-,$cgi{a}},$list{$map{$split{$_,$cgi{b}},$_},-}},|}';
743 testcase('1-2,3|1:2-3', 'P=text', 'a=:-,', 'b=1:2,3');
744 # Check that the outer $_ is restored after the inner $map.
745 print_to_file $test_template, '$list{$map{$split{-,$cgi{a}},$list{$map{$split{,$cgi{b}},$_},-}$_},|}';
746 testcase('1-2-3a|1-2-3b', 'P=text', 'a=a-b', 'b=123');
748 # Feature tests for $match.
749 print_to_file $test_template, '$match{$cgi{a},$cgi{b},$cgi{c}}';
750 testcase('true', 'P=text', 'a=ab*c+', 'b=ac');
751 testcase('', 'P=text', 'a=acb', 'b=abc');
752 testcase('true', 'P=text', 'a=[A-Z]bcD', 'b=abcd', 'c=i');
753 testcase('', 'P=text', 'a=[A-Z]bcD', 'b=abcd');
754 testcase('true', 'P=text', 'a=^abc$', "b=\nabc\ndef", 'c=m');
755 testcase('', 'P=text', 'a=^abc$', "b=\nabc\ndef");
756 testcase('true', 'P=text', 'a=abc.', "b=abc\n", 'c=s');
757 testcase('', 'P=text', 'a=abc.', "b=abc\n");
758 testcase('true', 'P=text', 'a= ABC #test_comment ', 'b=ABC', 'c=x');
759 testcase('', 'P=text', 'a= ABC #test_comment ', 'b=ABC');
761 # Feature tests for $sort.
762 print_to_file $test_template, '$list{$sort{$split{|,$cgi{input}}},|}';
763 testcase('pineapple', 'input=pineapple');
764 testcase('apple|banana|coconut', 'input=coconut|banana|apple');
765 testcase('1|b|b|c', 'input=b|c|b|1');
766 print_to_file $test_template, '$list{$sort{$split{|,$cgi{input}},$cgi{opt}},|}';
767 testcase('pineapple', 'input=pineapple', 'opt=');
768 testcase('pineapple', 'input=pineapple', 'opt=r');
769 testcase('pineapple', 'input=pineapple', 'opt=ru');
770 testcase('1|b|c', 'input=b|c|b|1', 'opt=u');
771 testcase('c|b|b|1', 'input=b|c|b|1', 'opt=r');
772 testcase('c|b|1', 'input=b|c|b|1', 'opt=ur');
773 testcase('-2|-.01|+99.99|+999|.0|0.0|.1|1|2|3|3.0|3.01|12|23|30', 'input=+999|2|1|12|23|3|30|3.01|3.0|.1|.0|0.0|-2|-.01|+99.99', 'opt=n');
774 testcase('-2|-.01|+999|.1|1|2|3|3.01|12|23|30', 'input=+999|2|1|12|23|3|30|3.01|3.0|.1|.0|0.0|-2|-.01|+99.99', 'opt=nu');
775 testcase('-2|-.01|+99.99|.1|1|2|3.0|3.01|12|23|30', 'input=+99.99|-.01|-2|0.0|.0|.1|3.0|3.01|30|3|23|12|1|2|+999', 'opt=nu');
776 testcase('30|23|12|3.01|3.0|3|2|1|.1|0.0|.0|+999|+99.99|-.01|-2', 'input=+999|2|1|12|23|3|30|3.01|3.0|.1|.0|0.0|-2|-.01|+99.99', 'opt=nr');
777 testcase('30|23|12|3.01|3|2|1|.1|+999|-.01|-2', 'input=+999|2|1|12|23|3|30|3.01|3.0|.1|.0|0.0|-2|-.01|+99.99', 'opt=nur');
778 testcase('30|23|12|3.01|3.0|2|1|.1|+99.99|-.01|-2', 'input=+99.99|-.01|-2|0.0|.0|.1|3.0|3.01|30|3|23|12|1|2|+999', 'opt=nur');
779 testcase('t 42|t 42:|t 42:1|t 42:09|t 42:9|t 42:11|t 42~', 'input=t 42:1|t 42|t 42:11|t 42:9|t 42~|t 42:|t 42:09', 'opt=#');
780 testcase(
781 'A7R1:2|A7R2:6|A7R2:9|A7R2:11|A7R2:19|A7R2:47A|A7R11:1|AA|R7:1.09|R7:4A|R7:6|R7:7A|R7:404|R7:444-10|R7:1521',
782 'input=A7R1:2|R7:1521|AA|R7:4A|R7:7A|A7R2:9|R7:444-10|A7R2:6|R7:1.09|A7R2:47A|A7R11:1|R7:6|A7R2:11|R7:404|A7R2:19',
783 'opt=#');
784 testcase('30|23|12|3|03|2|1|01|0|00', 'input=2|1|23|12|23|3|30|0|00|0|01|03', 'opt=#ru');
785 testcase('Exception: Unknown $sort option: x', 'input=b|c|b|1', 'opt=urx');
786 testcase('Exception: Invalid $sort option combination: n#', 'input=b|c', 'opt=n#');
787 testcase('Exception: Invalid $sort option combination: #rn', 'input=b|1', 'opt=#rn');
789 # Regression test to test suppression of Content-Type header for early
790 # exception.
791 testcase('Exception: HITSPERPAGE parameter must be &gt;= 0', 'HITSPERPAGE=-1');
793 # Regression test for $sort bug fixed in 1.4.12.
794 # Check $sort doesn't ensure_match (or ensure_query).
795 print_to_file $test_template, '$sort{test} $addfilter{Test}$querydescription';
796 testcase('test Query(Test)');
798 # Feature tests for $filesize.
799 print_to_file $test_template, '$filesize{$cgi{size}}';
800 testcase('', 'size=');
801 testcase('', 'size=-1');
802 testcase('0 bytes', 'size=0');
803 testcase('1 byte', 'size=1');
804 testcase('2 bytes', 'size=2');
805 testcase('1023 bytes', 'size=1023');
806 testcase('1.0K', 'size=1024');
807 testcase('1.1K', 'size=1127');
808 testcase('16.0K', 'size=16384');
809 testcase('1.0M', 'size=1048576');
810 testcase('1.8G', 'size=2000000000');
811 testcase('Exception: Filesize must be an integer', 'size=foo');
812 testcase('Exception: Filesize must be an integer', 'size=1a');
813 testcase('Exception: Filesize must be an integer', 'size=1.1');
815 # Feature tests for $keys.
816 print_to_file $test_template, '$setmap{x,bee,1,eel,6,dog,4,ant,2,fox,5,cat,3}/$list{$keys{x},|}/$keys{nosuch}/';
817 testcase('/ant|bee|cat|dog|eel|fox//');
819 # Feature tests for $log.
820 remove_tree($test_log);
821 # Chop off any error message at ": " so the test output doesn't depend on
822 # strerror() output, which can vary with platform and locale.
823 print_to_file $test_template, '$slice{$split{: ,$log{$cgi{LOG},$log{' . $test_log . '}}},0}';
824 testcase('filename can\'t contain ".."', 'LOG=evil/parent/..');
825 if (-f $test_log) {
826 remove_tree($test_log);
827 print "log entry not lazily evaluated for bad log file name\n";
828 ++$failed;
830 testcase('open failed', 'LOG=open/should/fail', 'ENTRY=$log{' . $test_log . '}');
831 if (-f $test_log) {
832 remove_tree($test_log);
833 print "log entry not lazily evaluated for open failure\n";
834 ++$failed;
836 print_to_file $test_template, '$log{' . $test_log . ',$lower{TEST}}';
837 testcase('');
839 open my $log, '<', $test_log or die $!;
840 local $/ = undef;
841 my $log_entry = <$log>;
842 close $log;
843 if ($log_entry !~ /^test\r?\n$/) {
844 print "log entry not written correctly\n";
845 ++$failed;
848 unlink $test_log or die $!;
850 # Feature tests for $terms.
851 print_to_file $test_indexscript, "text : index\nhost : boolean=H\nfoo : boolean=XFOO";
852 run_scriptindex(<<'END');
853 text=This is some text.
854 host=example.org
855 foo=bar
857 print_to_file $test_template, '$hitlist{$list{$if{$eq{$cgi{prefix},null},$terms,$terms{$cgi{prefix}}},|}}';
858 testcase('Ztext', 'P=text', 'B=Hexample.org', 'B=Hexample.com', 'prefix=null');
859 testcase('Hexample.org|Ztext', 'P=text', 'B=Hexample.org', 'B=Hexample.com', 'prefix=');
860 testcase('Hexample.org', 'P=text', 'B=Hexample.org', 'B=Hexample.com', 'prefix=H');
861 testcase('Ztext', 'P=text', 'B=Hexample.org', 'B=Hexample.com', 'prefix=Z');
862 testcase('', 'P=text', 'B=Hexample.org', 'B=Hexample.com', 'prefix=E');
864 print_to_file $test_template, '$msizelower $msize $msizeupper $msizeexact';
865 testcase('1 1 1 true', 'P=this');
866 testcase('1 1 1 true', 'P=Some text');
867 testcase('0 0 0 true', 'P=potato');
869 print_to_file $test_template, '$set{weighting,coord}$hitlist{$weight.}';
870 testcase('', 'P=aardvark');
871 testcase('1.000000.', 'P=texting');
872 testcase('', 'P=texting while driving');
873 testcase('', 'P=texting while driving', 'DEFAULTOP=AND');
874 testcase('1.000000.', 'P=texting while driving', 'DEFAULTOP=OR');
875 testcase('2.000000.', 'P=Some text');
876 # "this" and "is" are stopwords.
877 testcase('2.000000.', 'P=This is some text');
878 testcase('4.000000.', 'P="This" "is" some text');
879 testcase('4.000000.', 'P=+This +is some text');
880 testcase('4.000000.', 'P="This is some text"');
882 print_to_file $test_template, '$set{weightingpurefilter,coord}$hitlist{$weight.}';
883 testcase('', 'B=XFOOfoo');
884 testcase('1.000000.', 'B=Hexample.org');
885 testcase('', 'B=Hexample.org', 'B=XFOOfoo');
886 testcase('1.000000.', 'B=Hexample.org', 'B=Hexample.net');
887 testcase('2.000000.', 'B=Hexample.org', 'B=XFOObar');
888 testcase('3.000000.', 'B=Hexample.org', 'B=XFOObar', 'B=XFOObar');
890 print_to_file $test_template, '$filters';
891 testcase('Hexample.net~Hexample.org~.~~', 'B=Hexample.org', 'B=Hexample.net');
892 testcase('!Gmisc.test~.~~', 'N=Gmisc.test');
893 testcase('Hexample.net~Hexample.org~!Gmisc.test~.~~', 'B=Hexample.org', 'B=Hexample.net', 'N=Gmisc.test');
894 testcase('.20040612~~~1~', 'DATEVALUE=1', 'START=20040612');
895 testcase('.20040612~~~1~2', 'DATEVALUE=1', 'START=20040612', 'COLLAPSE=2');
896 testcase('.20040612~~30~2', 'START=20040612', 'SPAN=30', 'COLLAPSE=2');
897 testcase('.20040612~20160412~', 'START=20040612', 'END=20160412');
898 testcase('.~~~2', 'COLLAPSE=2');
899 testcase('.~~');
900 testcase('.~~1', 'SORT=1');
901 testcase('.~~1f', 'SORT=+1');
902 testcase('.~~1', 'SORT=-1');
903 testcase('.~~1-2+3-27', 'SORT=+1-2+03,-27');
904 testcase('.~~', 'SORTREVERSE=1');
905 testcase('.~~1f', 'SORT=1', 'SORTREVERSE=1');
906 testcase('.~~1', 'SORT=+1', 'SORTREVERSE=1');
907 testcase('.~~1f', 'SORT=-1', 'SORTREVERSE=1');
908 testcase('.~~1-2+3-27f', 'SORT=+1-2+03,-27', 'SORTREVERSE=1');
909 testcase('.~~', 'SORTAFTER=1');
910 testcase('.~~1R', 'SORT=1', 'SORTAFTER=1');
911 testcase('.~~1F', 'SORT=+1', 'SORTAFTER=1');
912 testcase('.~~1R', 'SORT=-1', 'SORTAFTER=1');
913 testcase('.~~1-2+3-27R', 'SORT=+1-2+03,-27', 'SORTAFTER=1');
914 testcase('.~~', 'SORTREVERSE=1', 'SORTAFTER=1');
915 testcase('.~~1F', 'SORT=1', 'SORTREVERSE=1', 'SORTAFTER=1');
916 testcase('.~~1R', 'SORT=+1', 'SORTREVERSE=1', 'SORTAFTER=1');
917 testcase('.~~1F', 'SORT=-1', 'SORTREVERSE=1', 'SORTAFTER=1');
918 testcase('.~~1-2+3-27F', 'SORT=+1-2+03,-27', 'SORTREVERSE=1', 'SORTAFTER=1');
919 testcase('.~~X', 'DOCIDORDER=A'); # Buggy, but kept for compatibility.
920 testcase('.~~D', 'DOCIDORDER=D');
921 testcase('.~~', 'DOCIDORDER=X'); # Buggy, but kept for compatibility.
922 testcase('.~~', 'DOCIDORDER=x'); # Buggy, but kept for compatibility.
924 print_to_file $test_template, '$cgi{AZ}|$cgi{AZ B}|$cgi{AZ.x}|$cgi{AZ.y}|$cgi{[}|$cgi{#}';
925 testcase('AZ|||||', 'AZ.x=3', 'AZ.y=4');
926 testcase('B|||||', 'AZ B.x=5', 'AZ B.y=12');
927 testcase('B|||||', "AZ\tB.x=5", "AZ\tB.y=12");
928 testcase('||||2 ]|', '[ 2 ].x=123');
929 testcase('||||2 ]|', "[\t2 ].x=123");
930 testcase("||||2\t]|", "[ 2\t].x=123");
931 testcase("||||2\t]|", "[\t2\t].x=123");
932 testcase('|||||12', '12.x=37');
933 testcase('DE|||||', 'AZ BC=DE');
934 testcase('DE|||||', 'AZ B C=DE');
935 testcase('DE|||||', "AZ\tBC=DE");
936 testcase('DE|||||', "AZ B\tC=DE");
937 testcase('DE|||||', "AZ\tB C=DE");
938 testcase('DE|||||', "AZ\tB\tC=DE");
940 print_to_file $test_template, '$cgi{Search}|$cgi{Type}|$cgi{Search Type}';
941 testcase('Discover-List||', 'Search Type=Discover-List');
943 print_to_file $test_template, '$cgiparams';
944 # We can't test the "no cgi parameters" case via testcase, as it passes a
945 # "dummy" parameter if there aren't any real ones.
946 #testcase('');
947 testcase('', '=1');
948 testcase("ABC", 'ABC=1');
949 testcase("ABC", 'ABC=1', 'ABC=2');
950 testcase("A\tAZ\tZ", 'A=1', 'A=2', 'A=3', 'AZ=1', 'AZ=2', 'Z=xxx');
951 testcase("\tabc", '=1', 'abc=1');
952 testcase("\tabc\tdef", '=1', 'abc=1', 'def=7');
954 # Feature tests for $highlight{}.
955 print_to_file $test_template, '$highlight{$cgi{text},$cgi{list}}';
956 testcase('A <b style="color:black;background-color:#ffff66">list</b> of <b style="color:black;background-color:#99ff99">words</b>', "list=list\twords", "text=A list of words");
958 print_to_file $test_template, '$highlight{$cgi{text},$cgi{list},$cgi{open}}';
959 testcase('A list of <b>words</b>', "list=words", "text=A list of words", "open=<b>");
960 testcase('A list of <span>words</span>', "list=words", "text=A list of words", "open=<span>");
962 print_to_file $test_template, '$highlight{$cgi{text},$cgi{list},$cgi{open},$cgi{close}}';
963 testcase('A list of <b>words</b>', "list=words", "text=A list of words", "open=<b>", "close=</b>");
964 testcase('A *list* of *words*', "list=words\tlist", "text=A list of words", "open=*", "close=*");
966 # Test setting seterror and mset size, should not run the query after setting error.
967 print_to_file $test_template, '$if{$cgi{ERR},$seterror{$cgi{ERR}}}$msize$if{$error,!$error!}';
968 testcase('1', 'P=text');
969 testcase('0!boo!', 'P=text', 'ERR=boo');
971 # Test arguments inside seterror are evaluated
972 print_to_file $test_template, '$set{error,sample error}$seterror{$opt{error}}$msize$if{$error,!$error!}';
973 testcase('0!sample error!', 'P=text');
975 # Test error message doesn't get sent through HTML entity encoding.
976 print_to_file $test_template, '$seterror{{ "error": true, "error_message": "Parameter cannot be > 9" }}$if{$error,$error}';
977 testcase('{ "error": true, "error_message": "Parameter cannot be > 9" }', 'P=text');
979 # Test msize when error set after running query, should not affect running of query.
980 print_to_file $test_template, '$last$if{$cgi{ERR},$seterror{$cgi{ERR}}}$msize$if{$error,!$error!}';
981 testcase('11', 'P=text');
982 testcase('11!boo!', 'P=text', 'ERR=boo');
984 # Feature tests for $hash{}
985 print_to_file $test_template, '$hash{$cgi{text},md5}';
986 testcase('098f6bcd4621d373cade4e832627b4f6', "text=test");
987 testcase('b4c216e4da73d1d01277ef46d0514821', "text=simple query");
988 testcase('d41d8cd98f00b204e9800998ecf8427e', "text=");
989 testcase('3302c94d3500c3f695b7e09b7acd420d', "text= test ");
991 # Feature tests for $base64{}
992 print_to_file $test_template, '$base64{$cgi{in}}';
993 testcase('', "in=");
994 testcase('IQ==', 'in=!');
995 testcase('T2s=', 'in=Ok');
996 testcase('aGA+aGA/', 'in=h`>h`?');
997 testcase('YmFzZTY0IHRlc3Q=', 'in=base64 test');
999 # Feature tests for $random{}
1000 print_to_file $test_template, '$or{$list{$map{$range{1,100},$set{num,$random{$cgi{x}}}$or{$lt{$opt{num},0},$gt{$opt{num},$cgi{x}}}},},OK}';
1001 testcase('OK', 'x=6');
1003 # Regression test - original implementation truncated data at first zero byte.
1004 print_to_file $test_template, '$hash{$cgi{text}$chr{0},md5}';
1005 testcase('e2a3e68d23ce348b8f68b3079de3d4c9', "text=test");
1007 print_to_file $test_template, '$hash{$cgi{text},invalidhash}';
1008 testcase('Exception: Unknown hash function: invalidhash', "text=test");
1010 # Regression test: $setrelevant crashed with no argument list prior to 1.4.16.
1011 print_to_file $test_template, '$setrelevant$or{$error,No error}';
1012 testcase('Exception: too few arguments to $setrelevant', 'P=text');
1013 print_to_file $test_template, '$setrelevant{}$or{$error,No error}';
1014 testcase('No error', 'P=text');
1016 # Feature tests for $uniq and $unique{}.
1017 print_to_file $test_template, '$list{$uniq{$split{$cgi{text}}},:}|$list{$unique{$split{$cgi{text}}},:}';
1018 testcase('|', 'text=');
1019 testcase('foo|foo', 'text=foo');
1020 testcase('apple:banana:cherry|apple:banana:cherry', 'text=apple apple banana banana banana cherry');
1021 testcase('a:b:r:a:c:a:d:a:b:r:a|a:b:r:c:d', 'text=a b r a c a d a b r a');
1022 testcase('x:y:z:y|x:y:z', 'text=x y z z y');
1024 # Regression test - $map with one argument wasn't rejected cleanly.
1025 print_to_file $test_template, '$map{foo}';
1026 testcase('Exception: too few arguments to $map');
1028 # Feature tests for $snippet{}.
1029 print_to_file $test_indexscript, 'text : index field';
1030 # Omit trailing \n on dump file as regression test for bug fixed in 1.4.20
1031 # where a final line without a newline was ignored.
1032 run_scriptindex("dummy=\ntext=A sentence is more than just a list of words - there is structure to it.");
1034 print_to_file $test_template, '$hitlist{$snippet{$field{text},20}}';
1035 testcase('...just a <strong>list</strong> of <strong>words</strong>...', 'P=word listing');
1037 print_to_file $test_template, '$hitlist{$snippet{$field{text},30,,<b>,</b>,}}';
1038 testcase('<b>words</b> - there is structure to', 'P=words');
1039 testcase('a <b>list of words</b> - there is', 'P="list of words"');
1041 # Test MORELIKE CGI parameter.
1042 print_to_file $test_indexscript, "id : boolean=Q\ntext : index field\n";
1043 run_scriptindex(<<'END');
1044 id=a1
1045 text=First document
1047 id=b2
1048 text=Second article
1050 id=c3
1051 text=Piece of writing three
1053 id=dummy
1054 text=Inflating termfreqs since Omega excludes terms with termfreq of one
1055 =first document second piece of writing three
1057 print_to_file $test_template, '$querydescription|$query';
1058 testcase('Query((Zfirst@1 OR Zdocument@2))|first document', 'MORELIKE=Qa1', 'DEFAULTOP=or');
1059 testcase('Query((Zfirst@1 OR Zdocument@2))|first OR document', 'MORELIKE=Qa1');
1060 testcase('Query((Zfirst@1 OR Zdocument@2))|first OR document', 'MORELIKE=Qa1', 'DEFAULTOP=and');
1061 # Test with docid.
1062 testcase('Query((Zfirst@1 OR Zdocument@2))|first document', 'MORELIKE=1', 'DEFAULTOP=OR');
1063 testcase('Query((Zfirst@1 OR Zdocument@2))|first OR document', 'MORELIKE=1');
1064 testcase('Query((Zfirst@1 OR Zdocument@2))|first OR document', 'MORELIKE=1', 'DEFAULTOP=phrase');
1065 # "article" is excluded by OmegaExpandDecider because it has termfreq 1.
1066 testcase('Query(Zsecond@1)|second', 'MORELIKE=Qb2', 'DEFAULTOP=or');
1067 testcase('Query(Zsecond@1)|second', 'MORELIKE=Qb2');
1068 # "of" is excluded because it's a stopword.
1069 testcase('Query((Zwrite@1 OR Zthree@2 OR Zpiec@3))|writing OR three OR piece', 'MORELIKE=Qc3');
1070 # Test with absent term.
1071 testcase('Query()|', 'MORELIKE=Qx9');
1072 # Test multiple MORELIKE parameters. We need to include the dummy document for
1073 # any terms to get a positive weight and be returned.
1074 testcase('Query((Zsecond@1 OR Zfirst@2 OR Zdocument@3))|second OR first OR document', 'MORELIKE=1', 'MORELIKE=2', 'MORELIKE=4');
1075 testcase('Query((Zsecond@1 OR Zfirst@2 OR Zdocument@3))|second OR first OR document', 'MORELIKE=Qa1', 'MORELIKE=2', 'MORELIKE=4');
1076 testcase('Query((Zsecond@1 OR Zfirst@2 OR Zdocument@3))|second OR first OR document', 'MORELIKE=Qa1', 'MORELIKE=Qb2', 'MORELIKE=Qdummy');
1078 # Test errors for scriptindex input format issues.
1079 print_to_file $test_indexscript, 'f : index field';
1080 test_scriptindex_error "no = in input line",
1081 "<stdin>:1: error: Expected = somewhere in this line",
1082 "a\nb=y\nc=z\n";
1083 test_scriptindex_error "no = in input line",
1084 "<stdin>:2: error: Expected = somewhere in this line",
1085 "a=x\nby\nc=z\n";
1086 test_scriptindex_error "no = in input line",
1087 "<stdin>:3: error: Expected = somewhere in this line",
1088 "a=x\n=yz\nbcd\ne=q";
1090 # Test handling of extra blank lines.
1091 print_to_file $test_indexscript, "id : unique=Q boolean=Q\nf : field";
1092 test_scriptindex "extra blank lines",
1093 "\nid=1\nf=a\n\n\nid=2\nf=b\n\n";
1094 test_scriptindex "extra blank lines with CRs",
1095 "\r\nid=1\r\nf=a\r\n\r\n\r\nid=2\r\nf=b\r\n\r\n";
1097 # Regression test for handling of no newline on final line, fixed in 1.4.22.
1099 # This used to report a bogus error on the last line:
1100 # error: Expected = somewhere in this line
1101 test_scriptindex "no newline on final line",
1102 "id=1\nf=hello\n=world";
1104 # Feature tests for scriptindex `index` and `indexnopos` actions.
1105 print_to_file $test_indexscript, "t : index=XT\nn : indexnopos=XN\n";
1106 test_scriptindex 'INDEX and INDEXNOPOS actions',
1107 "t=positional text\nn=no pos here";
1108 print_to_file $test_template, '$msize';
1109 testcase('1', 'P.XT="positional text"');
1110 testcase('1', 'P.XN=no AND pos');
1111 testcase('0', 'P.XN="no pos"');
1113 # Feature tests for scriptindex `split` action.
1114 print_to_file $test_indexscript, "STATUS : field split=| field=SPLITSTATUS\nSTATUS : field=x";
1115 test_scriptindex 'SPLIT action',
1116 'STATUS=PENDING|REVIEW';
1117 print_to_file $test_template, '$field{STATUS,1}/$field{x,1}/$list{$field{SPLITSTATUS,1},$.}';
1118 testcase('PENDING|REVIEW/PENDING|REVIEW/PENDING,REVIEW', 'P=text');
1120 # Feature test for scriptindex `split` action with `dedup` operation.
1121 print_to_file $test_indexscript, "STATUS : field split=|,dedup field=SPLITSTATUS\n";
1122 test_scriptindex 'test SPLIT with DEDUP',
1123 'STATUS=REVIEW|PENDING|PENDING|REVIEW';
1124 print_to_file $test_template, '$field{STATUS,1}/$list{$field{SPLITSTATUS,1},$.}';
1125 testcase('REVIEW|PENDING|PENDING|REVIEW/REVIEW,PENDING', 'P=text');
1127 # Feature test for scriptindex `split` action with `sort` operation.
1128 print_to_file $test_indexscript, "STATUS : field split=|,sort field=SPLITSTATUS\n";
1129 test_scriptindex 'test SPLIT with SORT',
1130 'STATUS=REVIEW|PENDING|PENDING|REVIEW';
1131 print_to_file $test_template, '$field{STATUS,1}/$list{$field{SPLITSTATUS,1},$.}';
1132 testcase('REVIEW|PENDING|PENDING|REVIEW/PENDING,PENDING,REVIEW,REVIEW', 'P=text');
1134 # Feature test for scriptindex `split` action with `none` operation.
1135 print_to_file $test_indexscript, "STATUS : field split=|,none field=SPLITSTATUS\n";
1136 test_scriptindex 'test SPLIT with NONE',
1137 'STATUS=REVIEW|PENDING|PENDING|REVIEW';
1138 print_to_file $test_template, '$field{STATUS,1}/$list{$field{SPLITSTATUS,1},$.}';
1139 testcase('REVIEW|PENDING|PENDING|REVIEW/REVIEW,PENDING,PENDING,REVIEW', 'P=text');
1141 # Feature test for scriptindex `split` action with `prefixes` operation.
1142 print_to_file $test_indexscript, "STATUS : field split=|,prefixes field=SPLITSTATUS\n";
1143 test_scriptindex 'test SPLIT with PREFIXES',
1144 'STATUS=REVIEW|PENDING|PENDING|REVIEW';
1145 print_to_file $test_template, '$field{STATUS,1}/$list{$field{SPLITSTATUS,1},$.}';
1146 testcase('REVIEW|PENDING|PENDING|REVIEW/REVIEW,REVIEW|PENDING,REVIEW|PENDING|PENDING,REVIEW|PENDING|PENDING|REVIEW', 'P=text');
1148 # Feature tests for scriptindex `split` action with no explicit operation.
1149 print_to_file $test_indexscript, "STATUS : field split=| field=SPLITSTATUS\n";
1150 test_scriptindex 'test SPLIT with implicit op',
1151 'STATUS=PENDING|REVIEW|PENDING|REVIEW';
1152 print_to_file $test_template, '$field{STATUS,1}/$list{$field{SPLITSTATUS,1},$.}';
1153 testcase('PENDING|REVIEW|PENDING|REVIEW/PENDING,REVIEW,PENDING,REVIEW', 'P=text');
1155 # Feature test for scriptindex `split` action with multi-character delimiter.
1156 print_to_file $test_indexscript, 'STATUS : field split=$. field=SPLITSTATUS';
1157 test_scriptindex 'test SPLIT with multi-char delimiter',
1158 'STATUS=PENDING$.$.REVIEW,PENDING$.REVIEW';
1159 print_to_file $test_template, '$field{STATUS,1}/$list{$field{SPLITSTATUS,1},|}';
1160 testcase('PENDING$.$.REVIEW,PENDING$.REVIEW/PENDING|REVIEW,PENDING|REVIEW', 'P=text');
1162 # Feature test for scriptindex `split` action with multi-character delimiter
1163 # with potential overlap.
1164 print_to_file $test_indexscript, "STATUS : field split=:: field=SPLITSTATUS\n";
1165 test_scriptindex 'test SPLIT with overlapping multi-char delimiter',
1166 'STATUS=::Foo::::Bar:Baz:::Hello::';
1167 print_to_file $test_template, '$field{STATUS,1}/$list{$field{SPLITSTATUS,1},|}';
1168 testcase('::Foo::::Bar:Baz:::Hello::/Foo|Bar:Baz|:Hello', 'P=text');
1170 # Feature test for scriptindex `split` action with multi-character delimiter
1171 # with `prefixes` operation.
1172 print_to_file $test_indexscript, "STATUS : field split=::,prefixes field=SPLITSTATUS\n";
1173 test_scriptindex 'test SPLIT with PREFIXES and multi-char delimiter',
1174 'STATUS=::Foo::::Bar:Baz:::Hello::';
1175 print_to_file $test_template, '$field{STATUS,1}/$list{$field{SPLITSTATUS,1},|}';
1176 testcase('::Foo::::Bar:Baz:::Hello::/::Foo|::Foo::|::Foo::::Bar:Baz|::Foo::::Bar:Baz:::Hello|::Foo::::Bar:Baz:::Hello::', 'P=text');
1178 # Feature test for scriptindex `split` action with quoted `,` delimiter.
1179 print_to_file $test_indexscript, "STATUS : field split=\",\" field=SPLITSTATUS\n";
1180 test_scriptindex 'test SPLIT with quoted comma delimiter',
1181 'STATUS=PENDING,REVIEW,PENDING,REVIEW';
1182 print_to_file $test_template, '$field{STATUS,1}/$list{$field{SPLITSTATUS,1},|}';
1183 testcase('PENDING,REVIEW,PENDING,REVIEW/PENDING|REVIEW|PENDING|REVIEW', 'P=text');
1185 # Feature test for nested scriptindex `split` action.
1186 print_to_file $test_indexscript, "in : split=; field=one lower split=\",\" field=two\nin : field";
1187 test_scriptindex 'nested SPLIT action',
1188 'in=a,b,c;10,21,32;XY,YZ';
1189 print_to_file $test_template, '$field{in,1}/$list{$field{one,1},|}/$list{$field{two,1},|}';
1190 testcase('a,b,c;10,21,32;XY,YZ/a,b,c|10,21,32|XY,YZ/a|b|c|10|21|32|xy|yz', 'P=text');
1192 # Test scriptindex `split` action error cases.
1193 print_to_file $test_indexscript, "in : split=\"\" field=one\nin : field";
1194 test_scriptindex_error "'split' error for empty separator",
1195 "$test_indexscript:1:13: error: Split delimiter can't be empty";
1196 print_to_file $test_indexscript, "in : split=|,foo field=one\nin : field";
1197 test_scriptindex_error "'split' error for invalid operation",
1198 "$test_indexscript:1:14: error: Bad split operation 'foo'";
1200 # Feature tests for scriptindex `hextobin` action.
1201 print_to_file $test_indexscript, 'hex : hextobin value=0';
1202 test_scriptindex 'HEXTOBIN action',
1203 'hex=
1205 hex=41
1207 hex=54657374
1209 hex=4b696C6c
1211 print_to_file $test_template, '$valuelowerbound{0}$list{$map{$split{$cgi{DOCIDS}},$value{0,$_}},|}|$valueupperbound{0}';
1212 testcase('A|A|Test|Kill|Test', 'DOCIDS=1 2 3 4');
1214 # Feature test error cases for scriptindex `hextobin` action.
1215 test_scriptindex_error 'bad hex digit',
1216 "<stdin>:1: error: hextobin: input must be all hex digits",
1217 'hex=7g';
1218 test_scriptindex_error 'bad hex length',
1219 "<stdin>:1: error: hextobin: input must have even length",
1220 'hex=404';
1222 # Feature test for scriptindex `spell` action.
1223 print_to_file $test_indexscript, "s : spell index\nn : index\n";
1224 test_scriptindex 'SPELL action',
1225 "s=some words test\n\nn=tent\n\n";
1226 print_to_file $test_template, '$set{flag_spelling_correction,true}$suggestion';
1227 testcase('some test', 'P=home nest');
1228 testcase('a test', 'P=a tent');
1229 testcase('', 'P=gent');
1231 # Feature tests for scriptindex `squash` and `ltrim`/`rtrim`/`trim` actions.
1232 print_to_file $test_indexscript, "squash : squash field\nltrim : ltrim field\nrtrim : rtrim field\ntrim : trim field\n";
1233 my $whitespace_test="\t lots\x0b\fof\t whitespace\f ";
1234 test_scriptindex 'SQUASH and trim actions',
1235 "squash=$whitespace_test
1236 ltrim=$whitespace_test
1237 rtrim=$whitespace_test
1238 trim=$whitespace_test
1240 squash=a b
1241 ltrim=a b
1242 rtrim=a b
1243 trim=a b
1245 squash=xyz
1246 ltrim=xyz
1247 rtrim=xyz
1248 trim=xyz
1250 print_to_file $test_template, '$json{$field{$cgi{F},$cgi{ID}}}';
1251 testcase('lots of whitespace', 'F=squash', 'ID=1');
1252 testcase('lots\u000b\fof\t whitespace\f ', 'F=ltrim', 'ID=1');
1253 testcase('\t lots\u000b\fof\t whitespace', 'F=rtrim', 'ID=1');
1254 testcase('lots\u000b\fof\t whitespace', 'F=trim', 'ID=1');
1255 for my $f (qw(squash ltrim rtrim trim)) {
1256 testcase('a b', "F=$f", 'ID=2');
1257 testcase('xyz', "F=$f", 'ID=3');
1260 # Feature tests for scriptindex `truncate` action.
1261 print_to_file $test_indexscript, "x : field=x truncate=9 field=9 truncate=3 field=3 truncate=2 field=2 truncate=1 field=1 truncate=0 field=0\n";
1262 test_scriptindex 'TRUNCATE action',
1263 'x=really long field
1265 x=xö xxxxö x
1267 x=ö xxxxö x
1269 x=x x xx🥝 x
1271 x=a test
1273 x=tri
1275 x=du
1281 print_to_file $test_template, '$foreach{$split{1 2 3 4 5 6 7 8 9},$set{d,$_}$list{$map{$split{x 9 3 2 1 0},$field{$_,$opt{d}}},|}:}';
1282 testcase('really long field|really|rea|re|r|:xö xxxxö x|xö|xö|x|x|:ö xxxxö x|ö xxxxö|ö|ö||:x x xx🥝 x|x x|x x|x|x|:a test|a test|a|a|a|:tri|tri|tri|tr|t|:du|du|du|du|d|:1|1|1|1|1|:|||||:');
1284 # Feature tests for scriptindex `unhtml` and `unxml` actions.
1285 print_to_file $test_indexscript, "t : unhtml field=h\nt : unxml field=x\n";
1286 test_scriptindex 'UNHTML and UNXML actions',
1287 't=<b>No</b>table
1289 t=<p>foo</p>d<p>bar<B>b</B></p>
1291 print_to_file $test_template, '$list{$map{$split{$cgi{DOCIDS}},$field{h,$_}:$field{x,$_}},|}';
1292 testcase('Notable:No table|foo d barb:foo d bar b', 'DOCIDS=1 2');
1294 # Feature test for scriptindex `weight` action.
1295 print_to_file $test_indexscript, "t : index\nw : weight=2 index\n";
1296 test_scriptindex 'WEIGHT action',
1297 't=test
1299 w=test
1301 t=test test test';
1302 print_to_file $test_template, '|$hitlist{$id|}';
1303 testcase('|3|2|1|', 'P=test');
1305 # Test bad parameter values to `weight` action.
1306 print_to_file $test_indexscript, 'foo : weight=-2 index weight=1.5 index=A weight=-1.5 index=B';
1307 test_scriptindex_error "bad 'weight' parameter",
1308 "$test_indexscript:1:14: error: Index action 'weight' takes a non-negative integer argument
1309 $test_indexscript:1:30: error: Index action 'weight' takes a non-negative integer argument
1310 $test_indexscript:1:49: error: Index action 'weight' takes a non-negative integer argument";
1312 # Test useless action warnings.
1313 print_to_file $test_indexscript, 'foo : index weight=2';
1314 test_scriptindex_warning "useless 'weight' action",
1315 "$test_indexscript:1:13: warning: Index action 'weight' has no effect
1316 $test_indexscript:1:13: note: Actions are executed from left to right";
1318 print_to_file $test_indexscript, 'foo : weight=2 weight=3 index';
1319 test_scriptindex_warning "useless 'weight' action",
1320 "$test_indexscript:1:7: warning: Index action 'weight' has no effect
1321 $test_indexscript:1:7: note: Actions are executed from left to right";
1323 print_to_file $test_indexscript, 'foo : index lower';
1324 test_scriptindex_warning "useless 'lower' action",
1325 "$test_indexscript:1:13: warning: Index action 'lower' has no effect
1326 $test_indexscript:1:13: note: Actions are executed from left to right";
1328 # Test bad fieldname errors.
1329 print_to_file $test_indexscript, 'foo *bar _bar b!ar: index';
1330 test_scriptindex_error 'bad field names',
1331 "$test_indexscript:1:5: error: field name must start with alphanumeric
1332 $test_indexscript:1:10: error: field name must start with alphanumeric
1333 $test_indexscript:1:16: error: bad character '!' in field name";
1335 # Test unwanted action argument.
1336 print_to_file $test_indexscript, 'foo : spell=test index';
1337 test_scriptindex_error 'unwanted action argument',
1338 "$test_indexscript:1:12: error: Index action 'spell' doesn't take an argument";
1340 # Test missing closing quote.
1341 print_to_file $test_indexscript, "foo : index=\"XFOO\nbar : index=\"XFOO\\\"\n";
1342 test_scriptindex_error 'missing closing quote',
1343 "$test_indexscript:1:18: error: No closing quote
1344 $test_indexscript:2:20: error: No closing quote";
1346 # Feature tests for scriptindex `termprefix` and `unprefix` actions.
1347 print_to_file $test_template, '$termprefix{$cgi{B}}|$unprefix{$cgi{B}}';
1348 testcase('|', 'B=');
1349 testcase('|something', 'B=something');
1350 testcase('|42', 'B=42');
1351 testcase('|3bad', 'B=3bad');
1352 testcase('|&something', 'B=&something');
1353 testcase('|:something', 'B=:something');
1354 testcase('H|example.org', 'B=Hexample.org');
1355 testcase('K|tag', 'B=Ktag');
1356 testcase('K|Capital', 'B=KCapital');
1357 testcase('K|:colon-tag', 'B=K:colon-tag');
1358 testcase('K|:Capital', 'B=K:Capital');
1359 testcase('XCOLOUR|red', 'B=XCOLOURred');
1360 testcase('XPUNC|:colon', 'B=XPUNC::colon');
1361 testcase('XPUNC|internal:colon', 'B=XPUNC:internal:colon');
1362 testcase('XPUNC|:Colon', 'B=XPUNC::Colon');
1363 testcase('XCASE|Upper', 'B=XCASE:Upper');
1364 testcase('XCASE|TITLE', 'B=XCASE:TITLE');
1365 testcase('XNUM|42', 'B=XNUM42');
1366 testcase('XNUM|3bad', 'B=XNUM3bad');
1368 # Regression test for $truncate with maxlen < the length of the indicator
1369 # string.
1370 print_to_file $test_template, '$truncate{$cgi{input},$cgi{maxlen},$cgi{ind},$cgi{ind2}}$seterror{$opt{error}}';
1371 testcase('w...', 'input=wwwwww', 'maxlen=4', 'ind=...', 'ind2=...');
1372 testcase('', 'input=s', 'maxlen=0', 'ind=...', 'ind2=...');
1374 # Feature tests for scriptindex `unique` action.
1375 print_to_file $test_indexscript, "id : boolean=Q unique=Q\nid f : field\n";
1376 test_scriptindex 'UNIQUE action',
1377 'id=1
1378 f=wan
1380 id=2
1381 f=too
1383 id=3
1384 f=free
1386 id=4
1387 f=fore
1389 id=1
1390 f=one
1392 id=2
1394 id=4
1397 id=3
1398 dummy=';
1399 print_to_file $test_template, '$field{id,$cgi{id}}|$field{f,$cgi{id}}$error';
1400 testcase('1|one', 'id=1');
1401 testcase('|DocNotFoundError: Document 2 not found', 'id=2');
1402 testcase('3|', 'id=3');
1403 testcase('4|', 'id=4');
1405 # Test `unique` action warning.
1406 print_to_file $test_indexscript, "id : unique=Q boolean=W\nid f : field\n";
1407 test_scriptindex_warning 'unique without boolean',
1408 "$test_indexscript:1:6: warning: Index action 'unique=Q' without 'boolean=Q'
1409 $test_indexscript:1:6: note: 'unique' doesn't implicitly add a boolean term";
1411 # Test `unique` action gives error when not used for a record.
1412 print_to_file $test_indexscript, "id : boolean=Q unique=Q\nid f : field\n";
1413 test_scriptindex_error 'missing unique field',
1414 "<stdin>:4: error: UNIQUE action unused in this record",
1415 "id=1\nf=wan\n\nf=";
1417 # Test $subdb and $subid.
1418 remove_tree($test_db);
1419 print_to_file $test_db, 'inmemory';
1420 print_to_file "${test_db}2", 'inmemory';
1421 print_to_file "${test_db}3", "inmemory\ninmemory\n";
1422 print_to_file $test_template, '$subdb{$cgi{ID}}|$subid{$cgi{ID}}';
1423 testcase("$test_db|1", 'ID=1');
1424 testcase("$test_db|1", 'ID=1', "DB=$test_db/${test_db}2");
1425 testcase("${test_db}2|1", 'ID=2', "DB=$test_db/${test_db}2");
1426 testcase("${test_db}|2", 'ID=3', "DB=$test_db/${test_db}2");
1427 testcase("${test_db}3|1", 'ID=1', "DB=${test_db}3");
1428 testcase("${test_db}3|999", 'ID=999', "DB=${test_db}3");
1429 testcase("$test_db|1", 'ID=1', "DB=$test_db", "DB=${test_db}3");
1430 testcase("${test_db}3|1", 'ID=2', "DB=$test_db", "DB=${test_db}3");
1431 testcase("${test_db}3|2", 'ID=3', "DB=$test_db", "DB=${test_db}3");
1432 testcase("$test_db|2", 'ID=4', "DB=$test_db", "DB=${test_db}3");
1433 testcase("${test_db}3|3", 'ID=5', "DB=$test_db", "DB=${test_db}3");
1434 remove_tree("${test_db}2","${test_db}3");
1436 # Feature tests for $field.
1437 print_to_file $test_indexscript, 'in : field="zer\0byte" hextobin field="field28\x02\x08"';
1438 run_scriptindex("in=4071004f3456\n");
1439 print_to_file $test_template, '$json{$field{zer$chr{0}byte,1}}|$json{$field{field28$chr{2}$chr{8},1}}|$error';
1440 testcase('4071004f3456|@q\u0000O4V|');
1442 # Feature tests for $jsonarray.
1443 print_to_file $test_template,
1444 join(', ',
1445 '$jsonarray{}',
1446 '$jsonarray{,$upper{$_}}',
1447 '$jsonarray{$split{b4 k9},"$json{$upper{$_}}"}',
1448 '$jsonarray{$split{a "b" c:\}}',
1449 '$jsonarray{$split{2 3 5 7},$mul{$_,$_}}');
1450 testcase('[], [], ["B4","K9"], ["a","\"b\"","c:\\\\"], [4,9,25,49]');
1452 # Feature tests for $jsonbool
1453 print_to_file $test_template, '$jsonbool{} $jsonbool{$eq{a,b}} $jsonbool{x} $jsonbool{0}';
1454 testcase('false false true true');
1456 # Feature tests for $jsonobject
1457 print_to_file $test_template, '$jsonobject{foo}';
1458 testcase('{}');
1459 print_to_file $test_template, '$setmap{foo,Han,Solo}$jsonobject{foo}';
1460 testcase('{"Han":"Solo"}');
1461 print_to_file $test_template, '$setmap{foo,key 1,value1,key"2,value\2,key3,value3}$jsonobject{foo}';
1462 testcase('{"key 1":"value1","key\"2":"value\\\\2","key3":"value3"}');
1463 print_to_file $test_template, '$setmap{foo,key 1,1,key"2,$split{1 2},key3,$split{2 3 5}}$jsonobject{foo,,$jsonarray{$_,$add{$_,1}}}';
1464 testcase('{"key 1":[2],"key\"2":[2,3],"key3":[3,4,6]}');
1465 print_to_file $test_template, '$setmap{foo,key 1,,key"2,1,key3,0}$jsonobject{foo,$upper{$_},$jsonarray{$_,$add{$_}}}';
1466 testcase('{"KEY 1":[],"KEY\"2":[1],"KEY3":[0]}');
1468 # Feature tests for $stoplist
1469 print_to_file $test_template, '$setmap{prefix,foo,XFOO}[$list{$stoplist,|}]';
1470 testcase('[a|the]', 'P.XFOO=the test', 'P=a test');
1472 # Feature tests for $unstem
1473 print_to_file $test_template, '$setmap{prefix,foo,XFOO}[$list{$unstem{$cgi{TERM}},|}]';
1474 testcase('[pots|pot|potting]', 'P.XFOO=(foo:pot OR luck) (potting OR shed)', 'P=flower OR foo:pots', 'TERM=ZXFOOpot');
1476 # Feature tests of scriptindex.
1478 # Regression test: non-zero exit status for unknown option.
1479 if (system("$scriptindex --to-be-or-not-to-be '$test_db' '$test_indexscript' > /dev/null < /dev/null 2>&1") == 0) {
1480 print "scriptindex didn't give error for unknown option\n";
1481 ++$failed;
1484 # Regression test: error given for multiple `unique` actions.
1485 print_to_file $test_indexscript, "id : boolean=Q unique=Q\nguid : boolean=G unique=G";
1486 test_scriptindex_error 'UNIQUE used more than once',
1487 "$test_indexscript:2:18: error: Index action 'unique' used more than once
1488 $test_indexscript:1:16: note: Previously used here";
1490 # Test we check for hash's argument being an integer (new in 1.4.6).
1491 print_to_file $test_indexscript, 'url : hash=37.3 boolean=Q unique=Q';
1492 test_scriptindex_error "'hash' with a non-integer argument",
1493 "$test_indexscript:1:14: error: Index action 'hash' takes an integer argument";
1495 # Test we give a helpful error for an action with a digit in (regression
1496 # test for fix in 1.4.6).
1498 # This used to give the confusing:
1499 # Unknown index action ''
1500 print_to_file $test_indexscript, 'url : index4';
1501 test_scriptindex_error 'bad index action with a digit',
1502 "$test_indexscript:1:7: error: Unknown index action 'index4'";
1504 # Test we give a helpful error when we're expecting an action but don't get
1505 # an identifier (regression test for fix in 1.4.23; this triggered an
1506 # infinitely repeating `error: Unknown index action ''` before that).
1507 print_to_file $test_indexscript, 'url :: index ?!';
1508 test_scriptindex_error 'bad syntax when expecting index action',
1509 "$test_indexscript:1:6: error: Expected index action, found ':'\n".
1510 "$test_indexscript:1:14: error: Expected index action, found '?!'";
1512 # Test we give a helpful error if an = sign is missed out before an optional
1513 # numeric argument (regression test for fix in 1.4.6).
1515 # This used to give the confusing:
1516 # Unknown index action ''
1517 print_to_file $test_indexscript, 'url : hash 42';
1518 test_scriptindex_error 'missing equals sign',
1519 "$test_indexscript:1:12: error: Unknown index action '42'";
1521 # Test we warn about spaces before and after '='.
1523 # This has never been documented as supported, and was deprecated in 1.4.6
1524 # because it resulted in this quietly using `hash` as the field name, which is
1525 # probably not what was intended:
1527 # url : field= hash boolean=Q unique=Q
1528 print_to_file $test_indexscript, 'url : field= hash boolean=Q unique=Q';
1529 test_scriptindex_warning 'space after "="',
1530 "$test_indexscript:1:13: warning: putting spaces between '=' and the argument is deprecated";
1532 print_to_file $test_indexscript, 'url : field =link';
1533 test_scriptindex_warning 'space before "="',
1534 "$test_indexscript:1:12: warning: putting spaces between the action and '=' is deprecated";
1536 # Feature tests for scriptindex `date` action.
1537 print_to_file $test_indexscript, "d : date=unix\ng : date=unixutc\n";
1538 # `date=unix` works in the current timezone, so set that explicitly so the
1539 # build doesn't fail if run in a timezone which is behind UTC.
1541 my $save_TZ = $ENV{TZ};
1542 $ENV{TZ} = 'UTC';
1543 test_scriptindex 'DATE action', "d=\n\nd=0\n\nd=1541478429";
1544 if (defined $save_TZ) {
1545 $ENV{TZ} = $save_TZ;
1546 } else {
1547 delete $ENV{TZ};
1550 print_to_file $test_template, '$list{$map{$range{1,3},$list{$allterms{$_}, }},|}';
1551 testcase '|D19700101 M197001 Y1970|D20181106 M201811 Y2018';
1553 # `date=unixutc` should always work in UTC regardless of the current timezone.
1554 test_scriptindex 'DATE action with unixutc',
1555 "g=\n\ng=0\n\ng=1541478429";
1556 testcase '|D19700101 M197001 Y1970|D20181106 M201811 Y2018';
1558 # Check nested $hitlist{} doesn't result in an infinite loop.
1559 # Regression test for bug fixed in 1.4.12.
1560 print_to_file $test_template, '<|$hitlist{$id(:$hitlist{$id:})|}>';
1561 testcase '<|2(:2:3:)|3(:2:3:)|>', 'B=Y1970', 'B=Y2018';
1563 # Feature tests for scriptindex `parsedate` action.
1564 print_to_file $test_indexscript, "DATE : field parsedate=%Y%m%d valuepacked=13\n";
1565 test_scriptindex 'PARSEDATE action',
1566 'DATE=19891204';
1567 print_to_file $test_template, '$field{DATE,1}|$unpack{$value{13,1}}|$date{$unpack{$value{13,1}}}';
1568 testcase '19891204|628732800|1989-12-04', 'P=text';
1570 # Feature tests for scriptindex `parsedate` action.
1571 print_to_file $test_indexscript, 'DATE: parsedate="%Y%m%d %H:%M:%S" field=time';
1572 test_scriptindex_warning 'PARSEDATE action',
1573 '<stdin>:1: warning: "20161202 12:04:22.000000" not fully matched by format "%Y%m%d %H:%M:%S" (".000000" left over) but indexing anyway',
1574 'DATE=20161202 12:04:22.000000';
1575 print_to_file $test_template, '$field{time,$cgi{id}}';
1576 # Test format which contains a space.
1577 testcase '1480680262', 'id=1';
1579 # Feature tests for scriptindex `parsedate` action.
1580 print_to_file $test_indexscript, 'DATE: parsedate="%Y%m%d %H:%M:%S %z" field=time';
1581 if (test_scriptindex_optional_error('PARSEDATE action with %z',
1582 'test-indexscript:1:34: error: Parsing timezone offsets with %z is not supported on this platform',
1583 'DATE=20161202 21:34:24 +0930') == 0) {
1584 print_to_file $test_template, '$field{time,$cgi{id}}';
1585 # Test that timezone adjustment is applied.
1586 testcase '1480680264', 'id=1';
1589 # Feature tests for scriptindex `valuenumeric` action.
1590 print_to_file $test_indexscript, "n : field valuenumeric=0\n";
1591 test_scriptindex 'VALUENUMERIC action',
1592 "n=0\n\nn=1.75\n\nn=-1000000000";
1593 print_to_file $test_template, '$sortableunserialise{$valuelowerbound{0}}|$list{$map{$range{1,3},$url{$value{0,$_}}},:}|$sortableunserialise{$valueupperbound{0}}';
1594 testcase '-1000000000.000000|%80:%A3:%1F%A4FS%60|1.750000';
1596 # Feature tests for scriptindex `load` action.
1597 print_to_file $test_indexscript, "file : load field\n";
1598 test_scriptindex_warning 'empty filename in load action',
1599 "<stdin>:1: warning: Empty filename in LOAD action",
1600 'file=';
1602 # Feature tests for quoted arguments.
1603 print_to_file $test_indexscript, <<'END';
1604 DATE : field=" spaces " date="yyyymmdd"
1605 TEXT: index="S"
1607 test_scriptindex 'quoted arguments', <<'END';
1608 DATE=19891204
1609 TEXT=This is sample text.
1610 NEXT=work
1612 print_to_file $test_template, '$freq{D19891204}|$field{ spaces ,1}';
1613 testcase '1|19891204', 'P.S=text';
1614 # Use $time to force the match to run.
1615 print_to_file $test_template, '$if{x$time,$freq{D19891204}}|$field{ spaces ,1}';
1616 testcase '1|19891204', 'P=';
1618 # Feature tests for escaping in quoted arguments.
1619 print_to_file $test_indexscript, 'esc : field="\tesca\x70e,test\\\\\\""' . "\n";
1620 test_scriptindex 'escaping in quoted arguments',
1621 'esc=test';
1622 print_to_file $test_template, '$field{$chr{9}escape$.test\\",1}';
1623 testcase 'test', 'P=';
1624 print_to_file $test_indexscript, 'x: split="xx\\' . "\n" . 'y: split="xx\q"' . "\n";
1625 test_scriptindex_error "bad escape sequences",
1626 "$test_indexscript:1:14: error: Bad escaping in quoted action argument
1627 $test_indexscript:2:14: error: Bad escape sequence '\\q'";
1628 # Ensure the location of the problem is always in the same column so we can
1629 # test against a fixed error message including line and column.
1630 for my $badesc (
1631 '"\x1"',
1632 '"z\xg1"',
1633 '"z\xg"',
1634 '"z\x"',
1635 '"\x1g"') {
1636 print_to_file $test_indexscript, "x: split=$badesc";
1637 test_scriptindex_error "bad hex digit in escape: '$badesc'",
1638 "$test_indexscript:1:14: error: Bad hex digit in escaping";
1641 # Regression test that a closing " with junk after is flagged.
1642 print_to_file $test_indexscript, 'date : field="test"index';
1643 test_scriptindex_error "junk after closing quote",
1644 "$test_indexscript:1:20: error: Unexpected character 'i' after closing quote";
1646 # Test we warn about useless actions.
1647 print_to_file $test_indexscript, 'date : field parsedate=%%Y%%m%%d';
1648 test_scriptindex_warning "useless 'parsedate'",
1649 "$test_indexscript:1:14: warning: Index action 'parsedate' has no effect
1650 $test_indexscript:1:14: note: Actions are executed from left to right";
1652 # Test a `parsedate` format error.
1653 print_to_file $test_indexscript, 'date : parsedate="%Y-%m-%d %Z" field';
1654 test_scriptindex_error "'%Z' in 'parsedate' format",
1655 "$test_indexscript:1:28: error: Parsing timezone names with %Z is not supported";
1657 # Test scriptindex `gap` action inserts a termpos gap.
1658 print_to_file $test_indexscript, 'text : index gap=5';
1659 test_scriptindex 'GAP action',
1660 "text=foo\ntext=bar\ntext=baz";
1661 print_to_file $test_template, '|$hitlist{$id|}';
1662 testcase('|', 'P="foo bar"');
1663 testcase('|', 'P=foo NEAR/5 bar');
1664 testcase('|', 'P=foo NEAR/11 baz');
1665 testcase('|1|', 'P=foo NEAR/6 bar');
1666 testcase('|1|', 'P=foo NEAR/12 baz');
1668 # The scriptindex `hash` action should require its argument is >= 6, so test 5
1669 # is rejected.
1670 print_to_file $test_indexscript, 'url : hash=5 boolean=Q unique=Q';
1671 test_scriptindex_error 'bad HASH argument',
1672 "$test_indexscript:1:12: error: Index action 'hash' takes an integer argument which must be at least 6";
1674 # And that 6 is accepted.
1675 print_to_file $test_indexscript, 'url : hash=6 boolean=Q unique=Q';
1676 test_scriptindex 'hash=6',
1677 'url=http://xapian.org';
1679 # Regression test to check `hash` works without argument (it was failing with
1680 # an assertion in unreleased versions prior to 1.4.6).
1681 print_to_file $test_indexscript, 'url : hash boolean=Q unique=Q';
1682 test_scriptindex 'HASH without argument',
1683 'url=http://xapian.org';
1685 # Test the same actions for multiple fields works (briefly broken in git master
1686 # before 1.5.0).
1687 print_to_file $test_indexscript, 'tag1 tag2 tag3 : boolean=T field';
1688 test_scriptindex 'multiple fields on an action line',
1689 "tag1=one\ntag2=two\ntag3=three";
1690 print_to_file $test_template, '$hitlist{$list{$terms{T},|}/$field{tag1}|$field{tag2}|$field{tag3}}';
1691 testcase('Tone/one|two|three', 'B=Tone');
1692 testcase('Tthree|Ttwo/one|two|three', 'B=Ttwo', 'B=Tthree');
1694 unlink $OMEGA_CONFIG_FILE, $test_indexscript, $test_template;
1695 remove_tree($test_db);
1696 if ($failed == 0) {
1697 exit 0;
1699 print "Failed $failed test(s)\n";
1700 exit 1;