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