xmatch 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|