File Coverage

blib/lib/Test/Mimic/Library.pm
Criterion Covered Total %
statement 129 290 44.4
branch 22 114 19.3
condition 17 30 56.6
subroutine 32 42 76.1
pod 21 22 95.4
total 221 498 44.3


line stmt bran cond sub pod time code
1             package Test::Mimic::Library;
2              
3 1     1   74337 use 5.006001; # for my $filehandle
  1         5  
  1         124  
4 1     1   7 use strict;
  1         1  
  1         37  
5 1     1   6 use warnings;
  1         6  
  1         155  
6              
7             our $VERSION = 0.012_006;
8              
9 1     1   992 use Test::Mimic::Library::MonitorScalar;
  1         3  
  1         159  
10 1     1   855 use Test::Mimic::Library::MonitorArray;
  1         3  
  1         34  
11 1     1   1879 use Test::Mimic::Library::MonitorHash;
  1         3  
  1         34  
12 1     1   1126 use Test::Mimic::Library::PlayScalar;
  1         3  
  1         31  
13 1     1   1337 use Test::Mimic::Library::PlayArray;
  1         2  
  1         23  
14 1     1   1570 use Test::Mimic::Library::PlayHash;
  1         3  
  1         36  
15 1     1   741 use Test::Mimic::Library::MonitorTiedScalar;
  1         3  
  1         27  
16 1     1   738 use Test::Mimic::Library::MonitorTiedArray;
  1         2  
  1         25  
17 1     1   743 use Test::Mimic::Library::MonitorTiedHash;
  1         3  
  1         30  
18              
19 1     1   7 use Scalar::Util qw;
  1         1  
  1         728  
20              
21             #use Data::Dump::Streamer if possible, otherwise Data::Dumper and ad hoc replacements.
22             BEGIN {
23 1 50   1   3 if ( eval { require Data::Dump::Streamer; 1 } ) {
  1         463  
  0         0  
24 0         0 Data::Dump::Streamer->import( qw<:undump Dump regex> );
25              
26             # Accepts a single argument. Returns true iff the argument is a regular expression created by qr.
27 0         0 *_is_pattern = sub { return scalar regex( $_[0] ); };
  0         0  
28              
29             # Accepts a single argument. Returns a string form of this argument that can be inverted
30             # (approximately) with _default_destringifier.
31             *_default_stringifier = sub {
32 0         0 return scalar Dump( $_[0] )->Names('TML_destringify_val')->KeyOrder('', 'lexical')->Out();
33 0         0 };
34             # The horrible name is my attempt to avoid collisions with variables from closures. Sadly, DDS doesn't
35             # allow package scoped names.
36              
37             # Accepts a string returned by _default_stringifier. Returns an approximation to the original value.
38             *_default_destringifier = sub {
39 0         0 my $TML_destringify_val;
40 0 0       0 eval( $_[0] . "; 1" )
41             or die "Unable to eval the string: $_[0]\nwith error: $@";
42 0         0 return $TML_destringify_val;
43 0         0 };
44             }
45             else {
46 1         1734 require Data::Dumper;
47 1         12148 Data::Dumper->import();
48              
49             # Accepts a single argument. Returns true if the argument is a regular expression created by qr that
50             # is not blessed. If it is blessed returns true iff the argument was blessed into the Regexp class.
51             # Returns false in all other cases. In other words, this gives false positives for non qr refs
52             # blessed into Regexp and false negatives for qr refs blessed into any other package.
53            
54             # NOTE: This is a major problem if we need to store qr refs blessed into other packages. We will
55             # attempt to dereference the qr object and tie the result. This will cause our code to die. False
56             # positives will merely cause incomplete recording and punt the responsibility of preserving the
57             # value to the stringifier.
58             *_is_pattern = sub {
59 17     17   34 my $type = ref( $_[0] );
60 17 50       32 if ( defined($type) ) {
61 17         41 my $class = blessed( $_[0] );
62 17 100       27 if ( defined($class) ) {
63 1         7 return $class eq 'Regexp';
64             }
65             else {
66 16         58 return $type eq 'Regexp';
67             }
68             }
69             else {
70 0         0 return ();
71             }
72 1         9 };
73              
74             # Accepts a single argument. Returns a string form of this argument that can be inverted
75             # (approximately) with _default_destringifier.
76 1     6   4 *_default_stringifier = sub { return scalar Dumper( $_[0] ); };
  6         24  
77              
78             # Accepts a string returned by _default_stringifier. Returns an approximation to the original value.
79             *_default_destringifier = sub {
80 5     5   5 my $VAR1;
81 5 50       366 eval( $_[0] . "; 1" )
82             or die "Unable to eval the string: $_[0]\nwith error: $@";
83 5         110 return $VAR1;
84 1         160 };
85             }
86             }
87              
88             require Exporter;
89              
90             our @ISA = qw;
91              
92             our %EXPORT_TAGS = (
93             'constants' => [ qw(
94             SCALAR_CONTEXT
95             LIST_CONTEXT
96             VOID_CONTEXT
97             STABLE
98             VOLATILE
99             NESTED
100             RETURN
101             EXCEPTION
102             ARBITRARY
103             CODE_E
104             SCALAR_E
105             ARRAY_E
106             HASH_E
107             ENCODE_TYPE
108             DATA
109             DATA_TYPE
110             HISTORY
111             CLASS
112             ) ],
113             );
114              
115             our @EXPORT_OK = (
116             qw<
117             encode
118             decode
119             monitor
120             play
121             monitor_args
122             monitor_args_by
123             play_args
124             play_args_by
125             gen_arg_key
126             gen_arg_key_by
127             stringify
128             stringify_by
129             destringify
130             destringify_by
131             init_records
132             load_records
133             write_records
134             get_references
135             execute
136             descend
137             load_preferences
138             >,
139             @{ $EXPORT_TAGS{'constants'} },
140             );
141              
142             our @EXPORT = qw(
143            
144             );
145              
146              
147             use constant {
148             # Array indices for the three contexts
149 1         5640 SCALAR_CONTEXT => 0,
150             LIST_CONTEXT => 1,
151             VOID_CONTEXT => 2,
152              
153             # Description of encoded data
154             STABLE => 200,
155             VOLATILE => 201,
156             NESTED => 202,
157              
158             # The two types of supported behavior
159             RETURN => 300,
160             EXCEPTION => 301,
161              
162             # Convenience values
163             ARBITRARY => 400, # For merely creating hash entries
164              
165             # Event types. Should we deprecate this?
166             CODE_E => 500,
167             SCALAR_E => 501,
168             ARRAY_E => 502,
169             HASH_E => 503,
170              
171             # Encoded data fields, i.e. indices.
172             ENCODE_TYPE => 0,
173             DATA => 1,
174              
175             # Reference table item fields, i.e. indices.
176             DATA_TYPE => 0,
177             HISTORY => 1,
178             CLASS => 2,
179              
180 1     1   8 };
  1         2  
181              
182             my $references; # A table containing recorded data for volatile references and objects. The index of a
183             # given reference is simply the number of references
184             # monitor saw before the reference under
185             # consideration.
186             my $address_to_index; # A hash ref mapping the address of a reference to its index in $references.
187             my $is_alive; # A hash ref mapping the address of a reference to its current alive state. This will
188             # be defined if the value stored at $address_to_index is current, undefined
189             # otherwise.
190             my $index_to_reference; # Almost, but not quite, the inverse of $address_to_index. Rather than mapping to the
191             # address of the reference it maps to the reference itself.
192              
193             # Preloaded methods go here.
194              
195             sub init_records {
196 1     1 1 8 $references = [];
197 1         3 $address_to_index = {};
198 1         3 $is_alive = {};
199 1         3 $index_to_reference = {};
200             }
201              
202             sub load_records {
203 0     0 1 0 my ($file_name) = @_;
204              
205 0         0 init_records();
206            
207 0 0       0 open( my $fh, '<', $file_name ) or die "Could not open file: $!";
208              
209 0         0 my $recorded_data;
210             {
211 0         0 local $/;
  0         0  
212 0         0 undef $/;
213 0         0 $recorded_data = <$fh>;
214             }
215 0         0 $references = destringify($recorded_data);
216              
217 0 0       0 close($fh) or die "Could not close file: $!";
218             }
219              
220             sub get_references {
221 1     1 1 58 return $references;
222             }
223              
224             sub write_records {
225 0     0 1 0 my ($file_name) = @_;
226              
227 0 0       0 open( my $fh, '>', $file_name ) or die "Could not open file: $!";
228 0         0 print $fh stringify($references);
229 0 0       0 close($fh) or die "Could not close file: $!";
230             }
231              
232             sub load_preferences {
233 0     0 0 0 my ($preferences) = @_;
234              
235 0 0       0 if ( defined( $preferences->{'string'} ) ) {
236 0         0 stringify_by( $preferences->{'string' } );
237             }
238 0 0       0 if ( defined( $preferences->{'destring'} ) ) {
239 0         0 destringify_by( $preferences->{'destring'} );
240             }
241 0         0 gen_arg_key_by($preferences);
242 0         0 monitor_args_by($preferences);
243 0         0 play_args_by($preferences);
244             }
245              
246             # Changes the current working directory to $dir. If $dir does not exist then it will be created.
247             # If it exists, but it is not a directory or any other error occurs descend will die.
248             sub descend {
249 0     0 1 0 my ($dir) = @_;
250              
251             # Move to the $dir directory, creating if needed.
252 0 0       0 if ( -e $dir ) {
253 0 0       0 if ( ! ( -d $dir ) ) {
254 0         0 die "$dir exists, but it is not a directory.";
255             }
256             }
257             else {
258 0 0       0 mkdir( $dir ) or die "Could not create directory: $!";
259             }
260 0 0       0 chdir($dir) or die "Could not change the current working directory: $!";
261             }
262              
263             sub execute {
264 0     0 1 0 my ( $package, $subroutine, $behavior, $args ) = @_;
265              
266             # Find proper behavior for these arguments.
267 0         0 my $key = gen_arg_key( $package, $subroutine, $args );
268              
269 0 0       0 if ( ! exists( $behavior->{$key} ) ) {
270 0         0 die "No call recorded with corresponding arguments. Package: $package, Subroutine: $subroutine, Key: $key";
271             }
272 0         0 my $context_to_result = $behavior->{$key};
273              
274             # Find proper behavior for this context.
275 0         0 my $index;
276 0 0       0 if (wantarray) {
    0          
277 0         0 $index = LIST_CONTEXT;
278             }
279             elsif ( defined wantarray ) {
280 0         0 $index = SCALAR_CONTEXT;
281             }
282             else {
283 0         0 $index = VOID_CONTEXT;
284             }
285 0         0 my $results = $context_to_result->[$index];
286 0 0       0 if ( ! defined( $results ) ) {
287 0         0 die "No call recorded in context $index. Package: $package, Subroutine: $subroutine, Key: $key";
288             }
289              
290             # Obtain the results for this call.
291 0 0       0 if ( @{$results} == 0 ) {
  0         0  
292 0         0 die "Call history exhausted. Package: $package, Subroutine: $subroutine, Key: $key";
293             }
294              
295 0         0 my ( $arg_signature, $stored_result ) = splice( @{$results}, 0, 2 );
  0         0  
296              
297             # Tie arguments making them behave as they were recorded behaving.
298 0         0 play_args( $package, $subroutine, $args, $arg_signature );
299            
300             # Perform appropriately
301 0         0 my ( $result_type, $result ) = @{$stored_result};
  0         0  
302 0 0       0 if ( $result_type == EXCEPTION ) {
    0          
303 0         0 die decode( $result );
304             }
305             elsif ( $result_type == RETURN ) {
306 0 0       0 if (wantarray) {
    0          
307 0         0 return @{ decode($result) };
  0         0  
308             }
309             elsif ( defined wantarray ) {
310 0         0 return decode($result);
311             }
312             else {
313 0         0 return;
314             }
315             }
316             else {
317 0         0 die "Bad result type <$result_type>. Package: $package, Subroutine: $subroutine, Key: $key";
318             }
319             }
320              
321             {
322             my $key_gens = {};
323              
324             # The best way to think of the key generator is as a hint to the mimic system. A constant map to
325             # 'the key' would work provided that all calls to a given subroutine occur in order. If a smarter
326             # map is used then the mimic system will be more flexible. Call order only must be preserved in each set
327             # of calls generated by the inverse map of each distinct key. Of course, if one call produces data that
328             # another requires it doesn't really make sense to change the order (in either the playback _or_ record
329             # stages).
330             #
331             # NOTE: The passed subroutine should probably not use the stored reference information. This is because
332             # out of order calls could then break. Consider subroutines foo and bar. Both take a hash
333             # reference. Suppose that in the recording stage foo is called first, bar second and that the same
334             # reference is passed both times. If the reference is created by the user, i.e. not returned from a
335             # mimicked subroutine or otherwise seen by the recorder, then foo will end up naming the reference.
336             # foo's key generator will not be able to include the reference name and will perhaps instead perform
337             # a straightforward stringification of the hash. bar's key generator on the other hand will be able to
338             # use the fact that we are monitoring the reference and may instead create a key like '[ VOLATILE, 47 ]'.
339             # Now suppose that in the playback stage the call order is reversed. The hash reference isn't named until
340             # the call to foo, so there is no way bar can recognize it.
341             #
342             # NOTE: Or maybe SCRATCH ALL OF THAT. The above problem sucks, but the alternative is worse. Suppose we
343             # do a _light_encode and then a stringification. If we played the object into existence then it is tied.
344             # If it is tied and we examine it we will consume it's output. Even we added logic to halt the
345             # consumption we don't have access to the most recent state of the object. Similarly, in the record phase
346             # we don't know what the next access will be when gen_arg_key is called, so we can't approximate state
347             # by considering the history information. We could allow gen_arg_key to cause history to build up like
348             # it was a user call, but then we are enforcing call order on the set of subroutines that share
349             # arguments. This is definitely a lesser of, err... 4 or 5, evils situation.
350             #
351             # NOTE: Additionally, you should avoid any calls to monitor, monitor_args or encode. These have the side
352             # effect of naming passed values which will break the built in monitor_args/play_args paradigm.
353             sub gen_arg_key_by {
354 1     1 1 1415 $key_gens = $_[0];
355             }
356              
357             sub gen_arg_key {
358 4     4 1 1263 my ( $package, $subroutine, $args ) = @_;
359 4         7 local $Test::Mimic::Recorder::SuspendRecording = 1;
360            
361 4         6 my $key_gen;
362 4 100 100     47 if ( defined( $key_gen = $key_gens->{'packages'}->{$package}->{'subs'}->{$subroutine}->{'key'} )
      100        
363             || defined( $key_gen = $key_gens->{'packages'}->{$package}->{'key'} )
364             || defined( $key_gen = $key_gens->{'key'} ) ) {
365              
366 3         6 return &{$key_gen}($args);
  3         11  
367             }
368             else {
369 1         5 return stringify( _light_encode( $args, 2 ) );
370             }
371             }
372             }
373              
374             {
375             # Each of these helper subroutines takes ( $val, $at_level, $type ).
376             my $scalar_action = sub { return [ 'SCALAR', _light_encode( ${ $_[0] }, $_[1] ) ]; };
377             my $simple_action = sub { return [ $_[2] ]; };
378             my %type_to_action = (
379             'REG_EXP' => $simple_action,
380             'SCALAR' => $scalar_action,
381             'REF' => $scalar_action,
382             'LVALUE' => $scalar_action,
383             'VSTRING' => $scalar_action,
384             'ARRAY' => sub {
385             my @temp = map( { _light_encode( $_, $_[1] ) } @{ $_[0] } );
386             return [ 'ARRAY', \@temp ];
387             },
388             'HASH' => sub {
389             my %temp;
390             @temp{ keys %{ $_[0] } } = map( { _light_encode( $_[0]->{$_}, $_[1] ) } keys %{ $_[0] } );
391             return [ 'HASH', \%temp];
392             },
393             'GLOB' => $simple_action,
394             'IO' => $simple_action,
395             'FORMAT' => $simple_action,
396             'CODE' => $simple_action,
397             );
398              
399             # RESULTS NOT SUITABLE FOR DECODE!
400             sub _light_encode {
401 23     23   767 my ( $val, $at_level ) = @_;
402              
403 23         57 my $type = reftype($val);
404 23 100       59 if ( ! $type ) { # If the value is not a reference...
    50          
405 10         41 return [ STABLE, $val ];
406             }
407             elsif ( exists( $type_to_action{$type} ) ) {
408 13         29 my $address = refaddr($val);
409 13 50       31 if ( defined( $is_alive->{$address} ) ) {
410 0         0 return [ VOLATILE, $address_to_index->{$address} ];
411             }
412              
413 13 50       27 if ( _is_pattern($val) ) { # reftype doesn't recognize patterns, so set $type manually.
414 0         0 $type = 'REG_EXP';
415             }
416            
417 13 100       46 if ( $at_level == 0 ) { # If we have reached the deepest requested layer...
418 2         13 return [ NESTED, [ $type ] ];
419             }
420             else {
421 11         14 $at_level--;
422             }
423            
424 11         13 my $coded = &{ $type_to_action{$type} }( $val, $at_level, $type );
  11         35  
425 11         47 return [ NESTED, $coded ];
426             }
427             else {
428 0         0 die "Unknown reference type <$type> from <$val>. Unable to encode.";
429             }
430             }
431             }
432              
433             # So you want to build your own key generator? That's great. One rule: Never ever ever view the state of the
434             # arguments you are mapping into keys. That won't be a problem will it? Didn't think so. Those of you
435             # nonconformists that think state is important can use get_id. For each component of a passed value, i.e.
436             # a single alias in the list, an array element of a dereferenced alias, an element of the array element
437             # dereferenced as a hash etc., that you wish to examine _at all_ you must first call get_id on the component.
438             # If it returns undef you can look at it, but if it is an aggregate you need to use get_id on it's components
439             # as well. If undef is not returned, then you will be given an index corresponding to the reference. It is
440             # guaranteed to be unique over the execution of the program and stable between the record and playback
441             # phases. This is due to the fact that what you think are real variables in the playback phase are really
442             # tied variables. They don't have any state and if you try to look at them you will just consume their fake
443             # state. This will cause everything to crash and burn. In conclusion, use get_id. In the future we may store
444             # state in the tied variables and allow you to look at them. Keep your fingers crossed.
445             sub get_id {
446 0     0 1 0 my ($val) = @_;
447              
448 0         0 my $address = refaddr($val);
449 0 0       0 if ( defined( $is_alive->{$address} ) ) {
450 0         0 return $address_to_index->{$address};
451             }
452             else {
453 0         0 return undef;
454             }
455             }
456              
457             {
458             my $stringifier = \&_default_stringifier;
459              
460             sub stringify_by {
461 1     1 1 861 $stringifier = $_[0];
462             }
463              
464             # Given an encoded element returns a string version. Should be suitable for use as a key in a hash as well as
465             # being invertible with destringify.
466             sub stringify {
467 7     7 1 3105 return &{$stringifier};
  7         98  
468             }
469             }
470              
471             {
472             my $destringifier = \&_default_destringifier;
473              
474             sub destringify_by {
475 1     1 1 584 $destringifier = $_[0];
476             }
477              
478             sub destringify {
479 6     6 1 395 return &{$destringifier};
  6         25  
480             }
481             }
482              
483             {
484             my $monitors = {};
485            
486             sub monitor_args_by {
487 1     1 1 4 $monitors = $_[0];
488             }
489              
490             # aliases act like references, but look like simple scalars. Because of this we have to be particularly
491             # cautious where they could appear. Barring XS code and the sub{\@_} construction we only need to worry
492             # about subroutine arguments, i.e. $_[i].
493             #
494             # Accepts a reference to an array of aliases,
495             # e.g. @_ from another subroutine. It will monitor each alias that is not read-only and return a tuple
496             # consisting of the total number of aliases from the array reference as well as a hash reference that takes
497             # an index of a mutable element in the array to the result of monitor being called on a reference to said
498             # element.
499             sub monitor_args {
500 4     4 1 1880 my ( $package, $subroutine, $aliases ) = @_;
501              
502 4         5 my $arg_monitor;
503 4 100 100     57 if ( defined( $arg_monitor = $monitors->{'packages'}->{$package}->{'subs'}->{$subroutine}->{'monitor_args'} )
      100        
504             || defined( $arg_monitor = $monitors->{'packages'}->{$package}->{'monitor_args'} )
505             || defined( $arg_monitor = $monitors->{'monitor_args'} ) ) {
506              
507 3         5 return &{$arg_monitor}($aliases);
  3         8  
508             }
509             else {
510 1         3 my $num_aliases = @{$aliases};
  1         2  
511 1         3 my %mutable;
512 1         8 for ( my $i = 0; $i < $num_aliases; $i++ ) {
513 2 50       28 if ( ! readonly( $aliases->[$i] ) ) {
514 2         8 $mutable{$i} = monitor( \$aliases->[$i] );
515             }
516             }
517 1         11 return [ $num_aliases, \%mutable ];
518             }
519             }
520             }
521              
522             {
523             my $players = {};
524              
525             sub play_args_by {
526 1     1 1 502 $players = $_[0];
527             }
528              
529             # Accepts an array of aliases and the tuple returned by monitor_args.
530             # Attempts to match the aliases in the array reference with those in the tuple. If everything matches the
531             # mutable passed aliases will be tied to behave as those monitored earlier, otherwise dies. The array and
532             # the tuple representing the original array are said to match if the total number of elements are the same
533             # and the mutable elements are the same, i.e. appear at the same indices.
534             sub play_args {
535 3     3 1 1365 my ( $package, $subroutine, $aliases, $coded_aliases ) = @_;
536              
537 3         5 my $arg_player;
538 3 50 100     35 if ( defined( $arg_player = $players->{'packages'}->{$package}->{'subs'}->{$subroutine}->{'play_args'} )
      66        
539             || defined( $arg_player = $players->{'packages'}->{$package}->{'play_args'} )
540             || defined( $arg_player = $players->{'play_args'} ) ) {
541              
542 3         3 &{$arg_player}( $aliases, $coded_aliases );
  3         9  
543             }
544             else {
545 0         0 my ( $orig_num_aliases, $mutable ) = @{$coded_aliases};
  0         0  
546              
547             # Apply a primitive signature check, list length.
548 0         0 my $cur_num_aliases = @{$aliases};
  0         0  
549 0 0       0 if ( $orig_num_aliases != $cur_num_aliases ) {
550 0         0 die "Signatures do not match. Unable to play_args from <$coded_aliases> onto <$aliases>.";
551             }
552              
553             # Consider each alias, tie the mutable aliases if everything matches, else die.
554 0         0 for ( my $i = 0; $i < $cur_num_aliases; $i++ ) {
555 0         0 my $cur_read_only = readonly( $aliases->[$i] );
556 0         0 my $orig_read_only = ! exists( $mutable->{$i} );
557              
558 0 0 0     0 if ( $cur_read_only && $orig_read_only ) { # If they are both read-only they match.
    0 0        
559 0         0 next; # We shouldn't try to tie a read-only variable. :)
560             }
561             elsif ( ! $cur_read_only && ! $orig_read_only ) { # If they are both mutable...
562 0         0 my $index = $mutable->{$i}->[DATA]; # See monitor.
563              
564 0 0       0 if ( defined( $index_to_reference->{$index} ) ) { # If we have already seen this value.
565 0         0 next;
566             }
567              
568             #TODO: Assuming we maintain address_to_index and is_alive during playback too we can
569             # check to see if $address_to_index{ refaddr( $index_to_reference{$index} ) } == $index.
570             # If it doesn't we know that there is a problem. <---- that like something Or.
571              
572 0         0 my ( $type, $history, $old_class ) = @{ $references->[$index] };
  0         0  
573 0         0 tie( $aliases->[$i], 'Test::Mimic::Library::PlayScalar', $history );
574 0         0 $index_to_reference->{$index} = \( $aliases->[$i] );
575 0         0 weaken( $index_to_reference->{$index} ); # Don't prevent the val from being gced.
576              
577             #NOTE: We need not bless the alias here. Either we produced it earlier, blessed it then and hit
578             # next above or the alias was produced externally and if blessed at all was blessed
579             # elsewhere.
580              
581 0         0 my $address = refaddr( \( $aliases->[$i] ) );
582 0         0 $address_to_index->{$address} = $index;
583 0         0 $is_alive->{$address} = \( $aliases->[$i] );
584 0         0 weaken( $is_alive->{$address} );
585              
586             }
587             else {
588 0         0 die "Mutable/immutable mismatch. Unable to play_args from <$coded_aliases> onto "
589             . "<$aliases>.";
590             }
591             }
592             }
593             }
594             }
595              
596             sub _get_type {
597 0     0   0 my ($val) = @_;
598              
599 0 0       0 if ( _is_pattern($val) ) {
600 0         0 return 'REG_EXP';
601             }
602             else {
603 0         0 my $type = reftype($val);
604 0 0 0     0 if ( $type eq 'REF' || $type eq 'LVALUE' || $type eq 'VSTRING' ) {
      0        
605 0         0 return 'SCALAR';
606             }
607             else {
608 0         0 return $type;
609             }
610             }
611             }
612              
613             {
614             # Each of these helper subroutines takes ( $val, $type ).
615             my $scalar_action = sub {
616             my $history = [];
617             if ( defined( my $old_tie = tied( ${ $_[0] } ) ) ) {
618             tie( ${ $_[0] }, 'Test::Mimic::Library::MonitorTiedScalar', $history, $old_tie );
619             }
620             else {
621             tie( ${ $_[0] }, 'Test::Mimic::Library::MonitorScalar', $history, $_[0] );
622             }
623             return [ 'SCALAR', $history ];
624             };
625             my $simple_action = sub { return [ $_[1], $_[0] ]; };
626             my %type_to_action = (
627             'REG_EXP' => $simple_action,
628             'SCALAR' => $scalar_action,
629             'REF' => $scalar_action,
630             'LVALUE' => $scalar_action,
631             'VSTRING' => $scalar_action,
632             'ARRAY' => sub {
633             my $history = [];
634             if ( defined( my $old_tie = tied( @{ $_[0] } ) ) ) {
635             tie( @{ $_[0] }, 'Test::Mimic::Library::MonitorTiedArray', $history, $old_tie );
636             }
637             else {
638             tie ( @{ $_[0] }, 'Test::Mimic::Library::MonitorArray', $history, $_[0] );
639             }
640             return [ 'ARRAY', $history ];
641             },
642             'HASH' => sub {
643             my $history = [];
644             if ( defined( my $old_tie = tied( %{ $_[0] } ) ) ) {
645             tie( %{ $_[0] }, 'Test::Mimic::Library::MonitorTiedHash', $history, $old_tie );
646             }
647             else {
648             tie ( %{ $_[0] }, 'Test::Mimic::Library::MonitorHash', $history, $_[0] );
649             }
650             return [ 'HASH', $history ];
651             },
652             'GLOB' => $simple_action,
653             'IO' => $simple_action,
654             'FORMAT' => $simple_action,
655             'CODE' => $simple_action,
656             );
657              
658             # Monitor, i.e. tie the value and record its state, if possible (recursively as needed), otherwise merely
659             # encapsulate the value as well as possible. In the second case proper storage and retrivial of the data
660             # becomes the responsibility of Test::Mimic::Recorder::stringify.
661             #
662             # Objects are handled, but to a limited extent. The main restriction is that a reference (or rather the
663             # 'object' behind the reference) can not change from being blessed to being unblessed anywhere that monitor
664             # will notice. Purely internal modifications, i.e. those occurring in a wrapped subroutine, are okay.
665             # Additionally, modifications occurring prior to the reference being monitored are okay. Also, it should be
666             # noted that references blessed into a package that is not being recorded will have their state recorded
667             # properly (including object info), but that object method calls on that reference will still not be
668             # recorded.
669             sub monitor {
670 2     2 1 4 my ( $val ) = @_;
671              
672 2         7 my $type = reftype($val);
673 2 50       7 if ( ! $type ) { # If this is not a reference...
674 0         0 return [ STABLE, $val ];
675             }
676             else {
677 2         6 my $address = refaddr($val);
678 2         4 my $index;
679              
680 2 50       7 if ( defined( $is_alive->{$address} ) ) { # If we are watching this reference...
681              
682             # NOTE: We are using defined as opposed to exists because a given address can be used by multiple
683             # references over the entire execution of the program. See the comment on weaken below.
684              
685 0         0 $index = $address_to_index->{$address};
686             }
687             else {
688             # Note that we are watching the reference.
689 2         6 $is_alive->{$address} = $val;
690 2         6 weaken( $is_alive->{$address} ); # This reference will be automatically set to undef when $$val is
691             # garbage collected.
692              
693 2 50       6 if ( _is_pattern($val) ) { # reftype doesn't recognize patterns, so set $type manually.
694 0         0 $type = 'REG_EXP';
695             }
696              
697             # Create a representation of the reference depending on its type.
698             # Monitors recursively as necessary.
699 2         4 my $reference;
700 2 50       6 if ( exists( $type_to_action{$type} ) ) {
701 2         4 $reference = &{ $type_to_action{$type} }( $val, $type );
  2         8  
702             }
703             else {
704 0         0 die "Unknown reference type <$type> from <$val>. Unable to monitor.";
705             }
706 2         6 $reference->[2] = blessed($val); # Mark this as either an object or a plain reference.
707              
708             # Store the representation of the reference into the references table.
709 2         4 push( @{$references}, $reference );
  2         4  
710 2         11 $index = $address_to_index->{$address} = $#{$references};
  2         7  
711             }
712 2         20 return [ VOLATILE, $index ];
713             }
714             }
715             }
716              
717             {
718             # Each of these helper subroutines takes ( $val, $at_level, $type ).
719             my $scalar_action = sub { return [ 'SCALAR', encode( ${ $_[0] }, $_[1] ) ]; };
720             my $simple_action = sub { return [ $_[2], $_[0] ]; };
721             my %type_to_action = (
722             'REG_EXP' => $simple_action,
723             'SCALAR' => $scalar_action,
724             'REF' => $scalar_action,
725             'LVALUE' => $scalar_action,
726             'VSTRING' => $scalar_action,
727             'ARRAY' => sub {
728             my @temp = map( { encode( $_, $_[1] ) } @{ $_[0] } );
729             return [ 'ARRAY', \@temp ];
730             },
731             'HASH' => sub {
732             my %temp;
733             @temp{ keys %{ $_[0] } } = map( { encode( $_[0]->{$_}, $_[1] ) } keys %{ $_[0] } );
734             return [ 'HASH', \%temp];
735             },
736             'GLOB' => $simple_action,
737             'IO' => $simple_action,
738             'FORMAT' => $simple_action,
739             'CODE' => $simple_action,
740             );
741              
742             # Performs an expansion wrap on the passed value until the given level then watches every component below.
743             # Returns a structure analogous to the original except that each component is recursively wrapped. This should
744             # only be used on static data. If circular references exist above the watch level or into the wrap level the
745             # behavior is undefined.
746             #
747             # For example if _watch was passed an array it would perhaps return [ VOLATILE, 453 ].
748             # _wrap_then_watch would return [ NESTED, [ ARRAY, [ [ STABLE, 'foo' ], [ STABLE, 'bar' ] ] ] ]
749             #
750             # This is useful when the data currently in the array is important, but the array itself has no special
751             # significance.
752             #
753             # Currently scalars et al., arrays, hashes, qr objects, code references are handled well.
754             # Filehandles are not being tied, ideally they would be, but the filehandle tying mechanism is
755             # not complete.
756             # Formats are in a similar position, but they probably shouldn't ever be redefined. (Check this.)
757             # Because of this that may not really be a problem.
758             # The entries in globs can not be tied. A special glob tie could potentially remedy this, but
759             # this does not currently exist.
760             #
761             # TODO: Handle circular references, also save space on DAGs.
762             # Idea: Scan through structure. Record all references in a big hash. If we see duplicates note them.
763             # The duplicates will exist as a special structure.
764             #
765             # [ CIRCULAR_NESTED, , [ ARRAY, blah...
766             # We have one additional type:
767             # [ DUP, ]
768             sub encode {
769 0     0 1   my ( $val, $at_level ) = @_;
770              
771 0 0         if ( $at_level == 0 ) { # If we have reached the volatile layer...
772 0           return monitor( $val );
773             }
774             else {
775 0           $at_level--;
776             }
777              
778 0           my $type = reftype($val);
779 0 0         if ( ! $type ) { # If the value is not a reference...
    0          
780 0           return [ STABLE, $val ];
781             }
782             elsif ( exists( $type_to_action{$type} ) ) {
783 0 0         if ( _is_pattern($val) ) { # reftype doesn't recognize patterns, so set $type manually.
784 0           $type = 'REG_EXP';
785             }
786 0           my $coded = &{ $type_to_action{$type} }( $val, $at_level, $type );
  0            
787 0           return [ NESTED, $coded ];
788             }
789             else {
790 0           die "Unknown reference type <$type> from <$val>. Unable to encode.";
791             }
792             }
793             }
794              
795             {
796             # Each of these helper subroutines takes ( $val ).
797             my $simple_action = sub { return $_[0]; };
798             my %type_to_action = (
799             'REG_EXP' => $simple_action,
800             'SCALAR' => sub {
801             my $temp = decode( $_[0] );
802             return \$temp;
803             },
804             'ARRAY' => sub {
805             my @temp = map( { decode( $_ ) } @{ $_[0] } );
806             return \@temp;
807             },
808             'HASH' => sub {
809             my %temp;
810             @temp{ keys %{ $_[0] } } = map( { decode( $_[0]->[$_] ) } keys %{ $_[0] } );
811             return \%temp;
812             },
813             'GLOB' => $simple_action,
814             'IO' => $simple_action,
815             'FORMAT' => $simple_action,
816             'CODE' => $simple_action,
817             );
818              
819             sub decode {
820 0     0 1   my ( $coded_val ) = @_;
821 0           my ( $code_type, $data ) = @{$coded_val};
  0            
822              
823 0 0         if ( $code_type == STABLE ) {
    0          
    0          
824 0           return $data;
825             }
826             elsif ( $code_type == NESTED ) {
827 0           my ( $ref_type, $val ) = @{$data};
  0            
828            
829 0 0         if ( exists( $type_to_action{$ref_type} ) ) {
830 0           return &{ $type_to_action{$ref_type} }( $val );
  0            
831             }
832             else {
833 0           die "Invalid reference type <$ref_type> from <$data> with value <$val>. Unable to decode.";
834             }
835             }
836             elsif ( $code_type == VOLATILE ) {
837 0           return play( $coded_val );
838             }
839             else {
840 0           die "Invalid code type <$code_type> from <$coded_val> with data <$data>. Unable to decode.";
841             }
842             }
843             }
844              
845             {
846             # Each of these helper subroutines takes ( $history ).
847             # This will be a single reference, i.e. not a true history, for types we do not tie.
848             my $simple_action = sub { return $_[0]; };
849             my %type_to_action = (
850             'REG_EXP' => $simple_action,
851             'SCALAR' => sub {
852             my $temp;
853             tie( $temp, 'Test::Mimic::Library::PlayScalar', $_[0] );
854             return \$temp;
855             },
856             'ARRAY' => sub {
857             my @temp;
858             tie( @temp, 'Test::Mimic::Library::PlayArray', $_[0] );
859             return \@temp;
860             },
861             'HASH' => sub {
862             my %temp;
863             tie( %temp, 'Test::Mimic::Library::PlayHash', $_[0] );
864             return \%temp;
865             },
866             'GLOB' => $simple_action,
867             'IO' => $simple_action,
868             'FORMAT' => $simple_action,
869             'CODE' => $simple_action,
870             );
871              
872             sub play {
873 0     0 1   my ( $coded_val ) = @_;
874            
875 0           my ( $type, $data ) = @{$coded_val};
  0            
876 0 0         if ( $type == STABLE ) {
    0          
877 0           return $data;
878             }
879             elsif ( $type == VOLATILE ) {
880 0 0         if ( defined( $index_to_reference->{$data} ) ) { # We are using defined because the weak
881             # references used in the hash will be set to
882             # undef upon the destruction of the
883             # corresponding values.
884 0           return $index_to_reference->{$data};
885             }
886             else {
887 0           my ( $type, $history, $class_name ) = @{ $references->[$data] };
  0            
888            
889 0           my $reference;
890 0 0         if ( exists( $type_to_action{$type} ) ) {
891 0           $reference = &{ $type_to_action{$type} }( $history );
  0            
892             }
893             else {
894 0           die "Unknown reference type <$type> at index <$data>. Unable to play.";
895             }
896              
897             # If this reference is supposed to point at an object, bless it.
898             # This will take place even if we didn't record the class. This may be a feature or a bug.
899 0 0         if ( defined($class_name) ) {
900 0           bless( $reference, $class_name );
901             }
902              
903             # Note the creation of this reference, so we don't recreate it and are aware of what recorded
904             # reference it corresponds to.
905 0           my $address = refaddr($reference);
906 0           $address_to_index->{$address} = $data;
907 0           $is_alive->{$address} = $reference;
908 0           weaken( $is_alive->{$address} );
909 0           $index_to_reference->{$data} = $reference;
910 0           weaken( $index_to_reference->{$data} ); # But don't prevent it from being gced. If we
911             # need to we can recreate it easily. ( Although the
912             # address may well be different. )
913              
914 0           return $reference;
915             }
916             }
917             else {
918 0           die "Unrecognized type <$type>. Unable to play.";
919             }
920             }
921             }
922              
923             1;
924             __END__