File Coverage

blib/lib/Test/Instruction.pm
Criterion Covered Total %
statement 81 151 53.6
branch 32 100 32.0
condition 1 8 12.5
subroutine 24 47 51.0
pod 3 5 60.0
total 141 311 45.3


line stmt bran cond sub pod time code
1             package Test::Instruction;
2              
3 5     5   77378 use 5.006; use strict; use warnings; our $VERSION = '0.04';
  5     5   48  
  5     5   40  
  5         14  
  5         161  
  5         31  
  5         16  
  5         292  
4 5     5   2443 use Compiled::Params::OO qw/cpo/;
  5         769126  
  5         55  
5 5     5   547 use Types::Standard qw/Optional Str Int Bool Any CodeRef ArrayRef HashRef/;
  5         11  
  5         40  
6 5     5   12930 use B qw/svref_2object/;
  5         20  
  5         269  
7 5     5   2401 use Switch::Again qw/switch/;
  5         21073  
  5         38  
8 5     5   3129 use Test::More;
  5         276690  
  5         42  
9 5     5   1382 use base 'Import::Export';
  5         13  
  5         2170  
10              
11             our (%EX, $validate);
12             BEGIN {
13 5     5   67 %EX = (
14             instruction => [qw/all/],
15             instructions => [qw/all/],
16             finish => [qw/all/]
17             );
18 5         42 $validate = cpo(
19             instruction => {
20             instance => Optional->of(Any),
21             meth => Optional->of(Str),
22             func => Optional->of(CodeRef),
23             args => Optional->of(Any),
24             args_list => Optional->of(Bool),
25             test => Optional->of(Str),
26             expected => Optional->of(Any),
27             catch => Optional->of(Bool),
28             key => Optional->of(Str),
29             index => Optional->of(Int),
30             debug => Optional->of(Bool),
31             },
32             instructions => {
33             name => Str,
34             run => ArrayRef,
35             build => Optional->of(HashRef),
36             instance => Optional->of(Any),
37             debug => Optional->of(Bool)
38             },
39             build => {
40             class => Str,
41             new => Optional->of(Str),
42             args => Optional->of(Any),
43             args_list => Optional->of(Bool)
44             },
45             debug => {
46             name => Str,
47             message => Str,
48             out => Optional->of(Any),
49             }
50             );
51             }
52              
53             sub instruction {
54 20     20 1 7711 my $instruction = $validate->instruction->(@_);
55            
56 20 50       1045 debug (
57             name => 'Test instruction',
58             message => 'Run the test instruction',
59             out => $instruction
60             ) if $instruction->debug;
61              
62 20         44 my ($test_name, @test) = ("", ());
63 20 50       57 if ( $instruction->catch ) {
64 0         0 $test_name = 'catch';
65 0 0       0 exits $instruction->test or $instruction->test('like');
66 0         0 eval { _run_the_code( $instruction ) };
  0         0  
67 0         0 @test = $@;
68             } else {
69 20         53 @test = _run_the_code( $instruction );
70 20         110 $test_name = shift @test;
71             }
72              
73 20 50       60 if ( not $instruction->test ) {
74 0         0 ok(0, "No 'test' passed with instruction");
75 0         0 return;
76             }
77              
78             debug (
79 20 50       67 name => $test_name,
80             message => 'Code for the test instruction has been executed',
81             out => \@test
82             ) if $instruction->debug;
83              
84             switch $instruction->test,
85             "ref" => sub {
86 0     0   0 return is_deeply( $test[0], $instruction->expected, "${test_name} is ref - is_deeply" );
87             },
88             ref_key_scalar => sub {
89 0 0   0   0 return ok(0, "No key passed to test - ref_key_scalar - testing - ${test_name}")
90             if (! $instruction->key );
91             return is(
92 0         0 $test[0]->{$instruction->key},
93             $instruction->expected,
94             sprintf "%s is ref - has scalar key: %s - is - %s",
95             $test_name,
96             $instruction->key,
97             $instruction->expected
98             );
99             },
100             ref_key_like => sub {
101 0 0   0   0 return ok(0, "No key passed to test - ref_key_like - testing - ${test_name}")
102             if (! $instruction->key );
103 0         0 my $like = $instruction->expected;
104             return like(
105 0         0 $test[0]->{$instruction->key},
106             qr/$like/,
107             sprintf "%s is ref - has scalar key: %s - like - %s",
108             $test_name,
109             $instruction->key,
110             $instruction->expected
111             );
112             },
113             ref_key_ref => sub {
114 0 0   0   0 return ok(0, "No key passed to test - ref_key_ref - testing - ${test_name}")
115             if (! $instruction->key );
116             return is_deeply(
117 0         0 $test[0]->{$instruction->key},
118             $instruction->expected,
119             sprintf "%s is ref - has ref key: %s - is_deeply - ref",
120             $test_name,
121             $instruction->key,
122             );
123             },
124             ref_index_scalar => sub {
125 0 0   0   0 return ok(0, "No index passed to test - ref_index_scalar - testing - ${test_name}")
126             if (! defined $instruction->index );
127 0         0 return is(
128             $test[0]->[$instruction->index],
129             $instruction->expected,
130             sprintf "%s is ref - has scalar index: %s - is - %s",
131             $test_name,
132             $instruction->index,
133             $instruction->expected
134             );
135             },
136             ref_index_ref => sub {
137 0 0   0   0 return ok(0, "No index passed to test - ref_index_ref - testing - ${test_name}")
138             if (! defined $instruction->index );
139 0         0 is_deeply(
140             $test[0]->[$instruction->index],
141             $instruction->expected,
142             sprintf "%s is ref - has ref index: %s - is_deeply - ref",
143             $test_name,
144             $instruction->index,
145             );
146             },
147             ref_index_like => sub {
148 0 0   0   0 return ok(0, "No index passed to test - ref_index_like - testing - ${test_name}")
149             if (! defined $instruction->index );
150 0         0 my $like = $instruction->expected;
151 0         0 return like(
152             $test[0]->[$instruction->index],
153             qr/$like/,
154             sprintf "%s is ref - has scalar index: %s - like - %s",
155             $test_name,
156             $instruction->index,
157             $instruction->expected
158             );
159             },
160             ref_index_obj => sub {
161 0 0   0   0 return ok(0, "No index passed to test - ref_index_obj - testing - ${test_name}")
162             if (! defined $instruction->index );
163 0         0 return isa_ok(
164             $test[0]->[$instruction->index],
165             $instruction->expected,
166             sprintf "%s is ref - has obj index: %s - isa_ok - %s",
167             $test_name,
168             $instruction->index,
169             $instruction->expected
170             );
171             },
172             list_index_scalar => sub {
173 0 0   0   0 return ok(0, "No index passed to test - list_index_scalar - testing - ${test_name}")
174             if (! defined $instruction->index );
175              
176 0         0 return is(
177             $test[$instruction->index],
178             $instruction->expected,
179             sprintf "%s is list - has scalar index: %s - is - %s",
180             $test_name,
181             $instruction->index,
182             $instruction->expected
183             );
184             },
185             list_index_ref => sub {
186 0 0   0   0 return ok(0, "No index passed to test - list_index_ref - testing - ${test_name}")
187             if (! defined $instruction->index );
188 0         0 return is_deeply(
189             $test[$instruction->index],
190             $instruction->expected,
191             sprintf "%s is list - has ref index: %s - is_deeply - ref",
192             $test_name,
193             $instruction->index,
194             );
195             },
196             list_index_like => sub {
197 0 0   0   0 return ok(0, "No index passed to test - list_index_like - testing - ${test_name}")
198             if (! defined $instruction->index );
199 0         0 my $like = $instruction->expected;
200 0         0 return is(
201             $test[$instruction->index],
202             qr/$like/,
203             sprintf "%s is list - has scalar index: %s - like - %s",
204             $test_name,
205             $instruction->index,
206             $instruction->expected
207             );
208             },
209             list_index_obj => sub {
210 0 0   0   0 return ok(0, "No index passed to test - list_index_obj - testing - ${test_name}")
211             if (! defined $instruction->index );
212 0         0 return isa_ok(
213             $test[$instruction->index],
214             $instruction->expected,
215             sprintf "%s is list - has obj index: %s - isa_ok - %s",
216             $test_name,
217             $instruction->index,
218             $instruction->expected
219             ),
220             },
221             list_key_scalar => sub {
222 0 0   0   0 return ok(0, "No key passed to test - list_key_scalar - testing - ${test_name}")
223             if (! $instruction->key );
224             return is(
225 0         0 {@test}->{$instruction->key},
226             $instruction->expected,
227             sprintf "%s is list - has scalar key: %s - is - %s",
228             $test_name,
229             $instruction->key,
230             $instruction->expected
231             );
232             },
233             list_key_ref => sub {
234 0 0   0   0 return ok(0, "No key passed to test - list_key_ref - testing - ${test_name}")
235             if (! $instruction->key );
236             return is_deeply(
237 0         0 {@test}->{$instruction->key},
238             $instruction->expected,
239             sprintf "%s is list - has ref key: %s - is_deeply - ref",
240             $test_name,
241             $instruction->key,
242             );
243             },
244             list_key_like => sub {
245 0 0   0   0 return ok(0, "No key passed to test - list_key_like - testing - ${test_name}")
246             if (! $instruction->key );
247 0         0 my $like = $instruction->expected;
248             return is(
249 0         0 {@test}->{$instruction->key},
250             qr/$like/,
251             sprintf "%s is list - has scalar key: %s - like - %s",
252             $test_name,
253             $instruction->key,
254             $instruction->expected
255             );
256             },
257             count => sub {
258 0     0   0 return is(
259             scalar @test,
260             $instruction->expected,
261             sprintf "%s is array - count - is - %s",
262             $test_name,
263             $instruction->expected
264             );
265             },
266             count_ref => sub {
267             return is(
268 0     0   0 scalar @{$test[0]},
  0         0  
269             $instruction->expected,
270             sprintf "%s is ref - count - is - %s",
271             $test_name,
272             $instruction->expected
273             );
274             },
275             scalar => sub {
276 1 50   1   469 return is( $test[0], $instruction->expected, sprintf "%s is scalar - is - %s",
277             $test_name, defined $instruction->expected ? $instruction->expected : 'undef');
278             },
279             hash => sub {
280 4 50   4   2068 return is_deeply(
281             scalar @test == 1 ? $test[0] : {@test},
282             $instruction->expected,
283             sprintf "%s is hash - is_deeply",
284             $test_name,
285             );
286             },
287             array => sub {
288 0 0   0   0 return is_deeply(
289             scalar @test == 1 ? $test[0] : \@test,
290             $instruction->expected,
291             sprintf "%s is array - is_deeply",
292             $test_name,
293             );
294             },
295             obj => sub {
296 2     2   1255 return isa_ok(
297             $test[0],
298             $instruction->expected,
299             sprintf "%s isa_ok - %s",
300             $test_name,
301             $instruction->expected
302             );
303             },
304             code => sub {
305 2     2   1085 return is(
306             ref $test[0],
307             'CODE',
308             sprintf "%s is a CODE block",
309             $test_name
310             );
311             },
312             code_execute => sub {
313             return is_deeply(
314 2 50   2   1068 $test[0]->($instruction->args ? @{$instruction->args} : ()),
  0         0  
315             $instruction->expected,
316             sprintf "%s is deeply %s",
317             $test_name,
318             $instruction->expected
319             );
320             },
321             like => sub {
322 1     1   543 my $like = $instruction->expected;
323 1         17 return like(
324             $test[0],
325             qr/$like/,
326             sprintf "%s is like - %s",
327             $test_name,
328             $instruction->expected
329             );
330             },
331             true => sub {
332 3     3   1714 return ok($test[0], "${test_name} is true - 1");
333             },
334             false => sub {
335 3     3   1806 return ok(!$test[0], "${test_name} is false - 0");
336             },
337             undef => sub {
338 0     0   0 return is($test[0], undef, "${test_name} is undef");
339             },
340             ok => sub {
341 2     2   1279 return ok(@test, "${test_name} is ok");
342             },
343             skip => sub {
344 0     0   0 return ok(1, "${test_name} - skip");
345             },
346             default => sub {
347 0     0   0 ok(0, "Unknown instruction $_[0]: passed to instrcution");
348 0         0 return;
349 20         739 };
350             }
351              
352             sub instructions {
353 2     2 1 117 my $instructions = $validate->instructions->(@_);
354              
355 2 50       108 debug (
356             name => $instructions->name,
357             message => 'running test instructions: ' + caller()
358             ) if $instructions->debug;
359              
360 2         16 ok(1, sprintf "instructions: %s", $instructions->name);
361              
362 2 100       826 my $instance = $instructions->build ? _build($instructions->build) : $instructions->instance;
363              
364 2 50       22 debug (
365             name => $instructions->name,
366             message => 'Built the test instance object',
367             out => $instance
368             ) if $instructions->debug;
369              
370 2         9 my %test_info = (
371             fail => 0,
372             tested => 0,
373             );
374              
375 2         4 for my $instruction (@{$instructions->run}) {
  2         7  
376 5         1480 $test_info{tested}++;
377            
378 5 50       17 debug (
379             name => $instructions->name,
380             message => 'Run the next test instruction',
381             out => $instruction
382             ) if $instructions->debug;
383            
384 5 100       13 if (my $subtests = delete $instruction->{instructions}) {
385             my ($test_name, $new_instance) = _run_the_code(
386             $validate->instruction->(
387             instance => $instance,
388             ($instructions->debug ? (debug => $instructions->debug) : ()),
389 1 50       6 %{$instruction}
  1         7  
390             )
391             );
392            
393 1 50       11 debug (
394             name => sprintf("%s -> %s", $instructions->name, $test_name),
395             message => 'Run the subtests of the test instruction',
396             out => $instruction
397             ) if $instructions->debug;
398            
399             $test_info{fail}++
400             unless instruction(
401             instance => $new_instance,
402             test => $instruction->{test},
403             ($instructions->debug ? (debug => $instructions->debug) : ()),
404             expected => $instruction->{expected}
405 1 50       7 );
    50          
406              
407 1 50       400 instructions(
408             instance => $new_instance,
409             run => $subtests,
410             name => sprintf("Subtest -> %s -> %s", $instructions->name, $test_name),
411             ($instructions->debug ? (debug => $instructions->debug) : ()),
412             );
413 1         314 next;
414             }
415              
416             $test_info{fail}++
417             unless instruction(
418             instance => $instance,
419             ($instructions->debug ? (debug => $instructions->debug) : ()),
420 4 50       11 %{$instruction}
  4 50       17  
421             );
422             }
423            
424 2 50       626 $test_info{ok} = $test_info{fail} ? 0 : 1;
425             return ok(
426             $test_info{ok},
427             sprintf(
428             "instructions: %s - tested %d instructions - success: %d - failure: %d",
429             $instructions->name,
430             $test_info{tested},
431             ($test_info{tested} - $test_info{fail}),
432             $test_info{fail}
433             )
434 2         23 );
435             }
436              
437             sub finish {
438 4     4 1 1788 my $done_testing = done_testing(shift);
439 4         3810 return $done_testing;
440             }
441              
442             sub _build {
443 1     1   8 my $build = $validate->build->(@_);
444 1   50     56 my $new = $build->new || 'new';
445 1 50       27 return $build->class->$new($build->args_list ? @{ $build->args } : defined $build->args ? $build->args : ());
  0 50       0  
446             }
447              
448             sub _run_the_code {
449 21     21   109 my $instruction = shift;
450 21 100       100 if ($instruction->meth) {
    100          
    50          
451 11         25 my $meth = $instruction->meth;
452             return (
453             "function: ${meth}",
454             $instruction->instance->$meth(
455             $instruction->args_list
456 11 50       123 ? @{ $instruction->args }
  0         0  
457             : $instruction->args
458             )
459             );
460             } elsif ($instruction->func) {
461 5         33 my $func_name = svref_2object($instruction->func)->GV->NAME;
462             return (
463             "function: ${func_name}",
464 5 100       31 $instruction->func->($instruction->args_list ? @{$instruction->args} : $instruction->args)
  2         8  
465             );
466             } elsif ($instruction->instance) {
467 5         20 return ('instance', $instruction->instance);
468             }
469              
470             die(
471 0           'instruction passed to _run_the_code must have a func, meth or instance key'
472             );
473             }
474              
475             sub caller_stack {
476 0     0 0   my @caller; my $i = 0; my @stack;
  0            
  0            
477 0           while(@caller = caller($i++)){
478 0 0         next if $caller[0] eq 'Log::JSON::Lines';
479 0           $stack[$i+1]->{module} = $caller[0];
480 0 0         $stack[$i+1]->{file} = $1 if $caller[1] =~ /([^\/]+)$/;;
481 0 0         $stack[$i+1]->{line} = $1 if $caller[2] =~ /(\d+)/;
482 0 0         $stack[$i]->{sub} = $1 if $caller[3] =~ /([^:]+)$/;
483             }
484             my $stacktrace = join '->', reverse map {
485 0 0         my $module = $_->{module} !~ m/^main$/ ? $_->{module} : $_->{file};
486             $_->{sub}
487             ? $module . '::' . $_->{sub} . ':' . $_->{line}
488             : $module . ':' . $_->{line}
489 0 0         } grep {
490 0           $_ && $_->{module} && $_->{line} && $_->{file}
491 0 0 0       } @stack;
      0        
492 0           return $stacktrace;
493             }
494              
495             sub debug {
496 0     0 0   my $debug = $validate->debug->(@_);
497 0           diag explain $debug->name . ' ~ ' . caller_stack();
498 0           diag explain $debug->message;
499 0           diag explain $debug->out;
500             }
501              
502             __END__