line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl -w |
2
|
|
|
|
|
|
|
#!/usr/bin/perl -d:ptkdb -w |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# This is module is based on a module with the same name, implemented |
5
|
|
|
|
|
|
|
# when working for Newtec Cy, located in Belgium, |
6
|
|
|
|
|
|
|
# http://www.newtec.be/. |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
package Data::Comparator; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# |
13
|
|
|
|
|
|
|
# The main entry point for this module is the sub data_comparator(). |
14
|
|
|
|
|
|
|
# It compares two sets of (structured) data and reports on the |
15
|
|
|
|
|
|
|
# differences found with a differences describing data structure. |
16
|
|
|
|
|
|
|
# |
17
|
|
|
|
|
|
|
# The algorithm used is of a subtractive kind. It subtracts the first |
18
|
|
|
|
|
|
|
# data structure given from the second one. This means that, since it |
19
|
|
|
|
|
|
|
# not possible to subtract what is not yet there, not all differences |
20
|
|
|
|
|
|
|
# are reported. To have a report of all differences between |
21
|
|
|
|
|
|
|
# structures A and B, first subtract A from B, next subtract B from A. |
22
|
|
|
|
|
|
|
# The two result sets are an exact description of the differences |
23
|
|
|
|
|
|
|
# between A and B (or should be, untested for the moment). |
24
|
|
|
|
|
|
|
# |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
7
|
|
|
7
|
|
157841
|
use strict; |
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
273
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
30
|
7
|
|
|
7
|
|
5141
|
use Clone 'clone'; |
|
7
|
|
|
|
|
24104
|
|
|
7
|
|
|
|
|
566
|
|
31
|
|
|
|
|
|
|
|
32
|
7
|
|
|
7
|
|
3693
|
use Data::Differences; |
|
7
|
|
|
|
|
19
|
|
|
7
|
|
|
|
|
7282
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# require Exporter; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
40
|
|
|
|
|
|
|
array_comparator |
41
|
|
|
|
|
|
|
hash_comparator |
42
|
|
|
|
|
|
|
data_comparator |
43
|
|
|
|
|
|
|
); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# |
47
|
|
|
|
|
|
|
# array_comparator() |
48
|
|
|
|
|
|
|
# |
49
|
|
|
|
|
|
|
# Compare two arrays, report on the differences found by returning an |
50
|
|
|
|
|
|
|
# array describing the differences between the two arrays. |
51
|
|
|
|
|
|
|
# |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub array_comparator |
54
|
|
|
|
|
|
|
{ |
55
|
63
|
|
|
63
|
0
|
82
|
my $array1 = shift; |
56
|
|
|
|
|
|
|
|
57
|
63
|
|
|
|
|
67
|
my $array2 = shift; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# my $result = { adder_operand => [], subtractor_operand => [], }; |
60
|
|
|
|
|
|
|
|
61
|
63
|
|
|
|
|
89
|
my $result = []; |
62
|
|
|
|
|
|
|
|
63
|
63
|
|
|
|
|
145
|
foreach my $index (0 .. $#$array1) |
64
|
|
|
|
|
|
|
{ |
65
|
133
|
100
|
|
|
|
233
|
if (exists $array2->[$index]) |
66
|
|
|
|
|
|
|
{ |
67
|
128
|
|
|
|
|
259
|
my $index_result = data_comparator($array1->[$index], $array2->[$index]); |
68
|
|
|
|
|
|
|
|
69
|
128
|
100
|
|
|
|
305
|
if (!$index_result->is_empty()) |
70
|
|
|
|
|
|
|
{ |
71
|
4
|
|
|
|
|
11
|
$result->[$index] = $index_result; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
else |
75
|
|
|
|
|
|
|
{ |
76
|
5
|
|
|
|
|
33
|
$result->[$index] |
77
|
|
|
|
|
|
|
= Data::Differences->new(clone(\$array1->[$index])); |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
63
|
|
|
|
|
183
|
foreach my $index ($#$array1 + 1 .. $#$array2) |
82
|
|
|
|
|
|
|
{ |
83
|
1
|
|
|
|
|
18
|
$result->[$index] |
84
|
|
|
|
|
|
|
= Data::Differences->new(clone(\$array2->[$index])); |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
63
|
|
|
|
|
198
|
return Data::Differences->new($result); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head1 NAME |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
Data::Comparator - recursively compare Perl datatypes |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=head1 SYNOPSIS |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
use Data::Comparator qw(data_comparator); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
$a = { 'foo' => 'bar', 'move' => 'zig' }; |
100
|
|
|
|
|
|
|
$b = [ 'alpha', 'beta', 'gamma', 'vlissides' ]; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
$diff = data_comparator($a, $b); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
use Data::Dumper; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
print Dumper($diff); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
if ($diff->is_empty()) |
109
|
|
|
|
|
|
|
{ |
110
|
|
|
|
|
|
|
print '$a and $b are alike\n'; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
else |
113
|
|
|
|
|
|
|
{ |
114
|
|
|
|
|
|
|
print '$a and $b are not alike\n'; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head1 DESCRIPTION |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Compare two sets of (structured) data, report on the differences found |
120
|
|
|
|
|
|
|
with a differences describing data structure. Additionally a set of |
121
|
|
|
|
|
|
|
expected differences may be given in the form of a differences |
122
|
|
|
|
|
|
|
describing data structure. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Returns a differences describing data structure, which is empty if no |
125
|
|
|
|
|
|
|
differences are found. The type of the result is the same as the type |
126
|
|
|
|
|
|
|
of the second data structure given. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
The algorithm used is of a subtractive kind. It subtracts the first |
129
|
|
|
|
|
|
|
data structure given from the second one. This means that, since it |
130
|
|
|
|
|
|
|
is not possible to subtract what is not given in the subtractor, not |
131
|
|
|
|
|
|
|
all differences are reported. To have a report of all differences |
132
|
|
|
|
|
|
|
between structures A and B, first subtract A from B, next subtract B |
133
|
|
|
|
|
|
|
from A, using this module. The two result sets are an exact |
134
|
|
|
|
|
|
|
description of the differences between A and B. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
It is possible to add any of the methods array_comparator(), |
137
|
|
|
|
|
|
|
hash_comparator(), data_comparator() to an existing object, or to use |
138
|
|
|
|
|
|
|
these as regular subs. |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=head1 NOTE |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
This module is used in the tests for Data::Merger(3) and |
143
|
|
|
|
|
|
|
Data::Transformator(3). |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=head1 BUGS |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Does only work with scalars, hashes and arrays. Does not work on |
148
|
|
|
|
|
|
|
self-referential structures. |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head1 AUTHOR |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
Hugo Cornelis, hugo.cornelis@gmail.com |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
Copyright 2007 Hugo Cornelis. |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or |
157
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=head1 SEE ALSO |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
Data::Merger(3), Data::Transformator(3), Data::Differences(3), |
162
|
|
|
|
|
|
|
Clone(3) |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=cut |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub data_comparator |
167
|
|
|
|
|
|
|
{ |
168
|
378
|
|
|
378
|
0
|
3441
|
my $data1 = shift; |
169
|
|
|
|
|
|
|
|
170
|
378
|
|
|
|
|
367
|
my $data2 = shift; |
171
|
|
|
|
|
|
|
|
172
|
378
|
|
|
|
|
360
|
my $expected_differences = shift; |
173
|
|
|
|
|
|
|
|
174
|
378
|
|
|
|
|
353
|
my $result; |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# get the types for the different arguments |
177
|
|
|
|
|
|
|
|
178
|
378
|
|
100
|
|
|
2172
|
my $data_type1 = (ref $data1 && "$data1") || ''; |
179
|
|
|
|
|
|
|
|
180
|
378
|
|
100
|
|
|
2027
|
my $data_type2 = (ref $data2 && "$data2") || ''; |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# first compare comparables |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# try to compare two hashes |
185
|
|
|
|
|
|
|
|
186
|
378
|
100
|
100
|
|
|
2727
|
if ($data_type1 =~ /HASH/ |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
187
|
|
|
|
|
|
|
&& $data_type2 =~ /HASH/) |
188
|
|
|
|
|
|
|
{ |
189
|
145
|
|
|
|
|
1135
|
$result = hash_comparator($data1, $data2); |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# or try to compare two arrays |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
elsif ($data_type1 =~ /ARRAY/ |
195
|
|
|
|
|
|
|
&& $data_type2 =~ /ARRAY/) |
196
|
|
|
|
|
|
|
{ |
197
|
63
|
|
|
|
|
153
|
$result = array_comparator($data1, $data2); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# or try to compare two scalars |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
elsif ($data_type1 =~ /SCALAR/ |
203
|
|
|
|
|
|
|
&& $data_type2 =~ /SCALAR/) |
204
|
|
|
|
|
|
|
{ |
205
|
6
|
|
|
|
|
12
|
$result = scalar_ref_comparator($data1, $data2); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# or try to compare two referenced references |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
elsif ($data_type1 =~ /REF/ |
211
|
|
|
|
|
|
|
&& $data_type2 =~ /REF/) |
212
|
|
|
|
|
|
|
{ |
213
|
2
|
|
|
|
|
4
|
$result = data_comparator($$data1, $$data2); |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# or try to compare two non references |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
elsif (!$data_type1 |
219
|
|
|
|
|
|
|
&& !$data_type2) |
220
|
|
|
|
|
|
|
{ |
221
|
161
|
|
|
|
|
663
|
$result = scalar_comparator($data1, $data2); |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# second, for non-comparables |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
else |
227
|
|
|
|
|
|
|
{ |
228
|
|
|
|
|
|
|
# simply clone second argument |
229
|
|
|
|
|
|
|
|
230
|
1
|
|
|
|
|
10
|
$result = Data::Differences->new(clone(\$data2)); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# if the user was already expecting differences |
234
|
|
|
|
|
|
|
|
235
|
378
|
100
|
|
|
|
742
|
if (defined $expected_differences) |
236
|
|
|
|
|
|
|
{ |
237
|
|
|
|
|
|
|
# compare the result with the expected differences |
238
|
|
|
|
|
|
|
|
239
|
6
|
|
|
|
|
11
|
$result = data_comparator($expected_differences, $result); |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
378
|
|
|
|
|
693
|
return $result; |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# |
247
|
|
|
|
|
|
|
# hash_comparator() |
248
|
|
|
|
|
|
|
# |
249
|
|
|
|
|
|
|
# Compare two hashes, report on the differences found by returning an |
250
|
|
|
|
|
|
|
# hash describing the differences between the two hashes. |
251
|
|
|
|
|
|
|
# |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
sub hash_comparator |
254
|
|
|
|
|
|
|
{ |
255
|
145
|
|
|
145
|
0
|
147
|
my $hash1 = shift; |
256
|
|
|
|
|
|
|
|
257
|
145
|
|
|
|
|
315
|
my $hash2 = shift; |
258
|
|
|
|
|
|
|
|
259
|
145
|
|
|
|
|
178
|
my $result = {}; |
260
|
|
|
|
|
|
|
|
261
|
145
|
|
|
|
|
320
|
foreach my $key (keys %$hash1) |
262
|
|
|
|
|
|
|
{ |
263
|
205
|
100
|
|
|
|
412
|
if (exists $hash2->{$key}) |
264
|
|
|
|
|
|
|
{ |
265
|
182
|
|
|
|
|
586
|
my $key_result = data_comparator($hash1->{$key}, $hash2->{$key}); |
266
|
|
|
|
|
|
|
|
267
|
182
|
100
|
|
|
|
418
|
if (!$key_result->is_empty()) |
268
|
|
|
|
|
|
|
{ |
269
|
3
|
|
|
|
|
8
|
$result->{$key} = $key_result; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
145
|
|
|
|
|
951
|
foreach my $key (grep { !exists $hash1->{$_} } keys %$hash2) |
|
184
|
|
|
|
|
429
|
|
275
|
|
|
|
|
|
|
{ |
276
|
2
|
|
|
|
|
23
|
$result->{$key} |
277
|
|
|
|
|
|
|
= Data::Differences->new(clone(\$hash2->{$key})); |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
145
|
|
|
|
|
801
|
return Data::Differences->new($result); |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# |
285
|
|
|
|
|
|
|
# scalar_comparator() |
286
|
|
|
|
|
|
|
# |
287
|
|
|
|
|
|
|
# Compare two scalar values, report on the differences found by |
288
|
|
|
|
|
|
|
# returning the second scalar value if it is different from the first |
289
|
|
|
|
|
|
|
# scalar value. |
290
|
|
|
|
|
|
|
# |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub scalar_comparator |
293
|
|
|
|
|
|
|
{ |
294
|
161
|
|
|
161
|
0
|
166
|
my $scalar1 = shift; |
295
|
|
|
|
|
|
|
|
296
|
161
|
|
|
|
|
169
|
my $scalar2 = shift; |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
#t two undefs is illegal. |
299
|
|
|
|
|
|
|
|
300
|
161
|
50
|
66
|
|
|
317
|
if (!defined $scalar1 && !defined $scalar2) |
301
|
|
|
|
|
|
|
{ |
302
|
5
|
|
|
|
|
34
|
return Data::Differences->new(clone(\undef)); |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
156
|
100
|
|
|
|
248
|
if (!defined $scalar2) |
306
|
|
|
|
|
|
|
{ |
307
|
5
|
|
|
|
|
41
|
return Data::Differences->new(clone(\$scalar2)); |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
151
|
100
|
|
|
|
296
|
if (($scalar1 cmp $scalar2) eq 0) |
311
|
|
|
|
|
|
|
{ |
312
|
148
|
|
|
|
|
1020
|
return Data::Differences->new(clone(\undef)); |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
else |
315
|
|
|
|
|
|
|
{ |
316
|
3
|
|
|
|
|
33
|
return Data::Differences->new(clone(\$scalar2)); |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# |
322
|
|
|
|
|
|
|
# scalar_ref_comparator() |
323
|
|
|
|
|
|
|
# |
324
|
|
|
|
|
|
|
# Compare two references to scalar values, report on the differences |
325
|
|
|
|
|
|
|
# found by returning the second reference if it is different from the |
326
|
|
|
|
|
|
|
# first reference. |
327
|
|
|
|
|
|
|
# |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub scalar_ref_comparator |
330
|
|
|
|
|
|
|
{ |
331
|
6
|
|
|
6
|
0
|
7
|
my $scalar1 = shift; |
332
|
|
|
|
|
|
|
|
333
|
6
|
|
|
|
|
8
|
my $scalar2 = shift; |
334
|
|
|
|
|
|
|
|
335
|
6
|
|
|
|
|
8
|
my $value1 = $$scalar1; |
336
|
|
|
|
|
|
|
|
337
|
6
|
|
|
|
|
7
|
my $value2 = $$scalar2; |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# for two undefs |
341
|
|
|
|
|
|
|
|
342
|
6
|
50
|
33
|
|
|
44
|
if (!defined $value1 |
|
|
50
|
33
|
|
|
|
|
343
|
|
|
|
|
|
|
&& !defined $value2) |
344
|
|
|
|
|
|
|
{ |
345
|
|
|
|
|
|
|
# return equality |
346
|
|
|
|
|
|
|
|
347
|
0
|
|
|
|
|
0
|
return Data::Differences->new(clone(\undef)); |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# for one undef |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
elsif (!defined $value1 |
353
|
|
|
|
|
|
|
|| !defined $value2) |
354
|
|
|
|
|
|
|
{ |
355
|
|
|
|
|
|
|
# return different |
356
|
|
|
|
|
|
|
|
357
|
0
|
|
|
|
|
0
|
return Data::Differences->new(clone(\$scalar2)); |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# in other cases |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
else |
363
|
|
|
|
|
|
|
{ |
364
|
|
|
|
|
|
|
# do a normal comparison by calling the generic comparator |
365
|
|
|
|
|
|
|
|
366
|
6
|
|
|
|
|
12
|
return data_comparator($value1, $value2); |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# if (($value1 cmp $value2) eq 0) |
369
|
|
|
|
|
|
|
# { |
370
|
|
|
|
|
|
|
# return Data::Differences->new(clone(\undef)); |
371
|
|
|
|
|
|
|
# } |
372
|
|
|
|
|
|
|
# else |
373
|
|
|
|
|
|
|
# { |
374
|
|
|
|
|
|
|
# return Data::Differences->new(clone(\$scalar2)); |
375
|
|
|
|
|
|
|
# } |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
1; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
|