File Coverage

blib/lib/Object/Exercise/Execute.pm
Criterion Covered Total %
statement 21 21 100.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 28 28 100.0


line stmt bran cond sub pod time code
1             # $Id: Exercise.pm 47 2007-06-04 15:22:42Z lembark $
2             #######################################################################
3             # housekeeping
4             #######################################################################
5              
6             package Object::Exercise::Execute;
7              
8             require 5.6.2;
9              
10 4     4   22 use strict;
  4         8  
  4         171  
11 4     4   4235 use Test::More;
  4         79684  
  4         52  
12 4     4   6374 use Test::Deep qw( cmp_deeply );
  4         53387  
  4         569  
13              
14 4     4   40 use Object::Exercise::Common qw( log_message continue verbose );
  4         11  
  4         41  
15              
16             ########################################################################
17             # package variables
18             ########################################################################
19              
20             our $VERSION = 1.00;
21              
22             # use to control breakpoints within the loop.
23             # our necessary to permit use of local.
24              
25             our $debug = '';
26              
27             # handle iterations: verbose controls reporting,
28             # continue ignores errors in the eval of a command.
29              
30             my $noplan = '';
31              
32             # dispatch table for loop commands.
33             # these are non-ref elements in the work queue.
34              
35             my %parmz =
36             (
37             # print anything unknown.
38              
39             '' => sub { print STDERR $_ },
40              
41             # otherwise set the appropriate variable.
42              
43             debug => sub { $debug = 1 },
44             nodebug => sub { $debug = 0 },
45              
46             continue => sub { $continue = 1 },
47             nocontinue => sub { $continue = 0 },
48              
49             verbose => sub { $verbose = 1 },
50             noverbose => sub { $verbose = 0 },
51             quiet => sub { $verbose = 0 },
52             );
53              
54             for
55             (
56             [ qw( quiet noverbose ) ],
57             [ qw( break debug ) ],
58             [ qw( nobreak nodebug ) ],
59             )
60             {
61             my( $alias, $existing ) = @$_;
62              
63             $parmz{ $alias } = $parmz{ $existing }
64             and next;
65              
66             die "Invalid alias '$alias' for unknown '$existing'"
67             }
68              
69             ########################################################################
70             # local utility subs
71             ########################################################################
72              
73             my $handle_error
74             = sub
75             {
76             my $cmd = pop;
77              
78             $log_message->( @_ );
79              
80             local $debug = 1;
81              
82             $DB::single = 1;
83              
84             # at this point &$cmd can be re-executed
85             # with its own breakpoint set via $debug.
86              
87             0
88             };
89              
90             # generate a closure from a command, method, and args.
91              
92             my $gen_command
93             = sub
94             {
95             my( $obj, $argz ) = @_;
96              
97             my $method = shift @$argz;
98              
99             sub
100             {
101             $DB::single = 1 if $debug;
102              
103             $obj->$method( @$argz )
104             }
105             };
106              
107             ########################################################################
108             # handle one element of the execution list.
109             ########################################################################
110              
111             my %ref_handlerz =
112             (
113             ARRAY =>
114             sub
115             {
116 4     4   28 use Scalar::Util qw( reftype );
  4         8  
  4         570  
117              
118             # this is the most common place to end up: dealing with
119             # an action + test or just an action.
120             #
121             # determine if this is a test (two arrayrefs)
122             # or just a command (one arrayref).
123             # append a message to the test if it isn't
124             # already three items long.
125              
126             my( $obj, $element ) = @_;
127              
128             my $argz = '';
129             my $expect = '';
130             my $method = '';
131             my $message = '';
132             my $compare = '';
133              
134             if
135             (
136             1 <= @$element
137             &&
138             'ARRAY' eq reftype $element->[0]
139             )
140             {
141 4     4   27 no warnings;
  4         8  
  4         280  
142              
143             ( $argz, $expect, $message ) = @$element;
144              
145             $compare = 1;
146              
147             $message ||= join ' ', @$argz, '->', @$expect;
148             }
149             else
150             {
151 4     4   24 no warnings;
  4         8  
  4         3353  
152              
153             @$argz = @$element;
154              
155             $message = join ' ', @$argz;
156             }
157              
158             my $cmd = $gen_command->( $obj, $argz );
159              
160             my $result = eval { [ &$cmd ] };
161              
162             if( $@ )
163             {
164             if( $continue || $expect eq '' )
165             {
166             pass "Expected failure: $message" unless $noplan;
167             }
168             else
169             {
170             fail "Unexpected failure: $message" unless $noplan;
171              
172             $handle_error->( "Failed execute: $message", $cmd );
173             }
174             }
175             elsif( $compare )
176             {
177             cmp_deeply $result, $expect, $message
178             and return;
179              
180             fail "Failed compare: $message" unless $noplan;
181              
182             $handle_error->
183             (
184             "Failed compare: $message",
185             'Found:', $result,
186             'Expect:', $expect,
187             $cmd
188             );
189             }
190             elsif( $verbose )
191             {
192             $log_message->( "Successful: $message" );
193             }
194             },
195              
196             CODE =>
197             sub
198             {
199             # re-dispatch the thing with the object first
200             # on the stack.
201              
202             my $action = splice @_, 1, 1;
203              
204             eval { &$action };
205              
206             $@ or return;
207              
208             if( $continue )
209             {
210             $log_message->( "Failure: $@", $action )
211             if $verbose;
212             }
213             else
214             {
215             $handle_error->( "Failure: $@", sub { &$action } );
216             }
217             },
218             );
219              
220             ########################################################################
221             # exported to caller
222              
223             sub
224             {
225              
226             # no reason to look this up in the symbol table every
227             # time, it won't change.
228              
229             my $obj = shift;
230              
231             my $count = 0;
232              
233             unless( $noplan )
234             {
235             $count
236             = grep
237             {
238             (ref $_) # ignore breaks
239             &&
240             (ref $_ eq q{ARRAY}) # check for array
241             &&
242             (ref $_->[0]) # test in initial location
243             }
244             @_;
245              
246             if( $count )
247             {
248             plan tests => $count;
249              
250             $log_message->( "Executing: $count tests" )
251             if $verbose;
252             }
253             else
254             {
255             plan tests => 1;
256             }
257             }
258              
259             TEST:
260             for( @_ )
261             {
262             # If the next item is not a reference at all --
263             # e.g., if it's a string such as 'break' --
264             # set $debug to true value and try the next test.
265              
266             if( my $type = reftype $_ )
267             {
268             my $handler = $ref_handlerz{ $type }
269             or die "Unable to handle item of type '$type'";
270              
271             $obj->$handler( $_ );
272             }
273             elsif( 0 < ( my $i = index $_, '=' ) )
274             {
275             my $key = substr $_, 0, $i;
276             my $val = substr $_, ++$i;
277              
278             $obj->{ $key } = $val;
279             }
280             elsif( my $handler = $parmz{ $_ } )
281             {
282             &$handler
283             }
284             else
285             {
286             # display the message and keep going.
287              
288             $log_message->( $_ );
289             }
290             }
291              
292             if( $noplan )
293             {
294             $log_message->( "Execution complete" )
295             if $verbose;
296             }
297             else
298             {
299             $count or pass "Execution complete";
300             }
301             }
302              
303             __END__