mezza-testunitxml 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ =begin rdoc
2
+ This file mixes in XML assertions in the Test::Unit::TestCase
3
+ class.
4
+
5
+ See #xml_assertions.rb for information about the assertions
6
+ that are mixed in.
7
+ =end
8
+
9
+ require 'test/unit/xml/xml_assertions'
10
+
11
+ module Test
12
+ module Unit
13
+
14
+ # The module Test::Unit::XML is mixed in into the class
15
+ # Test::Unit::TestCase
16
+ class TestCase
17
+ include Test::Unit::XML
18
+ end
19
+ end
20
+ end
21
+
@@ -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,141 @@
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
+ return false unless actual_node.instance_of? expected_node.class
26
+ case actual_node
27
+ when REXML::Document
28
+ # TODO: Implement Document comparison
29
+ true
30
+ when REXML::DocType
31
+ compare_doctypes(expected_node, actual_node)
32
+ when REXML::Element :
33
+ compare_elements(expected_node, actual_node)
34
+ when REXML::CData
35
+ compare_texts(expected_node, actual_node)
36
+ when REXML::Text
37
+ compare_texts(expected_node, actual_node)
38
+ when REXML::Comment
39
+ compare_comments(expected_node, actual_node)
40
+ when REXML::Instruction
41
+ compare_pi(expected_node, actual_node)
42
+ when REXML::XMLDecl
43
+ compare_xml_declaration(expected_node, actual_node)
44
+ #when REXML::Entity
45
+ # compare_xml_entities(expected_node, actual_node)
46
+ else
47
+ puts "Unknown node type #{actual_node.class}"
48
+ false
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def compare_doctypes(expected_node, actual_node)
55
+ return compare_system_id(expected_node.system, actual_node.system) &&
56
+ expected_node.public == actual_node.public &&
57
+ compare_xml_internal_dtd_subset(expected_node, actual_node)
58
+ end
59
+
60
+ def compare_system_id(expected_id, actual_id)
61
+ is_expected_urn = expected_id =~ /^urn:/i
62
+ is_actual_urn = actual_id =~ /^urn:/i
63
+ if is_expected_urn || is_actual_urn
64
+ expected_id == actual_id
65
+ else
66
+ true
67
+ end
68
+ end
69
+
70
+ def compare_elements(expected_node, actual_node)
71
+ return expected_node.name == actual_node.name &&
72
+ expected_node.namespace() == actual_node.namespace() &&
73
+ compare_attributes(expected_node.attributes, actual_node.attributes)
74
+ end
75
+
76
+ def compare_attributes(expected_attributes, actual_attributes)
77
+ return false unless attribute_count(expected_attributes) == attribute_count(actual_attributes)
78
+ expected_attributes.each_attribute do |expected_attribute|
79
+ expected_prefix = expected_attribute.prefix()
80
+ unless expected_prefix == 'xmlns' then
81
+ expected_name = expected_attribute.name
82
+ expected_namespace = expected_attribute.namespace
83
+ actual_attribute = actual_attributes.get_attribute_ns(expected_namespace, expected_name)
84
+ return false unless actual_attribute
85
+ return false if expected_attribute.value() != actual_attribute.value()
86
+ end
87
+ end
88
+ true
89
+ end
90
+
91
+ def attribute_count(attributes)
92
+ # Do not count namespace declarations
93
+ attributes.size - attributes.prefixes.size
94
+ end
95
+
96
+ def compare_texts(expected_node, actual_node)
97
+ expected_node.value.eql?(actual_node.value)
98
+ end
99
+
100
+ def compare_comments(expected_node, actual_node)
101
+ expected_node == actual_node
102
+ end
103
+
104
+ def compare_pi(expected_pi, actual_pi)
105
+ return expected_pi.target == actual_pi.target &&
106
+ expected_pi.content == actual_pi.content
107
+ end
108
+
109
+ def compare_xml_declaration(expected_decl, actual_decl)
110
+ return expected_decl == actual_decl
111
+ end
112
+
113
+ def compare_xml_internal_dtd_subset(expected_node, actual_node)
114
+ expected_subset = expected_node.children()
115
+ actual_subset = actual_node.children()
116
+ return false unless expected_subset.length == actual_subset.length
117
+ expected_subset.inject(true) { |memo, expected_decl|
118
+ case expected_decl
119
+ when REXML::Entity
120
+ memo &&
121
+ expected_decl.value == actual_node.entities[expected_decl.name].value &&
122
+ expected_decl.ndata == actual_node.entities[expected_decl.name].ndata
123
+ when REXML::NotationDecl
124
+ actual_notation_decl = actual_node.notation(expected_decl.name)
125
+ memo &&
126
+ actual_notation_decl != nil &&
127
+ expected_decl.name == actual_notation_decl.name &&
128
+ expected_decl.public == actual_notation_decl.public &&
129
+ expected_decl.system == actual_notation_decl.system
130
+ when REXML::Comment
131
+ true
132
+ else
133
+ raise "Unexpected node type in internal DTD subset of expected document: " + expected_decl.inspect
134
+ end
135
+ }
136
+ end
137
+
138
+ end
139
+ end
140
+ end
141
+ 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,61 @@
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
+ next_node = nil
19
+ if NodeIterator.has_children?(node) then
20
+ next_node = node[0] # The index should be 1 according to the REXML docs
21
+ elsif node.next_sibling_node
22
+ next_node = node.next_sibling_node
23
+ elsif NodeIterator.has_parent_with_sibling?(node)
24
+ next_node = node.parent.next_sibling_node
25
+ end
26
+ return next_node if node_filter.accept(next_node) || next_node == nil
27
+ find_next_node(next_node, node_filter)
28
+ end
29
+
30
+
31
+ def initialize(node, node_filter = NullNodeFilter.new)
32
+ @node_filter = node_filter
33
+ @next_node = node
34
+ end
35
+
36
+ def has_next()
37
+ @next_node ? true : false
38
+ end
39
+
40
+ def next
41
+ node = @next_node
42
+ @next_node = NodeIterator.find_next_node(node, @node_filter)
43
+ node
44
+ end
45
+
46
+
47
+
48
+
49
+ private
50
+
51
+ def NodeIterator.has_children?(node)
52
+ node.respond_to?(:[]) && node.respond_to?(:size) && node.size > 0
53
+ end
54
+
55
+ def NodeIterator.has_parent_with_sibling?(node)
56
+ node.parent && node.parent.next_sibling_node
57
+ end
58
+ end
59
+ end
60
+ end
61
+ 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
+
@@ -0,0 +1,31 @@
1
+ #! /usr/bin/ruby
2
+
3
+ module Test
4
+ module Unit
5
+ module XML
6
+
7
+ # This filter class accepts any node except text nodes
8
+ # that contain non-significant whitespace
9
+ class XmlEqualFilter
10
+ def accept(node)
11
+ case
12
+ when node.kind_of?(REXML::Text)
13
+ is_significant?(node.value)
14
+ when node.kind_of?(REXML::Entity)
15
+ false
16
+ when node.kind_of?(REXML::NotationDecl)
17
+ false
18
+ else
19
+ true
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def is_significant?(string)
26
+ string =~ /^\s*$/ ? false : true
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mezza-testunitxml
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.6
6
+ platform: ruby
7
+ authors:
8
+ - "Henrik M\xC3\xA5rtensson"
9
+ - Merul Patel
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-06-02 00:00:00 +01:00
15
+ default_executable:
16
+ dependencies: []
17
+
18
+ description:
19
+ email:
20
+ - dag.henrik.martensson@gmail.com
21
+ - merul.patel@gmail.com
22
+ executables: []
23
+
24
+ extensions: []
25
+
26
+ extra_rdoc_files: []
27
+
28
+ files:
29
+ - lib/test/unit/xml/attributes_mixin.rb
30
+ - lib/test/unit/xml/conditionals.rb
31
+ - lib/test/unit/xml/doctype_mixin.rb
32
+ - lib/test/unit/xml/nodeiterator.rb
33
+ - lib/test/unit/xml/notationdecl_mixin.rb
34
+ - lib/test/unit/xml/xml_assertions.rb
35
+ - lib/test/unit/xml/xmlequalfilter.rb
36
+ - lib/test/unit/xml.rb
37
+ has_rdoc: true
38
+ homepage: https://github.com/mezza/testunitxml
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 1.8.6
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.5.2
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: testunitxml extends the Test::Unit framework with an assertion for testing well-formed XML documents.
65
+ test_files: []
66
+