1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/rexml/test_core.rb
Kouhei Sutou 0f18bc7fca [ruby/rexml] Fix attribute's default namespace behavior
NOTE: It's a backward incompatible change. If we have any serious
problems with this change, we may revert this change.

The XML namespace specification says the default namespace doesn't
apply to attribute names but it does in REXML without this change:

https://www.w3.org/TR/xml-names/#uniqAttrs

> the default namespace does not apply to attribute names

REXML reports a parse error for the following XML that is described as
a valid XML in the XML nsmaspace specification without this change:

    <!-- http://www.w3.org is bound to n1 and is the default -->
    <x xmlns:n1="http://www.w3.org"
       xmlns="http://www.w3.org" >
      <good a="1"     b="2" />
      <good a="1"     n1:a="2" />
    </x>

If attribute doesn't have prefix, the attribute should return "" for
both #prefix and #namespace.

https://github.com/ruby/rexml/commit/9e4fd552bc
2019-08-04 11:54:27 +09:00

1517 lines
45 KiB
Ruby

# -*- coding: utf-8 -*-
# frozen_string_literal: false
require_relative "rexml_test_utils"
require "rexml/document"
require "rexml/parseexception"
require "rexml/output"
require "rexml/source"
require "rexml/formatters/pretty"
require "rexml/undefinednamespaceexception"
require_relative "listener"
module REXMLTests
class Tester < Test::Unit::TestCase
include REXMLTestUtils
include REXML
def setup
@xsa_source = <<-EOL
<?xml version="1.0"?>
<?xsl stylesheet="blah.xsl"?>
<!-- The first line tests the XMLDecl, the second tests PI.
The next line tests DocType. This line tests comments. -->
<!DOCTYPE xsa PUBLIC
"-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML"
"http://www.garshol.priv.no/download/xsa/xsa.dtd">
<xsa>
<vendor id="blah">
<name>Lars Marius Garshol</name>
<email>larsga@garshol.priv.no</email>
<url>http://www.stud.ifi.uio.no/~lmariusg/</url>
</vendor>
</xsa>
EOL
end
def test_bad_markup
[
"<pkg='version'> foo </pkg>",
'<0/>',
'<a>&</a>',
'<a>&a</a>',
# '<a>&a;</a>', # FIXME
'<a a="<"/>',
'<a 3="<"/>',
'<a a="1" a="2"/>',
'<a><!-- -- --></a>',
'<a><!-- ---></a>',
'<a>&#x00;</a>',
'<a>&#0;</a>',
"<a a='&#0;' />",
"<a>\f</a>",
"<a a='\f' />",
"<a>\000</a>",
# FIXME '<a' + [65535].pack('U') + ' />',
'<a>&#xfffe;</a>',
'<a>&#65535;</a>',
# FIXME '<a' + [0x0371].pack('U') + ' />',
# FIXME '<a a' + [0x0371].pack('U') + '="" />',
].each do |src|
assert_raise( ParseException, %Q{Parse #{src.inspect} should have failed!} ) do
Document.new(src)
end
end
end
def test_attribute
# Testing constructors
#a = Attribute.new "hello", "dolly"
#b = Attribute.new a
#d = Document.new( "<a hello='dolly' href='blah'/>" )
#c = d[0].attributes.get_attribute( "hello" )
#assert_equal a, b
#for attr in [ a, b, c]
# assert_equal "hello", attr.name
# assert_equal "dolly", attr.value
#end
# This because of a reported bug in attribute handling in 1.0a8
source = '<a att="A">blah</a>'
doc = Document.new source
doc.elements.each do |a|
a.attributes['att'] << 'B'
assert_equal "AB", a.attributes['att']
a.attributes['att'] = 'C'
assert_equal "C", a.attributes['att']
end
# Bryan Murphy <murphybryanp@yahoo.com>
text = "this is a {target[@name='test']/@value} test"
source = <<-EOL
<?xml version="1.0"?>
<doc search="#{text}"/>
EOL
xml = Document.new source
value = xml.root.attributes["search"]
assert_equal text, value.to_s
e = Element.new "test"
e.add_attributes({ "name1" => "test1", "name4" => "test4" })
e.add_attributes([["name3","test3"], ["name2","test2"]])
assert_equal "test1", e.attributes["name1"]
assert_equal "test2", e.attributes["name2"]
assert_equal "test3", e.attributes["name3"]
assert_equal "test4", e.attributes["name4"]
# ensure that the attributes come out in sorted order
assert_equal %w(<test
name1='test1'
name2='test2'
name3='test3'
name4='test4'/>).join(' '), e.to_s
end
def test_attribute_namespace_conflict
# https://www.w3.org/TR/xml-names/#uniqAttrs
message = <<-MESSAGE
Duplicate attribute "a"
Line: 4
Position: 140
Last 80 unconsumed characters:
MESSAGE
assert_raise_with_message(REXML::ParseException, message) do
Document.new(<<-XML)
<!-- http://www.w3.org is bound to n1 and n2 -->
<x xmlns:n1="http://www.w3.org"
xmlns:n2="http://www.w3.org" >
<bad a="1" a="2" />
<bad n1:a="1" n2:a="2" />
</x>
XML
end
end
def test_attribute_default_namespace
# https://www.w3.org/TR/xml-names/#uniqAttrs
document = Document.new(<<-XML)
<!-- http://www.w3.org is bound to n1 and is the default -->
<x xmlns:n1="http://www.w3.org"
xmlns="http://www.w3.org" >
<good a="1" b="2" />
<good a="1" n1:a="2" />
</x>
XML
attributes = document.root.elements.collect do |element|
element.attributes.each_attribute.collect do |attribute|
[attribute.prefix, attribute.namespace, attribute.name]
end
end
assert_equal([
[
["", "", "a"],
["", "", "b"],
],
[
["", "", "a"],
["n1", "http://www.w3.org", "a"],
],
],
attributes)
end
def test_cdata
test = "The quick brown fox jumped
& < & < \" '
over the lazy dog."
source = "<a><![CDATA[#{test}]]></a>"
d = REXML::Document.new( source )
# Test constructors
cdata = d[0][0]
assert_equal test, cdata.value
end
def test_comment
string = "This is a new comment!"
source = "<!--#{string}-->"
comment = Comment.new string
REXML::Formatters::Default.new.write( comment, out = "" )
assert_equal(source, out)
comment2 = Comment.new comment
assert_equal(comment, comment2)
assert_raise(ParseException) {
REXML::Document.new("<d><!- foo --></d>")
}
assert_raise(ParseException) {
REXML::Document.new("<d><!-- foo -></d>")
}
end
def test_whitespace
doc = Document.new "<root-element><first-element/></root-element>"
assert_equal 1, doc.root.size
assert_equal 1, doc.root.elements.size
doc = Document.new "<root-element>
<first-element/>
</root-element>"
assert_equal 3, doc.root.size
assert_equal 1, doc.root.elements.size
text = " This is text
with a lot of whitespace "
source = "<a>#{text}<b>#{text}</b><c>#{text}</c>#{text}</a>"
doc = Document.new( source, {
:respect_whitespace => %w{ a c }
} )
assert_equal text, doc.elements["//c"].text
string = ""
doc.root.each { |n| string << n.to_s if n.kind_of? Text }
assert_equal text+text, string
string =" lots of blank
space"
doc.root.add_element("d").add_element("c").text = string
doc.root.add_element("e").text = string
assert_equal string, doc.elements["/a/d/c"].text
assert string != doc.elements["/a/e"].text, "Text wasn't properly compressed"
doc = Document.new source, { :respect_whitespace => :all }
doc.root.add_element("d").text = string
assert_equal text, doc.root.text
nxt = ""
doc.root.each { |n| nxt << n.to_s if n.kind_of? Text }
assert_equal text+text, nxt
assert_equal text, doc.root.elements["b"].text
assert_equal text, doc.root.elements["c"].text
assert_equal string, doc.root.elements["d"].text
end
# This isn't complete. We need to check declarations and comments
def test_doctype
string = "something"
correct = "<!DOCTYPE something>"
doc = DocType.new(string)
assert_equal(string, doc.name)
doc.write(out="")
assert_equal(correct, out)
doc2 = DocType.new(doc)
assert_equal(doc.name, doc2.name)
assert_equal(doc.external_id, doc2.external_id)
correct = '<!DOCTYPE xsa PUBLIC "-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML" "http://www.garshol.priv.no/download/xsa/xsa.dtd">'
one_line_source = '<!DOCTYPE xsa PUBLIC "-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML" "http://www.garshol.priv.no/download/xsa/xsa.dtd"><a/>'
doc = Document.new( one_line_source )
doc = doc[0]
assert(doc)
doc.write(test="")
assert_equal(correct, test)
multi_line_source = '<!DOCTYPE xsa PUBLIC
"-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML"
"http://www.garshol.priv.no/download/xsa/xsa.dtd">
<a/>'
d = Document.new( multi_line_source )
doc = d[0]
assert(doc)
doc.write(test="")
assert_equal(correct, test)
odd_space_source = ' <!DOCTYPE
xsa PUBLIC "-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML"
"http://www.garshol.priv.no/download/xsa/xsa.dtd"> <a/>'
d = Document.new( odd_space_source )
dt = d.doctype
dt.write(test="")
assert_equal(correct, test)
# OK, the BIG doctype test, numba wun
doc = File.open(fixture_path("doctype_test.xml")) do |docin|
Document.new(docin)
end
doc.write(test="")
assert_equal(31, doc.doctype.size)
end
def test_document
# Testing cloning
source = "<element/>"
doc = Document.new source
# Testing Root
assert_equal doc.root.name.to_s, "element"
# Testing String source
source = @xsa_source
doc = Document.new source
assert_instance_of XMLDecl, doc.xml_decl
assert_instance_of DocType, doc.doctype
assert_equal doc.version, "1.0"
doc = File.open(fixture_path("dash.xml")) {|s| Document.new s }
assert_equal "content-2", doc.elements["//content-2"].name
end
def test_instruction
target = "use"
content = "ruby"
source = "<?#{target} #{content}?>"
instruction = Instruction.new target, content
instruction2 = Instruction.new instruction
assert_equal(instruction, instruction2)
REXML::Formatters::Default.new.write( instruction, out = "" )
assert_equal(source, out)
d = Document.new( source )
instruction2 = d[0]
assert_equal(instruction.to_s, instruction2.to_s)
assert_raise(ParseException) {
REXML::Document.new("<d><?foo bar></d>")
}
end
def test_parent
parent = Parent.new
begin
parent << "Something"
rescue Exception
parent << Comment.new("Some comment")
assert parent.size == 1, "size of parent should be 1"
else
assert_fail "should have gotten an exception trying to add a "+ "String to a Parent"
end
source = "<a><one/><three/><five/></a>"
doc = Document.new source
three = doc.root.elements["three"]
doc.root.insert_before( three, Element.new("two") )
nxt = doc.root.elements["one"]
string = ""
while nxt
string << nxt.name
nxt = nxt.next_sibling
end
assert_equal "onetwothreefive", string
doc.root.insert_after( three, Element.new("four") )
string = ""
doc.root.each { |element| string << element.name }
assert_equal "onetwothreefourfive", string
string = ""
nxt = doc.root.elements["five"]
while nxt
string << nxt.name
nxt = nxt.previous_sibling
end
assert_equal "fivefourthreetwoone", string
doc.insert_after "//two", Element.new("two-and-half")
string = doc.root.elements.collect {|x| x.name}.join
assert_equal "onetwotwo-and-halfthreefourfive", string
doc.elements["/a/five"].insert_before "../four", Element.new("three-and-half")
string = doc.root.elements.collect {|x| x.name}.join
assert_equal "onetwotwo-and-halfthreethree-and-halffourfive", string
doc.elements["/a/five"].previous_sibling = Element.new("four-and-half")
string = doc.root.elements.collect {|x| x.name}.join
assert_equal "onetwotwo-and-halfthreethree-and-halffourfour-and-halffive", string
doc.elements["/a/one"].next_sibling = Element.new("one-and-half")
string = doc.root.elements.collect {|x| x.name}.join
assert_equal "oneone-and-halftwotwo-and-halfthreethree-and-halffourfour-and-halffive", string
doc = Document.new "<a><one/><three/></a>"
doc.root[1,0] = Element.new "two"
string = ""
doc.root.each { |el| string << el.name }
assert_equal "onetwothree", string
end
# The Source classes are tested extensively throughout the test suite
def test_source
# Testing string source
source = @xsa_source
doc = Document.new source
assert_equal doc.root.name.to_s, "xsa"
# Testing IO source
doc = File.open(fixture_path("project.xml")) {|f| Document.new f }
assert_equal doc.root.name.to_s, "Project"
end
def test_text
f = REXML::Formatters::Default.new
string = "Some text"
text = Text.new(string)
assert_equal(string, text.to_s)
text2 = Text.new(text)
assert_equal(text, text2)
#testing substitution
string = "0 < ( 1 & 1 )"
correct = "0 &lt; ( 1 &amp; 1 )"
text = Text.new(string, true)
f.write(text,out="")
assert_equal(correct, out)
string = "Cats &amp; dogs"
text = Text.new(string, false, nil, true)
assert_equal(string, text.to_s)
string2 = "<a>#{string}</a>"
doc = Document.new( string2, {
:raw => %w{ a b }
} )
f.write(doc,out="")
assert_equal(string2, out)
b = doc.root.add_element( "b" )
b.text = string
assert_equal(string, b.get_text.to_s)
c = doc.root.add_element("c")
c.text = string
assert_equal("Cats &amp;amp; dogs", c.get_text.to_s)
# test all
string = "<a>&amp;<b>&lt;</b><c>&gt;<d>&quot;</d></c></a>"
doc = Document.new(string, { :raw => :all })
assert_equal( "&amp;", doc.elements["/a"][0].to_s )
assert_equal( "&", doc.elements["/a"].text )
assert_equal( "&lt;", doc.elements["/a/b"][0].to_s )
assert_equal( "<", doc.elements["/a/b"].text )
assert_equal( "&gt;", doc.elements["/a/c"][0].to_s )
assert_equal( ">", doc.elements["/a/c"].text )
assert_equal( '&quot;', doc.elements["//d"][0].to_s )
assert_equal( '"', doc.elements["//d"].text )
# test some other stuff
doc = Document.new('<a><b/></a>')
doc.root.text = 'Sean'
assert_equal( '<a><b/>Sean</a>', doc.to_s )
doc.root.text = 'Elliott'
assert_equal( '<a><b/>Elliott</a>', doc.to_s )
doc.root.add_element( 'c' )
assert_equal( '<a><b/>Elliott<c/></a>', doc.to_s )
doc.root.text = 'Russell'
assert_equal( '<a><b/>Russell<c/></a>', doc.to_s )
doc.root.text = nil
assert_equal( '<a><b/><c/></a>', doc.to_s )
end
def test_text_frozen
string = "Frozen".freeze
text = Text.new(string)
assert_equal(string, text.to_s)
end
def test_xmldecl
source = "<?xml version='1.0'?>"
# test args
# test no args
decl2 = XMLDecl.new
assert_equal source, decl2.to_s
# test XMLDecl
decl2 = XMLDecl.new "1.0"
assert_equal source, decl2.to_s
end
def test_xmldecl_utf_16be_encoding_name
assert_equal("<?xml version='1.0' encoding='UTF-16'?>",
XMLDecl.new("1.0", "UTF-16").to_s)
end
def each_test( element, xpath, num_children )
count = 0
element.each_element( xpath ) { |child|
count += 1
yield child if block_given?
}
assert_equal num_children, count
end
# This is the biggest test, as the number of permutations of xpath are
# enormous.
def test_element_access
# Testing each_element
doc = File.open(fixture_path("project.xml")) {|f| Document.new f }
each_test( doc, "/", 1 ) { |child|
assert_equal doc.name, child.name
}
each_test(doc, ".", 1) { |child| assert_equal doc, child }
each_test(doc.root, "..", 1) { |child| assert_equal doc, child }
each_test(doc.root, "*", 5)
each_test(doc, "Project/Datasets", 1) { |child|
assert_equal "Datasets", child.name
}
each_test(doc, "Project/Datasets/link", 2 )
each_test(doc.root, "/Project/Description", 1) {|child|
assert_equal "Description", child.name
}
each_test(doc.root, "./Description",1 ) { |child|
assert_equal "Description",child.name
}
each_test(doc.root, "../Project",1 ) { |child|
assert_equal doc.root, child
}
#each_test(doc,".../link",2) {|child| assert_equal "link",child.name.to_s}
# test get_element
first = doc.elements[ "Project" ]
assert_equal doc.root, first
second = doc.elements[ "Project" ].elements[1]
third = doc.elements[ "Project/Creator" ]
assert_equal second, third
fourth = doc.elements[ "Project/Datasets/link[@idref='18']" ]
assert_equal "Test data 1", fourth.attributes["name"]
# Testing each_predicate
each_test( doc, "Project/Datasets/link[@idref='18']", 1 ) { |child|
assert_equal "Test data 1", child.attributes["name"]
}
# testing next/previous_element
creator = doc.elements["//Creator"]
lm = creator.next_element
assert_equal "LastModifier", lm.name
assert_equal "Creator", lm.previous_element.name
end
def test_child
sean = Element.new "Sean"
rubbell = Element.new "Rubbell"
elliott = sean.add_element "Elliott"
sean << rubbell
assert_equal elliott, rubbell.previous_sibling
assert_equal rubbell, elliott.next_sibling
russell = Element.new "Russell"
rubbell.replace_with russell
assert_equal elliott, russell.previous_sibling
assert_equal russell, elliott.next_sibling
assert_nil russell.document
assert_equal sean, russell.root
end
# Most of this class is tested elsewhere. Here are the methods which
# aren't used in any other class
def test_element
sean = Element.new "Sean"
string = "1) He's a great guy!"
sean.text = string
russell = Element.new "Russell"
sean << russell
russell.attributes["email"] = "ser@germane-software.com"
assert_equal russell.attributes["email"], "ser@germane-software.com"
russell.attributes["webpage"] = "http://www.germane-software.com/~ser"
assert sean.has_text?, "element should have text"
assert_equal sean.text, string
assert sean.has_elements?, "element should have one element"
string = "2) What a stud!"
sean.add_text string
sean.text = "3) Super programmer!"
sean.text = nil
assert sean.has_text?, "element should still have text"
assert_equal sean.text, string
russell.delete_attribute "email"
assert_nil russell.attributes["email"]
russell.attributes.delete "webpage"
assert !russell.has_attributes?, "element should have no attributes"
end
def test_no_format
source = "<a><b><c>blah</c><d/></b></a>"
out = ""
doc = Document.new( source )
doc.write(out)
assert_equal(source, out)
end
def test_namespace
source = <<-EOF
<x xmlns:foo="http://www.bar.com/schema">
</x>
EOF
doc = Document.new(source)
assert_equal("http://www.bar.com/schema", doc.root.namespace( "foo" ))
source = <<-EOF
<!-- bar namespace is "someuri" -->
<foo:bar xmlns="default" xmlns:foo="someuri">
<!-- a namespace is "default" -->
<a/>
<!-- foo:b namespace is "someuri" -->
<foo:b>
<!-- c namespace is "default" -->
<c/>
</foo:b>
<!-- d namespace is "notdefault" -->
<d xmlns="notdefault">
<!-- e namespace is "notdefault" -->
<e/>
<f xmlns="">
<g/>
</f>
</d>
</foo:bar>
EOF
doc = Document.new source
assert_equal "someuri", doc.root.namespace
assert_equal "default", doc.root.elements[1].namespace
assert_equal "someuri", doc.root.elements[2].namespace
assert_equal "notdefault", doc.root.elements[ 3 ].namespace
# Testing namespaces in attributes
source = <<-EOF
<a xmlns:b="uri">
<b b:a="x" a="y"/>
<c xmlns="foo">
</c>
</a>
EOF
doc = Document.new source
b = doc.root.elements["b"]
assert_equal "x", b.attributes["b:a"]
assert_equal "y", b.attributes["a"]
doc = Document.new
doc.add_element "sean:blah"
doc.root.text = "Some text"
out = ""
doc.write(out)
assert_equal "<sean:blah>Some text</sean:blah>", out
end
def test_add_namespace
e = Element.new 'a'
e.add_namespace 'someuri'
e.add_namespace 'foo', 'otheruri'
e.add_namespace 'xmlns:bar', 'thirduri'
assert_equal 'someuri', e.attributes['xmlns']
assert_equal 'otheruri', e.attributes['xmlns:foo']
assert_equal 'thirduri', e.attributes['xmlns:bar']
end
def test_big_documentation
d = File.open(fixture_path("documentation.xml")) {|f| Document.new f }
assert_equal "Sean Russell", d.elements["documentation/head/author"].text.tr("\n\t", " ").squeeze(" ")
out = ""
d.write out
end
def test_tutorial
doc = File.open(fixture_path("tutorial.xml")) {|f| Document.new f }
out = ""
doc.write out
end
def test_stream
c = Listener.new
File.open(fixture_path("documentation.xml")) do |f|
Document.parse_stream( f, c )
end
assert(c.ts, "Stream parsing apparently didn't parse the whole file")
assert(c.te, "Stream parsing dropped end tag for documentation")
Document.parse_stream("<a.b> <c/> </a.b>", c)
Document.parse_stream("<a>&lt;&gt;&amp;</a>", c)
assert_equal('<>&', c.normalize)
end
def test_line
f = File.new(fixture_path("bad.xml"))
Document.new f
assert_fail "There should have been an error"
rescue Exception
# We should get here
assert($!.line == 5, "Should have been an error on line 5, "+
"but was reported as being on line #{$!.line}" )
ensure
f.close if f
end
def test_substitution
val = "a'b\"c"
el = Element.new("a")
el.attributes["x"] = val
REXML::Formatters::Default.new.write(el, out="")
nel = Document.new( out)
assert_equal( val, nel.root.attributes["x"] )
end
def test_exception
source = SourceFactory.create_from "<a/>"
p = ParseException.new( "dummy message", source )
begin
raise "dummy"
rescue Exception
p.continued_exception = $!
end
end
def test_bad_content
in_gt = '<root-el>content>content</root-el>'
in_lt = '<root-el>content<content</root-el>'
# This is OK
tree_gt = Document.new in_gt
assert_equal "content>content", tree_gt.elements[1].text
# This isn't
begin
Document.new in_lt
assert_fail "Should have gotten a parse error"
rescue ParseException
end
end
def test_iso_8859_1_output_function
out = ""
output = Output.new( out )
koln_iso_8859_1 = "K\xF6ln"
koln_utf8 = "K\xc3\xb6ln"
source = Source.new( koln_iso_8859_1, 'iso-8859-1' )
results = source.scan(/.*/)[0]
koln_utf8.force_encoding('UTF-8') if koln_utf8.respond_to?(:force_encoding)
assert_equal koln_utf8, results
output << results
if koln_iso_8859_1.respond_to?(:force_encoding)
koln_iso_8859_1.force_encoding('ISO-8859-1')
end
assert_equal koln_iso_8859_1, out
end
def test_attributes_each
doc = Document.new("<a xmlns:a='foo'><b x='1' y='2' z='3' a:x='4'/></a>")
count = 0
doc.root.elements[1].attributes.each {|k,v| count += 1 }
assert_equal 4, count
end
def test_delete_namespace
doc = Document.new "<a xmlns='1' xmlns:x='2'/>"
doc.root.delete_namespace
doc.root.delete_namespace 'x'
assert_equal "<a/>", doc.to_s
end
def test_each_element_with_attribute
doc = Document.new "<a><b id='1'/><c id='2'/><d id='1'/><e/></a>"
arry = []
block = proc { |e|
assert arry.include?(e.name)
arry.delete e.name
}
# Yields b, c, d
arry = %w{b c d}
doc.root.each_element_with_attribute( 'id', &block )
assert_equal 0, arry.size
# Yields b, d
arry = %w{b d}
doc.root.each_element_with_attribute( 'id', '1', &block )
assert_equal 0, arry.size
# Yields b
arry = ['b']
doc.root.each_element_with_attribute( 'id', '1', 1, &block )
assert_equal 0, arry.size
# Yields d
arry = ['d']
doc.root.each_element_with_attribute( 'id', '1', 0, 'd', &block )
assert_equal 0, arry.size
end
def test_each_element_with_text
doc = Document.new '<a><b>b</b><c>b</c><d>d</d><e/></a>'
arry = []
block = proc { |e|
assert arry.include?(e.name)
arry.delete e.name
}
# Yields b, c, d
arry = %w{b c d}
doc.root.each_element_with_text(&block)
assert_equal 0, arry.size
# Yields b, d
arry = %w{b c}
doc.root.each_element_with_text( 'b', &block )
assert_equal 0, arry.size
# Yields b
arry = ['b']
doc.root.each_element_with_text( 'b', 1, &block )
assert_equal 0, arry.size
# Yields d
arry = ['d']
doc.root.each_element_with_text( nil, 0, 'd', &block )
assert_equal 0, arry.size
end
def test_element_parse_stream
s = Source.new( "<a>some text</a>" )
l = Listener.new
class << l
def tag_start name, attributes
raise "Didn't find proper tag name" unless 'a'==name
end
end
Document::parse_stream(s, l)
end
def test_deep_clone
a = Document.new( '<?xml version="1"?><a x="y"><b>text</b>text<c><d><e>text</e></d></c></a>' )
b = a.deep_clone
assert_equal a.to_s, b.to_s
a = Document.new( '<a>some &lt; text <b> more &gt; text </b> &gt; </a>' )
b = a.deep_clone
assert_equal a.to_s, b.to_s
c = Document.new( b.to_s )
assert_equal a.to_s, c.to_s
end
def test_whitespace_before_root
a = <<EOL
<?xml version='1.0'?>
<blo>
<wak>
</wak>
</blo>
EOL
d = Document.new(a)
b = ""
d.write( b )
assert_equal a,b
end
def test_entities
a = Document.new( '<a>&#101;&#x65;&#252;</a>' )
assert_equal('eeü'.force_encoding("UTF-8"), a.root.text)
end
def test_element_decl
element_decl = Source.new("<!DOCTYPE foo [
<!ELEMENT bar (#PCDATA)>
]>")
doc = Document.new( element_decl )
d = doc[0]
assert_equal("<!ELEMENT bar (#PCDATA)>", d.to_s.split(/\n/)[1].strip)
end
def test_attlist_decl
doc = Document.new <<-EOL
<!DOCTYPE blah [
<!ATTLIST blah
xmlns CDATA "foo">
<!ATTLIST a
bar CDATA "gobble"
xmlns:one CDATA "two"
>
]>
<a xmlns:three='xxx' three='yyy'><one:b/><three:c/></a>
EOL
assert_equal 'gobble', doc.root.attributes['bar']
assert_equal 'xxx', doc.root.elements[2].namespace
assert_equal 'two', doc.root.elements[1].namespace
assert_equal 'foo', doc.root.namespace
doc = Document.new <<-EOL
<?xml version="1.0"?>
<!DOCTYPE schema SYSTEM "XMLSchema.dtd" [
<!ENTITY % p ''>
<!ENTITY % s ''>
<!ATTLIST schema
xmlns:svg CDATA #FIXED "http://www.w3.org/2000/svg"
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink"
xmlns:xml CDATA #FIXED "http://www.w3.org/XML/1998/namespace"
>]>
<schema/>
EOL
prefixes = doc.root.prefixes.sort
correct = ['svg', 'xlink', 'xml']
assert_equal correct, prefixes
end
def test_attlist_write
doc = File.open(fixture_path("foo.xml")) {|file| Document.new file }
out = ''
doc.write(out)
end
def test_more_namespaces
assert_raise( REXML::UndefinedNamespaceException,
%Q{Should have gotten an Undefined Namespace error} ) {
Document.new("<r><p><n:c/></p></r>")
}
doc2 = Document.new("<r xmlns:n='1'><p><n:c/></p></r>")
es = XPath.match(doc2, '//c')
assert_equal 0, es.size
es = XPath.match(doc2, '//n:c')
assert_equal 1, es.size
doc2.root.add_namespace('m', '2')
doc2.root.add_element("m:o")
es = XPath.match(doc2, './/o')
assert_equal 0, es.size
es = XPath.match(doc2, '//n:c')
assert_equal 1, es.size
end
def test_ticket_51
doc = REXML::Document.new <<-EOL
<test xmlns='1' xmlns:x='1'>
<a>X</a>
<x:a>Y</x:a>
<b xmlns='2'>
<a>Z</a>
</b>
</test>
EOL
# The most common case. People not caring about the namespaces much.
assert_equal( "XY", XPath.match( doc, "/*:test/*:a/text()" ).join )
assert_equal( "XY", XPath.match( doc, "/*:test/x:a/text()" ).join )
# Surprising? I don't think so, if you believe my definition of the "common case"
assert_equal( "XYZ", XPath.match( doc, "//*:a/text()" ).join )
# These are the uncommon cases. Namespaces are actually important, so we define our own
# mappings, and pass them in.
assert_equal( "XY", XPath.match( doc, "/f:test/f:a/text()", { "f" => "1" } ).join )
# The namespaces are defined, and override the original mappings
assert_equal( "XY", XPath.match( doc, "/*:test/*:a/text()", { "f" => "1" } ).join )
assert_equal( "", XPath.match( doc, "/x:test/x:a/text()", { "f" => "1" } ).join )
assert_equal( "XYZ", XPath.match( doc, "//*:a/text()", { "f" => "1" } ).join )
end
def test_processing_instruction
d = Document.new("<a><?foo bar?><?foo2 bar2?><b><?foo3 bar3?></b><?foo4 bar4?></a>")
assert_equal 4, XPath.match(d, '//processing-instruction()' ).size
match = XPath.match(d, "//processing-instruction('foo3')" )
assert_equal 1, match.size
assert_equal 'bar3', match[0].content
end
def test_oses_with_bad_EOLs
Document.new("\n\n\n<?xml version='1.0'?>\n\n\n<a/>\n\n")
end
# Contributed (with patch to fix bug) by Kouhei
def test_ignore_whitespace
source = "<a> <b/> abc <![CDATA[def]]> </a>"
context_all = {:ignore_whitespace_nodes => :all}
context_a = {:ignore_whitespace_nodes => %(a)}
context_b = {:ignore_whitespace_nodes => %(b)}
tests = [[[" abc ", "def"], context_all],
[[" abc ", "def"], context_a],
[[" ", " abc ", "def", " "], context_b]]
tests.each do |test|
assert_equal(test[0], Document.new(source, test[1]).root.texts.collect{|x|
x.to_s})
end
end
def test_0xD_in_preface
doc = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\x0D<opml version=\"1.0\">\x0D</opml>"
doc = Document.new doc
end
def test_hyphens_in_doctype
doc = REXML::Document.new <<-EOQ
<?xml version="1.0"?>
<!DOCTYPE a-b-c>
<a-b-c>
<a/>
</a-b-c>
EOQ
assert_equal('a-b-c', doc.doctype.name)
end
def test_accents
docs = [
%Q{<?xml version="1.0" encoding="ISO-8859-1"?>
<gnuPod>
<files>
<file id="57" artist="Coralie Cl\357\277\275ent" />
</files>
</gnuPod>},
'<?xml version="1.0" encoding="ISO-8859-1"?>
<gnuPod>
<files>
<file id="71" album="Astrakan Caf" />
</files>
</gnuPod>',
%Q{<?xml version="1.0" encoding="ISO-8859-1"?>
<gnuPod>
<files>
<file id="71" album="Astrakan Caf\357\277\275eria" />
</files>
</gnuPod>},
%Q{<?xml version="1.0" encoding="ISO-8859-1"?>
<gnuPod>
<files>
<file id="71" album="Astrakan Caf\357\277\275" />
</files>
</gnuPod>} ]
docs.each_with_index { |d,i|
begin
REXML::Document.new(d)
rescue
puts "#{i} => #{docs[i]}"
raise
end
}
end
def test_replace_text
e = REXML::Element.new( "a" )
e.add_text( "foo" )
assert_equal( "<a>foo</a>", e.to_s )
e[0].value = "bar"
assert_equal( "<a>bar</a>", e.to_s )
e[0].value = "<"
assert_equal( "<a>&lt;</a>", e.to_s )
assert_equal( "<", e[0].value )
end
def test_write_doctype
## XML Document and Declaration
document = REXML::Document.new
xmldecl = REXML::XMLDecl.new("1.0", "UTF-8")
document.add(xmldecl)
s = ""
document.write(s)
## XML Doctype
str = '<!DOCTYPE foo "bar">'
source = REXML::Source.new(str)
doctype = REXML::DocType.new(source)
document.add(doctype)
document.write(s)
## Element
element = REXML::Element.new("hoge")
document.add(element)
document.write(s)
end
def test_write_cdata
src = "<a>A</a>"
doc = REXML::Document.new( src )
out = ""
doc.write( out )
assert_equal( src, out )
src = "<a><![CDATA[A]]></a>"
doc = REXML::Document.new( src )
out = ""
doc.write( out )
assert_equal( src, out )
end
def test_namespace_attributes
source = <<-EOL
<a xmlns:x="1">
<x:b x:n="foo"/>
</a>
EOL
d = Document.new( source )
assert_equal( 'foo', REXML::XPath.first(d.root, "//x:b/@x:n").value )
assert_equal( nil, REXML::XPath.first(d.root, "//x:b/@x:n", {}))
end
def test_null_element_name
a = REXML::Document.new
assert_raise( RuntimeError ) {
a.add_element( nil )
}
end
def test_text_raw
# From the REXML tutorial
# (http://www.germane-software.com/software/rexml/test/data/tutorial.html)
doc = Document.new <<-EOL
<?xml version="1.0"?>
<!DOCTYPE schema SYSTEM "XMLSchema.dtd" [
<!ENTITY % s 'Sean'>
]>
<a/>
EOL
a = doc.root
# This makes sure that RAW text nodes don't have their entity strings
# replaced
t = Text.new "Sean", false, nil, true
a.text = t
assert_equal( "Sean", t.to_s )
assert_equal( "Sean", t.value )
# This makes sure that they do
t = Text.new "Sean", false, nil, false
a.text = t
assert_equal( "&s;", t.to_s )
assert_equal( "Sean", t.value )
t = Text.new "&s;", false, nil, true
a.text = t
assert_equal( "&s;", t.to_s )
assert_equal( "Sean", t.value )
t = Text.new "&s;", false, nil, true
a.text = t
assert_equal( "&s;", t.to_s )
assert_equal( "Sean", t.value )
# Ticket #44
t = REXML::Text.new( "&amp;", false, nil, true )
assert_equal( "&amp;", t.to_s )
t = REXML::Text.new("&amp;", false, false)
assert_equal( "&amp;amp;", t.to_s )
end
def test_to_xpath
doc = REXML::Document.new( %q{<tag1>
<tag2 name="tag2"/>
<tag2 name="tag2"/>
</tag1>})
names = %w{ /tag1/tag2[1] /tag1/tag2[2] }
doc.root.elements.each_with_index {|el, i|
assert_equal( names[i], el.xpath )
}
end
def test_transitive
doc = REXML::Document.new( "<a/>")
s = ""
doc.write( s, 0, true )
end
# This is issue #40
def test_replace_with
old = '<doc>old<foo/>old</doc>'
d = REXML::Document.new(old).root
new = REXML::Text.new('new',true,nil,true)
child = d.children[2]
child.replace_with(new)
assert_equal( new, d.children[2] )
end
def test_repeated_writes
a = IO.read(fixture_path("iso8859-1.xml"))
f = REXML::Formatters::Pretty.new
xmldoc = REXML::Document.new( a )
a_andre = xmldoc.elements['//image'].attributes['caption']
f.write(xmldoc,b="")
xmldoc = REXML::Document.new(b)
b_andre = xmldoc.elements['//image'].attributes['caption']
assert_equal( a_andre, b_andre )
f.write(xmldoc,c="")
xmldoc = REXML::Document.new(c)
c_andre = xmldoc.elements['//image'].attributes['caption']
assert_equal( b_andre, c_andre )
o = Output.new(d="","UTF-8")
f.write(xmldoc,o)
assert_not_equal( c, d )
end
def test_pretty_format_long_text_finite
n = 1_000_000
long_text = 'aaaa ' * n
xml = "<doc>#{long_text}</doc>"
formatter = REXML::Formatters::Pretty.new
document = nil
begin
document = REXML::Document.new(xml)
rescue REXML::ParseException
skip_message = "skip this test because we can't check Pretty#wrap " +
"works without #<SystemStackError: stack level too deep> on " +
"small memory system. #<RegexpError: failed to allocate memory> " +
"will be raised on the system. See also [ruby-dev:42599]."
return skip_message
end
output = ""
formatter.write(document, output)
assert_equal("<doc>\n" +
((" " + (" aaaa" * 15) + "\n") * (n / 15)) +
" " + ("aaaa " * (n % 15)) + "\n" +
"</doc>",
output)
end
def test_pretty_format_deep_indent
n = 6
elements = ""
n.times do |i|
elements << "<element#{i}>"
elements << "element#{i} " * 5
end
(n - 1).downto(0) do |i|
elements << "</element#{i}>"
end
xml = "<doc>#{elements}</doc>"
document = REXML::Document.new(xml)
formatter = REXML::Formatters::Pretty.new
formatter.width = 20
output = ""
formatter.write(document, output)
assert_equal(<<-XML.strip, output)
<doc>
<element0>
element0
element0
element0
element0
element0\s
<element1>
element1
element1
element1
element1
element1\s
<element2>
element2
element2
element2
element2
element2\s
<element3>
element3
element3
element3
element3
element3\s
<element4>
element4
element4
element4
element4
element4
\s
<element5>
element5 element5 element5 element5 element5\s
</element5>
</element4>
</element3>
</element2>
</element1>
</element0>
</doc>
XML
end
def test_ticket_58
doc = REXML::Document.new
doc << REXML::XMLDecl.default
doc << REXML::Element.new("a")
str = ""
doc.write(str)
assert_equal("<a/>", str)
doc = REXML::Document.new
doc << REXML::XMLDecl.new("1.0", "UTF-8")
doc << REXML::Element.new("a")
str = ""
doc.write(str)
assert_equal("<?xml version='1.0' encoding='UTF-8'?><a/>", str)
end
# Incomplete tags should generate an error
def test_ticket_53
assert_raise( REXML::ParseException ) {
REXML::Document.new( "<a><b></a>" )
}
assert_raise( REXML::ParseException ) {
REXML::Document.new( "<a><b>" )
}
assert_raise( REXML::ParseException ) {
REXML::Document.new( "<a><b/>" )
}
end
def test_ticket_52
source = "<!-- this is a single line comment -->"
d = REXML::Document.new(source)
d.write(k="")
assert_equal( source, k )
source = "<a><!-- Comment --></a>"
target = "<a>\n <!-- Comment -->\n</a>"
d = REXML::Document.new(source)
REXML::Formatters::Pretty.new(4).write(d,k="")
assert_equal( target, k )
end
def test_ticket_76
src = "<div>at&t"
assert_raise( ParseException, %Q{"#{src}" is invalid XML} ) {
REXML::Document.new(src)
}
end
def test_ticket_21
src = "<foo bar=value/>"
exception = assert_raise(ParseException) do
Document.new(src)
end
assert_equal(<<-DETAIL, exception.to_s)
Missing attribute value start quote: <bar>
Line: 1
Position: 16
Last 80 unconsumed characters:
DETAIL
end
def test_ticket_63
File.open(fixture_path("t63-1.xml")) {|f| Document.new(f) }
end
def test_ticket_75
d = File.open(fixture_path("t75.xml")) {|f| REXML::Document.new(f) }
assert_equal("tree", d.root.name)
end
def test_ticket_48_part_II
f = REXML::Formatters::Pretty.new
#- rexml sanity check (bugs in ruby 1.8.4, ruby 1.8.6)
xmldoc = Document.new("<test/>")
xmldoc << XMLDecl.new(XMLDecl::DEFAULT_VERSION, "UTF-8")
content = ['61c3a927223c3e26'].pack("H*")
content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
#- is some UTF-8 text but just to make sure my editor won't magically convert..
xmldoc.root.add_attribute('attr', content)
f.write(xmldoc,out=[])
xmldoc = REXML::Document.new(out.join)
sanity1 = xmldoc.root.attributes['attr']
f.write(xmldoc,out=[])
xmldoc = REXML::Document.new(out.join)
sanity2 = xmldoc.root.attributes['attr']
f.write(xmldoc,out=[])
assert_equal( sanity1, sanity2 )
end
def test_ticket_88
doc = REXML::Document.new("<?xml version=\"1.0\" encoding=\"shift_jis\"?>")
assert_equal("<?xml version='1.0' encoding='SHIFT_JIS'?>", doc.to_s)
doc = REXML::Document.new("<?xml version = \"1.0\" encoding = \"shift_jis\"?>")
assert_equal("<?xml version='1.0' encoding='SHIFT_JIS'?>", doc.to_s)
end
def test_ticket_85
xml = <<ENDXML
<foo>
<bar>
<bob name='jimmy'/>
</bar>
</foo>
ENDXML
yml = "<foo>
<bar>
<bob name='jimmy'/>
</bar>
</foo>"
# The pretty printer ignores all whitespace, anyway so output1 == output2
f = REXML::Formatters::Pretty.new( 2 )
d = Document.new( xml, :ignore_whitespace_nodes=>:all )
f.write( d, output1="" )
d = Document.new( xml )
f.write( d, output2="" )
# Output directives should override whitespace directives.
assert_equal( output1, output2 )
# The base case.
d = Document.new(yml)
f.write( d, output3="" )
assert_equal( output3.strip, output2.strip )
d = Document.new(yml)
f.write( d, output4="" )
assert_equal( output3.strip, output4.strip )
end
def test_ticket_91
source="<root>
<bah something='1' somethingelse='bah'>
<something>great</something>
</bah>
</root>"
expected="<root>
<bah something='1' somethingelse='bah'>
<something>great</something>
</bah>
<bah/>
</root>"
d = Document.new( source )
d.root.add_element( "bah" )
p=REXML::Formatters::Pretty.new(2)
p.compact = true # Don't add whitespace to text nodes unless necessary
p.write(d,out="")
assert_equal( expected, out )
end
def test_ticket_95
testd = REXML::Document.new "<a><b><c/><c/><c/></b></a>"
testd.write(out1="")
testd.elements["//c[2]"].xpath
testd.write(out2="")
assert_equal(out1,out2)
end
def test_ticket_102
doc = REXML::Document.new '<doc xmlns="ns"><item name="foo"/></doc>'
assert_equal( "foo", doc.root.elements["*:item"].attribute("name","ns").to_s )
assert_equal( "item", doc.root.elements["*:item[@name='foo']"].name )
end
def test_ticket_14
# Per .2.5 Node Tests of XPath spec
assert_raise( REXML::UndefinedNamespaceException,
%Q{Should have gotten an Undefined Namespace error} ) {
Document.new("<a><n:b/></a>")
}
end
# 5.7 Text Nodes
# Character data is grouped into text nodes. As much character data as
# possible is grouped into each text node: a text node never has an
# immediately following or preceding sibling that is a text node. The
# string-value of a text node is the character data. A text node always has
# at least one character of data.
def test_ticket_105
d = Document.new("<a/>")
d.root.add_text( "a" )
d.root.add_text( "b" )
assert_equal( 1, d.root.children.size )
end
# phantom namespace same as default namespace
def test_ticket_121
doc = REXML::Document.new(
'<doc xmlns="ns" xmlns:phantom="ns"><item name="foo">text</item></doc>'
)
assert_equal 'text', doc.text( "/*:doc/*:item[@name='foo']" )
assert_equal "name='foo'",
doc.root.elements["*:item"].attribute("name", "ns").inspect
assert_equal "<item name='foo'>text</item>",
doc.root.elements["*:item[@name='foo']"].to_s
end
def test_ticket_135
bean_element = REXML::Element.new("bean")
textToAdd = "(&#38;(|(memberof=CN=somegroupabcdefgh,OU=OUsucks,DC=hookemhorns,DC=com)(mail=*someco.com))(acct=%u)(!(extraparameter:2.2.222.222222.2.2.222:=2)))"
bean_element.add_element("prop", {"key"=> "filter"}).add_text(textToAdd)
doc = REXML::Document.new
doc.add_element(bean_element)
REXML::Formatters::Pretty.new(3).write( doc, out = "" )
assert_equal "<bean>\n <prop key='filter'>\n (&amp;#38;(|(memberof=CN=somegroupabcdefgh,OU=OUsucks,DC=hookemhorns,DC=com)(mail=*someco.com))(acct=%u)(!(extraparameter:2.2.222.222222.2.2.222:=2)))\n </prop>\n</bean>", out
end
def test_ticket_138
doc = REXML::Document.new(
'<svg xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" ' +
'inkscape:version="0.44" version="1.0"/>'
)
expected = {
"inkscape" => attribute("xmlns:inkscape",
"http://www.inkscape.org/namespaces/inkscape"),
"version" => {
"inkscape" => attribute("inkscape:version", "0.44"),
"" => attribute("version", "1.0"),
},
}
assert_equal(expected, doc.root.attributes)
assert_equal(expected, REXML::Document.new(doc.root.to_s).root.attributes)
end
def test_empty_doc
assert(REXML::Document.new('').children.empty?)
end
private
def attribute(name, value)
REXML::Attribute.new(name, value)
end
end
end