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