xmatch 0.1.1
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/LICENSE +20 -0
- data/README.markdown +53 -0
- data/Rakefile +31 -0
- data/lib/matcher/html_formatter.rb +47 -0
- data/lib/matcher/nokogiri_extensions.rb +76 -0
- data/lib/matcher/text_formatter.rb +26 -0
- data/lib/matcher/version.rb +3 -0
- data/lib/matcher/xmatch.html.erb +49 -0
- data/lib/matcher/xml.rb +62 -0
- data/lib/xmatch.rb +4 -0
- data/spec/fixtures/books1.xml +37 -0
- data/spec/fixtures/books10.xml +37 -0
- data/spec/fixtures/books2.xml +37 -0
- data/spec/fixtures/books3.xml +37 -0
- data/spec/fixtures/books4.xml +37 -0
- data/spec/fixtures/books5.xml +37 -0
- data/spec/fixtures/books6.xml +37 -0
- data/spec/fixtures/books7.xml +44 -0
- data/spec/fixtures/books8.xml +40 -0
- data/spec/fixtures/books9.xml +37 -0
- data/spec/matcher/smoke_spec.rb +25 -0
- data/spec/matcher/xml_spec.rb +328 -0
- data/spec/spec_helper.rb +8 -0
- metadata +148 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Peter Moran
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
XMatch
|
2
|
+
==============
|
3
|
+
|
4
|
+
XMatch is a Ruby library for comparing two XML documents and reporting on mismatches. An XML document will match another if:
|
5
|
+
|
6
|
+
* elements have the same name
|
7
|
+
* elements have the same number of children
|
8
|
+
* elements have the same number of attributes
|
9
|
+
* attributes have the same value
|
10
|
+
* text elements have the same content
|
11
|
+
|
12
|
+
Blank elements are ignored.
|
13
|
+
XMatch uses Nokogiri for xml parsing.
|
14
|
+
|
15
|
+
Matching XML
|
16
|
+
------------
|
17
|
+
Given two XML documents as strings, XMatch is run by:
|
18
|
+
|
19
|
+
xml = Matcher::Xml.new(lhs)
|
20
|
+
xml.match(rhs)
|
21
|
+
|
22
|
+
A matcher provides access to the match information by xpath values:
|
23
|
+
|
24
|
+
xml.matches
|
25
|
+
xml.mismatches
|
26
|
+
|
27
|
+
Custom matchers
|
28
|
+
---------------
|
29
|
+
The actual value of some xml elements are hard to know in advance, timestamps and ids being typical examples. XMatch allows custom matches to be applied
|
30
|
+
to provide a good guess at a match in advance of the match being run. Custom matchers are Ruby procs.
|
31
|
+
|
32
|
+
custom_matchers = "/bookstore/@id" => lambda {|elem| elem.value == '2'}
|
33
|
+
xml = Matcher::Xml.new("<bookstore id='1'></bookstore>", custom_matchers)
|
34
|
+
xml.match("<bookstore id='2'></bookstore>") # ==> true
|
35
|
+
|
36
|
+
Formatting match results
|
37
|
+
------------------------
|
38
|
+
|
39
|
+
The HTML formatter provides a cucumber-inspired report of the match results:
|
40
|
+
|
41
|
+
Matcher::HtmlFormatter.new(xml).format
|
42
|
+
|
43
|
+
Installation
|
44
|
+
------------
|
45
|
+
|
46
|
+
XMatch is packaged as a Gem. Install with:
|
47
|
+
|
48
|
+
gem install xmatch
|
49
|
+
|
50
|
+
Copyright
|
51
|
+
---------
|
52
|
+
|
53
|
+
Copyright (c) 2009 Peter Moran. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
require "rubygems"
|
3
|
+
require "rake"
|
4
|
+
require "rake/clean"
|
5
|
+
|
6
|
+
require 'xmatch'
|
7
|
+
|
8
|
+
require "spec/rake/spectask"
|
9
|
+
|
10
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
11
|
+
spec.libs << 'lib' << 'spec'
|
12
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
13
|
+
# spec.spec_opts << "-Du"
|
14
|
+
spec.spec_opts << "--color"
|
15
|
+
end
|
16
|
+
|
17
|
+
task :default => :spec
|
18
|
+
|
19
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
20
|
+
spec.libs << 'lib' << 'spec'
|
21
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
22
|
+
spec.rcov = true
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Match two xml documents"
|
26
|
+
task :match, :lhs, :rhs do |t, args|
|
27
|
+
puts "** Matching #{args[:lhs]} with #{args[:rhs]}"
|
28
|
+
xml = Matcher::Xml.new(File.read(args[:lhs]))
|
29
|
+
xml.match(File.read(args[:rhs]))
|
30
|
+
Matcher::HtmlFormatter.new(xml).format
|
31
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Matcher
|
5
|
+
|
6
|
+
class HtmlFormatter
|
7
|
+
|
8
|
+
TEMPLATE = File.dirname(__FILE__) + '/xmatch.html.erb'
|
9
|
+
|
10
|
+
def initialize(matcher, args = {})
|
11
|
+
@matcher = matcher
|
12
|
+
@report_dir = args[:report_dir] || File.dirname(__FILE__) + '/../../reports'
|
13
|
+
end
|
14
|
+
|
15
|
+
def format
|
16
|
+
FileUtils.mkdir_p(@report_dir)
|
17
|
+
File.open(File.join(@report_dir, "xmatch.html"), 'w') { |f| f.write(generate_html) }
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def generate_html
|
23
|
+
actual_filename = create_actual_file
|
24
|
+
expected_filename = create_expected_file
|
25
|
+
xml = @matcher
|
26
|
+
html = ERB.new(File.read(TEMPLATE))
|
27
|
+
html.result(binding)
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_expected_file
|
31
|
+
write_xml("expected.xml", @matcher.lhs)
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_actual_file
|
35
|
+
write_xml("actual.xml", @matcher.rhs)
|
36
|
+
end
|
37
|
+
|
38
|
+
def write_xml(name, xml)
|
39
|
+
File.open(File.join(@report_dir, name), 'w') { |f| f.write(xml)}
|
40
|
+
name
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Nokogiri
|
4
|
+
|
5
|
+
module XML
|
6
|
+
|
7
|
+
class Element
|
8
|
+
|
9
|
+
def match?(other, matcher)
|
10
|
+
@matcher = matcher
|
11
|
+
children_match?(other) &&
|
12
|
+
name_matches?(other) &&
|
13
|
+
attributes_match?(other)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def children_match?(other)
|
19
|
+
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}'")
|
27
|
+
match
|
28
|
+
end
|
29
|
+
|
30
|
+
def attributes_match?(other)
|
31
|
+
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}")
|
38
|
+
end
|
39
|
+
match
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class Document
|
45
|
+
def match?(other, matcher = nil)
|
46
|
+
true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Text
|
51
|
+
def match?(other, matcher)
|
52
|
+
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}'")
|
55
|
+
match
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Attr
|
60
|
+
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
|
66
|
+
|
67
|
+
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}'")
|
70
|
+
match
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Matcher
|
2
|
+
|
3
|
+
class TextFormatter
|
4
|
+
|
5
|
+
def format(matcher)
|
6
|
+
if matcher.mismatches.empty?
|
7
|
+
puts "Documents matched"
|
8
|
+
else
|
9
|
+
puts "Documents didn't match:"
|
10
|
+
puts matcher.mismatches.to_a.join(' : ')
|
11
|
+
puts
|
12
|
+
matcher.lhs.traverse do |e|
|
13
|
+
print e.path
|
14
|
+
mismatch = matcher.mismatches[e.path]
|
15
|
+
if mismatch
|
16
|
+
puts " <====== #{mismatch}"
|
17
|
+
else
|
18
|
+
puts
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
+
<head>
|
6
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
7
|
+
|
8
|
+
<title>XMatch Report</title>
|
9
|
+
|
10
|
+
<style type="text/css">
|
11
|
+
body { background-color: #fff; color: #333; }
|
12
|
+
|
13
|
+
body, p, ol, ul, td {
|
14
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
15
|
+
font-size: 12px;
|
16
|
+
line-height: 18px;
|
17
|
+
}
|
18
|
+
|
19
|
+
.matched { background-color: #DBFFB4 }
|
20
|
+
.mismatched { background-color: #FA5858 }
|
21
|
+
.unmatched { background-color: #FFFBD3 }
|
22
|
+
</style>
|
23
|
+
|
24
|
+
</head>
|
25
|
+
|
26
|
+
<body>
|
27
|
+
|
28
|
+
<h2>XMatch Report</h2>
|
29
|
+
<p><%= xml.mismatches.size %> mismatches found</p>
|
30
|
+
<p><a href='<%=expected_filename%>'>Expected xml</a> | <a href='<%=actual_filename%>'>Actual xml</a></p>
|
31
|
+
|
32
|
+
<table border="0" cellspacing="2" cellpadding="5">
|
33
|
+
<tr>
|
34
|
+
<th>Line</th>
|
35
|
+
<th>Path</th>
|
36
|
+
<th>Mismatch</th>
|
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] || ' ' %></td>
|
44
|
+
</tr>
|
45
|
+
<% end %>
|
46
|
+
</table>
|
47
|
+
|
48
|
+
</body>
|
49
|
+
</html>
|
data/lib/matcher/xml.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'matcher/nokogiri_extensions'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Matcher
|
5
|
+
|
6
|
+
class Xml
|
7
|
+
|
8
|
+
attr_reader :lhs, :rhs, :custom_matchers
|
9
|
+
|
10
|
+
def initialize(lhs, custom_matchers = {})
|
11
|
+
@lhs = parse(lhs)
|
12
|
+
@custom_matchers = custom_matchers
|
13
|
+
@results = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def match(actual)
|
17
|
+
@results.clear
|
18
|
+
@rhs = parse(actual)
|
19
|
+
compare(@lhs, @rhs)
|
20
|
+
end
|
21
|
+
|
22
|
+
def record(path, result, message = nil)
|
23
|
+
@results[path] = OpenStruct.new(:result => result, :message => message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def result_for(path)
|
27
|
+
return "matched" if matches[path]
|
28
|
+
return "mismatched" if mismatches[path]
|
29
|
+
"unmatched"
|
30
|
+
end
|
31
|
+
|
32
|
+
def matches
|
33
|
+
match_info = {}
|
34
|
+
@results.each_pair { |k, v| match_info[k] = '' if v.result }
|
35
|
+
match_info
|
36
|
+
end
|
37
|
+
|
38
|
+
def mismatches
|
39
|
+
match_info = {}
|
40
|
+
@results.each_pair { |k, v| match_info[k] = v.message unless v.result }
|
41
|
+
match_info
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def parse(xml)
|
47
|
+
xml_as_string = xml.instance_of?(Nokogiri::XML::Document) ? xml.to_xml : xml
|
48
|
+
Nokogiri::XML(xml_as_string) { |config| config.noblanks }
|
49
|
+
end
|
50
|
+
|
51
|
+
def compare(lhs, rhs)
|
52
|
+
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
|
57
|
+
match
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
data/lib/xmatch.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<author>James McGovern</author>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book category="WEB">
|
31
|
+
<title lang="en">Learning XML</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
</bookstore>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<author>James McGovern</author>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book category="WEB">
|
31
|
+
<title lang="en">Learning XMLxxx</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
</bookstore>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<author>James McGovern</author>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book category="WEB">
|
31
|
+
<title lang="en">Learning XML</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
</bookstore>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<authorxxx>James McGovern</authorxxx>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book category="WEB">
|
31
|
+
<title lang="en">Learning XML</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
</bookstore>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<author>James McGovern</author>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book category="WEB">
|
31
|
+
<title lang="en">Learning XMLx</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
</bookstore>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<author>James McGovern</author>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book category="WEBx">
|
31
|
+
<title lang="en">Learning XML</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
</bookstore>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<author>James McGovern</author>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book categoryx="WEB">
|
31
|
+
<title lang="en">Learning XML</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
</bookstore>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<author>James McGovern</author>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book category="WEB">
|
31
|
+
<title lang="en">Learning XML</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
<book category="foo">
|
38
|
+
<title lang="en">Learning XML</title>
|
39
|
+
<author>Erik T. Ray</author>
|
40
|
+
<year>2003</year>
|
41
|
+
<price>39.95</price>
|
42
|
+
</book>
|
43
|
+
|
44
|
+
</bookstore>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<author>James McGovern</author>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book category="WEB">
|
31
|
+
<title lang="en">Learning XML</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
</bookstore>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
|
3
|
+
<bookstore>
|
4
|
+
|
5
|
+
<book category="COOKING">
|
6
|
+
<title lang="en">Everyday Italian</title>
|
7
|
+
<author>Giada De Laurentiis</author>
|
8
|
+
<year>2005</year>
|
9
|
+
<price>30.00</price>
|
10
|
+
</book>
|
11
|
+
|
12
|
+
<book category="CHILDREN">
|
13
|
+
<title lang="en">Harry Potter</title>
|
14
|
+
<author>J K. Rowling</author>
|
15
|
+
<year>2005</year>
|
16
|
+
<price>29.99</price>
|
17
|
+
</book>
|
18
|
+
|
19
|
+
<book category="WEB">
|
20
|
+
<title lang="en">XQuery Kick Start</title>
|
21
|
+
<author>James McGovern</author>
|
22
|
+
<author>Per Bothner</author>
|
23
|
+
<author>Kurt Cagle</author>
|
24
|
+
<author>James Linn</author>
|
25
|
+
<author>Vaidyanathan Nagarajan</author>
|
26
|
+
<year>2003</year>
|
27
|
+
<price>49.99</price>
|
28
|
+
</book>
|
29
|
+
|
30
|
+
<book category="WEB" foo="bar">
|
31
|
+
<title lang="en">Learning XML</title>
|
32
|
+
<author>Erik T. Ray</author>
|
33
|
+
<year>2003</year>
|
34
|
+
<price>39.95</price>
|
35
|
+
</book>
|
36
|
+
|
37
|
+
</bookstore>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'matcher/xml'
|
3
|
+
|
4
|
+
describe Matcher::Xml do
|
5
|
+
|
6
|
+
it "should compare files" do
|
7
|
+
1.upto(10) do |i|
|
8
|
+
instance_variable_set("@xml#{i}", open(File.expand_path(File.join(__FILE__, "../../fixtures/books#{i}.xml"))).read)
|
9
|
+
end
|
10
|
+
|
11
|
+
xml = Matcher::Xml.new(@xml1)
|
12
|
+
xml.match(@xml2).should be_true
|
13
|
+
xml.match(@xml3).should be_false
|
14
|
+
xml.match(@xml4).should be_false
|
15
|
+
xml.match(@xml5).should be_false
|
16
|
+
xml.match(@xml6).should be_false
|
17
|
+
xml.match(@xml7).should be_false
|
18
|
+
xml.match(@xml9).should be_false
|
19
|
+
xml.match(@xml10).should be_false
|
20
|
+
xml.match(@xml8).should be_true
|
21
|
+
|
22
|
+
Matcher::Xml.new(@xml7).match(@xml1).should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,328 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
describe Matcher::Xml do
|
6
|
+
|
7
|
+
def verify_mismatch(path, message)
|
8
|
+
@xml.match(@rhs).should be_false
|
9
|
+
@xml.mismatches.should have(1).mismatch
|
10
|
+
@xml.mismatches[path].should == message
|
11
|
+
end
|
12
|
+
|
13
|
+
context "attributes" do
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
@xml = Matcher::Xml.new("<foo></foo>")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should initialise with a string" do
|
20
|
+
@xml.lhs.should be_a_kind_of(Nokogiri::XML::Document)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should initialise with a document" do
|
24
|
+
@xml.lhs.should be_a_kind_of(Nokogiri::XML::Document)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should provide the rhs after a match" do
|
28
|
+
@xml.match("<bar></bar>")
|
29
|
+
@xml.rhs.should be_a_kind_of(Nokogiri::XML::Document)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
before(:each) do
|
35
|
+
@lhs = <<-eos
|
36
|
+
<bookstore>
|
37
|
+
<book category="COOKING">
|
38
|
+
<title lang="en">Everyday Italian</title>
|
39
|
+
</book>
|
40
|
+
</bookstore>
|
41
|
+
eos
|
42
|
+
@xml = Matcher::Xml.new(@lhs)
|
43
|
+
end
|
44
|
+
|
45
|
+
context "matching" do
|
46
|
+
|
47
|
+
it "should be true when documents match" do
|
48
|
+
Matcher::Xml.new(@lhs).match(@lhs.clone).should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should provide empty mismatches on match" do
|
52
|
+
@xml.match(@lhs.clone).should be_true
|
53
|
+
@xml.mismatches.should be_empty
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should ignore blank elements" do
|
57
|
+
rhs = <<-eos
|
58
|
+
<bookstore>
|
59
|
+
<book category="COOKING">
|
60
|
+
<title lang="en">Everyday Italian</title>
|
61
|
+
</book>
|
62
|
+
|
63
|
+
|
64
|
+
</bookstore>
|
65
|
+
eos
|
66
|
+
@xml.match(rhs).should be_true
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should be true when a string is matched with a document" do
|
70
|
+
rhs = <<-eos
|
71
|
+
<bookstore>
|
72
|
+
<book category="COOKING">
|
73
|
+
<title lang="en">Everyday Italian</title>
|
74
|
+
</book>
|
75
|
+
|
76
|
+
|
77
|
+
</bookstore>
|
78
|
+
eos
|
79
|
+
|
80
|
+
Matcher::Xml.new(@lhs).match(Nokogiri::XML(rhs)).should be_true
|
81
|
+
end
|
82
|
+
|
83
|
+
context "elements" do
|
84
|
+
|
85
|
+
it "should not match when rhs has an extra element" do
|
86
|
+
@rhs = <<-eos
|
87
|
+
<bookstore>
|
88
|
+
<book category="COOKING">
|
89
|
+
<title lang="en">Everyday Italian</title>
|
90
|
+
</book>
|
91
|
+
<book></book>
|
92
|
+
</bookstore>
|
93
|
+
eos
|
94
|
+
verify_mismatch("/bookstore", "expected 1 children, got 2")
|
95
|
+
end
|
96
|
+
|
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
|
+
@rhs = <<-eos
|
107
|
+
<bookstore>
|
108
|
+
<book category="COOKING">
|
109
|
+
</book>
|
110
|
+
</bookstore>
|
111
|
+
eos
|
112
|
+
verify_mismatch("/bookstore/book/title", "expected 1 children, got 0")
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
context "names" do
|
118
|
+
|
119
|
+
it "should not match when rhs has a different element name" do
|
120
|
+
@rhs = <<-eos
|
121
|
+
<bookstore>
|
122
|
+
<bookx category="COOKING">
|
123
|
+
<title lang="en">Everyday Italian</title>
|
124
|
+
</bookx>
|
125
|
+
</bookstore>
|
126
|
+
eos
|
127
|
+
verify_mismatch("/bookstore/book", "expected element 'book', got 'bookx'")
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
context "attributes" do
|
133
|
+
|
134
|
+
it "should not match when an attribute names don't match" do
|
135
|
+
@rhs = <<-eos
|
136
|
+
<bookstore>
|
137
|
+
<book categoryx="COOKING">
|
138
|
+
<title lang="en">Everyday Italian</title>
|
139
|
+
</book>
|
140
|
+
</bookstore>
|
141
|
+
eos
|
142
|
+
verify_mismatch("/bookstore/book", "expected attribute missing")
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should not match when an attribute value doesn't match" do
|
146
|
+
@rhs = <<-eos
|
147
|
+
<bookstore>
|
148
|
+
<book category="COOKINGx">
|
149
|
+
<title lang="en">Everyday Italian</title>
|
150
|
+
</book>
|
151
|
+
</bookstore>
|
152
|
+
eos
|
153
|
+
verify_mismatch("/bookstore/book", "attribute 'category' expected 'COOKING', got 'COOKINGx'")
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should not match when rhs has an extra attribute" do
|
157
|
+
@rhs = <<-eos
|
158
|
+
<bookstore>
|
159
|
+
<book category="COOKING" foo="bar">
|
160
|
+
<title lang="en">Everyday Italian</title>
|
161
|
+
</book>
|
162
|
+
</bookstore>
|
163
|
+
eos
|
164
|
+
verify_mismatch("/bookstore/book", "expected 1 attributes, got 2")
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should not match when rhs has less attributes" do
|
168
|
+
@rhs = <<-eos
|
169
|
+
<bookstore>
|
170
|
+
<book category="COOKING">
|
171
|
+
<title>Everyday Italian</title>
|
172
|
+
</book>
|
173
|
+
</bookstore>
|
174
|
+
eos
|
175
|
+
verify_mismatch("/bookstore/book/title", "expected 1 attributes, got 0")
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should not match when rhs has more attributes" do
|
179
|
+
@rhs = <<-eos
|
180
|
+
<bookstore>
|
181
|
+
<book category="COOKING">
|
182
|
+
<title lang="en" foo="bar">Everyday Italian</title>
|
183
|
+
</book>
|
184
|
+
</bookstore>
|
185
|
+
eos
|
186
|
+
verify_mismatch("/bookstore/book/title", "expected 1 attributes, got 2")
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
context "contents" do
|
192
|
+
|
193
|
+
it "should not match when inner text contents don't match" do
|
194
|
+
@rhs = <<-eos
|
195
|
+
<bookstore>
|
196
|
+
<book category="COOKING">
|
197
|
+
<title lang="en">Everyday Italianx</title>
|
198
|
+
</book>
|
199
|
+
</bookstore>
|
200
|
+
eos
|
201
|
+
verify_mismatch("/bookstore/book/title/text()", "expected 'Everyday Italian', got 'Everyday Italianx'")
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
context "mismatches" do
|
209
|
+
|
210
|
+
it "should be empty to start with" do
|
211
|
+
Matcher::Xml.new(@lhs).mismatches.should be_empty
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should be reset when rematching" do
|
215
|
+
rhs = <<-eos
|
216
|
+
<bookstore>
|
217
|
+
<book category="COOKING">
|
218
|
+
</book>
|
219
|
+
</bookstore>
|
220
|
+
eos
|
221
|
+
@xml.match(rhs)
|
222
|
+
@xml.mismatches.should_not be_empty
|
223
|
+
@xml.match(@lhs)
|
224
|
+
@xml.mismatches.should be_empty
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'can have multiple mismatches' do
|
228
|
+
rhs = <<-eos
|
229
|
+
<bookstorex>
|
230
|
+
<book>
|
231
|
+
<title lang="en">Everyday Italian</title>
|
232
|
+
</book>
|
233
|
+
</bookstore>
|
234
|
+
eos
|
235
|
+
@xml.match(rhs)
|
236
|
+
@xml.mismatches.should have(2).mismatches
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should contain parent's path when an attribute doesn't match" do
|
240
|
+
|
241
|
+
lhs = <<-eos
|
242
|
+
<bookstore>
|
243
|
+
<book category="COOKING">
|
244
|
+
<title lang="en">Everyday Italian</title>
|
245
|
+
</book>
|
246
|
+
<book category="FOO">
|
247
|
+
<title lang="en">Everyday French</title>
|
248
|
+
</book>
|
249
|
+
</bookstore>
|
250
|
+
eos
|
251
|
+
|
252
|
+
|
253
|
+
@rhs = <<-eos
|
254
|
+
<bookstore>
|
255
|
+
<book category="COOKING">
|
256
|
+
<title lang="en">Everyday Italian</title>
|
257
|
+
</book>
|
258
|
+
<book foo="bar">
|
259
|
+
<title lang="en">Everyday French</title>
|
260
|
+
</book>
|
261
|
+
</bookstore>
|
262
|
+
eos
|
263
|
+
|
264
|
+
@xml = Matcher::Xml.new(lhs)
|
265
|
+
verify_mismatch("/bookstore/book[2]", "expected attribute missing")
|
266
|
+
end
|
267
|
+
|
268
|
+
context 'matches' do
|
269
|
+
|
270
|
+
it "should be provided with no message" do
|
271
|
+
lhs = "<bookstore><book></book></bookstore>"
|
272
|
+
xml = Matcher::Xml.new(lhs)
|
273
|
+
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
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
context "match results" do
|
284
|
+
|
285
|
+
it "provides all results"
|
286
|
+
|
287
|
+
it "returns 'matched' for a path that matched correctly" do
|
288
|
+
xml = Matcher::Xml.new("<bookstore></bookstore>")
|
289
|
+
xml.match("<bookstore></bookstore>")
|
290
|
+
xml.result_for("/bookstore").should == "matched"
|
291
|
+
end
|
292
|
+
|
293
|
+
it "returns 'mismatched' for a path that was mismatched" do
|
294
|
+
xml = Matcher::Xml.new("<bookstore></bookstore>")
|
295
|
+
xml.match("<bookstorex></bookstorex>")
|
296
|
+
xml.result_for("/bookstore").should == "mismatched"
|
297
|
+
end
|
298
|
+
|
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
|
+
end
|
306
|
+
|
307
|
+
context "custom matchers" do
|
308
|
+
|
309
|
+
it "can be provided" do
|
310
|
+
xml = Matcher::Xml.new("<bookstore id='1'></bookstore>", {"my path" => "my predicate"})
|
311
|
+
xml.custom_matchers.should have(1).matcher
|
312
|
+
end
|
313
|
+
|
314
|
+
it "can be used on an attribute value" do
|
315
|
+
custom_matchers = { "/bookstore/@id" => lambda {|elem| elem.value == '2'} }
|
316
|
+
xml = Matcher::Xml.new("<bookstore id='1'></bookstore>", custom_matchers)
|
317
|
+
xml.match("<bookstore id='2'></bookstore>").should be_true
|
318
|
+
end
|
319
|
+
|
320
|
+
it "can be used on an element value" do
|
321
|
+
custom_matchers = { "/bookstore/book/text()" => lambda {|elem| elem.content == 'bar'} }
|
322
|
+
xml = Matcher::Xml.new("<bookstore><book>foo</book></bookstore", custom_matchers)
|
323
|
+
xml.match("<bookstore><book>bar</book></bookstore").should be_true
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xmatch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Peter Moran
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-18 00:00:00 +11:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: nokogiri
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 4
|
33
|
+
- 2
|
34
|
+
version: 1.4.2
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: bundler
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 15
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
version: "1.0"
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rspec
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 27
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 3
|
64
|
+
- 0
|
65
|
+
version: 1.3.0
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id003
|
68
|
+
description:
|
69
|
+
email: workingpeter@gmail.com
|
70
|
+
executables: []
|
71
|
+
|
72
|
+
extensions: []
|
73
|
+
|
74
|
+
extra_rdoc_files: []
|
75
|
+
|
76
|
+
files:
|
77
|
+
- lib/matcher/html_formatter.rb
|
78
|
+
- lib/matcher/nokogiri_extensions.rb
|
79
|
+
- lib/matcher/text_formatter.rb
|
80
|
+
- lib/matcher/version.rb
|
81
|
+
- lib/matcher/xmatch.html.erb
|
82
|
+
- lib/matcher/xml.rb
|
83
|
+
- lib/xmatch.rb
|
84
|
+
- README.markdown
|
85
|
+
- LICENSE
|
86
|
+
- spec/fixtures/books1.xml
|
87
|
+
- spec/fixtures/books10.xml
|
88
|
+
- spec/fixtures/books2.xml
|
89
|
+
- spec/fixtures/books3.xml
|
90
|
+
- spec/fixtures/books4.xml
|
91
|
+
- spec/fixtures/books5.xml
|
92
|
+
- spec/fixtures/books6.xml
|
93
|
+
- spec/fixtures/books7.xml
|
94
|
+
- spec/fixtures/books8.xml
|
95
|
+
- spec/fixtures/books9.xml
|
96
|
+
- spec/matcher/smoke_spec.rb
|
97
|
+
- spec/matcher/xml_spec.rb
|
98
|
+
- spec/spec_helper.rb
|
99
|
+
- Rakefile
|
100
|
+
has_rdoc: true
|
101
|
+
homepage: http://github.com/pmoran/xmatch
|
102
|
+
licenses: []
|
103
|
+
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
hash: 3
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
version: "0"
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
hash: 3
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
version: "0"
|
127
|
+
requirements: []
|
128
|
+
|
129
|
+
rubyforge_project:
|
130
|
+
rubygems_version: 1.3.7
|
131
|
+
signing_key:
|
132
|
+
specification_version: 3
|
133
|
+
summary: A Ruby library for comparing XML documents and reporting on mismatches
|
134
|
+
test_files:
|
135
|
+
- spec/fixtures/books1.xml
|
136
|
+
- spec/fixtures/books10.xml
|
137
|
+
- spec/fixtures/books2.xml
|
138
|
+
- spec/fixtures/books3.xml
|
139
|
+
- spec/fixtures/books4.xml
|
140
|
+
- spec/fixtures/books5.xml
|
141
|
+
- spec/fixtures/books6.xml
|
142
|
+
- spec/fixtures/books7.xml
|
143
|
+
- spec/fixtures/books8.xml
|
144
|
+
- spec/fixtures/books9.xml
|
145
|
+
- spec/matcher/smoke_spec.rb
|
146
|
+
- spec/matcher/xml_spec.rb
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
- Rakefile
|