1package Test2::Tools::Defer;
2use strict;
3use warnings;
4
5our $VERSION = '0.000162';
6
7use Carp qw/croak/;
8
9use Test2::Util qw/get_tid/;
10use Test2::API qw{
11    test2_add_callback_exit
12    test2_pid test2_tid
13};
14
15our @EXPORT = qw/def do_def/;
16use base 'Exporter';
17
18my %TODO;
19
20sub def {
21    my ($func, @args) = @_;
22
23    my @caller = caller(0);
24
25    $TODO{$caller[0]} ||= [];
26    push @{$TODO{$caller[0]}} => [$func, \@args, \@caller];
27}
28
29sub do_def {
30    my $for = caller;
31    my $tests = delete $TODO{$for} or croak "No tests to run!";
32
33    for my $test (@$tests) {
34        my ($func, $args, $caller) = @$test;
35
36        my ($pkg, $file, $line) = @$caller;
37
38        chomp(my $eval = <<"        EOT");
39package $pkg;
40# line $line "(eval in Test2::Tools::Defer) $file"
41\&$func(\@\$args);
421;
43        EOT
44
45        eval $eval and next;
46        chomp(my $error = $@);
47
48        require Data::Dumper;
49        chomp(my $td = Data::Dumper::Dumper($args));
50        $td =~ s/^\$VAR1 =/\$args: /;
51        die <<"        EOT";
52Exception: $error
53--eval--
54$eval
55--------
56Tool:   $func
57Caller: $caller->[0], $caller->[1], $caller->[2]
58$td
59        EOT
60    }
61
62    return;
63}
64
65sub _verify {
66    my ($context, $exit, $new_exit) = @_;
67
68    my $not_ok = 0;
69    for my $pkg (keys %TODO) {
70        my $tests = delete $TODO{$pkg};
71        my $caller = $tests->[0]->[-1];
72        print STDOUT "not ok - deferred tests were not run!\n" unless $not_ok++;
73        print STDERR "# '$pkg' has deferred tests that were never run!\n";
74        print STDERR "#   $caller->[1] at line $caller->[2]\n";
75        $$new_exit ||= 255;
76    }
77}
78
79test2_add_callback_exit(\&_verify);
80
811;
82
83__END__
84
85=pod
86
87=encoding UTF-8
88
89=head1 NAME
90
91Test2::Tools::Defer - Write tests that get executed at a later time
92
93=head1 DESCRIPTION
94
95Sometimes you need to test things BEFORE loading the necessary functions. This
96module lets you do that. You can write tests, and then have them run later,
97after C<Test2> is loaded. You tell it what test function to run, and what
98arguments to give it.  The function name and arguments will be stored to be
99executed later. When ready, run C<do_def()> to kick them off once the functions
100are defined.
101
102=head1 SYNOPSIS
103
104    use strict;
105    use warnings;
106
107    use Test2::Tools::Defer;
108
109    BEGIN {
110        def ok => (1, 'pass');
111        def is => ('foo', 'foo', 'runs is');
112        ...
113    }
114
115    use Test2::Tools::Basic;
116
117    do_def(); # Run the tests
118
119    # Declare some more tests to run later:
120    def ok => (1, "another pass");
121    ...
122
123    do_def(); # run the new tests
124
125    done_testing;
126
127=head1 EXPORTS
128
129=over 4
130
131=item def function => @args;
132
133This will store the function name, and the arguments to be run later. Note that
134each package has a separate store of tests to run.
135
136=item do_def()
137
138This will run all the stored tests. It will also reset the list to be empty so
139you can add more tests to run even later.
140
141=back
142
143=head1 SOURCE
144
145The source code repository for Test2-Suite can be found at
146F<https://github.com/Test-More/Test2-Suite/>.
147
148=head1 MAINTAINERS
149
150=over 4
151
152=item Chad Granum E<lt>exodist@cpan.orgE<gt>
153
154=back
155
156=head1 AUTHORS
157
158=over 4
159
160=item Chad Granum E<lt>exodist@cpan.orgE<gt>
161
162=back
163
164=head1 COPYRIGHT
165
166Copyright 2018 Chad Granum E<lt>exodist@cpan.orgE<gt>.
167
168This program is free software; you can redistribute it and/or
169modify it under the same terms as Perl itself.
170
171See F<http://dev.perl.org/licenses/>
172
173=cut
174