File Coverage

blib/lib/Test/Instruction.pm
Criterion Covered Total %
statement 88 155 56.7
branch 38 104 36.5
condition 1 8 12.5
subroutine 26 47 55.3
pod 3 5 60.0
total 156 319 48.9


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