File Coverage

blib/lib/Test2/Tools/xUnit.pm
Criterion Covered Total %
statement 72 72 100.0
branch 26 26 100.0
condition 4 6 66.6
subroutine 15 16 93.7
pod n/a
total 117 120 97.5


line stmt bran cond sub pod time code
1             package Test2::Tools::xUnit 0.006;
2              
3 13     13   58991 use v5.12;
  13         69  
4 13     13   75 use warnings;
  13         24  
  13         434  
5              
6 13     13   97 use B;
  13         32  
  13         581  
7 13     13   6280 use Test2::Workflow;
  13         362817  
  13         629  
8 13     13   6424 use Test2::Workflow::Runner;
  13         496173  
  13         414  
9 13     13   120 use Test2::Workflow::Task::Action;
  13         30  
  13         6681  
10              
11             sub import {
12 13     13   196 my @caller = caller;
13              
14             # This sets up the root Test2::Workflow::Build for the package we are
15             # being called from. All tests will be added as actions later.
16             my $root = Test2::Workflow::init_root(
17             $caller[0],
18       0     code => sub { },
19 13         103 frame => \@caller,
20             );
21              
22             # Each test method is run in its own instance. This setup action will
23             # be called before each test method is invoked, and instantiates a new
24             # object.
25             #
26             # If the caller does not provide a "new" constructor, we bless a hashref
27             # into the calling package and use that.
28             #
29             # Each coderef is called with the Test2::Workflow::Runner as the first
30             # argument. We abuse this so that we can pass the same instance variable
31             # to the setup, test and teardown methods.
32             $root->add_primary_setup(
33             Test2::Workflow::Task::Action->new(
34             code => sub {
35             shift->{xUnit}
36 21 100   21   53394 = $caller[0]->can('new')
37             ? $caller[0]->new
38             : bless {}, $caller[0];
39             },
40 13         4874 name => 'object_construction',
41             frame => \@caller,
42             scaffold => 1,
43             )
44             );
45              
46             # We add a follow-up task to the top hub in the stack, which will be
47             # executed when done_testing or END is seen.
48             Test2::API::test2_stack->top->follow_up(
49 13     13   4226 sub { Test2::Workflow::Runner->new( task => $root->compile )->run } );
  13         10210  
50              
51 13         149223 my $orig = $caller[0]->can('MODIFY_CODE_ATTRIBUTES');
52              
53             # This sub will be called whenever the Perl interpreter hits a subroutine
54             # with attributes in our caller.
55             #
56             # It closes over $root so that it can add the actions, and @caller so that
57             # it knows which package it's in.
58             my $modify_code_attributes = sub {
59 33     33   437047 my ( $pkg, $code, @attrs ) = @_;
60              
61 33         437 my $name = B::svref_2object($code)->GV->NAME;
62              
63 33         219 my ( $method, $class_method, %options, @unhandled );
64              
65 33         85 for (@attrs) {
66 39 100       206 if ( $_ eq 'Test' ) {
    100          
    100          
    100          
    100          
    100          
    100          
67 22         54 $method = 'add_primary';
68             }
69             # All the setup methods count as 'scaffolding'.
70             # Test2::Workflow docs are light on what this actually does;
71             # something to do with filtering out the events? Anyway,
72             # Test2::Tools::Spec does it.
73             elsif ( $_ eq 'BeforeEach' ) {
74 2         3 $method = 'add_primary_setup';
75 2         4 $options{scaffold} = 1;
76             }
77             elsif ( $_ eq 'AfterEach' ) {
78 2         4 $method = 'add_primary_teardown';
79 2         6 $options{scaffold} = 1;
80             }
81             # BeforeAll/AfterAll are called as class methods, not instance
82             # methods.
83             elsif ( $_ eq 'BeforeAll' ) {
84 1         2 $method = 'add_setup';
85 1         2 $options{scaffold} = 1;
86 1         2 $class_method = 1;
87             }
88             elsif ( $_ eq 'AfterAll' ) {
89 1         3 $method = 'add_teardown';
90 1         2 $options{scaffold} = 1;
91 1         2 $class_method = 1;
92             }
93             # We default to the name of the current method if no reason is
94             # given for Skip/Todo.
95             elsif (/^Skip(?:\((.+)\))?/) {
96 3         4 $method = 'add_primary';
97 3   66     21 $options{skip} = $1 || $name;
98             }
99             elsif (/^Todo(?:\((.+)\))?/) {
100 4         7 $method = 'add_primary';
101 4   66     26 $options{todo} = $1 || $name;
102             }
103             # All unhandled attributes are returned for someone else to
104             # deal with.
105             else {
106 4         11 push @unhandled, $_;
107             }
108             }
109              
110 33 100       419 if ($method) {
111             my $task = Test2::Workflow::Task::Action->new(
112             code => $class_method
113 2     2   3242 ? sub { $caller[0]->$code }
114 29     29   10160 : sub { shift->{xUnit}->$code },
115 30 100       372 frame => \@caller,
116             name => $name,
117             %options,
118             );
119              
120 30         5593 $root->$method($task);
121             }
122              
123 33         264 @_ = ( $pkg, $code, @unhandled );
124 33 100       105 if ($orig) {
125 1         7 goto $orig;
126             }
127             else {
128             # A package like Attribute::Handlers might have modified @ISA
129             # after we were imported. Note that SUPER won't work because it
130             # finds the compile-time package of this sub.
131 13     13   148 no strict 'refs';
  13         33  
  13         2189  
132 32         52 my @parents = @{ $pkg . '::ISA' };
  32         146  
133 32 100       123 @parents = 'UNIVERSAL' unless @parents;
134 32         77 for my $parent (@parents) {
135 32 100       231 if ( my $subref = $parent->can('MODIFY_CODE_ATTRIBUTES') ) {
136 4         25 goto $subref;
137             }
138             }
139             }
140              
141 28         121 return @unhandled;
142 13         98 };
143              
144 13     13   102 no strict 'refs';
  13         34  
  13         459  
145 13     13   114 no warnings 'redefine';
  13         48  
  13         1304  
146              
147 13         175 *{"$caller[0]::MODIFY_CODE_ATTRIBUTES"} = $modify_code_attributes;
  13         470  
148             }
149              
150             1;