xmatch 0.1.1 → 0.1.2
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/lib/matcher/html_formatter.rb +17 -3
- data/lib/matcher/nokogiri_extensions.rb +40 -31
- data/lib/matcher/version.rb +1 -1
- data/lib/matcher/xmatch.html.erb +6 -7
- data/lib/matcher/xml.rb +13 -13
- data/spec/matcher/html_formatter_spec.rb +11 -0
- data/spec/matcher/xml_spec.rb +13 -32
- metadata +6 -4
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'fileutils'
|
3
|
+
require 'ostruct'
|
3
4
|
|
4
5
|
module Matcher
|
5
6
|
|
@@ -9,20 +10,33 @@ module Matcher
|
|
9
10
|
|
10
11
|
def initialize(matcher, args = {})
|
11
12
|
@matcher = matcher
|
12
|
-
@report_dir = args[:report_dir] ||
|
13
|
+
@report_dir = args[:report_dir] || '/tmp/xmatch'
|
13
14
|
end
|
14
15
|
|
15
16
|
def format
|
17
|
+
match_data = []
|
18
|
+
@matcher.lhs.traverse do |elem|
|
19
|
+
next if elem.xml?
|
20
|
+
match_data << match_info_for(elem)
|
21
|
+
elem.attributes.values.each { | attr | match_data << match_info_for(attr) }
|
22
|
+
end
|
23
|
+
|
16
24
|
FileUtils.mkdir_p(@report_dir)
|
17
|
-
File.open(File.join(@report_dir, "xmatch.html"), 'w') { |f| f.write(generate_html) }
|
25
|
+
File.open(File.join(@report_dir, "xmatch.html"), 'w') { |f| f.write(generate_html(match_data)) }
|
18
26
|
end
|
19
27
|
|
20
28
|
private
|
29
|
+
|
30
|
+
def match_info_for(elem)
|
31
|
+
result = @matcher.result_for(elem.path)
|
32
|
+
OpenStruct.new(:result => result, :line => elem.line, :path => elem.path, :message => @matcher.mismatches[elem.path])
|
33
|
+
end
|
21
34
|
|
22
|
-
def generate_html
|
35
|
+
def generate_html(data)
|
23
36
|
actual_filename = create_actual_file
|
24
37
|
expected_filename = create_expected_file
|
25
38
|
xml = @matcher
|
39
|
+
match_info = data
|
26
40
|
html = ERB.new(File.read(TEMPLATE))
|
27
41
|
html.result(binding)
|
28
42
|
end
|
@@ -4,71 +4,80 @@ module Nokogiri
|
|
4
4
|
|
5
5
|
module XML
|
6
6
|
|
7
|
+
class Node
|
8
|
+
|
9
|
+
def matching(other, matcher)
|
10
|
+
other_elem = other.at_xpath(path)
|
11
|
+
matcher.record(self, false, "not found") unless other_elem
|
12
|
+
other_elem
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Document
|
17
|
+
|
18
|
+
def match?(other, matcher)
|
19
|
+
matching(other, matcher)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
7
24
|
class Element
|
8
25
|
|
9
26
|
def match?(other, matcher)
|
10
27
|
@matcher = matcher
|
11
|
-
|
12
|
-
|
13
|
-
|
28
|
+
other_elem = matching(other, matcher)
|
29
|
+
return false unless other_elem
|
30
|
+
children_match?(other_elem) & attributes_match?(other_elem)
|
14
31
|
end
|
15
32
|
|
16
33
|
private
|
17
34
|
|
18
35
|
def children_match?(other)
|
19
36
|
match = children.size == other.children.size
|
20
|
-
@matcher.record(
|
21
|
-
match
|
22
|
-
end
|
23
|
-
|
24
|
-
def name_matches?(other)
|
25
|
-
match = name == other.name
|
26
|
-
@matcher.record(path, match, "expected element '#{name}', got '#{other.name}'")
|
37
|
+
@matcher.record(self, match, "expected #{children.size} children, got #{other.children.size}")
|
27
38
|
match
|
28
39
|
end
|
29
40
|
|
30
41
|
def attributes_match?(other)
|
31
42
|
match = attributes.size == other.attributes.size
|
32
|
-
|
33
|
-
attributes.
|
34
|
-
|
35
|
-
end
|
36
|
-
else
|
37
|
-
@matcher.record(path, match, "expected #{attributes.size} attributes, got #{other.attributes.size}")
|
43
|
+
unless match
|
44
|
+
@matcher.record(self, match, "expected #{attributes.size} attributes, got #{other.attributes.size}")
|
45
|
+
return false
|
38
46
|
end
|
47
|
+
|
48
|
+
attributes.values.each { |attr| match = match & attr.match?(other, @matcher) }
|
39
49
|
match
|
40
50
|
end
|
41
51
|
|
42
52
|
end
|
43
53
|
|
44
|
-
class Document
|
45
|
-
def match?(other, matcher = nil)
|
46
|
-
true
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
54
|
class Text
|
55
|
+
|
51
56
|
def match?(other, matcher)
|
57
|
+
@matcher = matcher
|
58
|
+
other_elem = matching(other, matcher)
|
59
|
+
return false unless other_elem
|
60
|
+
|
52
61
|
custom_matcher = matcher.custom_matchers[path]
|
53
|
-
match = custom_matcher ? custom_matcher.call(
|
54
|
-
matcher.record(
|
62
|
+
match = custom_matcher ? custom_matcher.call(other_elem) : (content == other_elem.content)
|
63
|
+
@matcher.record(self, match, "expected '#{content}', got '#{other_elem.content}'")
|
55
64
|
match
|
56
65
|
end
|
66
|
+
|
57
67
|
end
|
58
68
|
|
59
69
|
class Attr
|
70
|
+
|
60
71
|
def match?(other, matcher)
|
61
|
-
|
62
|
-
|
63
|
-
matcher.record(parent.path, false, "expected attribute missing")
|
64
|
-
return false
|
65
|
-
end
|
72
|
+
other_elem = matching(other, matcher)
|
73
|
+
return false unless other_elem
|
66
74
|
|
67
75
|
custom_matcher = matcher.custom_matchers[path]
|
68
|
-
match = custom_matcher ? custom_matcher.call(
|
69
|
-
matcher.record(
|
76
|
+
match = custom_matcher ? custom_matcher.call(other_elem) : (value == other_elem.value)
|
77
|
+
matcher.record(self, match, "expected '#{value}', got '#{other_elem.value}'")
|
70
78
|
match
|
71
79
|
end
|
80
|
+
|
72
81
|
end
|
73
82
|
|
74
83
|
end
|
data/lib/matcher/version.rb
CHANGED
data/lib/matcher/xmatch.html.erb
CHANGED
@@ -35,13 +35,12 @@
|
|
35
35
|
<th>Path</th>
|
36
36
|
<th>Mismatch</th>
|
37
37
|
</tr>
|
38
|
-
<%
|
39
|
-
|
40
|
-
|
41
|
-
<td><%=
|
42
|
-
|
43
|
-
|
44
|
-
</tr>
|
38
|
+
<% match_info.each do | match | %>
|
39
|
+
<tr class=<%= match.result %> >
|
40
|
+
<td><%= match.line %></td>
|
41
|
+
<td><%= match.path %></td>
|
42
|
+
<td><%= match.message || ' ' %></td>
|
43
|
+
</tr>
|
45
44
|
<% end %>
|
46
45
|
</table>
|
47
46
|
|
data/lib/matcher/xml.rb
CHANGED
@@ -19,8 +19,8 @@ module Matcher
|
|
19
19
|
compare(@lhs, @rhs)
|
20
20
|
end
|
21
21
|
|
22
|
-
def record(
|
23
|
-
@results[path] = OpenStruct.new(:result => result, :message => message)
|
22
|
+
def record(lhs, result, message)
|
23
|
+
@results[lhs.path] = OpenStruct.new(:result => result, :message => message)
|
24
24
|
end
|
25
25
|
|
26
26
|
def result_for(path)
|
@@ -28,20 +28,22 @@ module Matcher
|
|
28
28
|
return "mismatched" if mismatches[path]
|
29
29
|
"unmatched"
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def matches
|
33
|
-
|
34
|
-
@results.each_pair { |k, v| match_info[k] = '' if v.result }
|
35
|
-
match_info
|
33
|
+
results_that_are(true)
|
36
34
|
end
|
37
35
|
|
38
36
|
def mismatches
|
39
|
-
|
40
|
-
@results.each_pair { |k, v| match_info[k] = v.message unless v.result }
|
41
|
-
match_info
|
37
|
+
results_that_are(false)
|
42
38
|
end
|
43
39
|
|
44
40
|
private
|
41
|
+
|
42
|
+
def results_that_are(value)
|
43
|
+
match_info = {}
|
44
|
+
@results.each_pair { |path, info| match_info[path] = info.message if info.result == value}
|
45
|
+
match_info
|
46
|
+
end
|
45
47
|
|
46
48
|
def parse(xml)
|
47
49
|
xml_as_string = xml.instance_of?(Nokogiri::XML::Document) ? xml.to_xml : xml
|
@@ -50,10 +52,8 @@ module Matcher
|
|
50
52
|
|
51
53
|
def compare(lhs, rhs)
|
52
54
|
return false unless lhs && rhs
|
53
|
-
match =
|
54
|
-
lhs.
|
55
|
-
match = match & compare(child, rhs.children[i])
|
56
|
-
end
|
55
|
+
match = true
|
56
|
+
lhs.traverse { |node| match = match & node.match?(rhs, self) }
|
57
57
|
match
|
58
58
|
end
|
59
59
|
|
data/spec/matcher/xml_spec.rb
CHANGED
@@ -4,9 +4,9 @@ require 'nokogiri'
|
|
4
4
|
|
5
5
|
describe Matcher::Xml do
|
6
6
|
|
7
|
-
def verify_mismatch(path, message)
|
8
|
-
@xml.match(@rhs)
|
9
|
-
@xml.mismatches.should have(
|
7
|
+
def verify_mismatch(path, message, count = 1)
|
8
|
+
match = @xml.match(@rhs)
|
9
|
+
@xml.mismatches.should have(count).mismatch
|
10
10
|
@xml.mismatches[path].should == message
|
11
11
|
end
|
12
12
|
|
@@ -95,21 +95,13 @@ describe Matcher::Xml do
|
|
95
95
|
end
|
96
96
|
|
97
97
|
it "should not match when rhs has a missing element" do
|
98
|
-
@lhs = <<-eos
|
99
|
-
<bookstore>
|
100
|
-
<book category="COOKING">
|
101
|
-
<title lang="en">Everyday Italian</title>
|
102
|
-
</book>
|
103
|
-
</bookstore>
|
104
|
-
eos
|
105
|
-
|
106
98
|
@rhs = <<-eos
|
107
99
|
<bookstore>
|
108
100
|
<book category="COOKING">
|
109
101
|
</book>
|
110
102
|
</bookstore>
|
111
103
|
eos
|
112
|
-
verify_mismatch("/bookstore/book/title", "
|
104
|
+
verify_mismatch("/bookstore/book/title", "not found", 2)
|
113
105
|
end
|
114
106
|
|
115
107
|
end
|
@@ -124,7 +116,7 @@ describe Matcher::Xml do
|
|
124
116
|
</bookx>
|
125
117
|
</bookstore>
|
126
118
|
eos
|
127
|
-
verify_mismatch("/bookstore/book", "
|
119
|
+
verify_mismatch("/bookstore/book", "not found", 3)
|
128
120
|
end
|
129
121
|
|
130
122
|
end
|
@@ -139,7 +131,7 @@ describe Matcher::Xml do
|
|
139
131
|
</book>
|
140
132
|
</bookstore>
|
141
133
|
eos
|
142
|
-
verify_mismatch("/bookstore/book", "
|
134
|
+
verify_mismatch("/bookstore/book/@category", "not found")
|
143
135
|
end
|
144
136
|
|
145
137
|
it "should not match when an attribute value doesn't match" do
|
@@ -150,7 +142,7 @@ describe Matcher::Xml do
|
|
150
142
|
</book>
|
151
143
|
</bookstore>
|
152
144
|
eos
|
153
|
-
verify_mismatch("/bookstore/book", "
|
145
|
+
verify_mismatch("/bookstore/book/@category", "expected 'COOKING', got 'COOKINGx'")
|
154
146
|
end
|
155
147
|
|
156
148
|
it "should not match when rhs has an extra attribute" do
|
@@ -233,11 +225,10 @@ describe Matcher::Xml do
|
|
233
225
|
</bookstore>
|
234
226
|
eos
|
235
227
|
@xml.match(rhs)
|
236
|
-
@xml.mismatches.should have(
|
228
|
+
@xml.mismatches.should have(4).mismatches
|
237
229
|
end
|
238
230
|
|
239
231
|
it "should contain parent's path when an attribute doesn't match" do
|
240
|
-
|
241
232
|
lhs = <<-eos
|
242
233
|
<bookstore>
|
243
234
|
<book category="COOKING">
|
@@ -262,18 +253,16 @@ describe Matcher::Xml do
|
|
262
253
|
eos
|
263
254
|
|
264
255
|
@xml = Matcher::Xml.new(lhs)
|
265
|
-
verify_mismatch("/bookstore/book[2]", "
|
256
|
+
verify_mismatch("/bookstore/book[2]/@category", "not found")
|
266
257
|
end
|
267
258
|
|
268
259
|
context 'matches' do
|
269
260
|
|
270
|
-
it "should
|
271
|
-
lhs = "<bookstore><book
|
261
|
+
it "should contain each match" do
|
262
|
+
lhs = "<bookstore><book>foo</book></bookstore>"
|
272
263
|
xml = Matcher::Xml.new(lhs)
|
273
264
|
xml.match(lhs)
|
274
|
-
xml.matches.should have(
|
275
|
-
xml.matches.should include("/bookstore")
|
276
|
-
xml.matches.values.all? {|m| m == ''}.should be_true
|
265
|
+
xml.matches.should have(3).matches
|
277
266
|
end
|
278
267
|
|
279
268
|
end
|
@@ -282,26 +271,18 @@ describe Matcher::Xml do
|
|
282
271
|
|
283
272
|
context "match results" do
|
284
273
|
|
285
|
-
it "provides all results"
|
286
|
-
|
287
274
|
it "returns 'matched' for a path that matched correctly" do
|
288
275
|
xml = Matcher::Xml.new("<bookstore></bookstore>")
|
289
276
|
xml.match("<bookstore></bookstore>")
|
290
277
|
xml.result_for("/bookstore").should == "matched"
|
291
278
|
end
|
292
279
|
|
293
|
-
it "returns '
|
280
|
+
it "returns 'unmatched' for a path that was not found" do
|
294
281
|
xml = Matcher::Xml.new("<bookstore></bookstore>")
|
295
282
|
xml.match("<bookstorex></bookstorex>")
|
296
283
|
xml.result_for("/bookstore").should == "mismatched"
|
297
284
|
end
|
298
285
|
|
299
|
-
it "returns 'unmatched' for a path that was not matched at all" do
|
300
|
-
xml = Matcher::Xml.new("<bookstore><foo></foo></bookstore>")
|
301
|
-
xml.match("<bookstorex></bookstorex>")
|
302
|
-
xml.result_for("/bookstore/foo").should == "unmatched"
|
303
|
-
end
|
304
|
-
|
305
286
|
end
|
306
287
|
|
307
288
|
context "custom matchers" do
|
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: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
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-19 00:00:00 +11:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- spec/fixtures/books7.xml
|
94
94
|
- spec/fixtures/books8.xml
|
95
95
|
- spec/fixtures/books9.xml
|
96
|
+
- spec/matcher/html_formatter_spec.rb
|
96
97
|
- spec/matcher/smoke_spec.rb
|
97
98
|
- spec/matcher/xml_spec.rb
|
98
99
|
- spec/spec_helper.rb
|
@@ -142,6 +143,7 @@ test_files:
|
|
142
143
|
- spec/fixtures/books7.xml
|
143
144
|
- spec/fixtures/books8.xml
|
144
145
|
- spec/fixtures/books9.xml
|
146
|
+
- spec/matcher/html_formatter_spec.rb
|
145
147
|
- spec/matcher/smoke_spec.rb
|
146
148
|
- spec/matcher/xml_spec.rb
|
147
149
|
- spec/spec_helper.rb
|