File Coverage

blib/lib/Test/XML/Simple.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Test::XML::Simple;
2              
3 8     8   99313 use strict;
  8         11  
  8         211  
4 8     8   32 use warnings;
  8         10  
  8         293  
5              
6             our $VERSION = '1.05';
7              
8 8     8   40 use Test::Builder;
  8         9  
  8         137  
9 8     8   2172 use Test::More;
  8         16595  
  8         65  
10 8     8   5515 use Test::LongString;
  8         14163  
  8         44  
11 8     8   7486 use XML::LibXML;
  0            
  0            
12              
13             my $Test = Test::Builder->new();
14             my $Xml;
15              
16             sub import {
17             my $self = shift;
18             my $caller = caller;
19             no strict 'refs';
20             *{$caller.'::xml_valid'} = \&xml_valid;
21             *{$caller.'::xml_node'} = \&xml_node;
22             *{$caller.'::xml_is'} = \&xml_is;
23             *{$caller.'::xml_is_long'} = \&xml_is_long;
24             *{$caller.'::xml_is_deeply'} = \&xml_is_deeply;
25             *{$caller.'::xml_is_deeply_long'} = \&xml_is_deeply_long;
26             *{$caller.'::xml_like'} = \&xml_like;
27             *{$caller.'::xml_like_long'} = \&xml_like_long;
28              
29             $Test->exported_to($caller);
30             $Test->plan(@_);
31             }
32              
33             sub xml_valid($;$) {
34             my ($xml, $comment) = @_;
35             my $parsed_xml = _valid_xml($xml);
36             return 0 unless $parsed_xml;
37              
38             ok $parsed_xml, $comment;
39             }
40              
41             sub _valid_xml {
42             my $xml = shift;
43            
44             local $Test::Builder::Level = $Test::Builder::Level + 2;
45             return fail("XML is not defined") unless defined $xml;
46             return fail("XML is missing") unless $xml;
47             if ( ref $xml ) {
48             return fail("accept only 'XML::LibXML::Document' as object") unless ref $xml eq 'XML::LibXML::Document';
49             $Xml = $xml;
50             }
51             else {
52             return fail("string can't contain XML: no tags")
53             unless ($xml =~ //);
54             eval { $Xml = XML::LibXML->new->parse_string($xml); };
55             do { chomp $@; return fail($@) } if $@;
56             }
57             return $Xml;
58             }
59              
60             sub _find {
61             my ($xml_xpath, $xpath) = @_;
62             my @nodeset = $xml_xpath->findnodes($xpath);
63             local $Test::Builder::Level = $Test::Builder::Level + 2;
64             return fail("Couldn't find $xpath") unless @nodeset;
65             wantarray ? @nodeset : \@nodeset;
66             }
67            
68              
69             sub xml_node($$;$) {
70             my ($xml, $xpath, $comment) = @_;
71              
72             my $parsed_xml = _valid_xml($xml);
73             return 0 unless $parsed_xml;
74              
75             my $nodeset = _find($parsed_xml, $xpath);
76             return 0 if !$nodeset;
77              
78             ok(scalar @$nodeset, $comment);
79             }
80              
81              
82             sub xml_is($$$;$) {
83             return _xml_is( \&is_string, @_ );
84             }
85              
86             sub xml_is_long($$$;$) {
87             _xml_is(\&is, @_);
88             }
89              
90             sub _xml_is {
91             my ($comp_sub, $xml, $xpath, $value, $comment) = @_;
92              
93             local $Test::Builder::Level = $Test::Builder::Level + 2;
94             my $parsed_xml = _valid_xml($xml);
95             return 0 unless $parsed_xml;
96              
97             my $nodeset = _find($parsed_xml, $xpath);
98             return 0 if !$nodeset;
99              
100             my $ok = 1;
101             foreach my $node (@$nodeset) {
102             my @kids = $node->getChildNodes;
103             my $node_ok;
104             if (@kids) {
105             $node_ok = $comp_sub->( $kids[0]->toString, $value, $comment );
106             }
107             else {
108             my $got = $node->toString;
109             $got =~ s/^.*="(.*)"/$1/;
110             $node_ok = is $got, $value, $comment;
111             }
112              
113             # returns NOT OK if even one of tests fails
114             $ok = 0 unless $node_ok;
115             }
116              
117             return $ok;
118             }
119              
120             sub xml_is_deeply($$$;$) {
121             _xml_is_deeply(\&is_string, @_);
122             }
123              
124             sub xml_is_deeply_long($$$;$) {
125             _xml_is_deeply(\&is, @_);
126             }
127              
128             sub _xml_is_deeply {
129             my ($is_sub, $xml, $xpath, $candidate, $comment) = @_;
130              
131             my $parsed_xml = _valid_xml($xml);
132             return 0 unless $parsed_xml;
133              
134             my $candidate_xp;
135             eval {$candidate_xp = XML::LibXML->new->parse_string($candidate) };
136             return 0 unless $candidate_xp;
137              
138             my $parsed_thing = $parsed_xml->findnodes($xpath)->[0];
139             my $candidate_thing = $candidate_xp->findnodes('/')->[0];
140              
141             $candidate_thing = $candidate_thing->documentElement
142             if $parsed_thing->isa('XML::LibXML::Element');
143              
144             $is_sub->($parsed_thing->toString,
145             $candidate_thing->toString,
146             $comment);
147             }
148              
149             sub xml_like($$$;$) {
150             _xml_like(\&like_string, @_);
151             }
152              
153             sub xml_like_long($$$;$) {
154             _xml_like(\&like, @_);
155             }
156              
157             sub _xml_like {
158             my ($like_sub, $xml, $xpath, $regex, $comment) = @_;
159              
160             my $parsed_xml = _valid_xml($xml);
161             return 0 unless $parsed_xml;
162              
163             my $nodeset = _find($parsed_xml, $xpath);
164             return 0 if !$nodeset;
165              
166             foreach my $node (@$nodeset) {
167             my @kids = $node->getChildNodes;
168             my $found;
169             if (@kids) {
170             foreach my $kid (@kids) {
171             if ($kid->toString =~ /$regex/) {
172             $found = 1;
173             return $like_sub->($kid->toString, $regex, $comment);
174             }
175             }
176             if (! $found) {
177             $comment = "(no comment)" unless defined $comment;
178             local $Test::Builder::Level = $Test::Builder::Level + 2;
179             return ok(0, "$comment - no match in tag contents (including CDATA)");
180             }
181             }
182             else {
183             my $got = $node->toString;
184             $got =~ s/^.*="(.*)"/$1/;
185             local $Test::Builder::Level = $Test::Builder::Level + 2;
186             return $like_sub->( $got, $regex, $comment );
187             }
188             }
189             }
190              
191             1;
192             __END__