require "runit/testcase"
require "runit/cui/testrunner"

require "rexml/document"
require "rexml/parseexception"
require "test/listener"

class Tester < RUNIT::TestCase
  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_attribute
		# Testing constructors
		a = REXML::Attribute.new "hello", "dolly"
		b = REXML::Attribute.new a
		source = REXML::SourceFactory.create_from "hello='dolly' href='blah'"
		c = REXML::Attribute.new source

		assert_equals a, b
		for attr in [ a, b, c]
			assert_equals "hello", attr.name
			assert_equals "dolly", attr.value
		end
		assert_equals source.buffer.strip, "href='blah'"

		# This because of a reported bug in attribute handling in 1.0a8
		source = '<a att="A">blah</a>'
		doc = REXML::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  = REXML::Document.new source
		value = xml.root.attributes["search"]
		assert_equal text, value.to_s

		e = REXML::Element.new "test"
		e.add_attributes({ "name1" => "test1", "name2" => "test2" })
		e.add_attributes([["name3","test3"], ["name4","test4"]])
		assert_equal "test1", e.attributes["name1"]
		assert_equal "test2", e.attributes["name2"]
		assert_equal "test3", e.attributes["name3"]
		assert_equal "test4", e.attributes["name4"]
	end

  def test_cdata
	 test = "The quick brown fox jumped
	 over the lazy dog."

	 source = "<![CDATA[#{test}]]>"

	 # Test constructors
	 cdata = REXML::CData.new REXML::SourceFactory.create_from( source ), true
	 assert_equals test, cdata.string
  end

  def test_comment
	 string = "This is a new comment!"
	 source = "<!--#{string}-->"
	 comment = REXML::Comment.new string
	 out = ""
	 comment.write( out )
	 assert_equals source, out

	 comment2 = REXML::Comment.new comment
	 assert_equals comment, comment2

	 comment2 = REXML::Comment.new REXML::SourceFactory.create_from( source )
	 assert_equals comment, comment2
  end

  def test_whitespace
	 doc = REXML::Document.new "<root-element><first-element/></root-element>"
	 assert_equal 1, doc.root.size
	 assert_equal 1, doc.root.elements.size
	 doc = REXML::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 = REXML::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? REXML::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 compressed properly"

	 doc = REXML::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? REXML::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 = REXML::DocType.new string
		assert_equals string, doc.name
		out = ""
		doc.write(out)
		assert_equals correct, out

		doc2 = REXML::DocType.new doc
		assert_equals doc.name, doc2.name
		assert_equals 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 = REXML::DocType.new REXML::SourceFactory.create_from(one_line_source)
		assert_not_nil doc
		test = ""
		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/>'
		doc = REXML::DocType.new REXML::SourceFactory.create_from(multi_line_source)
		assert_not_nil doc
		test = ""
		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/>'
		doc = REXML::DocType.new REXML::SourceFactory.create_from(odd_space_source)
		assert_not_nil doc
		test = ""
		doc.write(test)
		assert_equal correct, test

		# OK, the BIG doctype test, numba wun
		docin = File.new "benchmarks/doctype_test.xml"
		doc = REXML::Document.new docin
		test = ""
		doc.write(test)
		assert_equal 31, doc.doc_type.size

		# Here's a little ditty from Tobias...
		src = <<-EOL
			<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
				"http://www.w3.org/TR/SVG/DTD/svg10.dtd"
				[
					<!-- <!ENTITY % fast-slow "0 0  .5 1">-->
					<!--<!ENTITY % slow-fast ".5 0  1 1">-->
					<!ENTITY hover_ani
						'<animateTransform attributeName="transform"
						 type="scale" restart="whenNotActive" values="1;0.96"
						 dur="0.5s" calcMode="spline" keySplines="0 0  .5 1"
						 fill="freeze" begin="mouseover"/>
						 <animateTransform  attributeName="transform"
						 type="scale" restart="whenNotActive" values="0.96;1"
						 dur="0.5s" calcMode="spline" keySplines=".5 0  1 1"
						 fill="freeze" begin="mouseover+0.5s"/>'
					>
				]
			> <a/>
		EOL
		doc = REXML::Document.new src
	end

	def test_document
		# Testing cloning
		source = "<element/>"
		doc = REXML::Document.new source
		doc2 = REXML::Document.new doc

		# Testing Root
		assert_equals doc.root.name.to_s, "element"

		# Testing String source
		source = @xsa_source
		doc = REXML::Document.new source
		assert_instance_of REXML::XMLDecl, doc.xml_decl
		assert_instance_of REXML::DocType, doc.doc_type 
		assert_equals doc.version, "1.0"

		source = File.new( "benchmarks/dash.xml" )
		doc = REXML::Document.new source
		assert_equal "content-2", doc.elements["//content-2"].name
	end

	def test_instruction
		target = "use"
		content = "ruby"
		source = "<?#{target} #{content}?>"

		instruction = REXML::Instruction.new target, content
		instruction2 = REXML::Instruction.new instruction
		assert_equals instruction, instruction2
		out = ""
		instruction.write(out)
		assert_equals source, out

		instruction2 = REXML::Instruction.new REXML::SourceFactory.create_from(source)
		assert_equals instruction, instruction2
	end

	def test_parent
		parent = REXML::Parent.new
		begin
			parent << "Something"
		rescue
			parent << REXML::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 = REXML::Document.new source
		three = doc.root.elements["three"]
		doc.root.insert_before( three, REXML::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, REXML::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", REXML::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", REXML::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 = REXML::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 = REXML::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 = REXML::Document.new "<a><one/><three/></a>"
		doc.root[1,0] = REXML::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 = REXML::Document.new source
		assert_equals doc.root.name.to_s, "xsa"

		# Testing IO source
		doc = REXML::Document.new File.new("benchmarks/project.xml")
		assert_equals doc.root.name.to_s, "Project"
	end

	def test_text
		string = "Some text"
		text = REXML::Text.new string
		assert_equal string, text.to_s
		text2 = REXML::Text.new text
		assert_equal text, text2
		string = REXML::SourceFactory.create_from "Some text<element/>"
		text2 = REXML::Text.new string
		assert_equal text, text2
		#testing substitution
		string = "0 < ( 1 & 1 )"
		correct = "0 &lt; ( 1 &amp; 1 )"
		text = REXML::Text.new string, true
		out = ""
		text.write out
		assert_equal correct, out

		string = "Cats &amp; dogs"
		text = REXML::Text.new string, false, true
		assert_equal string, text.string

		string2 = "<a>#{string}</a>"
		doc = REXML::Document.new( string2, { 
			:raw => %w{ a b }
		} )
		out = ""
		doc.write out, -1
		assert_equal string2, out
		b = doc.root.add_element "b"
		b.text = string
		assert_equal string, b.text

		c = doc.root.add_element "c"
		c.text = string
		assert_equal "Cats & dogs", c.text

		# test all
		string = "<a>&amp;<b>&lt;</b><c>&gt;<d>&quot;</d></c></a>"
		doc = REXML::Document.new string, { :raw => :all }
		assert_equal "&amp;", doc.elements["/a"].text
		assert_equal "&lt;", doc.elements["/a/b"].text
		assert_equal "&gt;", doc.elements["/a/c"].text
		assert_equal "&quot;", doc.elements["//d"].text
	end

	def test_xmldecl
		source = "<?xml version='1.0'?>"
		# test args
		# test no args
		decl2 = REXML::XMLDecl.new
		assert_equals source, decl2.to_s
		# test Source
		decl = REXML::XMLDecl.new REXML::SourceFactory.create_from(source)
		# test XMLDecl
		decl2 = REXML::XMLDecl.new "1.0"
		assert_equals decl, decl2
	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 = REXML::Document.new File.new("benchmarks/project.xml")

		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 = REXML::Element.new "Sean"
		rubbell = REXML::Element.new "Rubbell"
		elliott = sean.add_element "Elliott"
		sean << rubbell
		assert_equals elliott, rubbell.previous_sibling
		assert_equals rubbell, elliott.next_sibling

		russell = REXML::Element.new "Russell"
		rubbell.replace_with russell
		assert_equals elliott, russell.previous_sibling
		assert_equals 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 = REXML::Element.new "Sean"
		string = "1) He's a great guy!"
		sean.text = string
		russell = REXML::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 = REXML::Document.new source
		doc.write out, -1
		assert_equal source, out
	end

	def test_namespace
		source = <<-EOF
		<x xmlns:foo="http://www.bar.com/schema">
		</x>
		EOF
		doc = REXML::Document.new source
		assert_equal "http://www.bar.com/schema", doc.root.namespace "foo"
		source = <<-EOF
		<!-- bar namespace is "someuri" -->
		<foo:bar xmlns="default" xmlsns: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 = REXML::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 "default", doc.elements["//c"].namespace
		assert_equal "notdefault", doc.root.elements[ 3 ].namespace
		assert_equal "notdefault", doc.elements[ "//e" ].namespace
		assert_equal "", doc.elements[ "//f" ].namespace
		assert_equal "", doc.elements[ "//g" ].namespace

		# Testing namespaces in attributes
		source = <<-EOF
		<a xmlns:b="uri">
			<b b:a="x" a="y"/>
			<c xmlns="foo">
			</c>
		</a>
		EOF
		doc = REXML::Document.new source
		b = doc.root.elements["b"]
		assert_equal "x", b.attributes["b:a"]
		assert_equal "y", b.attributes["a"]

		doc = REXML::Document.new
		doc.add_element "sean:blah"
		doc.root.text = "Some text"
		out = ""
		doc.write out, -1
		assert_equal "<sean:blah>Some text</sean:blah>", out
	end


	def test_big_documentation
		f = File.new "documentation.xml"
		d = REXML::Document.new f
		assert_equal "Sean Russell", d.elements["documentation/head/author"].text
		out = ""
		d.write out
	end

	def test_tutorial
		doc = REXML::Document.new File.new "docs/tutorial.xml"
		out = ""
		doc.write out
	end

	def test_stream
		c = Listener.new
		REXML::Document.parse_stream File.new("documentation.xml"), c
		assert c.ts, "Stream parsing apparantly didn't parse the whole file"
		assert c.te, "Stream parsing dropped end tag for documentation"

		REXML::Document.parse_stream "<a.b> <c/> </a.b>", c
	end

	def test_name
		# Bug contributed by Tobias Reif <tobiasreif@pinkjuice.com>
		max_size = 40000
		input = "<error><fileBiggerThan#{(max_size / 1000.00).to_s}KB/></error>"

		doc = REXML::Document.new(input)
		assert_equal "fileBiggerThan40.0KB", doc.root.elements[1].name 
	end

	def test_entities
		# Tobias Reif pointed this one out
		source = "<a>&#233; is a valid chara&#99;ter. Also, &amp;, &lt;, and &gt; should come back &#112;roperly formatted.</a>"
		doc = REXML::Document.new source
		out = ""
		doc.write out, -1
		assert_equal source, out

		# Bugs found by Tobias Reif
		in_gt = '<root-el>content>content</root-el>'
		in_lt = '<root-el>content<content</root-el>'
		in_am = '<root-el>content&content</root-el>'

		# I don't think this is a problem
		#assert_exception( Exception, 
		#	"should have gotten a malformed content error" ) {
		#		REXML::Document.new in_gt
		#}
		assert_exception( REXML::ParseException,
			"Should have gotten a malformed content error") {
			REXML::Document.new in_lt
		}
		assert_exception( REXML::ParseException,
			"Should have gotten a malformed content error") {
			REXML::Document.new in_am
		}
	end

	def test_line
		begin
			doc = REXML::Document.new File.new( "test/bad.xml" )
		rescue Exception
			# We should get here
			er = $!
			assert_equal 4, $!.line
		else
			assert_fail "There should have been an error"
		end
	end

	def test_substitution
		val = "a'b\"c"
		el = REXML::Element.new "a"
		el.attributes["x"] = val
		out = ""
		el.write out

		nel = REXML::Document.new out
		assert_equal val, nel.root.attributes["x"]
	end

	# Is this really a bug?  Should insert_after on any element be allowed to
	# insert an element anywhere in the tree?
	def test_insert_after_bug
		# Bug contributed by Erik Terpstra <erik@solidcode.net>
		#doc = REXML::Document.new "<te><p>A</p><p>B</p></te>"
		#para = doc.elements["//te/p[1]"]
		#para2 = doc.elements["//te/p[2]"]
		#doc.root.insert_after para2, para
		#doc = REXML::Document.new "<art><te><p>A</p><p>B</p></te></art>"
		#puts "DOC: #{doc}"
		#para = doc.elements["//te/p[1]"]
		#puts "PARA: #{para}"
		#para2 = doc.elements["//te/p[2]"]
		#puts "PARA2: #{para2}"
		#doc.root.insert_after para2, para
	 end

	 def test_exception
		 source = REXML::SourceFactory.create_from "<a/>"
		 p = REXML::ParseException.new( "dummy message", source )
		 s = p.to_s
		 begin
			 raise "dummy"
		 rescue Exception
			 p.continued_exception = $!
		 end
		 s = p.to_s
	 end
end
