xref: /openbsd-src/gnu/usr.bin/perl/cpan/Test-Harness/lib/App/Prove/State.pm (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
1b39c5158Smillertpackage App::Prove::State;
2b39c5158Smillert
3b39c5158Smillertuse strict;
46fb12b70Safresh1use warnings;
5b39c5158Smillert
6b39c5158Smillertuse File::Find;
7b39c5158Smillertuse File::Spec;
8b39c5158Smillertuse Carp;
9b39c5158Smillert
10b39c5158Smillertuse App::Prove::State::Result;
11b39c5158Smillertuse TAP::Parser::YAMLish::Reader ();
12b39c5158Smillertuse TAP::Parser::YAMLish::Writer ();
136fb12b70Safresh1use base 'TAP::Base';
14b39c5158Smillert
15b39c5158SmillertBEGIN {
16b39c5158Smillert    __PACKAGE__->mk_methods('result_class');
17b39c5158Smillert}
18b39c5158Smillert
19b39c5158Smillertuse constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );
20b39c5158Smillertuse constant NEED_GLOB => IS_WIN32;
21b39c5158Smillert
22b39c5158Smillert=head1 NAME
23b39c5158Smillert
24b39c5158SmillertApp::Prove::State - State storage for the C<prove> command.
25b39c5158Smillert
26b39c5158Smillert=head1 VERSION
27b39c5158Smillert
28*3d61058aSafresh1Version 3.48
29b39c5158Smillert
30b39c5158Smillert=cut
31b39c5158Smillert
32*3d61058aSafresh1our $VERSION = '3.48';
33b39c5158Smillert
34b39c5158Smillert=head1 DESCRIPTION
35b39c5158Smillert
36b39c5158SmillertThe C<prove> command supports a C<--state> option that instructs it to
37b39c5158Smillertstore persistent state across runs. This module implements that state
38b39c5158Smillertand the operations that may be performed on it.
39b39c5158Smillert
40b39c5158Smillert=head1 SYNOPSIS
41b39c5158Smillert
42b39c5158Smillert    # Re-run failed tests
436fb12b70Safresh1    $ prove --state=failed,save -rbv
44b39c5158Smillert
45b39c5158Smillert=cut
46b39c5158Smillert
47b39c5158Smillert=head1 METHODS
48b39c5158Smillert
49b39c5158Smillert=head2 Class Methods
50b39c5158Smillert
51b39c5158Smillert=head3 C<new>
52b39c5158Smillert
53b39c5158SmillertAccepts a hashref with the following key/value pairs:
54b39c5158Smillert
55b39c5158Smillert=over 4
56b39c5158Smillert
57b39c5158Smillert=item * C<store>
58b39c5158Smillert
59b39c5158SmillertThe filename of the data store holding the data that App::Prove::State reads.
60b39c5158Smillert
61898184e3Ssthen=item * C<extensions> (optional)
62b39c5158Smillert
63898184e3SsthenThe test name extensions.  Defaults to C<.t>.
64b39c5158Smillert
65b39c5158Smillert=item * C<result_class> (optional)
66b39c5158Smillert
67b39c5158SmillertThe name of the C<result_class>.  Defaults to C<App::Prove::State::Result>.
68b39c5158Smillert
69b39c5158Smillert=back
70b39c5158Smillert
71b39c5158Smillert=cut
72b39c5158Smillert
73b39c5158Smillert# override TAP::Base::new:
74b39c5158Smillertsub new {
75b39c5158Smillert    my $class = shift;
76b39c5158Smillert    my %args = %{ shift || {} };
77b39c5158Smillert
78b39c5158Smillert    my $self = bless {
79b39c5158Smillert        select     => [],
80b39c5158Smillert        seq        => 1,
81b39c5158Smillert        store      => delete $args{store},
82898184e3Ssthen        extensions => ( delete $args{extensions} || ['.t'] ),
8391f110e0Safresh1        result_class =>
8491f110e0Safresh1          ( delete $args{result_class} || 'App::Prove::State::Result' ),
85b39c5158Smillert    }, $class;
86b39c5158Smillert
87b39c5158Smillert    $self->{_} = $self->result_class->new(
88b39c5158Smillert        {   tests      => {},
89b39c5158Smillert            generation => 1,
90b39c5158Smillert        }
91b39c5158Smillert    );
92b39c5158Smillert    my $store = $self->{store};
93b39c5158Smillert    $self->load($store)
94b39c5158Smillert      if defined $store && -f $store;
95b39c5158Smillert
96b39c5158Smillert    return $self;
97b39c5158Smillert}
98b39c5158Smillert
99b39c5158Smillert=head2 C<result_class>
100b39c5158Smillert
101b39c5158SmillertGetter/setter for the name of the class used for tracking test results.  This
102b39c5158Smillertclass should either subclass from C<App::Prove::State::Result> or provide an
103b39c5158Smillertidentical interface.
104b39c5158Smillert
105b39c5158Smillert=cut
106b39c5158Smillert
107898184e3Ssthen=head2 C<extensions>
108b39c5158Smillert
109898184e3SsthenGet or set the list of extensions that files must have in order to be
110898184e3Ssthenconsidered tests. Defaults to ['.t'].
111b39c5158Smillert
112b39c5158Smillert=cut
113b39c5158Smillert
114898184e3Ssthensub extensions {
115b39c5158Smillert    my $self = shift;
116898184e3Ssthen    $self->{extensions} = shift if @_;
117898184e3Ssthen    return $self->{extensions};
118b39c5158Smillert}
119b39c5158Smillert
120b39c5158Smillert=head2 C<results>
121b39c5158Smillert
122b39c5158SmillertGet the results of the last test run.  Returns a C<result_class()> instance.
123b39c5158Smillert
124b39c5158Smillert=cut
125b39c5158Smillert
126b39c5158Smillertsub results {
127b39c5158Smillert    my $self = shift;
128b39c5158Smillert    $self->{_} || $self->result_class->new;
129b39c5158Smillert}
130b39c5158Smillert
131b39c5158Smillert=head2 C<commit>
132b39c5158Smillert
133b39c5158SmillertSave the test results. Should be called after all tests have run.
134b39c5158Smillert
135b39c5158Smillert=cut
136b39c5158Smillert
137b39c5158Smillertsub commit {
138b39c5158Smillert    my $self = shift;
139b39c5158Smillert    if ( $self->{should_save} ) {
140b39c5158Smillert        $self->save;
141b39c5158Smillert    }
142b39c5158Smillert}
143b39c5158Smillert
144b39c5158Smillert=head2 Instance Methods
145b39c5158Smillert
146b39c5158Smillert=head3 C<apply_switch>
147b39c5158Smillert
148b39c5158Smillert $self->apply_switch('failed,save');
149b39c5158Smillert
150b39c5158SmillertApply a list of switch options to the state, updating the internal
151b39c5158Smillertobject state as a result. Nothing is returned.
152b39c5158Smillert
153b39c5158SmillertDiagnostics:
154b39c5158Smillert    - "Illegal state option: %s"
155b39c5158Smillert
156b39c5158Smillert=over
157b39c5158Smillert
158b39c5158Smillert=item C<last>
159b39c5158Smillert
160b39c5158SmillertRun in the same order as last time
161b39c5158Smillert
162b39c5158Smillert=item C<failed>
163b39c5158Smillert
164b39c5158SmillertRun only the failed tests from last time
165b39c5158Smillert
166b39c5158Smillert=item C<passed>
167b39c5158Smillert
168b39c5158SmillertRun only the passed tests from last time
169b39c5158Smillert
170b39c5158Smillert=item C<all>
171b39c5158Smillert
172b39c5158SmillertRun all tests in normal order
173b39c5158Smillert
174b39c5158Smillert=item C<hot>
175b39c5158Smillert
176b39c5158SmillertRun the tests that most recently failed first
177b39c5158Smillert
178b39c5158Smillert=item C<todo>
179b39c5158Smillert
180b39c5158SmillertRun the tests ordered by number of todos.
181b39c5158Smillert
182b39c5158Smillert=item C<slow>
183b39c5158Smillert
184b39c5158SmillertRun the tests in slowest to fastest order.
185b39c5158Smillert
186b39c5158Smillert=item C<fast>
187b39c5158Smillert
188b39c5158SmillertRun test tests in fastest to slowest order.
189b39c5158Smillert
190b39c5158Smillert=item C<new>
191b39c5158Smillert
192b39c5158SmillertRun the tests in newest to oldest order.
193b39c5158Smillert
194b39c5158Smillert=item C<old>
195b39c5158Smillert
196b39c5158SmillertRun the tests in oldest to newest order.
197b39c5158Smillert
198b39c5158Smillert=item C<save>
199b39c5158Smillert
200b39c5158SmillertSave the state on exit.
201b39c5158Smillert
202b39c5158Smillert=back
203b39c5158Smillert
204b39c5158Smillert=cut
205b39c5158Smillert
206b39c5158Smillertsub apply_switch {
207b39c5158Smillert    my $self = shift;
208b39c5158Smillert    my @opts = @_;
209b39c5158Smillert
210b39c5158Smillert    my $last_gen      = $self->results->generation - 1;
211b39c5158Smillert    my $last_run_time = $self->results->last_run_time;
212b39c5158Smillert    my $now           = $self->get_time;
213b39c5158Smillert
214b39c5158Smillert    my @switches = map { split /,/ } @opts;
215b39c5158Smillert
216b39c5158Smillert    my %handler = (
217b39c5158Smillert        last => sub {
218b39c5158Smillert            $self->_select(
21991f110e0Safresh1                limit => shift,
220b39c5158Smillert                where => sub { $_->generation >= $last_gen },
221b39c5158Smillert                order => sub { $_->sequence }
222b39c5158Smillert            );
223b39c5158Smillert        },
224b39c5158Smillert        failed => sub {
225b39c5158Smillert            $self->_select(
22691f110e0Safresh1                limit => shift,
227b39c5158Smillert                where => sub { $_->result != 0 },
228b39c5158Smillert                order => sub { -$_->result }
229b39c5158Smillert            );
230b39c5158Smillert        },
231b39c5158Smillert        passed => sub {
23291f110e0Safresh1            $self->_select(
23391f110e0Safresh1                limit => shift,
23491f110e0Safresh1                where => sub { $_->result == 0 }
23591f110e0Safresh1            );
236b39c5158Smillert        },
237b39c5158Smillert        all => sub {
23891f110e0Safresh1            $self->_select( limit => shift );
239b39c5158Smillert        },
240b39c5158Smillert        todo => sub {
241b39c5158Smillert            $self->_select(
24291f110e0Safresh1                limit => shift,
243b39c5158Smillert                where => sub { $_->num_todo != 0 },
244b39c5158Smillert                order => sub { -$_->num_todo; }
245b39c5158Smillert            );
246b39c5158Smillert        },
247b39c5158Smillert        hot => sub {
248b39c5158Smillert            $self->_select(
24991f110e0Safresh1                limit => shift,
250b39c5158Smillert                where => sub { defined $_->last_fail_time },
251b39c5158Smillert                order => sub { $now - $_->last_fail_time }
252b39c5158Smillert            );
253b39c5158Smillert        },
254b39c5158Smillert        slow => sub {
25591f110e0Safresh1            $self->_select(
25691f110e0Safresh1                limit => shift,
25791f110e0Safresh1                order => sub { -$_->elapsed }
25891f110e0Safresh1            );
259b39c5158Smillert        },
260b39c5158Smillert        fast => sub {
26191f110e0Safresh1            $self->_select(
26291f110e0Safresh1                limit => shift,
26391f110e0Safresh1                order => sub { $_->elapsed }
26491f110e0Safresh1            );
265b39c5158Smillert        },
266b39c5158Smillert        new => sub {
26791f110e0Safresh1            $self->_select(
26891f110e0Safresh1                limit => shift,
26991f110e0Safresh1                order => sub { -$_->mtime }
27091f110e0Safresh1            );
271b39c5158Smillert        },
272b39c5158Smillert        old => sub {
27391f110e0Safresh1            $self->_select(
27491f110e0Safresh1                limit => shift,
27591f110e0Safresh1                order => sub { $_->mtime }
27691f110e0Safresh1            );
277b39c5158Smillert        },
278b39c5158Smillert        fresh => sub {
27991f110e0Safresh1            $self->_select(
28091f110e0Safresh1                limit => shift,
28191f110e0Safresh1                where => sub { $_->mtime >= $last_run_time }
28291f110e0Safresh1            );
283b39c5158Smillert        },
284b39c5158Smillert        save => sub {
285b39c5158Smillert            $self->{should_save}++;
286b39c5158Smillert        },
287b39c5158Smillert        adrian => sub {
288b39c5158Smillert            unshift @switches, qw( hot all save );
289b39c5158Smillert        },
290b39c5158Smillert    );
291b39c5158Smillert
292b39c5158Smillert    while ( defined( my $ele = shift @switches ) ) {
293b39c5158Smillert        my ( $opt, $arg )
294b39c5158Smillert          = ( $ele =~ /^([^:]+):(.*)/ )
295b39c5158Smillert          ? ( $1, $2 )
296b39c5158Smillert          : ( $ele, undef );
297b39c5158Smillert        my $code = $handler{$opt}
298b39c5158Smillert          || croak "Illegal state option: $opt";
299b39c5158Smillert        $code->($arg);
300b39c5158Smillert    }
301b39c5158Smillert    return;
302b39c5158Smillert}
303b39c5158Smillert
304b39c5158Smillertsub _select {
305b39c5158Smillert    my ( $self, %spec ) = @_;
306b39c5158Smillert    push @{ $self->{select} }, \%spec;
307b39c5158Smillert}
308b39c5158Smillert
309b39c5158Smillert=head3 C<get_tests>
310b39c5158Smillert
311b39c5158SmillertGiven a list of args get the names of tests that should run
312b39c5158Smillert
313b39c5158Smillert=cut
314b39c5158Smillert
315b39c5158Smillertsub get_tests {
316b39c5158Smillert    my $self    = shift;
317b39c5158Smillert    my $recurse = shift;
318b39c5158Smillert    my @argv    = @_;
319b39c5158Smillert    my %seen;
320b39c5158Smillert
321b39c5158Smillert    my @selected = $self->_query;
322b39c5158Smillert
323b39c5158Smillert    unless ( @argv || @{ $self->{select} } ) {
324b39c5158Smillert        @argv = $recurse ? '.' : 't';
325b39c5158Smillert        croak qq{No tests named and '@argv' directory not found}
326b39c5158Smillert          unless -d $argv[0];
327b39c5158Smillert    }
328b39c5158Smillert
329b39c5158Smillert    push @selected, $self->_get_raw_tests( $recurse, @argv ) if @argv;
330b39c5158Smillert    return grep { !$seen{$_}++ } @selected;
331b39c5158Smillert}
332b39c5158Smillert
333b39c5158Smillertsub _query {
334b39c5158Smillert    my $self = shift;
335b39c5158Smillert    if ( my @sel = @{ $self->{select} } ) {
336b39c5158Smillert        warn "No saved state, selection will be empty\n"
337b39c5158Smillert          unless $self->results->num_tests;
338b39c5158Smillert        return map { $self->_query_clause($_) } @sel;
339b39c5158Smillert    }
340b39c5158Smillert    return;
341b39c5158Smillert}
342b39c5158Smillert
343b39c5158Smillertsub _query_clause {
344b39c5158Smillert    my ( $self, $clause ) = @_;
345b39c5158Smillert    my @got;
346b39c5158Smillert    my $results = $self->results;
347b39c5158Smillert    my $where = $clause->{where} || sub {1};
348b39c5158Smillert
349b39c5158Smillert    # Select
350b39c5158Smillert    for my $name ( $results->test_names ) {
351b39c5158Smillert        next unless -f $name;
352b39c5158Smillert        local $_ = $results->test($name);
353b39c5158Smillert        push @got, $name if $where->();
354b39c5158Smillert    }
355b39c5158Smillert
356b39c5158Smillert    # Sort
357b39c5158Smillert    if ( my $order = $clause->{order} ) {
358b39c5158Smillert        @got = map { $_->[0] }
359b39c5158Smillert          sort {
360b39c5158Smillert                 ( defined $b->[1] <=> defined $a->[1] )
361b39c5158Smillert              || ( ( $a->[1] || 0 ) <=> ( $b->[1] || 0 ) )
362b39c5158Smillert          } map {
363b39c5158Smillert            [   $_,
364b39c5158Smillert                do { local $_ = $results->test($_); $order->() }
365b39c5158Smillert            ]
366b39c5158Smillert          } @got;
367b39c5158Smillert    }
368b39c5158Smillert
36991f110e0Safresh1    if ( my $limit = $clause->{limit} ) {
37091f110e0Safresh1        @got = splice @got, 0, $limit if @got > $limit;
37191f110e0Safresh1    }
37291f110e0Safresh1
373b39c5158Smillert    return @got;
374b39c5158Smillert}
375b39c5158Smillert
376b39c5158Smillertsub _get_raw_tests {
377b39c5158Smillert    my $self    = shift;
378b39c5158Smillert    my $recurse = shift;
379b39c5158Smillert    my @argv    = @_;
380b39c5158Smillert    my @tests;
381b39c5158Smillert
382b39c5158Smillert    # Do globbing on Win32.
38391f110e0Safresh1    if (NEED_GLOB) {
38491f110e0Safresh1        eval "use File::Glob::Windows";    # [49732]
38591f110e0Safresh1        @argv = map { glob "$_" } @argv;
38691f110e0Safresh1    }
387898184e3Ssthen    my $extensions = $self->{extensions};
388b39c5158Smillert
389b39c5158Smillert    for my $arg (@argv) {
390b39c5158Smillert        if ( '-' eq $arg ) {
391b39c5158Smillert            push @argv => <STDIN>;
392b39c5158Smillert            chomp(@argv);
393b39c5158Smillert            next;
394b39c5158Smillert        }
395b39c5158Smillert
396b39c5158Smillert        push @tests,
397b39c5158Smillert            sort -d $arg
398b39c5158Smillert          ? $recurse
399898184e3Ssthen              ? $self->_expand_dir_recursive( $arg, $extensions )
40091f110e0Safresh1              : map { glob( File::Spec->catfile( $arg, "*$_" ) ) }
40191f110e0Safresh1              @{$extensions}
402b39c5158Smillert          : $arg;
403b39c5158Smillert    }
404b39c5158Smillert    return @tests;
405b39c5158Smillert}
406b39c5158Smillert
407b39c5158Smillertsub _expand_dir_recursive {
408898184e3Ssthen    my ( $self, $dir, $extensions ) = @_;
409b39c5158Smillert
410b39c5158Smillert    my @tests;
411898184e3Ssthen    my $ext_string = join( '|', map {quotemeta} @{$extensions} );
412898184e3Ssthen
413b39c5158Smillert    find(
414b39c5158Smillert        {   follow      => 1,      #21938
415b39c5158Smillert            follow_skip => 2,
416b39c5158Smillert            wanted      => sub {
417b39c5158Smillert                -f
418898184e3Ssthen                  && /(?:$ext_string)$/
419b39c5158Smillert                  && push @tests => $File::Find::name;
420b39c5158Smillert              }
421b39c5158Smillert        },
422b39c5158Smillert        $dir
423b39c5158Smillert    );
424b39c5158Smillert    return @tests;
425b39c5158Smillert}
426b39c5158Smillert
427b39c5158Smillert=head3 C<observe_test>
428b39c5158Smillert
429b39c5158SmillertStore the results of a test.
430b39c5158Smillert
431b39c5158Smillert=cut
432b39c5158Smillert
433b39c5158Smillert# Store:
434b39c5158Smillert#     last fail time
435b39c5158Smillert#     last pass time
436b39c5158Smillert#     last run time
437b39c5158Smillert#     most recent result
438b39c5158Smillert#     most recent todos
439b39c5158Smillert#     total failures
440b39c5158Smillert#     total passes
441b39c5158Smillert#     state generation
442b39c5158Smillert#     parser
443b39c5158Smillert
444b39c5158Smillertsub observe_test {
445b39c5158Smillert
446b39c5158Smillert    my ( $self, $test_info, $parser ) = @_;
447b39c5158Smillert    my $name = $test_info->[0];
448b39c5158Smillert    my $fail = scalar( $parser->failed ) + ( $parser->has_problems ? 1 : 0 );
449b39c5158Smillert    my $todo = scalar( $parser->todo );
450b39c5158Smillert    my $start_time = $parser->start_time;
451b39c5158Smillert    my $end_time   = $parser->end_time,
452b39c5158Smillert
453b39c5158Smillert      my $test = $self->results->test($name);
454b39c5158Smillert
455b39c5158Smillert    $test->sequence( $self->{seq}++ );
456b39c5158Smillert    $test->generation( $self->results->generation );
457b39c5158Smillert
458b39c5158Smillert    $test->run_time($end_time);
459b39c5158Smillert    $test->result($fail);
460b39c5158Smillert    $test->num_todo($todo);
461b39c5158Smillert    $test->elapsed( $end_time - $start_time );
462b39c5158Smillert
463b39c5158Smillert    $test->parser($parser);
464b39c5158Smillert
465b39c5158Smillert    if ($fail) {
466b39c5158Smillert        $test->total_failures( $test->total_failures + 1 );
467b39c5158Smillert        $test->last_fail_time($end_time);
468b39c5158Smillert    }
469b39c5158Smillert    else {
470b39c5158Smillert        $test->total_passes( $test->total_passes + 1 );
471b39c5158Smillert        $test->last_pass_time($end_time);
472b39c5158Smillert    }
473b39c5158Smillert}
474b39c5158Smillert
475b39c5158Smillert=head3 C<save>
476b39c5158Smillert
477b39c5158SmillertWrite the state to a file.
478b39c5158Smillert
479b39c5158Smillert=cut
480b39c5158Smillert
481b39c5158Smillertsub save {
482b39c5158Smillert    my ($self) = @_;
483b39c5158Smillert
484b39c5158Smillert    my $store = $self->{store} or return;
485b39c5158Smillert    $self->results->last_run_time( $self->get_time );
486b39c5158Smillert
487b39c5158Smillert    my $writer = TAP::Parser::YAMLish::Writer->new;
488b39c5158Smillert    local *FH;
489b39c5158Smillert    open FH, ">$store" or croak "Can't write $store ($!)";
490b39c5158Smillert    $writer->write( $self->results->raw, \*FH );
491b39c5158Smillert    close FH;
492b39c5158Smillert}
493b39c5158Smillert
494b39c5158Smillert=head3 C<load>
495b39c5158Smillert
496b39c5158SmillertLoad the state from a file
497b39c5158Smillert
498b39c5158Smillert=cut
499b39c5158Smillert
500b39c5158Smillertsub load {
501b39c5158Smillert    my ( $self, $name ) = @_;
502b39c5158Smillert    my $reader = TAP::Parser::YAMLish::Reader->new;
503b39c5158Smillert    local *FH;
504b39c5158Smillert    open FH, "<$name" or croak "Can't read $name ($!)";
505b39c5158Smillert
506b39c5158Smillert    # XXX this is temporary
507b39c5158Smillert    $self->{_} = $self->result_class->new(
508b39c5158Smillert        $reader->read(
509b39c5158Smillert            sub {
510b39c5158Smillert                my $line = <FH>;
511b39c5158Smillert                defined $line && chomp $line;
512b39c5158Smillert                return $line;
513b39c5158Smillert            }
514b39c5158Smillert        )
515b39c5158Smillert    );
516b39c5158Smillert
517b39c5158Smillert    # $writer->write( $self->{tests} || {}, \*FH );
518b39c5158Smillert    close FH;
519b39c5158Smillert    $self->_regen_seq;
520b39c5158Smillert    $self->_prune_and_stamp;
521b39c5158Smillert    $self->results->generation( $self->results->generation + 1 );
522b39c5158Smillert}
523b39c5158Smillert
524b39c5158Smillertsub _prune_and_stamp {
525b39c5158Smillert    my $self = shift;
526b39c5158Smillert
527b39c5158Smillert    my $results = $self->results;
528b39c5158Smillert    my @tests   = $self->results->tests;
529b39c5158Smillert    for my $test (@tests) {
530b39c5158Smillert        my $name = $test->name;
531b39c5158Smillert        if ( my @stat = stat $name ) {
532b39c5158Smillert            $test->mtime( $stat[9] );
533b39c5158Smillert        }
534b39c5158Smillert        else {
535b39c5158Smillert            $results->remove($name);
536b39c5158Smillert        }
537b39c5158Smillert    }
538b39c5158Smillert}
539b39c5158Smillert
540b39c5158Smillertsub _regen_seq {
541b39c5158Smillert    my $self = shift;
542b39c5158Smillert    for my $test ( $self->results->tests ) {
543b39c5158Smillert        $self->{seq} = $test->sequence + 1
544b39c5158Smillert          if defined $test->sequence && $test->sequence >= $self->{seq};
545b39c5158Smillert    }
546b39c5158Smillert}
547b39c5158Smillert
548b39c5158Smillert1;
549