rubyjedi-testunitxml 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/CHANGES ADDED
@@ -0,0 +1,19 @@
1
+ = Changes
2
+
3
+ == Version 0.1.5
4
+
5
+ * Fixed a bug when comparing attributes containing entity references.
6
+ * Added an <tt>assert_xml_not_equal</tt> method.
7
+
8
+ == Version 0.1.4
9
+
10
+ * Added support for Doctype comparisons in +assert_xml_equal+
11
+ * Added <tt>setup.rb</tt> file to distribution packages
12
+ * Added installation section to README file
13
+ * Added links to online tutorial to README file
14
+ * Added CHANGES file
15
+
16
+
17
+ == Version 0.1.3
18
+
19
+ The initial release.
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ = MIT-LICENSE
2
+
3
+ Copyright � 2006 Henrik M�rtensson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a
6
+ copy of this software and associated documentation files (the "Software"),
7
+ to deal in the Software without restriction, including without limitation
8
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+ and/or sell copies of the Software, and to permit persons to whom the
10
+ Software is furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,119 @@
1
+ = Test::Unit::XML
2
+ == An XML Test Framework
3
+
4
+ Version: 0.1.5
5
+
6
+ Author: Henrik M�rtensson
7
+
8
+ (c) 2006 by Henrik M�rtensson
9
+
10
+ == Introduction
11
+
12
+ Test::Unit::XML extends the Test::Unit framework with
13
+ an assertion for testing well-formed XML documents.
14
+
15
+ Using Test::Unit::XML is easy. All you have to do is to
16
+ require +testunitxml+, and you will then have an
17
+ +assert_xml_equal+ assertion available in the
18
+ Test::Unit::TestCase class.
19
+
20
+ In addition to the API documentation included in the package, you can get information about how
21
+ to use Test::Unit::XML from the following sources:
22
+
23
+ * The online tutorial[http://kallokain.blogspot.com/2006/01/testunitxml-quick-start-tutorial.html] at
24
+ the Kallokain[http://kallokain.blogspot.com/] blog.
25
+ * The {test cases}[link:../../test/] included in the distribution package.
26
+
27
+ == Installation
28
+
29
+ === Install Using the +RubyGem+ Package Manager
30
+
31
+ The easiest way to install Test::Unit::XML is to make a remote installation via the RubyGem
32
+ package manager:
33
+
34
+ <tt>gem install testunitxml</tt>
35
+
36
+ If you have downloaded a gem package from Rubyforge, you can do a local installation:
37
+
38
+ <tt>cd <em>download_directory_path</em></tt>
39
+
40
+ <tt>gem install testunitxml -l</tt>
41
+
42
+ === Install from a Zip file or Tarball
43
+
44
+ If you do not have RubyGem installed, you can download a Zip file or tarball and install
45
+ from it instead:
46
+
47
+ 1:: Unpack the Zip or tarball archive.
48
+ 2:: cd to the directory you just unpacked.
49
+ 3:: Run the command:
50
+ <tt>ruby setup.rb install</tt>
51
+
52
+
53
+
54
+ == What Does _Equal_ XML Documents Mean?
55
+
56
+ It is hard to define exactly what _equal_ means in the
57
+ context of XML documents. I have tried to follow W3C
58
+ XML recommendations as far as possible. There are a
59
+ few things worthy of note:
60
+
61
+ * Namespace _declarations_, i.e. attributes that declare
62
+ namespaces, are ignored for comparison purposes. (The
63
+ namespaces _are_ used in comparisons.) The reason is
64
+ that XML processors may move the declarations from one
65
+ element to another, or change prefixes in ways that
66
+ cannot be directly controlled by a programmer. For
67
+ example, two different XSLT processors could use
68
+ the same stylesheet and produce XML documents that use
69
+ different namespace prefixes, and have declarations
70
+ on different elements, but are still considered equal.
71
+
72
+ * Text nodes that are empty or contain only whitespace
73
+ are ignored for comparison purposes. This makes it
74
+ easier to test output from various transformation
75
+ engines. These often produce extraneous whitespace.
76
+
77
+ == The Future
78
+
79
+ There are a few things in the pipeline:
80
+
81
+ * assert_xml_equal_structure - checks that the structure
82
+ of two documents are equal, but ignores content, attributes,
83
+ processing istructions, comments, CDATA, and doctype
84
+ declarations.
85
+ * assert_xml_similar - Like assert_xml_equal, but ignores the
86
+ order of child elements.
87
+ * Configurability. It _might_ be useful to be able to set
88
+ configuration options for testing. I'll have to think a
89
+ bit about this though.
90
+ * Document difference functions, like the Java XMLUnit test
91
+ suite.
92
+
93
+ I plan to implement these features as I need them in other
94
+ projects, so there is no time plan, and no guarantee as to the
95
+ order in which I'll implement anything.
96
+
97
+ == License
98
+
99
+ See the {MIT-LICENSE}[link:files/MIT-LICENSE.html] file.
100
+
101
+ == Contact
102
+
103
+ You can email bug reports, opinions and questions to
104
+ mailto:self@henrikmartensson.org. You may also wish to visit
105
+ my home page, www.henrikmartensson.org, for more information
106
+ about Test::Unit::XML and other projects. I will write about
107
+ Test::Unit::XML at the the Kallokain[http://kallokain.blogspot.com/]
108
+ blog. You are welcome to visit, and comment.
109
+
110
+ If you find Test::Unit::XML useful, please do tell me about it. I would like
111
+ to list projects that use it on the {Test::Unit::XML web site}[http://testunitxml.rubyforge.org/].
112
+
113
+ If you find Test::Unit::XML lacking in some respect, or buggy,
114
+ I am even more interested. I can't fix bugs I do not know about.
115
+
116
+ Finally, if you write about Test::Unit::XML, I'd like to link to
117
+ the article on my web site, or at least mention it if you write
118
+ for a magazine, so please tell me.
119
+
@@ -0,0 +1,21 @@
1
+ module REXML
2
+
3
+ # The REXML::Attributes mix-in adds methods that are useful for
4
+ # attribute collections, but not present in the standard
5
+ # REXML::Attributes class
6
+ class Attributes
7
+
8
+ # The +get_attribute_ns+ method retrieves a method by its namespace
9
+ # and name. Thus it is possible to reliably identify an attribute
10
+ # even if an XML processor has changed the prefix.
11
+ def get_attribute_ns(namespace, name)
12
+ each_attribute() { |attribute|
13
+ if name == attribute.name &&
14
+ namespace == attribute.namespace()
15
+ return attribute
16
+ end
17
+ }
18
+ nil
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,147 @@
1
+
2
+ module Test
3
+ module Unit
4
+ module XML
5
+
6
+ # This singleton class compares all types of REXML nodes.
7
+ class Conditionals
8
+
9
+ private_class_method :new
10
+ @@conditionals = nil
11
+
12
+ # The +create+ method is used to create a singleton instance
13
+ # of the Conditionals class.
14
+ def Conditionals.create
15
+ @@conditionals = new unless @@conditionals
16
+ @@conditionals
17
+ end
18
+
19
+ # The method compares two REXML nodes representing an XML document,
20
+ # or part of a document. If the nodes are equal, the method returns
21
+ # +true+. If the nodes are not equal, the method returns +false+.
22
+ # If the nodes have child nodes, for example if the nodes are
23
+ # +Element+ nodes with content, they will _not_ be recursively compared.
24
+ def compare_xml_nodes(expected_node, actual_node)
25
+ #puts "Conditionals, Expected: #{expected_node.class} : #{expected_node.name}"
26
+ #puts "Conditionals, Expected: #{actual_node.class} : #{actual_node.name}"
27
+ return false unless actual_node.instance_of? expected_node.class
28
+ return false if expected_node == nil && actual_node != nil
29
+ return false if expected_node != nil && actual_node == nil
30
+ #puts "actual_node is nil" unless actual_node
31
+ #puts "expected_node is nil" unless expected_node
32
+ case actual_node
33
+ when REXML::Document
34
+ # TODO: Implement Document comparison
35
+ true
36
+ when REXML::DocType
37
+ compare_doctypes(expected_node, actual_node)
38
+ when REXML::Element
39
+ compare_elements(expected_node, actual_node)
40
+ when REXML::CData
41
+ compare_texts(expected_node, actual_node)
42
+ when REXML::Text
43
+ compare_texts(expected_node, actual_node)
44
+ when REXML::Comment
45
+ compare_comments(expected_node, actual_node)
46
+ when REXML::Instruction
47
+ compare_pi(expected_node, actual_node)
48
+ when REXML::XMLDecl
49
+ compare_xml_declaration(expected_node, actual_node)
50
+ #when REXML::Entity
51
+ # compare_xml_entities(expected_node, actual_node)
52
+ else
53
+ puts "Unknown node type #{actual_node.class}"
54
+ false
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def compare_doctypes(expected_node, actual_node)
61
+ return compare_system_id(expected_node.system, actual_node.system) &&
62
+ expected_node.public == actual_node.public &&
63
+ compare_xml_internal_dtd_subset(expected_node, actual_node)
64
+ end
65
+
66
+ def compare_system_id(expected_id, actual_id)
67
+ is_expected_urn = expected_id =~ /^urn:/i
68
+ is_actual_urn = actual_id =~ /^urn:/i
69
+ if is_expected_urn || is_actual_urn
70
+ expected_id == actual_id
71
+ else
72
+ true
73
+ end
74
+ end
75
+
76
+ def compare_elements(expected_node, actual_node)
77
+ return expected_node.name == actual_node.name &&
78
+ expected_node.namespace() == actual_node.namespace() &&
79
+ compare_attributes(expected_node.attributes, actual_node.attributes)
80
+ end
81
+
82
+ def compare_attributes(expected_attributes, actual_attributes)
83
+ return false unless attribute_count(expected_attributes) == attribute_count(actual_attributes)
84
+ expected_attributes.each_attribute do |expected_attribute|
85
+ expected_prefix = expected_attribute.prefix()
86
+ unless expected_prefix == 'xmlns' then
87
+ expected_name = expected_attribute.name
88
+ expected_namespace = expected_attribute.namespace
89
+ actual_attribute = actual_attributes.get_attribute_ns(expected_namespace, expected_name)
90
+ return false unless actual_attribute
91
+ return false if expected_attribute.value() != actual_attribute.value()
92
+ end
93
+ end
94
+ true
95
+ end
96
+
97
+ def attribute_count(attributes)
98
+ # Do not count namespace declarations
99
+ attributes.size - attributes.prefixes.size
100
+ end
101
+
102
+ def compare_texts(expected_node, actual_node)
103
+ expected_node.value.eql?(actual_node.value)
104
+ end
105
+
106
+ def compare_comments(expected_node, actual_node)
107
+ expected_node == actual_node
108
+ end
109
+
110
+ def compare_pi(expected_pi, actual_pi)
111
+ return expected_pi.target == actual_pi.target &&
112
+ expected_pi.content == actual_pi.content
113
+ end
114
+
115
+ def compare_xml_declaration(expected_decl, actual_decl)
116
+ return expected_decl == actual_decl
117
+ end
118
+
119
+ def compare_xml_internal_dtd_subset(expected_node, actual_node)
120
+ expected_subset = expected_node.children()
121
+ actual_subset = actual_node.children()
122
+ return false unless expected_subset.length == actual_subset.length
123
+ expected_subset.inject(true) { |memo, expected_decl|
124
+ case expected_decl
125
+ when REXML::Entity
126
+ memo &&
127
+ expected_decl.value == actual_node.entities[expected_decl.name].value &&
128
+ expected_decl.ndata == actual_node.entities[expected_decl.name].ndata
129
+ when REXML::NotationDecl
130
+ actual_notation_decl = actual_node.notation(expected_decl.name)
131
+ memo &&
132
+ actual_notation_decl != nil &&
133
+ expected_decl.name == actual_notation_decl.name &&
134
+ expected_decl.public == actual_notation_decl.public &&
135
+ expected_decl.system == actual_notation_decl.system
136
+ when REXML::Comment
137
+ true
138
+ else
139
+ raise "Unexpected node type in internal DTD subset of expected document: " + expected_decl.inspect
140
+ end
141
+ }
142
+ end
143
+
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,54 @@
1
+
2
+ require 'test/unit/xml/notationdecl_mixin'
3
+
4
+ module REXML
5
+
6
+ # The REXML::DocType mix-in adds methods that are useful for
7
+ # Doctype declarations, but not present in the standard
8
+ # REXML::DocType class
9
+ class DocType
10
+
11
+ # This method retrieves the public identifier identifying the document's DTD.
12
+ def public
13
+ case @external_id
14
+ when "SYSTEM"
15
+ nil
16
+ when "PUBLIC"
17
+ strip_quotes(@long_name)
18
+ end
19
+ end
20
+
21
+ # This method retrieves the system identifier identifying the document's DTD
22
+ def system
23
+ case @external_id
24
+ when "SYSTEM"
25
+ strip_quotes(@long_name)
26
+ when "PUBLIC"
27
+ @uri.kind_of?(String) ? strip_quotes(@uri) : nil
28
+ end
29
+ end
30
+
31
+ # This method returns a list of notations that have been declared in the
32
+ # _internal_ DTD subset. Notations in the external DTD subset are not listed.
33
+ def notations
34
+ children().select {|node| node.kind_of?(REXML::NotationDecl)}
35
+ end
36
+
37
+ # Retrieves a named notation. Only notations declared in the internal
38
+ # DTD subset can be retrieved.
39
+ def notation(name)
40
+ notations.find { |notation_decl|
41
+ notation_decl.name == name
42
+ }
43
+ end
44
+
45
+ private
46
+
47
+ def strip_quotes(quoted_string)
48
+ quoted_string =~ /^[\'\"].*[\�\"]$/ ?
49
+ quoted_string[1, quoted_string.length-2] :
50
+ quoted_string
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,67 @@
1
+ #! /usr/bin/ruby
2
+
3
+ module Test
4
+ module Unit
5
+ module XML
6
+ class NodeIterator
7
+
8
+ class NullNodeFilter
9
+ def accept(node)
10
+ true
11
+ end
12
+ end
13
+
14
+ # This class method takes a node as an argument and locates the
15
+ # next node. The first argument is a node. The second argument
16
+ # is an optional node filter.
17
+ def NodeIterator.find_next_node(node, node_filter = NullNodeFilter.new)
18
+ #puts "In NodeIterator: #{node.class}"
19
+ #puts " has_children: #{NodeIterator.has_children?(node)}"
20
+ #puts " next_sibling_node: #{node.next_sibling_node}"
21
+ #puts " has_parent_with_sibling?: #{NodeIterator.has_parent_with_sibling?(node)}"
22
+ #node.write if node.respond_to?(:name) && node.name == 'TestThing'
23
+ return nil unless node
24
+ next_node = nil
25
+ if NodeIterator.has_children?(node) then
26
+ next_node = node[0] # The index should be 1 according to the REXML docs
27
+ elsif node.next_sibling_node
28
+ next_node = node.next_sibling_node
29
+ elsif NodeIterator.has_parent_with_sibling?(node)
30
+ next_node = node.parent.next_sibling_node
31
+ end
32
+ return next_node if node_filter.accept(next_node) || next_node == nil
33
+ find_next_node(next_node, node_filter)
34
+ end
35
+
36
+
37
+ def initialize(node, node_filter = NullNodeFilter.new)
38
+ @node_filter = node_filter
39
+ @next_node = node
40
+ end
41
+
42
+ def has_next()
43
+ @next_node ? true : false
44
+ end
45
+
46
+ def next
47
+ node = @next_node
48
+ @next_node = NodeIterator.find_next_node(node, @node_filter)
49
+ node
50
+ end
51
+
52
+
53
+
54
+
55
+ private
56
+
57
+ def NodeIterator.has_children?(node)
58
+ node.respond_to?(:[]) && node.respond_to?(:size) && node.size > 0
59
+ end
60
+
61
+ def NodeIterator.has_parent_with_sibling?(node)
62
+ node.parent && node.parent.next_sibling_node
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,54 @@
1
+ module REXML
2
+
3
+ # The REXML::NotationDecl mix-in adds methods that are useful for
4
+ # notation declarations, but not present in the standard
5
+ # REXML::NotationDecl class
6
+ class NotationDecl
7
+
8
+ # This method retrieves the name of the notation.
9
+ def name
10
+ @name
11
+ end
12
+
13
+ # This method retrieves the system identifier specified in the notation
14
+ # declaration. If there is no system identifier defined, the method returns
15
+ # +nil+
16
+ def system
17
+ parse_rest(@rest)[1]
18
+ end
19
+
20
+ # This method retrieves the public identifier specified in the notation
21
+ # declaration. If there is no public identifier defined, the method returns
22
+ # +nil+
23
+ def public
24
+ return nil unless @middle == "PUBLIC"
25
+ parse_rest(@rest)[0]
26
+ end
27
+
28
+ private
29
+
30
+ def parse_rest(rest)
31
+ case rest
32
+ when /^"([^"]+)"\s+"([^"]+)"$/
33
+ return [$1,$2]
34
+ when /^'([^']+)'\s+'([^']+)'$/
35
+ return [$1,$2]
36
+ when /^"([^"]+)"\s+'([^']+)'$/
37
+ return [$1,$2]
38
+ when /^'([^']+)'\s+"([^"]+)"$/
39
+ return [$1,$2]
40
+ when /^"([^"]+)"$/
41
+ return [nil, $1] if @middle == 'SYSTEM'
42
+ return [$1, nil] if @middle == 'PUBLIC'
43
+ raise "Unknown notation keyword: #{@middle}"
44
+ when /^'([^']+)'$/
45
+ return [nil, $1] if @middle == 'SYSTEM'
46
+ return [$1, nil] if @middle == 'PUBLIC'
47
+ raise "Unknown notation keyword: #{@middle}"
48
+ else
49
+ raise "Could not parse \@rest variable in REXML::NotationDecl: |#{@rest}|"
50
+ end
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,163 @@
1
+ #! /usr/bin/ruby
2
+
3
+ require 'rexml/document'
4
+ require 'test/unit/xml/attributes_mixin' # Must be required after rexml/document
5
+ require 'test/unit/xml/doctype_mixin' # Must be required after rexml/document
6
+ require 'test/unit/xml/notationdecl_mixin' # Must be required after rexml/document
7
+ require 'test/unit'
8
+ require 'test/unit/xml/conditionals'
9
+ require 'test/unit/xml/xmlequalfilter'
10
+ require 'test/unit/xml/nodeiterator'
11
+
12
+ =begin rdoc
13
+ This module contains assertions about XML documents. The assertions are
14
+ meant to be mixed in to test classes such as Test::Unit::TestCase.
15
+ =end
16
+ module Test
17
+ module Unit
18
+ module XML
19
+
20
+ # This method checks whether two well-formed XML documents are equal.
21
+ # Two XML documents are considered equal if:
22
+ # * They contain the same type of nodes, in the same order,
23
+ # except for text nodes that are empty, or contain only
24
+ # whitespace. Such text nodes are ignored.
25
+ # * The corresponding nodes in the two documents are equal.
26
+ #
27
+ # Nodes are tested for equality as follows:
28
+ # XML Declarations::
29
+ # XML declarations are equal if they have the same version,
30
+ # encoding, and stand-alone pseudo-attributes.
31
+ # Doctype::
32
+ # Doctypes are equal if they fulfill all of the following conditions:
33
+ # * They have the same public identifier, or neither has a public identifier
34
+ # * If one of the doctypes has a system identifier that is a URN,
35
+ # the other doctype must have a system identifier that is the same URN.
36
+ # System identifiers that are URLs are ignored for comparison purposes.
37
+ # The reason is that the same DTD is very often stored in many different
38
+ # locations (for example different directories on different computers).
39
+ # Therefore the physical location of the DTD does not say anything useful
40
+ # about whether two documents are equal.
41
+ # * An entity declaration present in one of the doctype declarations
42
+ # must also be present in the other.
43
+ # * A notation declaration present in one of the doctype declarations
44
+ # must also be present in the other.
45
+ # Internal General Entity Declaration::
46
+ # Internal General entity declarations are equal if they have the same name,
47
+ # and the same value.
48
+ # External General Entity Declaration::
49
+ # External general entity declarations are equal if they have the same name,
50
+ # and if the identifiers are of the same type (PUBLIC or SYSTEM) and have
51
+ # the same value. Note that if the identifiers are URLs, a comparison may
52
+ # fail even though both URLS point to the same resource, for example if one
53
+ # URL is relative and the other is absolute.
54
+ # Notation Declaration::
55
+ # Notation declarations are equal if they have the same name,
56
+ # and if the identifiers are of the same type (PUBLIC or SYSTEM) and have
57
+ # the same value.
58
+ # Elements::
59
+ # Elements are considered equal if they have the same generic
60
+ # identifier (tag name), belong to the same namespace, and have the same
61
+ # attributes. Note that the namespace _prefixes_ of two elements may be different
62
+ # as long as they belong to the same namespace.
63
+ # Attributes::
64
+ # Attributes are equal if they belong to the same namespace,
65
+ # have the same name, and the same value.
66
+ # Namespace Declarations::
67
+ # Namespace _declarations_ (attributes named <tt>xmlns:<em>prefix</em></tt>)
68
+ # are ignored. There are several reasons for this:
69
+ # - As long as two elements or attributes
70
+ # belong to the same namespace, it does not matter what prefixes
71
+ # are used. XML processors may also change prefixes in unpredictable
72
+ # ways without this being an error.
73
+ # - XML processors may _move_ namespace
74
+ # declarations from one element to another (usually an ancestor,
75
+ # sometimes a descendant) without this being an error, or under
76
+ # control by the programmer.
77
+ # - XML processors may _add_ extraneous namespace declarations
78
+ # in a manner that is hard for programmers to control.
79
+ # Processing Instructions::
80
+ # Processing instructions are considered equal if the string
81
+ # values of their targets and contents are equal.
82
+ # Text::
83
+ # Text nodes are equal if their values are equal. However, empty
84
+ # text nodes, and text nodes containing only whitespace are ignored.
85
+ # CDATA::
86
+ # CDATA nodes are equal if their text content is equal. Whitespace
87
+ # is _not_ normalized.
88
+ # Comments::
89
+ # Comments are equal if they have the same content.
90
+ #
91
+ # The +expected_doc+ and +actual_doc+ arguments to this method may be of
92
+ # the following types:
93
+ # * A +REXML+ node, usually a <tt>REXML::Document</tt> or <tt>REXML::Element</tt>
94
+ # * A +File+ or other +IO+ object representing an XML document
95
+ # * A string containing an XML document
96
+ def assert_xml_equal(expected_doc, actual_doc, message = nil)
97
+ expected_doc = parse_xml(expected_doc)
98
+ actual_doc = parse_xml(actual_doc)
99
+ _wrap_assertion do
100
+ full_message = build_message(message, <<EOT, actual_doc.inspect, expected_doc.inspect)
101
+
102
+ <?> expected to be equal to
103
+ <?> but was not.
104
+ EOT
105
+ assert_block(full_message){are_equal?(expected_doc, actual_doc)}
106
+ end
107
+ end
108
+
109
+
110
+ # This method compares two XML documents and returns +true+ if they are
111
+ # _not_ equal, +false+ otherwise. This is the inverse of assert_xml_equal.
112
+ def assert_xml_not_equal(expected_doc, actual_doc, message = nil)
113
+ expected_doc = parse_xml(expected_doc)
114
+ actual_doc = parse_xml(actual_doc)
115
+ _wrap_assertion do
116
+ full_message = build_message(message, <<EOT, actual_doc.inspect, expected_doc.inspect)
117
+
118
+ <?> expected not to be equal to
119
+ <?> but was equal.
120
+ EOT
121
+ assert_block(full_message){ ! are_equal?(expected_doc, actual_doc)}
122
+ end
123
+ end
124
+
125
+
126
+ private
127
+
128
+ def parse_xml(xml)
129
+ case xml
130
+ when IO
131
+ REXML::Document.new(xml)
132
+ when String
133
+ REXML::Document.new(xml)
134
+ else
135
+ xml
136
+ end
137
+ end
138
+
139
+ def are_equal?(expected_doc, actual_doc)
140
+ comparator = Conditionals.create
141
+ iterate(expected_doc, actual_doc) do |expected_node, actual_node|
142
+ unless comparator.compare_xml_nodes(expected_node, actual_node)
143
+ return false
144
+ end
145
+ end
146
+ true
147
+ end
148
+
149
+ def iterate(expected_doc, actual_doc)
150
+ filter = Test::Unit::XML::XmlEqualFilter.new()
151
+ expected_iterator = NodeIterator.new(expected_doc, filter)
152
+ actual_iterator = NodeIterator.new(actual_doc, filter)
153
+ while expected_iterator.has_next()
154
+ expected_node = expected_iterator.next()
155
+ actual_node = actual_iterator.next()
156
+ yield expected_node, actual_node
157
+ end
158
+ end
159
+
160
+ end
161
+ end
162
+ end
163
+