line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Test::Mimic::Generator; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
29916
|
use 5.006001; #for open( my $fh... |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
43
|
|
4
|
1
|
|
|
1
|
|
7
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
39
|
|
5
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
290
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = 0.009_005; |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
#Returns the name of the package that objects returned by new are blessed into. For encapuslation |
10
|
|
|
|
|
|
|
#purposes this may not be Test::Mimic::Generator. Should be considered protected. |
11
|
|
|
|
|
|
|
sub get_object_package { |
12
|
1
|
|
|
1
|
0
|
4
|
my ($class) = @_; |
13
|
1
|
|
|
|
|
9
|
return $class . '::Object'; |
14
|
|
|
|
|
|
|
} |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# See the POD documentation below. |
17
|
|
|
|
|
|
|
sub new { |
18
|
1
|
|
|
1
|
1
|
14
|
my ($class) = @_; |
19
|
1
|
|
|
|
|
5
|
return bless( [], $class->get_object_package() ); |
20
|
|
|
|
|
|
|
} |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
package Test::Mimic::Generator::_Implementation; |
23
|
|
|
|
|
|
|
|
24
|
1
|
|
|
1
|
|
1108
|
use Test::Mimic::Library qw< stringify stringify_by destringify DATA descend >; |
|
1
|
|
|
|
|
44077
|
|
|
1
|
|
|
|
|
399
|
|
25
|
1
|
|
|
1
|
|
15
|
use Cwd qw; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
228
|
|
26
|
1
|
|
|
1
|
|
1529
|
use File::Copy; |
|
1
|
|
|
|
|
3607
|
|
|
1
|
|
|
|
|
74
|
|
27
|
1
|
|
|
1
|
|
8
|
use Data::Dumper; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
505
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# Construct constants to access member variables. |
30
|
|
|
|
|
|
|
BEGIN { |
31
|
1
|
|
|
1
|
|
3
|
my $offset = 0; |
32
|
1
|
|
|
|
|
3
|
for my $field ( qw< TYPEGLOBS EXTRA OPERATION_SEQUENCE READ_DIR > ) { |
33
|
4
|
|
|
2
|
|
1300
|
eval("sub $field { return $offset; }"); |
|
2
|
|
|
1
|
|
10
|
|
|
1
|
|
|
2
|
|
3
|
|
|
2
|
|
|
3
|
|
17
|
|
|
3
|
|
|
|
|
42
|
|
34
|
4
|
|
|
|
|
1789
|
$offset++; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# See the POD documentation below. |
39
|
|
|
|
|
|
|
sub Test::Mimic::Generator::Object::load { |
40
|
1
|
|
|
1
|
|
8
|
my ($self, $dir_name) = @_; |
41
|
|
|
|
|
|
|
|
42
|
1
|
50
|
|
|
|
52
|
open( my $fh, '<', $dir_name . '/additional_info.rec' ) or die "Could not open file: $!"; |
43
|
|
|
|
|
|
|
|
44
|
1
|
|
|
|
|
3
|
my $recorded_data; |
45
|
|
|
|
|
|
|
{ |
46
|
1
|
|
|
|
|
2
|
local $/; |
|
1
|
|
|
|
|
4
|
|
47
|
1
|
|
|
|
|
3
|
undef $/; |
48
|
1
|
|
|
|
|
136
|
$recorded_data = <$fh>; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
1
|
50
|
|
|
|
17
|
close($fh) or die "Could not close file: $!"; |
52
|
1
|
|
|
|
|
7
|
my $living_data = destringify($recorded_data); |
53
|
|
|
|
|
|
|
|
54
|
1
|
|
|
|
|
1502
|
$self->[TYPEGLOBS] = $living_data->[0]; #This could change later, so I'm listing all the assigns explicitly. |
55
|
1
|
|
|
|
|
36
|
$self->[EXTRA] = $living_data->[1]; |
56
|
1
|
|
|
|
|
36
|
$self->[OPERATION_SEQUENCE] = $living_data->[2]; |
57
|
1
|
|
|
|
|
34
|
$self->[READ_DIR] = $dir_name; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# See the POD documentation below. |
61
|
|
|
|
|
|
|
sub Test::Mimic::Generator::Object::write { |
62
|
1
|
|
|
1
|
|
8
|
my ( $self, $write_dir, @packages ) = @_; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# Either select all recorded packages to write or verify that the requested packages were recorded. |
65
|
1
|
50
|
|
|
|
6
|
if ( @packages == 0 ) { # If no packages were selected explicitly... |
66
|
1
|
|
|
|
|
3
|
@packages = keys %{ $self->[TYPEGLOBS] }; |
|
1
|
|
|
|
|
52
|
|
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
else { |
69
|
0
|
|
|
|
|
0
|
for my $package (@packages) { |
70
|
0
|
0
|
|
|
|
0
|
if ( ! exists( $self->[TYPEGLOBS]->{$package} ) ) { |
71
|
0
|
|
|
|
|
0
|
die "The $package package was not found in the loaded recording."; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
1
|
|
|
|
|
13
|
my $top_level = abs_path(); |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# Move to the $write_dir/lib directory, creating dirs as needed. |
79
|
1
|
|
|
|
|
7
|
descend($write_dir); |
80
|
1
|
|
|
|
|
50
|
descend('lib'); |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# Consider each package, construct and write the .pm file. |
83
|
1
|
|
|
|
|
38
|
my $start_path = abs_path(); |
84
|
1
|
|
|
|
|
3
|
for my $package (@packages) { |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# Gets the name of the .pm file, descends to where it will be located. |
87
|
1
|
|
|
|
|
16
|
my @dirs = split( /::/, $package ); |
88
|
1
|
|
|
|
|
4
|
my $filename = pop(@dirs) . '.pm'; |
89
|
1
|
|
|
|
|
3
|
for my $dir (@dirs ) { |
90
|
0
|
|
|
|
|
0
|
descend($dir); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# Open, write and close the .pm file. |
94
|
1
|
50
|
|
|
|
133
|
open( my $fh, '>', $filename ) or die "Could not open file: $!"; |
95
|
1
|
|
|
|
|
32
|
_create($package, $self->[TYPEGLOBS]->{$package}, $self->[EXTRA]->{$package}, $fh ); |
96
|
1
|
50
|
|
|
|
53
|
close($fh) or die "Could not close file: $!"; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# Move to the top of our fake library hierarchy. |
99
|
1
|
50
|
|
|
|
34
|
chdir($start_path) or die "Could not change the current working directory: $!"; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Rename the history file so that the controller recognizes it. |
103
|
1
|
50
|
|
|
|
18
|
chdir($top_level) or die "Could not change the current working directory: $!"; |
104
|
1
|
50
|
|
|
|
44
|
copy( $self->[READ_DIR] . '/history_from_recorder.rec', $write_dir . '/history_for_playback.rec' ) |
105
|
|
|
|
|
|
|
or die "Unable to copy file: $!"; |
106
|
|
|
|
|
|
|
# NOTE: In the future we may modify the contents of the file as well. |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
{ |
110
|
|
|
|
|
|
|
# A few useful constant maps. |
111
|
|
|
|
|
|
|
my %TYPE_TO_SIGIL = ( 'ARRAY' => '@', 'HASH' => '%', 'SCALAR' => '$' ); |
112
|
|
|
|
|
|
|
my %TYPE_TO_TIE = ( |
113
|
|
|
|
|
|
|
'ARRAY' => 'Test::Mimic::Library::PlayArray', |
114
|
|
|
|
|
|
|
'HASH' => 'Test::Mimic::Library::PlayHash', |
115
|
|
|
|
|
|
|
'SCALAR' => 'Test::Mimic::Library::PlayScalar', |
116
|
|
|
|
|
|
|
); |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# Accepts a package name, the corresponding pseudo symbol table, the corresponding extra hash ref |
119
|
|
|
|
|
|
|
# and a filehandle to write to. Assembles the code for the mock package and writes it to disk. |
120
|
|
|
|
|
|
|
sub _create { |
121
|
1
|
|
|
1
|
|
2
|
my ( $package, $pseudo_symbol_table, $extra, $fh ) = @_; |
122
|
|
|
|
|
|
|
|
123
|
1
|
|
|
|
|
10
|
my $header_code = join( "\n", |
124
|
|
|
|
|
|
|
'package ' . $package . ';', |
125
|
|
|
|
|
|
|
'', |
126
|
|
|
|
|
|
|
'use strict;', |
127
|
|
|
|
|
|
|
'use warnings;', |
128
|
|
|
|
|
|
|
'', |
129
|
|
|
|
|
|
|
'BEGIN {', #TODO: Check to see if Test::Mimic is loaded, allow requiring fake pack directly etc. |
130
|
|
|
|
|
|
|
' Test::Mimic::prepare_for_use();', |
131
|
|
|
|
|
|
|
'}', |
132
|
|
|
|
|
|
|
'', |
133
|
|
|
|
|
|
|
'use Scalar::Util;', |
134
|
|
|
|
|
|
|
'', |
135
|
|
|
|
|
|
|
'use Test::Mimic::Library qw< execute get_references HISTORY decode destringify >;', |
136
|
|
|
|
|
|
|
'use Test::Mimic::Library::PlayScalar;', |
137
|
|
|
|
|
|
|
'use Test::Mimic::Library::PlayArray;', |
138
|
|
|
|
|
|
|
'use Test::Mimic::Library::PlayHash;', |
139
|
|
|
|
|
|
|
'', |
140
|
|
|
|
|
|
|
'', |
141
|
|
|
|
|
|
|
); |
142
|
1
|
|
|
|
|
8
|
print $fh $header_code; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# Create code to tie package variables. |
145
|
1
|
|
|
|
|
4
|
my $package_var_code = join( "\n", |
146
|
|
|
|
|
|
|
'BEGIN {', |
147
|
|
|
|
|
|
|
' my $references = get_references();', |
148
|
|
|
|
|
|
|
'', |
149
|
|
|
|
|
|
|
); |
150
|
1
|
|
|
|
|
2
|
for my $typeglob ( keys %{$pseudo_symbol_table} ) { |
|
1
|
|
|
|
|
23
|
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# Tie the current typeglob |
153
|
18
|
|
|
|
|
21
|
my %slots = %{ $pseudo_symbol_table->{$typeglob} }; |
|
18
|
|
|
|
|
55
|
|
154
|
18
|
|
|
|
|
27
|
delete $slots{'CODE'}; |
155
|
18
|
|
|
|
|
20
|
delete $slots{'CONSTANT'}; |
156
|
|
|
|
|
|
|
# NOTE: You may (some day) need to delete other types too. |
157
|
18
|
|
|
|
|
947
|
for my $type ( keys %slots ) { |
158
|
4
|
|
|
|
|
30
|
$package_var_code .= "\n" . ' tie( ' |
159
|
|
|
|
|
|
|
. $TYPE_TO_SIGIL{$type} . $package . '::' . $typeglob # Full name including sigil |
160
|
|
|
|
|
|
|
. ', q<' . $TYPE_TO_TIE{$type} |
161
|
|
|
|
|
|
|
. '>, $references->[' |
162
|
|
|
|
|
|
|
. $slots{$type}->[DATA] # Index for the reference, ...->[ENCODE_TYPE] |
163
|
|
|
|
|
|
|
# must be VOLATILE. Check? |
164
|
|
|
|
|
|
|
. ']->[HISTORY] );'; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
} |
167
|
1
|
|
|
|
|
3
|
$package_var_code .= "\n" . '}' . "\n\n"; |
168
|
1
|
|
|
|
|
3
|
print $fh $package_var_code; |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# Create code for generating constants. |
171
|
1
|
|
|
|
|
2
|
my $constant_code = 'use constant {' . "\n"; |
172
|
1
|
|
|
|
|
3
|
for my $symbol ( keys %{$pseudo_symbol_table} ) { |
|
1
|
|
|
|
|
4
|
|
173
|
18
|
|
|
|
|
24
|
my $typeglob = $pseudo_symbol_table->{$symbol}; |
174
|
18
|
100
|
|
|
|
41
|
if ( exists( $typeglob->{'CONSTANT'} ) ) { |
175
|
2
|
|
|
|
|
9
|
$constant_code .= ' ' . _string_to_perl($symbol) . ' => decode( destringify( ' |
176
|
|
|
|
|
|
|
. _string_to_perl( stringify( $typeglob->{'CONSTANT'} ) ) . ' ) ),' . "\n"; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
} |
180
|
1
|
|
|
|
|
3
|
$constant_code .= '};' . "\n\n"; |
181
|
1
|
|
|
|
|
3
|
print $fh $constant_code; |
182
|
|
|
|
|
|
|
|
183
|
1
|
|
|
|
|
2
|
my @ancestors = %{ $extra->{'ISA'} }; |
|
1
|
|
|
|
|
6
|
|
184
|
1
|
|
|
|
|
14
|
my $isa_code = join( "\n", |
185
|
|
|
|
|
|
|
'{', |
186
|
|
|
|
|
|
|
' my %ancestors = qw< ' . "@ancestors" . ' >;', # Interpolation is needed here. |
187
|
|
|
|
|
|
|
'', |
188
|
|
|
|
|
|
|
' sub isa {', |
189
|
|
|
|
|
|
|
' my ( $self, $type ) = @_;', |
190
|
|
|
|
|
|
|
'', |
191
|
|
|
|
|
|
|
' if ( Scalar::Util::reftype($self) ) {', |
192
|
|
|
|
|
|
|
' my $name = Scalar::Util::blessed($self);', |
193
|
|
|
|
|
|
|
' if ($name) {', |
194
|
|
|
|
|
|
|
' return exists( $ancestors{$name} );', |
195
|
|
|
|
|
|
|
' }', |
196
|
|
|
|
|
|
|
' else {', |
197
|
|
|
|
|
|
|
' return ();', |
198
|
|
|
|
|
|
|
' }', |
199
|
|
|
|
|
|
|
' }', |
200
|
|
|
|
|
|
|
' else {', |
201
|
|
|
|
|
|
|
' return exists( $ancestors{$self} );', |
202
|
|
|
|
|
|
|
' }', |
203
|
|
|
|
|
|
|
' }', |
204
|
|
|
|
|
|
|
'}', |
205
|
|
|
|
|
|
|
'', |
206
|
|
|
|
|
|
|
'', |
207
|
|
|
|
|
|
|
); |
208
|
|
|
|
|
|
|
# TODO: Make this dependent on user options. |
209
|
1
|
|
|
|
|
3
|
print $fh $isa_code; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# Create code for user defined subroutines |
212
|
1
|
|
|
|
|
2
|
my $prototypes = $extra->{'PROTOTYPES'}; |
213
|
1
|
|
|
|
|
2
|
for my $symbol ( keys %{$pseudo_symbol_table} ) { |
|
1
|
|
|
|
|
5
|
|
214
|
18
|
|
|
|
|
26
|
my $typeglob = $pseudo_symbol_table->{$symbol}; |
215
|
18
|
100
|
|
|
|
43
|
if ( exists( $typeglob->{'CODE'} ) ) { |
216
|
11
|
|
|
|
|
15
|
my $sub_code = '{' . "\n"; # Of course, I could say "{\n". I am being overly verbose in an |
217
|
|
|
|
|
|
|
# attempt to very explicitly separate out strings that |
218
|
|
|
|
|
|
|
# interpolate. This is a problem because the perl code that I am |
219
|
|
|
|
|
|
|
# writing often uses scalars that could be accidentally |
220
|
|
|
|
|
|
|
# interpolated. If I come back to this line and add a scalar (or |
221
|
|
|
|
|
|
|
# array) I don't want it to bite me. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# Create the code for the behavior hash. |
224
|
11
|
|
|
|
|
30
|
my $behavior_code = stringify( $typeglob->{'CODE'} ); |
225
|
11
|
|
|
|
|
1525
|
$sub_code .= 'my $behavior = destringify( ' . _string_to_perl($behavior_code) . ' );' . "\n"; |
226
|
|
|
|
|
|
|
|
227
|
11
|
|
|
|
|
23
|
my $prototype = $prototypes->{$symbol}; |
228
|
11
|
100
|
|
|
|
51
|
$sub_code .= join( "\n", |
229
|
|
|
|
|
|
|
'', |
230
|
|
|
|
|
|
|
' sub ' . $symbol . ( defined($prototype) ? " ($prototype)" : '' ) . ' {', |
231
|
|
|
|
|
|
|
' return execute( q<' . $package . '>, q<' . $symbol . '>, $behavior, \@_ );', |
232
|
|
|
|
|
|
|
' }', |
233
|
|
|
|
|
|
|
'}', |
234
|
|
|
|
|
|
|
'', |
235
|
|
|
|
|
|
|
'', |
236
|
|
|
|
|
|
|
); |
237
|
|
|
|
|
|
|
|
238
|
11
|
|
|
|
|
250
|
print $fh $sub_code; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# Given a string returns a Perl expression (as a string) that evaluates to the passed string. |
245
|
|
|
|
|
|
|
sub _string_to_perl { |
246
|
15
|
|
|
15
|
|
389
|
my ($string) = @_; |
247
|
|
|
|
|
|
|
|
248
|
15
|
|
|
|
|
38
|
my $code = Dumper($string); |
249
|
15
|
|
|
|
|
1031
|
$code =~ s/^.*?= //; |
250
|
15
|
|
|
|
|
85
|
$code =~ s/;.*?\n$//; |
251
|
|
|
|
|
|
|
|
252
|
15
|
|
|
|
|
96
|
return $code; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
1; |
256
|
|
|
|
|
|
|
__END__ |