xmatch 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +2 -2
- data/lib/matcher/html_formatter.rb +16 -13
- data/lib/matcher/nokogiri_extensions.rb +5 -5
- data/lib/matcher/version.rb +1 -1
- data/lib/matcher/xmatch.html.erb +4 -2
- data/lib/matcher/xml.rb +8 -4
- data/spec/matcher/html_formatter_spec.rb +0 -3
- data/spec/matcher/xml_spec.rb +31 -12
- metadata +4 -4
data/README.markdown
CHANGED
@@ -27,9 +27,9 @@ A matcher provides access to the match information by xpath values:
|
|
27
27
|
Custom matchers
|
28
28
|
---------------
|
29
29
|
The actual values of some xml elements are hard to know in advance (timestamps and ids being typical examples). XMatch allows custom matchers to be applied
|
30
|
-
to provide a good guess at a match in advance of the match being run. Custom matchers are
|
30
|
+
to provide a good guess at a match in advance of the match being run. Custom matchers are predicates provided as Ruby Procs, identified by the xpath of the element they should be applied to.
|
31
31
|
|
32
|
-
custom_matchers = { "/bookstore/@id" => lambda {|elem| elem.value
|
32
|
+
custom_matchers = { "/bookstore/@id" => lambda {|elem| elem.value.match(/\d+/) }
|
33
33
|
xml = Matcher::Xml.new("<bookstore id='1'></bookstore>", custom_matchers)
|
34
34
|
xml.match("<bookstore id='2'></bookstore>") # ==> true
|
35
35
|
|
@@ -22,35 +22,38 @@ module Matcher
|
|
22
22
|
elem.attributes.values.each { | attr | match_data << match_info_for(attr) }
|
23
23
|
end
|
24
24
|
match_data.sort! {|a, b| a.line <=> b.line}
|
25
|
-
|
25
|
+
|
26
26
|
FileUtils.mkdir_p(@report_dir)
|
27
|
-
filename = "xmatch.html"
|
28
|
-
filename = "#{@prefix}-xmatch.html" if @prefix
|
27
|
+
filename = @prefix ? "#{@prefix}-xmatch.html" : "xmatch.html"
|
29
28
|
File.open(File.join(@report_dir, filename), 'w') { |f| f.write(generate_html(match_data)) }
|
30
29
|
end
|
31
30
|
|
32
31
|
private
|
33
|
-
|
32
|
+
|
34
33
|
def match_info_for(elem)
|
34
|
+
info = @matcher.results[elem.path]
|
35
35
|
result = @matcher.result_for(elem.path)
|
36
|
-
|
37
|
-
|
36
|
+
OpenStruct.new(:result => result,
|
37
|
+
:line => elem.line,
|
38
|
+
:path => elem.path,
|
39
|
+
:expected => info ? info.expected : Matcher::Xml::EXISTENCE,
|
40
|
+
:actual => info ? info.actual : Matcher::Xml::UNMATCHED)
|
38
41
|
end
|
39
|
-
|
42
|
+
|
40
43
|
def generate_html(data)
|
41
44
|
actual_filename = create_actual_file
|
42
45
|
expected_filename = create_expected_file
|
43
|
-
xml = @matcher
|
46
|
+
xml = @matcher
|
44
47
|
completedness = compute_completedness
|
45
48
|
match_info = data
|
46
49
|
html = ERB.new(File.read(TEMPLATE))
|
47
|
-
html.result(binding)
|
50
|
+
html.result(binding)
|
48
51
|
end
|
49
|
-
|
52
|
+
|
50
53
|
def compute_completedness
|
51
54
|
(@matcher.matches.size.to_f / @matcher.results.size.to_f * 100).to_i
|
52
55
|
end
|
53
|
-
|
56
|
+
|
54
57
|
def create_expected_file
|
55
58
|
write_xml("expected", @matcher.lhs)
|
56
59
|
end
|
@@ -61,11 +64,11 @@ module Matcher
|
|
61
64
|
|
62
65
|
def write_xml(name, xml)
|
63
66
|
file_name = "#{name}-#{Time.now.to_i}.xml"
|
64
|
-
file_name = "#{@prefix}-#{file_name}" if @prefix
|
67
|
+
file_name = "#{@prefix}-#{file_name}" if @prefix
|
65
68
|
File.open(File.join(@report_dir, file_name), 'w') { |f| f.write(xml)}
|
66
69
|
file_name
|
67
70
|
end
|
68
|
-
|
71
|
+
|
69
72
|
end
|
70
73
|
|
71
74
|
end
|
@@ -8,7 +8,7 @@ module Nokogiri
|
|
8
8
|
|
9
9
|
def matching(other, matcher)
|
10
10
|
other_elem = other.at_xpath(path)
|
11
|
-
matcher.record(self, false, Matcher::Xml::NOT_FOUND) unless other_elem
|
11
|
+
matcher.record(self.path, false, Matcher::Xml::EXISTENCE, Matcher::Xml::NOT_FOUND) unless other_elem
|
12
12
|
other_elem
|
13
13
|
end
|
14
14
|
end
|
@@ -34,14 +34,14 @@ module Nokogiri
|
|
34
34
|
|
35
35
|
def children_match?(other)
|
36
36
|
match = children.size == other.children.size
|
37
|
-
@matcher.record(self, match, "
|
37
|
+
@matcher.record(self.path, match, "#{children.size} children", "#{other.children.size} children")
|
38
38
|
match
|
39
39
|
end
|
40
40
|
|
41
41
|
def attributes_match?(other)
|
42
42
|
match = attributes.size == other.attributes.size
|
43
43
|
unless match
|
44
|
-
@matcher.record(self, match, "
|
44
|
+
@matcher.record(self.path, match, "#{attributes.size} attributes", "#{other.attributes.size} attributes")
|
45
45
|
return false
|
46
46
|
end
|
47
47
|
|
@@ -60,7 +60,7 @@ module Nokogiri
|
|
60
60
|
|
61
61
|
custom_matcher = matcher.custom_matchers[path]
|
62
62
|
match = custom_matcher ? custom_matcher.call(other_elem) : (content == other_elem.content)
|
63
|
-
@matcher.record(self, match,
|
63
|
+
@matcher.record(self.path, match, content, other_elem.content)
|
64
64
|
match
|
65
65
|
end
|
66
66
|
|
@@ -74,7 +74,7 @@ module Nokogiri
|
|
74
74
|
|
75
75
|
custom_matcher = matcher.custom_matchers[path]
|
76
76
|
match = custom_matcher ? custom_matcher.call(other_elem) : (value == other_elem.value)
|
77
|
-
matcher.record(self, match,
|
77
|
+
matcher.record(self.path, match, value, other_elem.value)
|
78
78
|
match
|
79
79
|
end
|
80
80
|
|
data/lib/matcher/version.rb
CHANGED
data/lib/matcher/xmatch.html.erb
CHANGED
@@ -35,13 +35,15 @@ Expected xml document is <b><%= completedness %>%</b> 'matched'.
|
|
35
35
|
<tr>
|
36
36
|
<th>Line</th>
|
37
37
|
<th>Path</th>
|
38
|
-
<th>
|
38
|
+
<th>Expected</th>
|
39
|
+
<th>Actual</th>
|
39
40
|
</tr>
|
40
41
|
<% match_info.each do | match | %>
|
41
42
|
<tr class=<%= match.result %> >
|
42
43
|
<td><%= match.line %></td>
|
43
44
|
<td><%= match.path %></td>
|
44
|
-
<td><%= match.
|
45
|
+
<td><%= match.expected %></td>
|
46
|
+
<td><%= match.actual %></td>
|
45
47
|
</tr>
|
46
48
|
<% end %>
|
47
49
|
</table>
|
data/lib/matcher/xml.rb
CHANGED
@@ -5,7 +5,9 @@ module Matcher
|
|
5
5
|
|
6
6
|
class Xml
|
7
7
|
|
8
|
-
NOT_FOUND = "Not found
|
8
|
+
NOT_FOUND = "[Not found]"
|
9
|
+
EXISTENCE = "[Existence]"
|
10
|
+
UNMATCHED = "[Unmatched]"
|
9
11
|
|
10
12
|
attr_reader :lhs, :rhs, :custom_matchers, :results
|
11
13
|
|
@@ -21,8 +23,10 @@ module Matcher
|
|
21
23
|
compare(@lhs, @rhs)
|
22
24
|
end
|
23
25
|
|
24
|
-
def record(
|
25
|
-
|
26
|
+
def record(path, result, expected, actual)
|
27
|
+
# support 0 as true (for regex matches)
|
28
|
+
r = !result || result.nil? ? false : true
|
29
|
+
@results[path] = OpenStruct.new(:result => r, :expected => expected, :actual => actual)
|
26
30
|
end
|
27
31
|
|
28
32
|
def result_for(path)
|
@@ -43,7 +47,7 @@ module Matcher
|
|
43
47
|
|
44
48
|
def results_that_are(value)
|
45
49
|
match_info = {}
|
46
|
-
@results.
|
50
|
+
@results.each { |path, info| match_info[path] = info if info.result == value}
|
47
51
|
match_info
|
48
52
|
end
|
49
53
|
|
data/spec/matcher/xml_spec.rb
CHANGED
@@ -4,10 +4,12 @@ require 'nokogiri'
|
|
4
4
|
|
5
5
|
describe Matcher::Xml do
|
6
6
|
|
7
|
-
def verify_mismatch(path,
|
7
|
+
def verify_mismatch(path, expected, actual, count = 1)
|
8
8
|
match = @xml.match(@rhs)
|
9
9
|
@xml.mismatches.should have(count).mismatch
|
10
|
-
@xml.mismatches[path].should
|
10
|
+
@xml.mismatches[path].result.should be_false
|
11
|
+
@xml.mismatches[path].expected.should == expected.to_s
|
12
|
+
@xml.mismatches[path].actual.should == actual
|
11
13
|
end
|
12
14
|
|
13
15
|
context "attributes" do
|
@@ -91,7 +93,7 @@ describe Matcher::Xml do
|
|
91
93
|
<book></book>
|
92
94
|
</bookstore>
|
93
95
|
eos
|
94
|
-
verify_mismatch("/bookstore", "
|
96
|
+
verify_mismatch("/bookstore", "1 children", "2 children")
|
95
97
|
end
|
96
98
|
|
97
99
|
it "should not match when rhs has a missing element" do
|
@@ -101,7 +103,7 @@ describe Matcher::Xml do
|
|
101
103
|
</book>
|
102
104
|
</bookstore>
|
103
105
|
eos
|
104
|
-
verify_mismatch("/bookstore/book/title", Matcher::Xml::NOT_FOUND, 2)
|
106
|
+
verify_mismatch("/bookstore/book/title", Matcher::Xml::EXISTENCE, Matcher::Xml::NOT_FOUND, 2)
|
105
107
|
end
|
106
108
|
|
107
109
|
end
|
@@ -116,7 +118,7 @@ describe Matcher::Xml do
|
|
116
118
|
</bookx>
|
117
119
|
</bookstore>
|
118
120
|
eos
|
119
|
-
verify_mismatch("/bookstore/book", Matcher::Xml::NOT_FOUND, 3)
|
121
|
+
verify_mismatch("/bookstore/book", Matcher::Xml::EXISTENCE, Matcher::Xml::NOT_FOUND, 3)
|
120
122
|
end
|
121
123
|
|
122
124
|
end
|
@@ -131,7 +133,7 @@ describe Matcher::Xml do
|
|
131
133
|
</book>
|
132
134
|
</bookstore>
|
133
135
|
eos
|
134
|
-
verify_mismatch("/bookstore/book/@category", Matcher::Xml::NOT_FOUND)
|
136
|
+
verify_mismatch("/bookstore/book/@category", Matcher::Xml::EXISTENCE, Matcher::Xml::NOT_FOUND)
|
135
137
|
end
|
136
138
|
|
137
139
|
it "should not match when an attribute value doesn't match" do
|
@@ -142,7 +144,7 @@ describe Matcher::Xml do
|
|
142
144
|
</book>
|
143
145
|
</bookstore>
|
144
146
|
eos
|
145
|
-
verify_mismatch("/bookstore/book/@category",
|
147
|
+
verify_mismatch("/bookstore/book/@category", 'COOKING', "COOKINGx")
|
146
148
|
end
|
147
149
|
|
148
150
|
it "should not match when rhs has an extra attribute" do
|
@@ -153,7 +155,7 @@ describe Matcher::Xml do
|
|
153
155
|
</book>
|
154
156
|
</bookstore>
|
155
157
|
eos
|
156
|
-
verify_mismatch("/bookstore/book", "
|
158
|
+
verify_mismatch("/bookstore/book", "1 attributes", "2 attributes")
|
157
159
|
end
|
158
160
|
|
159
161
|
it "should not match when rhs has less attributes" do
|
@@ -164,7 +166,7 @@ describe Matcher::Xml do
|
|
164
166
|
</book>
|
165
167
|
</bookstore>
|
166
168
|
eos
|
167
|
-
verify_mismatch("/bookstore/book/title", "
|
169
|
+
verify_mismatch("/bookstore/book/title", "1 attributes", "0 attributes")
|
168
170
|
end
|
169
171
|
|
170
172
|
it "should not match when rhs has more attributes" do
|
@@ -175,7 +177,7 @@ describe Matcher::Xml do
|
|
175
177
|
</book>
|
176
178
|
</bookstore>
|
177
179
|
eos
|
178
|
-
verify_mismatch("/bookstore/book/title", "
|
180
|
+
verify_mismatch("/bookstore/book/title", "1 attributes", "2 attributes")
|
179
181
|
end
|
180
182
|
|
181
183
|
end
|
@@ -190,7 +192,7 @@ describe Matcher::Xml do
|
|
190
192
|
</book>
|
191
193
|
</bookstore>
|
192
194
|
eos
|
193
|
-
verify_mismatch("/bookstore/book/title/text()", "
|
195
|
+
verify_mismatch("/bookstore/book/title/text()", "Everyday Italian", "Everyday Italianx")
|
194
196
|
end
|
195
197
|
|
196
198
|
end
|
@@ -257,7 +259,7 @@ describe Matcher::Xml do
|
|
257
259
|
eos
|
258
260
|
|
259
261
|
@xml = Matcher::Xml.new(lhs)
|
260
|
-
verify_mismatch("/bookstore/book[2]/@category", Matcher::Xml::NOT_FOUND)
|
262
|
+
verify_mismatch("/bookstore/book[2]/@category", Matcher::Xml::EXISTENCE, Matcher::Xml::NOT_FOUND)
|
261
263
|
end
|
262
264
|
|
263
265
|
context 'matches' do
|
@@ -268,6 +270,15 @@ describe Matcher::Xml do
|
|
268
270
|
xml.match(lhs)
|
269
271
|
xml.matches.should have(3).matches
|
270
272
|
end
|
273
|
+
|
274
|
+
it "should have expected and actual results" do
|
275
|
+
lhs = "<bookstore><book>foo</book></bookstore>"
|
276
|
+
xml = Matcher::Xml.new(lhs)
|
277
|
+
xml.match(lhs).should be_true
|
278
|
+
match_info = xml.matches["/bookstore"]
|
279
|
+
match_info.expected.should == "1 children"
|
280
|
+
match_info.actual.should == "1 children"
|
281
|
+
end
|
271
282
|
|
272
283
|
end
|
273
284
|
|
@@ -307,6 +318,14 @@ describe Matcher::Xml do
|
|
307
318
|
xml = Matcher::Xml.new("<bookstore><book>foo</book></bookstore", custom_matchers)
|
308
319
|
xml.match("<bookstore><book>bar</book></bookstore").should be_true
|
309
320
|
end
|
321
|
+
|
322
|
+
it "supports regex style matching" do
|
323
|
+
custom_matchers = { "/bookstore/book/text()" => lambda {|elem| elem.content =~ /bar/} }
|
324
|
+
xml = Matcher::Xml.new("<bookstore><book>foo</book></bookstore", custom_matchers)
|
325
|
+
xml.match("<bookstore><book>bar</book></bookstore").should be_true
|
326
|
+
xml.mismatches.should be_empty
|
327
|
+
xml.matches.should have(3).matches
|
328
|
+
end
|
310
329
|
|
311
330
|
end
|
312
331
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xmatch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 5
|
10
|
+
version: 0.1.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Peter Moran
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-11-
|
18
|
+
date: 2010-11-23 00:00:00 +11:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|