1package t::Common;
2
3use strict;
4use lib  qw {blib/lib}, ".";
5use vars qw /@ISA @EXPORT @EXPORT_OK $DEBUG/;
6
7use Regexp::Common;
8use Exporter ();
9
10use warnings;
11
12@ISA       = qw /Exporter/;
13@EXPORT    = qw /run_tests run_new_tests NORMAL_PASS NORMAL_FAIL FAIL $DEBUG/;
14@EXPORT_OK = qw /cross criss_cross pass fail
15                 d pd dd pdd l ll L LL a aa w ww _x xx X XX h hh
16                 gimme sample/;
17
18my @STATES = qw /pass fail/;
19
20our $SKIP;
21
22use constant   NORMAL_PASS =>  0x01;   # Normal test, should pass.
23use constant   NORMAL_FAIL =>  0x02;   # Normal test, should fail.
24use constant   NORMAL      =>  NORMAL_PASS | NORMAL_FAIL;
25use constant   FAIL        =>  0x04;   # Test for failure.
26
27sub run_test;
28sub run_old_keep;
29sub run_fail;
30sub count_me;
31sub is_skipped;
32
33
34my $count;
35
36sub stringify;
37sub stringify {
38    my $arg = shift;
39
40    if    (!defined $arg)        {return ""}
41    elsif (!ref $arg)            {$arg =~ s/\\/\\\\/g;
42                                  $arg =~ s/\n/\\n/g;
43                                  $arg =~ s/\t/\\t/g;
44                                  return "$arg"}
45    elsif ( ref $arg eq "ARRAY") {
46        local $" = ", ";
47        return "[@{[map {q{'} . stringify ($_) . q{'}} @$arg]}]";
48    }
49    else {return ref $arg}
50}
51
52sub mess {
53    my $str = stringify $_;
54    my $com = join " " => map {stringify $_} @_;
55    $count ++;
56    if ($SKIP) {printf qq !%4d # SKIP: %s\n! => $count, $SKIP;}
57    else       {printf qq !%4d - %-40s (%s)\n! => $count, qq !"$str"!, $com;}
58}
59
60sub pass {print          "ok ";             &mess}
61sub fail {print +$SKIP ? "ok " : "not ok "; &mess}
62
63sub Fail {
64    my $mess = shift;
65    my %args = @_;
66
67    if ($args {got} && $args {expected}) {
68        printf "# Expected: '%s'\n", stringify $args {expected};
69        printf "# Got:      '%s'\n", stringify $args {got};
70    }
71
72    fail $mess;
73}
74
75
76sub import {
77    if (@_ > 1 && $_ [-1] =~ /^\d+\.\d+$/) {
78        my $version = pop;
79        if ($version > $]) {
80            print "1..1\n";
81            print "ok 1\n";
82            exit;
83        }
84    }
85    __PACKAGE__ -> export_to_level (1, @_);
86}
87
88#
89# Return a cross product from its arguments. Arguments are array refs.
90# Result is a list of array refs.
91#
92sub cross {
93    my @r = [];
94       @r = map {my $s = $_; map {[@$_ => $s]} @r} @$_ for @_;
95       @r
96}
97sub criss_cross {
98    my ($f, $s) = @_;
99    my @r;
100
101    push @r => cross @$f [0 .. $_ - 1], $$s [$_], @$f [$_ + 1 .. $#$f]
102               for 0 .. $#$f;
103
104    @r;
105}
106
107sub __ {map {defined () ? $_ : "UNDEF"} @_}
108
109sub count_test_runs {
110    my ($tests, $passes, $failures) = @_;
111
112    my $keep     = 0;
113    my $normal   = 0;
114    my $fail     = 0;
115
116    foreach my $test (@$tests) {
117        while (my ($name, $mask) = each %{$test -> [2]}) {
118            $normal += @{$passes   -> {$name}} if $mask & NORMAL;
119            $keep   += @{$passes   -> {$name}} if $mask & NORMAL_PASS;
120            $fail   += @{$failures -> {$name}} if $mask & FAIL;
121        }
122    }
123
124    1 + $normal + $keep + $fail;
125}
126
127# Arguments:
128#    tests:   hash ref with the re's, names, and when to (not)match.
129#    good:    ref to array with arrays, parts making patterns.
130#    bad:     ref to array with arrays, parts not making patterns.
131#    query:   code ref, creates query strings.
132#    wanted:  code ref, creates list what keep should return.
133#
134#             Filter arguments are used to filter chunks before trying them.
135#             All of them are code refs.
136#    filter:          filter everything.
137#    filter_passes:   filter passes.
138#    filter_failures: filter failures.
139#    filter_test:     filter called with testname.
140sub run_tests {
141    my %args = @_;
142
143    my $tests    = $args {tests};
144
145    # Collect the names of all tags.
146    my %tag_names;
147       @tag_names {keys %{$_ -> [2]}} = () foreach @$tests;
148
149    my (@passes, @failures);
150
151    if ($args {good}) {
152        @passes   = cross @{$args {good}};
153
154        @failures = ();
155        foreach my $i (0 .. $#{$args {good}}) {
156            push @failures => cross @{$args {good}} [0 .. $i - 1],
157                                      $args {bad}   [$i],
158                                    @{$args {good}} [$i + 1 .. $#{$args {good}}]
159        }
160    }
161    elsif ($args {good_list}) {
162        @passes   = @{$args {good_list}};
163    }
164
165    # General filters.
166    @passes   = grep {$args {filter_passes} -> ($_)} @passes
167                if defined $args {filter_passes};
168    @passes   = grep {$args {filter} -> ($_)} @passes
169                if defined $args {filter};
170
171    @failures = grep {$args {filter_failures} -> ($_)} @failures
172                if defined $args {filter_failures};
173    @failures = grep {$args {filter} -> ($_)} @failures
174                if defined $args {filter};
175
176    my (%passes, %failures);
177    # Specific filters.
178    if (defined $args {filter_test}) {
179        foreach my $name (keys %tag_names) {
180            $passes   {$name} = [grep {$args {filter_test} ->
181                                             (pass    => $name, $_)} @passes];
182            $failures {$name} = [grep {$args {filter_test} ->
183                                             (failure => $name, $_)} @failures];
184        }
185    }
186    else {
187        foreach my $name (keys %tag_names) {
188            $passes   {$name} = [@passes];
189            $failures {$name} = [@failures];
190        }
191    }
192
193    my $runs = count_test_runs $tests, \%passes, \%failures;
194    print "1..$runs\n";
195
196    print "ok ", ++ $count, "\n";
197
198    my @test_names = map {$_ -> [1]} @$tests;
199    my @tag_names  = keys %tag_names;
200
201    my $wanted = $args {wanted};
202    foreach my $test (@$tests) {
203        my ($name, $re, $matches) = @$test;
204
205        while (my ($tag, $match) = each %$matches) {
206            if ($match & NORMAL) {
207                foreach my $pass (@{$passes {$tag}}) {
208                    local $_ = $args {query} -> ($tag => $pass);
209
210                    run_test     re    => $re,
211                                 name  => $name,
212                                 match => $match & NORMAL_PASS;
213
214                    run_old_keep re     => $re,
215                                 name   => $name,
216                                 tag    => $tag,
217                                 parts  => $pass,
218                                 wanted => $wanted if $match & NORMAL_PASS;
219                }
220            }
221            if ($match & FAIL) {
222                foreach my $failure (@{$failures {$tag}}) {
223                    local $_ = $args {query} -> ($tag => $failure);
224
225                    run_fail re    =>  $re,
226                             name  =>  $name;
227                }
228            }
229        }
230    }
231}
232
233
234
235
236sub run_test {
237    my %args = @_;
238
239    my $re           = $args {re};
240    my $name         = $args {name};
241    my $should_match = $args {match};
242
243    my $match = /^$re/;   # Not anchored at the end on purpose.
244    my $good  = $match && $_ eq $&;
245    my $line  = $good ? "match" : $match ? "wrong match (got: $&)" : "no match";
246       $line .= "; $name";
247    if ($should_match) {$good  ? pass $line : fail $line}
248    else               {$match ? fail $line : pass $line}
249}
250
251sub array_cmp {
252    my ($a1, $a2) = @_;
253    return 0 unless @$a1 eq @$a2;
254    foreach my $i (0 .. $#$a1) {
255     # !defined $$a1 [$i] && !defined $$a2 [$i] ||
256     #  defined $$a1 [$i] &&  defined $$a2 [$i] && $$a1 [$i] eq $$a2 [$i]
257      (!defined $$a1 [$i] || $$a1 [$i] eq "") &&
258      (!defined $$a2 [$i] || $$a2 [$i] eq "") ||
259        defined $$a1 [$i] &&  defined $$a2 [$i] && $$a1 [$i] eq $$a2 [$i]
260        or return 0;
261    }
262    return 1;
263}
264
265sub run_old_keep {
266    my %args = @_;
267
268    my $re         = $args {re};     # Regexp that's being tried.
269    my $name       = $args {name};   # Name of the test.
270    my $tag        = $args {tag};    # Tag to pass to wanted sub.
271    my $parts      = $args {parts};  # Parts to construct string from.
272    my $wanted_sub = $args {wanted}; # Sub to contruct wanted array from.
273
274    my @chunks = /^$re->{-keep}$/;
275    unless (@chunks) {fail "no match; $name - keep"; return}
276
277    my $wanted = $wanted_sub -> ($tag => $parts);
278
279    local $" = ", ";
280    array_cmp (\@chunks, $wanted)
281         ? pass "match; $name - keep"
282         : $DEBUG ?  fail "wrong match,\n#      got [@{[__ @chunks]}]\n" .
283                                        "# expected [@{[__ @$wanted]}]"
284                  :  fail "wrong match [@{[__ @chunks]}]"
285}
286
287##################
288#                #
289# New style subs #
290#                #
291##################
292
293#
294# Messages printed at end are of the form:
295#   [XX/Y/ZZ], with XX denoting the type of match, Y the expected result,
296#              and ZZ the result.
297#
298#   XX: - RE:  Regular expression
299#       - SB:  Subroutine call
300#       - OM:  OO -> match
301#       - OS:  OO -> subs
302#       - KP:  Regular expression with -keep
303#
304#    Y: -  P:  Expected to pass
305#       -  F:  Expected to fail
306#
307#   ZZ: - MT:  Pattern matched correctly
308#       - NM:  Pattern did not match
309#       - WM:  Pattern matched, but incorrectly.
310
311
312#
313# Given a regex and a string, test whether the regex fails to match.
314# Matching anything other than the entire string is a pass (as it regex
315# fails to match the entire string)
316#
317sub run_fail {
318    my %args = @_;
319
320    my $re   = $args {re};
321    my $name = $args {name};
322
323    /^$re/ && $_ eq $& ? fail "[RE/F/MT] $name"
324                       : pass "[RE/F/NM] $name";
325}
326
327
328#
329# Same as 'run_fail', except now not a regex, but a subroutine is given.
330#
331sub run_sub_fail {
332    my %args = @_;
333
334    my $sub  = $args {sub};
335    my $name = $args {name};
336    my @args = $args {sub_args} ? ref $args {sub_args} ? @{$args {sub_args}}
337                                                       :   $args {sub_args}
338                                : ();
339
340    $_ =~ $sub -> (@args) && $_ eq $& ? fail "[SB/F/MT] $name"
341                                      : pass "[SB/F/NM] $name";
342}
343
344#
345# We can test whether it matched, but we can't really test whether
346# it matched the entire string. $& relates to the last successful
347# match in the current scope, but the match done in $re -> matches()
348# is done in a subscope. @-/@+ are equally useless.
349#
350sub run_OO_pass {
351    my %args  = @_;
352
353    my $re    = $args {re};
354    my $name  = $args {name};
355
356    my $match = $re -> matches ($_);
357
358    if ($match) {pass "[OM/P/MT] $name"}
359    else        {fail "[OM/P/NM] $name"}
360
361}
362
363
364#
365# Test whether the subroutine gives the right answer.
366#
367sub run_sub_pass {
368    my %args  = @_;
369
370    my $sub   = $args {sub};
371    my $name  = $args {name};
372    my @args  = $args {sub_args} ? ref $args {sub_args} ? @{$args {sub_args}}
373                                                        :   $args {sub_args}
374                                 : ();
375
376    my $match = $_ =~ $sub -> (@args);
377    my $good  = $match && $_ eq $&;
378
379    if    ($good)  {pass "[SB/P/MT] $name"}
380    elsif ($match) {Fail "[SB/P/WM] $name", got => $&, expected => $_}
381    else           {fail "[SB/P/NM] $name"}
382}
383
384
385#
386# Check whether the substitution (only for OO) works correctly.
387#
388sub run_OO_substitution_pass {
389    my %args  = @_;
390
391    my $re    = $args {re};
392    my $name  = $args {name};
393    my $token = $args {token} || "---";
394
395    my $sub   = $re -> subs ($_, $token);
396    my $good  = $sub eq $token;
397
398    if    ($good)      {pass "[OS/P/MT] $name"}
399    elsif ($sub ne $_) {Fail "[OS/P/NM] $name", got => $sub, expected => $token}
400    else               {fail "[OS/P/WM] $name"}
401}
402
403
404sub run_pass {
405    my %args = @_;
406
407    my $re           = $args {re};
408    my $name         = $args {name};
409
410    my $match   = /^$re/;   # Not anchored at the end on purpose.
411    my $good    = $match && $_ eq $&;
412    my $perfect = $good  && !defined $1;  # Should *not* set $1 and friends.
413
414    if    ($perfect) {pass "[RE/P/MT] $name"}
415    elsif ($good)    {fail "[RE/P/MT], sets \$1; $name"}
416    elsif ($match)   {Fail "[RE/P/WM] $name", got => $&, expected => $_}
417    else             {fail "[RE/P/NM] $name"}
418}
419
420
421sub run_keep {
422    my %args = @_;
423
424    my $re         = $args {re};     # Regexp that's being tried.
425    my $name       = $args {name};   # Name of the test.
426    my $wanted     = $args {wanted}; # Wanted list.
427
428    my @chunks = /^$re->{-keep}/;
429    unless (@chunks) {fail "[KP/P/NM] $name"; return}
430
431    array_cmp (\@chunks, $wanted)
432         ? pass "[KP/P/MT] $name"
433         : Fail "[KP/P/WM] $name", got => \@chunks, expected => $wanted;
434}
435
436sub get_args {
437    my $key = shift;
438    foreach my $ref (@_) {
439        next unless exists $$ref {$key};
440        return ref $$ref {$key} eq 'ARRAY' ? @{$$ref {$key}} : $$ref {$key}
441    }
442    return;
443}
444
445sub run_new_test_set {
446    my %args     = @_;
447
448    my $test_set = $args {test_set};
449    my $targets  = $args {targets};
450    my $name     = $$test_set {name};
451    my $regex    = $$test_set {regex} || $$test_set {re}; # Getting tired of
452                                                          # getting this wrong.
453    my $sub      = $$test_set {sub};
454    my $sub_args = $$test_set {sub_args};
455    my $keep     = $regex -> {-keep};
456
457    my $pass     = $$test_set {pass};
458    my $fail     = $$test_set {fail};
459
460    my $skip_sub = $$test_set {skip_sub};
461
462    #
463    # Run the passes.
464    #
465    foreach my $target_info (@$pass) {
466        my $target_name = $$target_info {name};
467        my $query  = $$targets {$target_name} {query};
468        next unless $$targets {$target_name} {list} &&
469                  @{$$targets {$target_name} {list}};
470        my $un_seen = @{$$targets {$target_name} {list}};
471        my $samples = count_me $$targets {$target_name} {list},
472                               $$target_info {limit},
473                               $$test_set {limit};
474        foreach my $parts (@{$$targets {$target_name} {list}}) {
475            next unless $samples > rand $un_seen --;
476            $samples --;
477
478            #
479            # Calculate the sections we're going to skip.
480            #
481            my %skips;
482            foreach my $skip (qw /RE SB OO OM OS KP/) {
483                $skips {$skip} = is_skipped $skip => $target_info, $test_set;
484            }
485            $skips {OM} ||= $skips {OO};
486            $skips {OS} ||= $skips {OO};
487
488            #
489            # Find the thing we need to match against.
490            # Note that we're going to match against $_.
491            #
492            my @args    =  ref $parts ? @$parts : $parts;
493            my @qargs   =  get_args query_args => $target_info, $test_set;
494            local $_    =  $query     ? $query -> (@qargs, @args)  :
495                           ref $parts ? join "" => @$parts : $parts;
496
497            #
498            # See whether we want to skip the test
499            #
500            local $SKIP = $skip_sub && $skip_sub -> (pass => $_);
501
502            #
503            # Find out the things {-keep} should return.
504            # The thing we match agains is in $_.
505            #
506            my @wanted;
507            unless ($skips {KP}) {
508                my @wargs   =  get_args wanted_args => $target_info, $test_set;
509                my $w_sub   =  $$target_info {wanted} ||
510                               $$targets {$target_name} {wanted};
511                @wanted     =  $w_sub ? $w_sub -> (@wargs, @args) : $_;
512                if (@wanted == 1 && ref $wanted [0] eq "ARRAY") {
513                    @wanted =  @{$wanted [0]};
514                }
515            }
516
517            run_pass                 name     => $name,
518                                     re       => $regex      unless $skips {RE};
519            run_OO_pass              name     => $name,
520                                     re       => $regex      unless $skips {OM};
521            run_OO_substitution_pass name     => $name,
522                                     re       => $regex      unless $skips {OS};
523            run_sub_pass             name     => $name,
524                                     sub_args => $sub_args,
525                                     sub      => $sub   if $sub && !$skips {SB};
526            run_keep                 name     => $name,
527                                     re       => $keep,
528                                     wanted   => \@wanted    unless $skips {KP};
529        }
530    }
531
532    #
533    # Run the failures.
534    #
535    foreach my $target_info (@$fail) {
536        my $target_name = $$target_info {name};
537        my $query = $$targets {$target_name} {query};
538        next unless $$targets {$target_name} {list} &&
539                  @{$$targets {$target_name} {list}};
540        my $un_seen = @{$$targets {$target_name} {list}};
541        my $samples   = count_me $$targets {$target_name} {list},
542                                 $$target_info {limit},
543                                 $$test_set {limit};
544        foreach my $parts (@{$$targets {$target_name} {list}}) {
545            next unless $samples > rand $un_seen --;
546            $samples --;
547
548            my @args  = ref $parts ? @$parts : $parts;
549            my @qargs = get_args query_args => $target_info, $test_set;
550            local $_  = $query     ? $query -> (@qargs, @args)
551                      : ref $parts ? join "" => @$parts : $parts;
552
553            local $SKIP = $skip_sub && $skip_sub -> (fail => $_);
554
555            my %skips;
556            foreach my $skip (qw /RE SB/) {
557                $skips {$skip} = is_skipped $skip => $target_info, $test_set;
558            }
559
560            run_fail                 name     => $name,
561                                     re       => $regex      unless $skips {RE};
562            run_sub_fail             name     => $name,
563                                     sub_args => $sub_args,
564                                     sub      => $sub   if $sub && !$skips {SB};
565        }
566    }
567}
568
569#
570# If there's no list, or an empty list, 0 tests have to be run.
571# If no limits are given, return the size of the list.
572# Else, for the first defined limit,
573#           if the limit is negative, return the size of the list,
574#           else if the limit is 0, return 0,
575#           else if the limit is less than 1, treat it as a fraction,
576#           else, return the smaller of the limit and the size of the list.
577#
578sub count_me {
579    my ($list, @limits) = @_;
580
581    return 0 unless $list && @$list;
582    foreach my $limit (@limits) {
583        if (defined $limit) {
584            return @$list if $limit < 0;
585            return int (@$list * $limit) if $limit < 1;
586            return $limit if $limit < @$list;
587            return @$list;
588        }
589    }
590    @$list;
591}
592
593
594#
595# Normify any 'pass','fail' and 'skip' entries in a test.
596# What we want is a 'pass' and a 'fail' pointing to an array of hashes,
597# each hash being a 'target'.
598#
599# Since we are passed a reference, the modification is done in situ.
600#
601sub normify {
602    my $test = shift;
603    foreach my $state (@STATES) {
604        my @list;
605
606        foreach my $postfix ("", "_arg") {
607            my $key = "$state$postfix";
608            next unless exists $$test {$key};
609            my $targets = $$test {$key};
610            if (ref $targets eq 'ARRAY') {
611                foreach my $thingy (@$targets) {
612                    if (ref $thingy eq 'HASH') {
613                        push @list => $thingy;
614                    }
615                    elsif (!ref $thingy) {
616                        push @list => {name => $thingy}
617                    }
618                }
619            }
620            elsif (ref $targets eq 'HASH') {
621                push @list => $targets;
622            }
623            else {
624                push @list => {name => $targets};
625            }
626            delete $$test {$key};
627        }
628
629        $$test {$state} = \@list;
630    }
631
632    #
633    # Skips.
634    #
635    if (!exists $$test {skip}) {$$test {skip} = {}}
636    elsif (ref  $$test {skip} eq 'ARRAY') {
637        $$test {skip} = {map {$_ => 1} @{$$test {skip}}}
638    }
639
640    foreach my $state (@STATES) {
641        foreach my $target (@{$$test {state}}) {
642            if (!exists $$target {skip}) {$$target {skip} = {}}
643            elsif (ref  $$target {skip}) {
644                $$target {skip} = {map {$_ => 1} @{$$target {skip}}}
645            }
646        }
647    }
648}
649
650sub is_skipped {
651    my ($type, @things) = @_;
652    foreach my $thingy (@things) {
653        return $$thingy {skip} {$type} if defined $$thingy {skip} {$type};
654    }
655    return;
656}
657
658sub mult {
659    my ($state, $has_sub, @things) = @_;
660
661    my $mult;
662
663    # Regular expression test.
664    $mult ++ unless is_skipped RE => @things;
665
666    # Subroutine check.
667    $mult ++ if $has_sub && !is_skipped SB => @things;
668
669    if ($state eq "pass") {
670        # OO checks.
671        $mult ++ unless is_skipped OO => @things or is_skipped OM => @things;
672        $mult ++ unless is_skipped OO => @things or is_skipped OS => @things;
673        # Keep check.
674        $mult ++ unless is_skipped RE => @things or is_skipped KP => @things;
675    }
676
677    return $mult;
678}
679
680sub run_new_tests {
681    my %args = @_;
682
683    my ($tests, $targets, $version, $version_from,
684        $extra_runs, $extra_runs_sub) =
685        @args {qw /tests targets version version_from
686                   extra_runs extra_runs_sub/};
687
688    #
689    # Modify any 'pass' and 'fail' entries to arrays of hashes.
690    #
691    foreach my $test (@$tests) {
692        normify $test;
693    }
694
695    #
696    # Count the number of runs.
697    #
698    my  $runs  = defined $version_from;  # VERSION test.
699    my  $no_tests;
700    if ($extra_runs) {
701        $runs  += $extra_runs;
702        $count += $extra_runs;
703    }
704
705    if (defined $version && $version > $]) {
706        $no_tests = 1;
707    }
708    else {
709        # Count the tests to be run.
710        foreach my $test (@$tests) {
711            # Test: pass: regex, regex/keep, OO, OO-substitution, sub (if given)
712            #       fail: regex, sub (if given).
713            my $has_sub = $$test {sub} ? 1 : 0;
714
715            for my $state (@STATES) {
716                foreach my $target (@{$$test {$state}}) {
717                    my $size = count_me $$targets {$$target {name}} {list},
718                                        $$target {limit},
719                                        $$test   {limit};
720                    $runs += $size * mult $state, $has_sub => $target, $test;
721                }
722            }
723        }
724    }
725
726    print "1..$runs\n";
727
728    # Check whether a version is defined.
729    if (defined $version_from) {
730        print "ok ", ++ $count, "\n";
731    }
732
733    if ($extra_runs_sub) {
734        $extra_runs_sub -> (\$count)
735    }
736
737    unless ($no_tests) {
738        foreach my $test (@$tests) {
739            run_new_test_set test_set => $test,
740                             targets  => $targets;
741        }
742    }
743}
744
745#
746# Function to produce random strings.
747#
748
749# Digit.
750sub  d {int rand 10}
751# Positive digit.
752sub pd {1 + int rand 9}
753# String of digits.
754sub dd {my ($min, $max) = @_ > 1 ? (@_) : ($_ [0], $_ [0]);
755        join "" => map {d} 1 .. $min + int rand ($max - $min)}
756# String of digits, not all 0.
757sub pdd {my ($min, $max) = @_ > 1 ? (@_) : ($_ [0], $_ [0]);
758    TRY: my $dd = join "" => map {d} 1 .. $min + int rand ($max - $min);
759         goto TRY unless $dd =~ /[^0]/;
760         $dd}
761# Lowercase letter.
762sub  l {chr (ord ('a') + int rand 26)}
763# String of lowercase letters.
764sub ll {my ($min, $max) = @_ > 1 ? (@_) : ($_ [0], $_ [0]);
765        join "" => map {l} 1 .. $min + int rand ($max - $min)}
766# Uppercase letter.
767sub  L {chr (ord ('a') + int rand 26)}
768# String of uppercase letters.
769sub LL {my ($min, $max) = @_ > 1 ? (@_) : ($_ [0], $_ [0]);
770        join "" => map {L} 1 .. $min + int rand ($max - $min)}
771# Alpha.
772sub  a {50 < rand (100) ? l : L}
773# String of alphas.
774sub aa {my ($min, $max) = @_ > 1 ? (@_) : ($_ [0], $_ [0]);
775        join "" => map {a} 1 .. $min + int rand ($max - $min)}
776# Alphanum.
777sub  w {52 < rand (62) ? d : a}
778# String of alphanums.
779sub ww {my ($min, $max) = @_ > 1 ? (@_) : ($_ [0], $_ [0]);
780        join "" => map {w} 1 .. $min + int rand ($max - $min)}
781# Lowercase hex digit.
782sub _x {(0 .. 9, 'a' .. 'f') [int rand 16]}
783# String of lowercase hex digits.
784sub xx {my ($min, $max) = @_ > 1 ? (@_) : ($_ [0], $_ [0]);
785        join "" => map {_x} 1 .. $min + int rand ($max - $min)}
786# Uppercase hex digit.
787sub  X {(0 .. 9, 'A' .. 'F') [int rand 16]}
788# String of uppercase hex digits.
789sub XX {my ($min, $max) = @_ > 1 ? (@_) : ($_ [0], $_ [0]);
790        join "" => map {X} 1 .. $min + int rand ($max - $min)}
791# Any case hex digit
792sub  h {(0 .. 9, 'A' .. 'F', 'a' .. 'f') [int rand 22]}
793# String of anycase hex digits
794sub hh {my ($min, $max) = @_ > 1 ? (@_) : ($_ [0], $_ [0]);
795        join "" => map {h} 1 .. $min + int rand ($max - $min)}
796
797
798#
799# Pass a number N and a callback C. Return N different results from C.
800# Will do at most 100 * N tries.
801#
802sub gimme {
803    my ($count, $call) = @_;
804    my %cache;
805    foreach (1 .. 100 * $count) {
806        $cache {$call -> ()} = 1;
807        last if keys %cache >= $count;
808    }
809    keys %cache;
810}
811
812#
813# Given a number N, and a list of things, return a sample of N
814#
815sub sample {
816    my $N = shift;
817    return @_ if @_ <= $N;
818
819    my @cache = splice @_ => 0, $N;
820    my $count = $N;
821    map {rand ++ $count < $N and splice @cache, rand @cache, 1, $_} @_;
822
823    @cache;
824}
825
826
827
8281;
829
830__END__
831
832=head1 DESCRIPTION
833
834C<run_new_tests> is called with three (named) parameters:
835
836=over 4
837
838=item C<tests>
839
840A references to an array of I<tests> (explained below).
841
842=item C<targets>
843
844A reference to a hash of I<targets> (explained below).
845
846=item C<version_from>
847
848The name of the file that is checked for a version number.
849
850=back
851
852=head2 Targets
853
854Targets provide a set of strings to match against. Targets are
855indexed by name. Each target is a hash, with the following keys:
856
857=over 4
858
859=item C<list>
860
861Required. This is a reference to an array that will act as building
862blocks to build strings to match against. In the simplest form, this
863is just an array with strings - but typically, this is an array of
864arrays, each subarray used to create a string.
865
866=item C<query>
867
868A coderef. For each entry in array given above, this coderef is called.
869It takes a set of arguments and returns a string to match against. If
870the corresponding entry in C<list> is reference to an array, all its
871elements are passed - otherwise, the entry is passed as a whole. Extra
872arguments provided with C<query_args> below are prepended. If no coderef
873is given, C<sub {$_ [0]}> is assumed.
874
875=item C<wanted>
876
877A coderef. If the target is used for positive matches (that is, it's
878expected to match), this sub is called with the same arguments as C<query>
879- except that C<wanted_args> are prepended. It should return a list of
880strings as if the regular expression was called with C<{-keep}>. The
881string to match against may be assumed to be C<$_>. If no coderef is given,
882C<sub {$_}> is assumed.
883
884=back
885
886=head2 Tests
887
888The tests to run are put in an array, and run in that order. Each test
889tests a specific pattern. Up to seven types of tests are performed, depending
890whether the tests includes expected failures, expected passes or both.
891Expected passes are tested as a regular expression, as a regular expression
892with the C<{-keep}> option, as a subroutine, as an object using the C<match>
893method, and as an object using the C<subs> method. Expected failures are
894tested as a regular expression, and as a subroutine. Each test is a hash
895with the following keys:
896
897=over 4
898
899=item C<name>
900
901The name of this test - mostly used in the test output.
902
903=item C<regex>
904
905The pattern to test with.
906
907=item C<sub>
908
909The subroutine to test with, if any.
910
911=item C<sub_args>
912
913Any arguments that need to be passed into the subroutine. If more than
914one argument needs to be passed, use a reference to an array - the array
915will be flattened when calling the subroutine.
916
917=item C<query_args>
918
919Extra arguments to pass into the C<query> coderef for all the targets
920belonging to this tests, if not overriden as discussed below.
921
922=item C<wanted_args>
923
924Extra arguments to pass into the C<wanted> coderef for all the targets
925belonging to this tests, if not overriden as discussed below.
926
927=item C<pass>
928
929Indicates which targets (discussed above) should be run with expected
930passes.  The value of C<pass> is either a reference to an array - the
931array containing the names of the targets to run, or a reference to a
932hash. In the latter case, the keys are the targets to be run, while the
933keys are hash references, containing more configuration options for the
934target. Values allowed:
935
936=over 4
937
938=item C<query_args>
939
940Extra arguments to pass into the C<query> coderef belonging to this test.
941See discussion above.
942
943=item C<wanted_args>
944
945Extra arguments to pass into the C<wanted> coderef belonging to this test.
946See discussion above.
947
948=back
949
950=item C<fail>
951
952As C<pass>, except that it will list targets with an expected failure.
953
954=back
955