File Coverage

blib/lib/Test/XML/Ordered.pm
Criterion Covered Total %
statement 132 151 87.4
branch 51 72 70.8
condition 9 14 64.2
subroutine 23 23 100.0
pod 3 3 100.0
total 218 263 82.8


line stmt bran cond sub pod time code
1             package Test::XML::Ordered;
2             $Test::XML::Ordered::VERSION = '0.2.0';
3 2     2   131824 use strict;
  2         12  
  2         58  
4 2     2   9 use warnings;
  2         5  
  2         43  
5              
6 2     2   30 use 5.010;
  2         6  
7              
8 2     2   815 use XML::LibXML::Reader;
  2         88136  
  2         233  
9              
10 2     2   990 use Test::More;
  2         127166  
  2         13  
11              
12 2     2   501 use base 'Exporter';
  2         4  
  2         191  
13              
14 2     2   12 use vars '@EXPORT_OK';
  2         3  
  2         3143  
15              
16             @EXPORT_OK = (qw(is_xml_ordered));
17              
18             sub new
19             {
20 5     5 1 9 my $class = shift;
21 5         9 my $self = {};
22              
23 5         9 bless $self, $class;
24              
25 5         25 $self->_init(@_);
26              
27 5         9 return $self;
28             }
29              
30             sub _got
31             {
32 784     784   1760 return shift->{got_reader};
33             }
34              
35             sub _expected
36             {
37 750     750   1437 return shift->{expected_reader};
38             }
39              
40             sub _init
41             {
42 5     5   10 my ($self, $args) = @_;
43              
44             $self->{got_reader} =
45 5         9 XML::LibXML::Reader->new(@{$args->{got_params}});
  5         24  
46             $self->{expected_reader} =
47 5         466 XML::LibXML::Reader->new(@{$args->{expected_params}});
  5         16  
48              
49 5         297 $self->{diag_message} = $args->{diag_message};
50              
51 5         18 $self->{got_end} = 0;
52 5         9 $self->{expected_end} = 0;
53              
54 5         8 return;
55             }
56              
57             sub _got_end
58             {
59 160     160   305 return shift->{got_end};
60             }
61              
62             sub _expected_end
63             {
64 157     157   315 return shift->{expected_end};
65             }
66              
67             sub _read_got
68             {
69 233     233   249 my $self = shift;
70              
71 233 100       289 if ($self->_got->read() <= 0)
72             {
73 3         5 $self->{got_end} = 1;
74             }
75              
76 233         317 return;
77             }
78              
79             sub _read_expected
80             {
81 199     199   209 my $self = shift;
82              
83 199 100       244 if ($self->_expected->read() <= 0)
84             {
85 3         5 $self->{expected_end} = 1;
86             }
87              
88 199         261 return;
89             }
90              
91             sub _next_elem
92             {
93 160     160   177 my $self = shift;
94              
95 160         238 $self->_read_got();
96 160         254 $self->_read_expected();
97              
98 160         230 return;
99             }
100              
101             sub _ns
102             {
103 194     194   211 my $elem = shift;
104 194         331 my $ns = $elem->namespaceURI();
105              
106 194 100       383 return defined($ns) ? $ns : "";
107             }
108              
109             sub _compare_loop
110             {
111 5     5   7 my $self = shift;
112              
113             my $calc_prob = sub {
114 2     2   4 my $args = shift;
115              
116 2 50       6 if (!exists($args->{param}))
117             {
118 0         0 die "No 'param' specified.";
119             }
120             return
121             {
122             verdict => 0,
123             param => $args->{param},
124             (exists($args->{got})
125             ? (got => $args->{got}, expected => $args->{expected})
126 2 50       25 : ()
127             ),
128             }
129 5         22 };
130              
131             NODE_LOOP:
132 5   66     13 while ((!$self->_got_end()) && (!$self->_expected_end()))
133             {
134 268         338 my $type = $self->_got->nodeType();
135 268         355 my $exp_type = $self->_expected->nodeType();
136              
137 268 100       568 if ($type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE())
    100          
    50          
    100          
    100          
138             {
139 73         121 $self->_read_got();
140 73         102 redo NODE_LOOP;
141             }
142             elsif ($exp_type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE())
143             {
144 38         61 $self->_read_expected();
145 38         44 redo NODE_LOOP;
146             }
147             elsif ($type != $exp_type)
148             {
149 0         0 return $calc_prob->({param => "nodeType"});
150             }
151             elsif ($type == XML_READER_TYPE_TEXT())
152             {
153 39         50 my $got_text = $self->_got->value();
154 39         63 my $expected_text = $self->_expected->value();
155              
156 39         63 foreach my $t ($got_text, $expected_text)
157             {
158 78         238 $t =~ s{\A\s+}{}ms;
159 78         245 $t =~ s{\s+\z}{}ms;
160 78         294 $t =~ s{\s+}{ }gms;
161             }
162 39 50       91 if ($got_text ne $expected_text)
163             {
164 0         0 return $calc_prob->(
165             {
166             param => "text",
167             got => $got_text,
168             expected => $expected_text,
169             }
170             );
171             }
172             }
173             elsif ($type == XML_READER_TYPE_ELEMENT())
174             {
175             my $check = sub {
176 61 50   61   101 if ($self->_got->localName() ne $self->_expected->localName())
177             {
178 0         0 return $calc_prob->({param => "element_name"});
179             }
180 61 50       104 if (_ns($self->_got) ne _ns($self->_expected))
181             {
182 0         0 return $calc_prob->({param => "mismatch_ns"});
183             }
184              
185             my $list_attrs = sub {
186 122         163 my ($elem) = @_;
187              
188 122         133 my @list;
189              
190 122 100       230 if ($elem->moveToFirstAttribute())
191             {
192             my $add = sub {
193              
194 72         90 my $ns = _ns($elem);
195 72 100       134 if ($ns ne 'http://www.w3.org/2000/xmlns/')
196             {
197 59         186 push @list,
198             { ns => $ns, localName => $elem->localName() };
199             }
200 45         136 };
201              
202 45         96 $add->();
203 45         101 while ($elem->moveToNextAttribute() > 0)
204             {
205 27         31 $add->();
206             }
207 45 50       134 if ($elem->moveToElement() <= 0)
208             {
209 0         0 die "Cannot move back to element.";
210             }
211             }
212              
213 122         169 foreach my $attr (@list)
214             {
215             $attr->{value} = ((length($attr->{ns})
216             ? $elem->getAttributeNs(
217             $attr->{localName},
218             $attr->{ns},
219             )
220             : $elem->getAttribute($attr->{localName})
221 59 100 50     236 ) // '');
222             }
223              
224             return
225             [
226             sort {
227 122         235 ($a->{ns} cmp $b->{ns})
228             or
229             ($a->{localName} cmp $b->{localName})
230 22 50       70 } @list
231             ]
232             ;
233 61         171 };
234              
235 61         81 my @got_attrs = @{$list_attrs->($self->_got())};
  61         80  
236 61         85 my @exp_attrs = @{$list_attrs->($self->_expected())};
  61         111  
237              
238 61   100     141 while (@got_attrs and @exp_attrs)
239             {
240 29         46 my $got_a = shift(@got_attrs);
241 29         36 my $exp_a = shift(@exp_attrs);
242              
243 29 50       56 if ($got_a->{ns} ne $exp_a->{ns})
244             {
245             return
246             $calc_prob->(
247             {
248             param => "attr_ns",
249             got => $got_a->{ns},
250             expected => $exp_a->{ns},
251             }
252 0         0 );
253             }
254 29 50       48 if ($got_a->{localName} ne $exp_a->{localName})
255             {
256             return
257             $calc_prob->(
258             {
259             param => "attr_localName",
260             got => $got_a->{localName},
261             expected => $exp_a->{localName},
262             }
263 0         0 );
264             }
265 29 100       92 if ($got_a->{value} ne $exp_a->{value})
266             {
267             return
268             $calc_prob->(
269             {
270             param => "attr_value",
271             got => $got_a->{value},
272             expected => $exp_a->{value},
273             }
274 1         4 );
275             }
276             }
277 60 100       83 if (@got_attrs)
278             {
279 1         3 return $calc_prob->(
280             {
281             param => "extra_attr_got",
282             got => $self->_got,
283             expected => $self->_expected,
284             }
285             );
286             }
287 59 50       83 if (@exp_attrs)
288             {
289 0         0 return $calc_prob->(
290             {
291             param => "extra_attr_expected",
292             got => $self->_got,
293             expected => $self->_expected,
294             }
295             );
296             }
297 59         232 return;
298 60         230 };
299              
300 60 100       82 if (my $ret = $check->())
301             {
302 2         24 return $ret;
303             }
304              
305 58         94 my $is_got_empty = $self->_got->isEmptyElement;
306 58         84 my $is_expected_empty = $self->_expected->isEmptyElement;
307              
308 58 100 66     348 if ($is_got_empty && (!$is_expected_empty))
    50 33        
309             {
310 1         3 $self->_read_expected();
311 1 50       2 if (my $ret = $check->())
312             {
313 0         0 return $ret;
314             }
315             }
316             elsif ($is_expected_empty && (!$is_got_empty))
317             {
318 0         0 $self->_read_got();
319 0 0       0 if (my $ret = $check->())
320             {
321 0         0 return $ret;
322             }
323             }
324             }
325             }
326             continue
327             {
328 155         231 $self->_next_elem();
329             }
330              
331 3         14 return { verdict => 1};
332             }
333              
334             sub _get_diag_message
335             {
336 2     2   5 my ($self, $status_struct) = @_;
337              
338 2 50       16 if ($status_struct->{param} eq "nodeType")
    50          
    50          
    50          
    100          
    50          
    50          
339             {
340             return
341 0         0 "Different Node Type!\n"
342             . "Got: " . $self->_got->nodeType() . " at line " . $self->_got->lineNumber()
343             . "\n"
344             . "Expected: " . $self->_expected->nodeType() . " at line " . $self->_expected->lineNumber()
345             ;
346             }
347             elsif ($status_struct->{param} eq "text")
348             {
349             return
350 0         0 "Texts differ: Got <<$status_struct->{got}>> at " . $self->_got->lineNumber(). " ; Expected <<$status_struct->{expected}>> at ". $self->_expected->lineNumber();
351             }
352             elsif ($status_struct->{param} eq "element_name")
353             {
354             return
355 0         0 "Got name: " . $self->_got->localName(). " at " . $self->_got->lineNumber() .
356             " ; " .
357             "Expected name: " . $self->_expected->localName() . " at " .$self->_expected->lineNumber();
358             }
359             elsif ($status_struct->{param} eq "mismatch_ns")
360             {
361             return
362 0         0 "Got Namespace: " . _ns($self->_got). " at " . $self->_got->lineNumber() .
363             " ; " .
364             "Expected Namespace: " . _ns($self->_expected) . " at " .$self->_expected->lineNumber();
365             }
366             elsif ($status_struct->{param} eq "extra_attr_got")
367             {
368             return
369 1         3 "Extra attribute for got at " . $self->_got->lineNumber() .
370             " ; " .
371             "Expected at " .$self->_expected->lineNumber();
372             }
373             elsif ($status_struct->{param} eq "attr_localName")
374             {
375             return
376 0         0 "Got Attribute localName: <<$status_struct->{got}>> at " . $self->_got->lineNumber() .
377             " ; " .
378             "Expected Attribute localName: <<$status_struct->{expected}>> at " .$self->_expected->lineNumber();
379             }
380             elsif ($status_struct->{param} eq "attr_value")
381             {
382             return
383 1         5 "Got Attribute value: <<$status_struct->{got}>> at " . $self->_got->lineNumber() .
384             " ; " .
385             "Expected Attribute value: <<$status_struct->{expected}>> at " .$self->_expected->lineNumber();
386             }
387             else
388             {
389 0         0 die "Unknown param: $status_struct->{param}";
390             }
391             }
392              
393             sub compare
394             {
395 5     5 1 9 local $Test::Builder::Level = $Test::Builder::Level+1;
396              
397 5         9 my $self = shift;
398              
399 5         13 $self->_next_elem();
400              
401 5         11 my $status_struct = $self->_compare_loop();
402 5         11 my $verdict = $status_struct->{verdict};
403              
404 5 100       10 if (!$verdict)
405             {
406 2         5 diag($self->_get_diag_message($status_struct));
407             }
408              
409 5         1009 return ok($verdict, $self->{diag_message});
410             }
411              
412             sub is_xml_ordered
413             {
414 5     5 1 5781 local $Test::Builder::Level = $Test::Builder::Level+1;
415              
416 5         14 my ($got_params, $expected_params, $args, $message) = @_;
417              
418 5         28 my $comparator =
419             Test::XML::Ordered->new(
420             {
421             got_params => $got_params,
422             expected_params => $expected_params,
423             diag_message => $message,
424             }
425             );
426              
427 5         15 return $comparator->compare();
428             }
429              
430             1;
431              
432             __END__