File Coverage

blib/lib/Trace/Mask/Test.pm
Criterion Covered Total %
statement 117 130 90.0
branch 31 50 62.0
condition 14 39 35.9
subroutine 13 13 100.0
pod 2 2 100.0
total 177 234 75.6


line stmt bran cond sub pod time code
1             package Trace::Mask::Test;
2 3     3   201756 use strict;
  3         4  
  3         68  
3 3     3   9 use warnings;
  3         3  
  3         63  
4              
5 3     3   342 use Trace::Mask::Util qw/mask_frame mask_line mask_call/;
  3         16  
  3         152  
6 3     3   642 use Trace::Mask::Reference qw/trace/;
  3         3  
  3         166  
7              
8 3     3   14 use Carp qw/croak/;
  3         3  
  3         167  
9 3     3   13 use Scalar::Util qw/reftype/;
  3         4  
  3         118  
10 3     3   20 use List::Util qw/min/;
  3         4  
  3         173  
11              
12 3     3   12 use base 'Exporter';
  3         5  
  3         2507  
13             our @EXPORT = qw{test_tracer NA};
14             our @EXPORT_OK = qw{
15             test_stack_hide test_stack_shift test_stack_stop test_stack_no_start
16             test_stack_alter test_stack_shift_and_hide test_stack_shift_short
17             test_stack_hide_short test_stack_shift_and_alter test_stack_full_combo
18             test_stack_restart test_stack_special test_stack_lock
19             };
20              
21 751     751 1 1951 sub NA() { \&NA }
22              
23             sub test_tracer {
24 11     11 1 8676 my %params = @_;
25 11         22 my $convert = delete $params{convert};
26 11         16 my $trace = delete $params{trace};
27 11   50     29 my $name = delete $params{name} || 'tracer test';
28 11   100     27 my $type = delete $params{type} || 'return';
29              
30 11 50 33     101 croak "your must provide a 'convert' callback coderef"
      33        
31             unless $convert && ref($convert) && reftype($convert) eq 'CODE';
32              
33 11 50 33     77 croak "your must provide a 'trace' callback coderef"
      33        
34             unless $trace && ref($trace) && reftype($trace) eq 'CODE';
35              
36 11         13 my %tests;
37 11 50       25 if (keys %params) {
38 0         0 my @bad;
39              
40 0         0 for my $test (keys %params) {
41 0         0 my $sub;
42 0 0 0     0 $sub = __PACKAGE__->can($test) if !ref($test) && $test =~ m/^test_/ && $test !~ m/test_tracer/;
      0        
43 0 0 0     0 if($sub && ref($sub) && reftype($sub) eq 'CODE') {
      0        
44 0         0 $tests{$test} = $sub;
45             }
46             else{
47 0         0 push @bad => $test;
48             }
49             }
50              
51 0 0       0 croak "Invalid test(s): " . join(', ', map {"'$_'"} sort @bad)
  0         0  
52             if @bad;
53             }
54             else {
55 11         376 for my $sym (keys %Trace::Mask::Test::) {
56 1307 100       1562 next unless $sym =~ m/^test_/;
57 154 100       190 next if $sym =~ m/test_tracer/;
58 143   50     421 my $sub = __PACKAGE__->can($sym) || next;
59 143         195 $tests{$sym} = $sub;
60             }
61             }
62              
63 11         117 require Test2::Tools::Compare;
64 11         28 require Test2::Tools::Subtest;
65 11         26 require Test2::API;
66              
67 11         26 my $ctx = Test2::API::context();
68              
69 11         1272 my $results = {};
70 11         16 my $expects = {};
71 11         12 my $ok;
72 11         22 my $sig_die = $SIG{__DIE__};
73              
74             Test2::Tools::Subtest::subtest_buffered($name => sub {
75 11     11   3438 local $SIG{__DIE__} = $sig_die;
76 11         24 my $sctx = Test2::API::context();
77 11         1329 $sctx->set_trace($ctx->trace);
78 11         136 for my $test (sort keys %tests) {
79 143         304 my $sub = $tests{$test};
80 143         109 my $result;
81             {
82 143         146 my ($ok, $error, $out, $warning);
  143         95  
83             {
84 143         113 local $@;
  143         115  
85 143         481 local $SIG{__WARN__} = $SIG{__WARN__};
86 143         166 local *STDERR = *STDERR;
87              
88 143 100       330 if ($type eq 'warning') {
    100          
89             $SIG{__WARN__} = sub {
90 26 50       45 die "Got multiple warnings!\n$warning\n$_[0]\n" if $warning;
91 26         161 ($warning) = @_;
92 26         99 };
93             }
94             elsif($type eq 'sigwarn') {
95 26         42 local *Trace::Mask::Test::FAKE;
96 26         31 *STDERR = *Trace::Mask::Test::FAKE;
97 26         27 $warning = "";
98 1 50   1   584 open(STDERR, '>', \$warning) || do {
  1         8  
  1         5  
  26         225  
99 0         0 print "Could not open temp STDERR: $!\n";
100 0         0 die "Could not open temp STDERR: $!";
101             };
102             }
103              
104 143         779 $ok = eval { $out = $sub->($trace); 1 };
  143         438  
  91         149  
105 143         502 $error = $@;
106             }
107              
108 143 100 66     391 if ($type eq 'exception') {
    100          
    50          
109 52 50       82 die "Callback did not throw an exception!\n" if $ok;
110 52         45 $ok = 1;
111 52         55 $result = $error;
112             }
113             elsif ($type eq 'return') {
114 39 50       55 die "Callback did not return anything!\n" unless $out;
115 39         42 $result = $out;
116             }
117             elsif($type eq 'warning' || $type eq 'sigwarn') {
118 52 50       72 die "Callback did not issue any warnings!\n" unless $warning;
119 52         54 $result = $warning;
120             }
121             else {
122 0         0 die "Invalid type: '$type'\n";
123             }
124              
125 143 50       192 die $error unless $ok;
126              
127 143         273 $result = $convert->($result);
128             }
129 143         4649 my $expect = $sub->(\&trace);
130 143         238 for my $frame (@$expect) {
131 682 50       434 $frame->[1] = [ map { ref($_) ? "$_" : $_ } @{$frame->[1]} ];
  572         1104  
  682         656  
132             }
133              
134 143         286 $results->{$test} = $result;
135 143         163 $expects->{$test} = $expect;
136              
137 143         280 my $size = min(scalar(@$result), scalar(@$expect));
138 143         255 for(my $i = 0; $i < $size; $i++) { # Frame
139 682         736 delete $expect->[$i]->[2]; # Remove the mask
140              
141             # Args may not be available
142 682 100 66     877 unless ($result->[$i]->[1] && @{$result->[$i]->[1]}) {
  682         1414  
143 140         120 delete $expect->[$i]->[1];
144 140         113 delete $result->[$i]->[1];
145             }
146              
147 682         463 for (my $j = @{$expect->[$i]->[0]} - 1; $j >= 0; $j--) {
  682         991  
148 7502 100       6502 if (exists $result->[$i]->[0]->[$j]) {
149 3162 100 66     8834 $expect->[$i]->[0]->[$j] = sub { 1 } if ref($result->[$i]->[0]->[$j]) && $result->[$i]->[0]->[$j] == \&NA;
  750         227519  
150             }
151             else {
152 4340         2252 pop @{$expect->[$i]->[0]};
  4340         6095  
153             }
154             }
155             }
156              
157 143         327 delete $_->[2] for @$expect;
158 143         305 $ok = Test2::Tools::Compare::like($result, $expect, $test);
159 143         91179 local $Data::Dumper::Sortkeys = 1;
160 143         141 local $Data::Dumper::Sparseseen = 1;
161 3     3   1506 use Data::Dumper;
  3         13920  
  3         6985  
162 143 50       361 print Dumper($result, $expect) unless $ok;
163              
164             }
165 11         46 $sctx->release;
166 11         105 });
167              
168 11         8378 $ctx->release;
169              
170 11 50       391 return $ok unless wantarray;
171 0           return ($ok, $results, $expects);
172             }
173              
174              
175              
176              
177             #line 1 "mask_test_hide.pl"
178             sub test_stack_hide { # line 1
179             my ($callback) = @_; # line 2
180             mask_frame(stop => 1, hide => 1); # line 3
181             hide_1($callback, 'a'); # line 4
182             } # line 5
183              
184             sub hide_1 { my $code = shift; @_ = (@_); hide_2($code, 'b') } # line 7
185             sub hide_2 { my $code = shift; @_ = (@_); hide_3($code, 'c') } # line 8
186             sub hide_3 { my $code = shift; @_ = (@_); mask_frame(hide => 2); hide_4($code, 'd') } # line 9
187             sub hide_4 { my $code = shift; @_ = (@_); hide_5($code, 'e') } # line 10
188             sub hide_5 { my $code = shift; @_ = (@_); $code->() } # line 11
189              
190              
191              
192              
193             #line 1 "mask_test_shift.pl"
194             sub test_stack_shift { # line 1
195             my ($callback) = @_; # line 2
196             mask_frame(stop => 1, hide => 1); # line 3
197             shift_1($callback, 'a'); # line 4
198             } # line 5
199              
200             sub shift_1 { my $code = shift; @_ = (@_); shift_2($code, 'b') } # line 7
201             sub shift_2 { my $code = shift; @_ = (@_); shift_3($code, 'c') } # line 8
202             sub shift_3 { my $code = shift; @_ = (@_); shift_4($code, 'd') } # line 9
203             sub shift_4 { my $code = shift; @_ = (@_); mask_frame(shift => 2); shift_5($code, 'e') } # line 10
204             sub shift_5 { my $code = shift; @_ = (@_); $code->() } # line 11
205              
206              
207              
208              
209             #line 1 "mask_test_stop.pl"
210             sub test_stack_stop { # line 1
211             my ($callback) = @_; # line 2
212             mask_frame(stop => 1, hide => 1); # line 3
213             stop_1($callback, 'a'); # line 4
214             } # line 5
215              
216             sub stop_1 { my $code = shift; @_ = (@_); stop_2($code, 'b') } # line 7
217             sub stop_2 { my $code = shift; @_ = (@_); mask_frame(stop => 1); stop_3($code, 'c') } # line 8
218             sub stop_3 { my $code = shift; @_ = (@_); stop_4($code, 'd') } # line 9
219             sub stop_4 { my $code = shift; @_ = (@_); stop_5($code, 'e') } # line 10
220             sub stop_5 { my $code = shift; @_ = (@_); $code->() } # line 11
221              
222              
223              
224              
225             #line 1 "mask_test_no_start.pl"
226             sub test_stack_no_start { # line 1
227             my ($callback) = @_; # line 2
228             mask_frame(stop => 1, hide => 1); # line 3
229             no_start_1($callback, 'a'); # line 4
230             } # line 5
231              
232             sub no_start_1 { my $code = shift; @_ = (@_); no_start_2($code, 'b') } # line 7
233             sub no_start_2 { my $code = shift; @_ = (@_); no_start_3($code, 'c') } # line 8
234             sub no_start_3 { my $code = shift; @_ = (@_); no_start_4($code, 'd') } # line 9
235             sub no_start_4 { my $code = shift; @_ = (@_); mask_frame(no_start => 1); no_start_5($code, 'e') } # line 10
236             sub no_start_5 { my $code = shift; @_ = (@_); mask_frame(no_start => 1); mask_call({no_start => 1}, $code) } # line 11
237              
238              
239              
240              
241             #line 1 "mask_test_alter.pl"
242             sub test_stack_alter { # line 1
243             my ($callback) = @_; # line 2
244             mask_frame(stop => 1, hide => 1); # line 3
245             alter_1($callback, 'a'); # line 4
246             } # line 5
247              
248             sub alter_1 { my $code = shift; @_ = (@_); alter_2($code, 'b') } # line 7
249             sub alter_2 { my $code = shift; @_ = (@_); alter_3($code, 'c') } # line 8
250             sub alter_3 { my $code = shift; @_ = (@_); alter_4($code, 'd') } # line 9
251             sub alter_4 { # line 10
252             my $code = shift; # line 11
253             @_ = (@_); # line 12
254             mask_frame( # line 13
255             0 => 'Foo::Bar', # line 14
256             1 => 'Foo/Bar.pm', # line 15
257             2 => '42', # line 16
258             3 => 'Foo::Bar::foobar', # line 17
259             999 => 'x' # line 18
260             ); # line 19
261             alter_5($code, 'e') # line 20
262             } # line 21
263             sub alter_5 { my $code = shift; @_ = (@_); $code->() } # line 22
264              
265              
266              
267              
268             #line 1 "mask_test_s_and_h.pl"
269             sub test_stack_shift_and_hide { # line 1
270             my ($callback) = @_; # line 2
271             mask_frame(stop => 1, hide => 1); # line 3
272             s_and_h_1($callback, 'a'); # line 4
273             } # line 5
274              
275             sub s_and_h_1 { my $code = shift; @_ = (@_); s_and_h_2($code, 'b') } # line 7
276             sub s_and_h_2 { my $code = shift; @_ = (@_); s_and_h_3($code, 'c') } # line 8
277             sub s_and_h_3 { my $code = shift; @_ = (@_); mask_frame(hide => 1); s_and_h_4($code, 'd') } # line 9
278             sub s_and_h_4 { my $code = shift; @_ = (@_); mask_frame(shift => 1); s_and_h_5($code, 'e') } # line 10
279             sub s_and_h_5 { my $code = shift; @_ = (@_); $code->() } # line 11
280              
281              
282              
283              
284             #line 1 "mask_test_shift_short.pl"
285             sub test_stack_shift_short { # line 1
286             my ($callback) = @_; # line 2
287             mask_frame(stop => 1, hide => 1); # line 3
288             shift_short_1($callback, 'a'); # line 4
289             } # line 5
290              
291             sub shift_short_1 { my $code = shift; @_ = (@_); shift_short_2($code, 'b') } # line 7
292             sub shift_short_2 { my $code = shift; @_ = (@_); shift_short_3($code, 'c') } # line 8
293             sub shift_short_3 { my $code = shift; @_ = (@_); shift_short_4($code, 'd') } # line 9
294             sub shift_short_4 { my $code = shift; @_ = (@_); mask_frame(shift => 5); shift_short_5($code, 'e') } # line 10
295             sub shift_short_5 { my $code = shift; @_ = (@_); $code->() } # line 11
296              
297              
298              
299              
300             #line 1 "mask_test_hide_short.pl"
301             sub test_stack_hide_short { # line 1
302             my ($callback) = @_; # line 2
303             mask_frame(stop => 1, hide => 1); # line 3
304             hide_short_1($callback, 'a'); # line 4
305             } # line 5
306              
307             sub hide_short_1 { my $code = shift; @_ = (@_); hide_short_2($code, 'b') } # line 7
308             sub hide_short_2 { my $code = shift; @_ = (@_); hide_short_3($code, 'c') } # line 8
309             sub hide_short_3 { my $code = shift; @_ = (@_); hide_short_4($code, 'd') } # line 9
310             sub hide_short_4 { my $code = shift; @_ = (@_); mask_frame(hide => 5); hide_short_5($code, 'e') } # line 10
311             sub hide_short_5 { my $code = shift; @_ = (@_); $code->() } # line 11
312              
313              
314              
315              
316             #line 1 "mask_test_s_and_a.pl"
317             sub test_stack_shift_and_alter { # line 1
318             my ($callback) = @_; # line 2
319             mask_frame(stop => 1, hide => 1); # line 3
320             s_and_a_1($callback, 'a'); # line 4
321             } # line 5
322              
323             sub s_and_a_1 { my $code = shift; @_ = (@_); s_and_a_2($code, 'b') } # line 7
324             sub s_and_a_2 { my $code = shift; @_ = (@_); s_and_a_3($code, 'c') } # line 8
325             sub s_and_a_3 { # line 9
326             my $code = shift; # line 10
327             @_ = (@_); # line 11
328             mask_frame(0 => 'x', 1 => 'x', 2 => '100', 3 => 'x', 4 => 'x'); # line 12
329             s_and_a_4($code, 'd'); # line 13
330             } # line 14
331             sub s_and_a_4 { # line 15
332             my $code = shift; # line 16
333             @_ = (@_); # line 17
334             mask_frame(0 => 'y', 1 => 'y', 2 => '200', 3 => 'y', 4 => 'y', shift => 1); # line 18
335             s_and_a_5($code, 'e'); # line 19
336             } # line 20
337             sub s_and_a_5 { my $code = shift; @_ = (@_); $code->() } # line 21
338              
339              
340              
341              
342             #line 1 "mask_test_full_combo.pl"
343             sub test_stack_full_combo { # line 1
344             my ($callback) = @_; # line 2
345             mask_frame(stop => 1, hide => 1); # line 3
346             full_combo_1($callback, 'a'); # line 4
347             } # line 5
348              
349             sub full_combo_1 { my $code = shift; @_ = (@_); full_combo_2($code, 'b') } # line 7
350             sub full_combo_2 { my $code = shift; @_ = (@_); mask_frame('pause' => 1, 'restart' => 1); full_combo_3($code, 'c') } # line 8
351             sub full_combo_3 { my $code = shift; @_ = (@_); full_combo_4($code, 'd') } # line 9
352             sub full_combo_4 { my $code = shift; @_ = (@_); mask_frame('pause' => 1); full_combo_5($code, 'e') } # line 10
353             sub full_combo_5 { my $code = shift; @_ = (@_); full_combo_6($code, 'f') } # line 11
354             sub full_combo_6 { my $code = shift; @_ = (@_); full_combo_7($code, 'g') } # line 12
355             sub full_combo_7 { my $code = shift; @_ = (@_); full_combo_8($code, 'h') } # line 13
356             sub full_combo_8 { my $code = shift; @_ = (@_); full_combo_9($code, 'i') } # line 14
357             sub full_combo_9 { my $code = shift; @_ = (@_); mask_frame(0 => 'xxx'); full_combo_10($code, 'j') } # line 15
358             sub full_combo_10 { my $code = shift; @_ = (@_); full_combo_11($code, 'k') } # line 16
359             sub full_combo_11 { my $code = shift; @_ = (@_); full_combo_12($code, 'l') } # line 17
360             sub full_combo_12 { my $code = shift; @_ = (@_); full_combo_13($code, 'm') } # line 18
361             sub full_combo_13 { my $code = shift; @_ = (@_); mask_frame(0 => 'foo', 5 => 'foo'); full_combo_14($code, 'n') } # line 19
362             sub full_combo_14 { my $code = shift; @_ = (@_); mask_frame(hide => 1); full_combo_15($code, 'o') } # line 20
363             sub full_combo_15 { my $code = shift; @_ = (@_); full_combo_16($code, 'p') } # line 21
364             sub full_combo_16 { my $code = shift; @_ = (@_); full_combo_17($code, 'q') } # line 22
365             sub full_combo_17 { my $code = shift; @_ = (@_); mask_frame(shift => 3, 0 => 'bar', 5 => 'bar'); full_combo_18($code, 'r') } # line 23
366             sub full_combo_18 { my $code = shift; @_ = (@_); full_combo_19($code, 's') } # line 24
367             sub full_combo_19 { my $code = shift; @_ = (@_); full_combo_20($code, 't') } # line 25
368             sub full_combo_20 { my $code = shift; @_ = (@_); mask_frame(no_start => 1); mask_call({no_start => 1}, $code) } # line 26
369              
370              
371              
372              
373             #line 1 "mask_test_restart.pl"
374             sub test_stack_restart { # line 1
375             my ($callback) = @_; # line 2
376             mask_frame(stop => 1, hide => 1); # line 3
377             restart_1($callback, 'a'); # line 4
378             } # line 5
379              
380             sub restart_1 { my $code = shift; @_ = (@_); restart_2($code, 'b') } # line 7
381             sub restart_2 { my $code = shift; @_ = (@_); mask_frame('restart' => 1); restart_3($code, 'c') } # line 8
382             sub restart_3 { my $code = shift; @_ = (@_); restart_4($code, 'd') } # line 9
383             sub restart_4 { my $code = shift; @_ = (@_); mask_frame('pause' => 1); restart_5($code, 'e') } # line 10
384             sub restart_5 { my $code = shift; @_ = (@_); restart_6($code, 'f') } # line 11
385             sub restart_6 { my $code = shift; @_ = (@_); mask_call({no_start => 1}, $code) } # line 12
386              
387              
388              
389              
390             #line 1 "mask_test_special.pl"
391             sub test_stack_special { # line 1
392             my ($callback) = @_; # line 2
393             mask_frame(stop => 1, hide => 1); # line 3
394             special_1($callback, 'a'); # line 4
395             } # line 5
396              
397             sub special_1 { my $code = shift; @_ = (@_); special_2($code, 'b') }
398             sub special_2 { my $code = shift; @_ = (@_); unimport($code, 'c') }
399             sub unimport { my $code = shift; @_ = (@_); mask_frame(hide => 1); special_3($code, 'd') }
400             sub special_3 { my $code = shift; @_ = (@_); special_4($code, 'e') }
401             sub special_4 { my $code = shift; @_ = (@_); special_5($code, 'f') }
402             sub special_5 { my $code = shift; @_ = (@_); mask_frame(pause => 1); special_6($code, 'g') }
403             sub special_6 { my $code = shift; @_ = (@_); $code->() }
404              
405              
406              
407              
408             #line 1 "mask_test_lock.pl"
409             sub test_stack_lock { # line 1
410             my ($callback) = @_; # line 2
411             mask_frame(stop => 1, hide => 1); # line 3
412             lock_1($callback, 'a'); # line 4
413             } # line 5
414              
415             sub lock_1 { my $code = shift; @_ = (@_); lock_2($code, 'b') }
416             sub lock_2 { my $code = shift; @_ = (@_); lock_x($code, 'c') }
417             sub lock_x { my $code = shift; @_ = (@_); mask_frame(hide => 1, lock => 1); lock_3($code, 'd') }
418             sub lock_3 { my $code = shift; @_ = (@_); lock_4($code, 'e') }
419             sub lock_4 { my $code = shift; @_ = (@_); lock_5($code, 'f') }
420             sub lock_5 { my $code = shift; @_ = (@_); mask_frame(pause => 1); lock_6($code, 'g') }
421             sub lock_6 { my $code = shift; @_ = (@_); $code->() }
422              
423              
424             1;
425              
426             =pod
427              
428             =head1 NAME
429              
430             Trace::Mask::Test - Tools for testing Trace::Mask compliance.
431              
432             =head1 DESCRIPTION
433              
434             This package provides tools for testing tracers. This allows you to check that
435             a tracer complies with the specifications.
436              
437             =head1 SYNOPSIS
438              
439             use Trace::Mask::Test qw/test_tracer/;
440              
441             test_tracer(
442             name => 'my tracer',
443             trace => \&trace,
444             type => 'return',
445             convert => sub {
446             my $stack = shift;
447             ...
448             return $stack;
449             },
450             );
451              
452             =head1 EXPORTS
453              
454             =over 4
455              
456             =item NA()
457              
458             Placeholder value for use in test_tracer to represent fields the tracer does
459             not provide.
460              
461             =item ($ok, $result, $expect) = test_tracer(trace => \&trace, convert => \&convert, name => "my test")
462              
463             =item ($ok, $result, $expect) = test_tracer(trace => \&trace, convert => \&convert, name => "my test", %tests)
464              
465             This will verify that a tracer follows the specification. This will run every
466             test in the test list below with both the specified tracer and the refewrence
467             tracer, it will then compare the results.
468              
469             In scalar context the sub returns a true or false indicating if the test passed
470             or failed. In List context the sub will return the boolen $ok value, the
471             arrayref produced from your stack, and the arrayref produced by the reference
472             tracer. This behavior gives you the ability to debug the final structures, and
473             manually compare them.
474              
475             =over 4
476              
477             =item name => "..."
478              
479             Specify a name for your test.
480              
481             =item trace => \&trace
482              
483             This should be your tracer, or a subroutine that calls it. This subroutine is
484             called in scalar context. This can return the trace in any form you want so
485             long as it is returned in a scalar.
486              
487             =item convert => \&convert
488              
489             This will be given the scalar your tracer returns as its only input argument.
490             This sub should convert the trace to a standard form for comparison.
491              
492             convert => sub {
493             my ($trace) = @_;
494             ...
495             return [
496             [[$package1, NA(), $line1, $subname1, ...], \@args]
497             [[$package2, $file2, $line2, $subname2, ...], \@args]
498             ]
499             },
500              
501             The standard return is an arrayref with an arrayref for each stack frame. Each
502             frame arrayref should itself contain 2 arrayrefs. The first arrayref should
503             contain the fields caller() would return for that level. The second arrayref
504             should contain arguments that the function was called with. You can use the ref
505             returned from C in place of any value that cannot be obtained from your
506             stack trace results. In addition it only checks values you have specified, if
507             you only list the first 4 fields from caller then only the first 4 are checked.
508              
509             =item type => 'return'
510              
511             The trace function will return a trace in a scalar, use that as our trace.
512              
513             =item type => 'exception'
514              
515             The trace function will throw an exception, intercept it and use the exception
516             as the trace.
517              
518             =item type => 'warning'
519              
520             The trace function will issue a warning, intecept it using a C<$SIG{__WARN__}>
521             override, then use the warning as our trace.
522              
523             B If your tracer issues more than 1 warning and exception will be
524             thrown.
525              
526             B Since this uses a C<$SIG{__WARN__}>, it cannot be used to check traces
527             that require a custom C<$SIG{__WARN__} override. See the 'sigwarn' type below
528             if you are testing a tool that rewrites warnings.
529              
530             =item type => 'sigwarn'
531              
532             The trace function will issue a warning, but a custom $SIG{__WARN__} needs to
533             modify it before we see it. This will NOT override C<$SIG{__WARN__}>, instead
534             it will intercept all output to STDERR when it calls your tracer.
535              
536             =item %tests
537              
538             If you do not specify any tests then all will be run. If you only want to run a
539             subset of tests then you can list them with a true value.
540              
541             test_tracer(
542             name => "foo",
543             type => 'return',
544             trace => \&trace,
545             convert => sub { ... },
546              
547             test_stack_hide => 1,
548             test_stack_shift => 1,
549             test_stack_stop => 1,
550             test_stack_no_start => 1,
551             test_stack_alter => 1,
552             test_stack_shift_and_hide => 1,
553             test_stack_shift_short => 1,
554             test_stack_hide_short => 1,
555             test_stack_shift_and_alter => 1,
556             test_stack_full_combo => 1,
557             );
558              
559             =back
560              
561             =back
562              
563             =head2 OPTIONAL EXPORTS / TESTS
564              
565             =over 4
566              
567             =item test_stack_hide(\&callback)
568              
569             =item test_stack_shift(\&callback)
570              
571             =item test_stack_stop(\&callback)
572              
573             =item test_stack_no_start(\&callback)
574              
575             =item test_stack_alter(\&callback)
576              
577             =item test_stack_shift_and_hide(\&callback)
578              
579             =item test_stack_shift_short(\&callback)
580              
581             =item test_stack_hide_short(\&callback)
582              
583             =item test_stack_shift_and_alter(\&callback)
584              
585             =item test_stack_full_combo(\&callback)
586              
587             =back
588              
589             =head1 SOURCE
590              
591             The source code repository for Trace-Mask can be found at
592             F.
593              
594             =head1 MAINTAINERS
595              
596             =over 4
597              
598             =item Chad Granum Eexodist@cpan.orgE
599              
600             =back
601              
602             =head1 AUTHORS
603              
604             =over 4
605              
606             =item Chad Granum Eexodist@cpan.orgE
607              
608             =back
609              
610             =head1 COPYRIGHT
611              
612             Copyright 2015 Chad Granum Eexodist7@gmail.comE.
613              
614             This program is free software; you can redistribute it and/or
615             modify it under the same terms as Perl itself.
616              
617             See F
618              
619             =cut