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.
@@ -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] || File.dirname(__FILE__) + '/../../reports'
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
- children_match?(other) &&
12
- name_matches?(other) &&
13
- attributes_match?(other)
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(path, match, "expected #{children.size} children, got #{other.children.size}")
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
- if match
33
- attributes.each_pair do |name, lhs|
34
- match = match && lhs.match?(other.attributes[name], @matcher)
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(other) : (content == other.content)
54
- matcher.record(path, match, "expected '#{content}', got '#{other.content}'")
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
- if other.nil?
62
- # record parent's path: nokogiri's traverse won't find attrs as children, so formatter won't report on them
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(other) : (value == other.value)
69
- matcher.record(parent.path, match, "attribute '#{name}' expected '#{value}', got '#{other.value}'")
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
@@ -1,3 +1,3 @@
1
1
  module Representative
2
- VERSION = "0.1.1".freeze
2
+ VERSION = "0.1.2".freeze
3
3
  end
@@ -35,13 +35,12 @@
35
35
  <th>Path</th>
36
36
  <th>Mismatch</th>
37
37
  </tr>
38
- <% xml.lhs.traverse do | elem | %>
39
- <% next if elem.xml? %>
40
- <tr class=<%= xml.result_for(elem.path) %> >
41
- <td><%= elem.line %></td>
42
- <td><%= elem.path %></td>
43
- <td><%= xml.mismatches[elem.path] || '&nbsp;' %></td>
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 || '&nbsp;' %></td>
43
+ </tr>
45
44
  <% end %>
46
45
  </table>
47
46
 
@@ -19,8 +19,8 @@ module Matcher
19
19
  compare(@lhs, @rhs)
20
20
  end
21
21
 
22
- def record(path, result, message = nil)
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
- match_info = {}
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
- match_info = {}
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 = lhs.match?(rhs, self)
54
- lhs.children.each_with_index do |child, i|
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
 
@@ -0,0 +1,11 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ require 'nokogiri'
4
+
5
+ describe Matcher::HtmlFormatter do
6
+
7
+ it "should allow output file to be specified"
8
+
9
+ it "should show percentage complete"
10
+
11
+ end
@@ -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).should be_false
9
- @xml.mismatches.should have(1).mismatch
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", "expected 1 children, got 0")
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", "expected element 'book', got 'bookx'")
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", "expected attribute missing")
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", "attribute 'category' expected 'COOKING', got 'COOKINGx'")
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(2).mismatches
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]", "expected attribute missing")
256
+ verify_mismatch("/bookstore/book[2]/@category", "not found")
266
257
  end
267
258
 
268
259
  context 'matches' do
269
260
 
270
- it "should be provided with no message" do
271
- lhs = "<bookstore><book></book></bookstore>"
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(2).matches
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 'mismatched' for a path that was mismatched" do
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: 25
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 1
10
- version: 0.1.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 00:00:00 +11:00
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