1# coding: binary
2
3require "rexml_test_utils"
4
5require "rexml/document"
6require "rexml/parseexception"
7require "rexml/formatters/default"
8
9class ContribTester < Test::Unit::TestCase
10  include REXMLTestUtils
11  include REXML
12
13  XML_STRING_01 = <<DELIMITER
14<?xml version="1.0" encoding="UTF-8"?>
15<biblio>
16  <entry type="Book">
17    <author>Thomas, David; Hunt, Andrew</author>
18    <language>english</language>
19    <publisher>Addison-Wesley</publisher>
20    <title>Programming Ruby. The Pragmatic Programmer's Guide</title>
21    <year>2000</year>
22  </entry>
23  <entry type="Book">
24    <author>Blammo, Blah</author>
25    <language>english</language>
26    <publisher>Hubbabubba</publisher>
27    <title>Foozboozer's Life</title>
28    <type>Book</type>
29    <year>2002</year>
30  </entry>
31</biblio>
32DELIMITER
33
34  XML_STRING_02 = <<DELIMITER
35<biblio>
36  <entry type="Book">
37    <language>english</language>
38    <publisher>Addison-Wesley</publisher>
39    <title>Programming Ruby. The Pragmatic Programmer's Guide</title>
40    <type>Book</type>
41    <year>2000</year>
42  </entry>
43  <entry type="Book">
44    <author>Blammo, Blah</author>
45    <language>english</language>
46    <publisher>Hubbabubba</publisher>
47    <title>Foozboozer's Life</title>
48    <type>Book</type>
49    <year>2002</year>
50  </entry>
51</biblio>
52DELIMITER
53
54  # Tobias Reif <tobiasreif@pinkjuice.com>
55  def test_bad_doctype_Tobias
56    source = <<-EOF
57     <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
58        "http://www.w3.org/TR/SVG/DTD/svg10.dtd"
59       [
60       <!-- <!ENTITY % fast-slow "0 0  .5 1">-->
61       <!--<!ENTITY % slow-fast ".5 0  1 1">-->
62       <!ENTITY hover_ani
63        '<animateTransform attributeName="transform"
64         type="scale" restart="whenNotActive" values="1;0.96"
65         dur="0.5s" calcMode="spline" keySplines="0 0  .5 1"
66         fill="freeze" begin="mouseover"/>
67         <animateTransform  attributeName="transform"
68         type="scale" restart="whenNotActive" values="0.96;1"
69         dur="0.5s" calcMode="spline" keySplines=".5 0  1 1"
70         fill="freeze" begin="mouseover+0.5s"/>'
71       >
72       ]
73     >
74    EOF
75    doc = REXML::Document.new source
76    doc.write(out="")
77    assert(out[/>\'>/] != nil, "Couldn't find >'>")
78    assert(out[/\]>/] != nil, "Couldn't find ]>")
79  end
80
81  # Peter Verhage
82  def test_namespace_Peter
83    source = <<-EOF
84    <?xml version="1.0"?>
85    <config:myprog-config xmlns:config="http://someurl/program/version">
86    <!-- main options -->
87      <config:main>
88        <config:parameter name="name"  value="value"/>
89      </config:main>
90    </config:myprog-config>
91    EOF
92    doc = REXML::Document.new source
93    assert_equal "myprog-config", doc.root.name
94    count = 0
95    REXML::XPath.each(doc, "x:myprog-config/x:main/x:parameter",
96      {"x"=>"http://someurl/program/version"}) { |element|
97        assert_equal "name", element.attributes["name"]
98      count += 1;
99    }
100    assert_equal 1, count
101    assert_equal "myprog-config", doc.elements["config:myprog-config"].name
102  end
103
104  # Tobias Reif <tobiasreif@pinkjuice.com>
105  def test_complex_xpath_Tobias
106    source = <<-EOF
107    <root>
108      <foo>
109        <bar style="baz"/>
110        <blah style="baz"/>
111        <blam style="baz"/>
112      </foo>
113      <wax>
114        <fudge>
115          <noodle/>
116        </fudge>
117      </wax>
118    </root>
119    EOF
120    # elements that have child elements
121    #  but not grandchildren
122    #  and not children that don't have a style attribute
123    #  and not children that have a unique style attribute
124    complex_path = "*[* "+
125      "and not(*/node()) "+
126      "and not(*[not(@style)]) "+
127      "and not(*/@style != */@style)]"
128    doc = REXML::Document.new source
129    results = REXML::XPath.match( doc.root, complex_path )
130    assert(results)
131    assert_equal 1, results.size
132    assert_equal "foo", results[0].name
133  end
134
135  # "Chris Morris" <chrismo@charter.net>
136  def test_extra_newline_on_read_Chris
137    text = 'test text'
138    e = REXML::Element.new('Test')
139    e.add_text(text)
140    REXML::Formatters::Default.new.write(e,out="")
141
142    doc = REXML::Document.new(out)
143    outtext = doc.root.text
144
145    assert_equal(text, outtext)
146  end
147
148  # Tobias Reif <tobiasreif@pinkjuice.com>
149  def test_other_xpath_Tobias
150    schema = <<-DELIM
151    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
152      elementFormDefault="qualified">
153      <xs:element name="rect">
154        <xs:complexType>
155          <xs:attribute name="width" type="xs:byte" use="required"/>
156          <xs:attribute name="height" type="xs:byte" use="required"/>
157        </xs:complexType>
158      </xs:element>
159      <xs:element name="svg">
160        <xs:complexType>
161          <xs:sequence>
162            <xs:element ref="rect"/>
163          </xs:sequence>
164        </xs:complexType>
165      </xs:element>
166    </xs:schema>
167    DELIM
168
169    doc = REXML::Document.new schema
170
171    result = REXML::XPath.first(doc.root, 'xs:element[descendant::xs:element[@ref]]')
172    assert result
173    assert_equal "svg", result.attributes['name']
174    result = REXML::XPath.first(doc, 'element[descendant::element[@ref]]')
175    assert_nil result
176  end
177
178  #this first test succeeds, to check if stuff is set up correctly
179  def test_xpath_01_TobiasReif
180    doc = Document.new XML_STRING_01.dup
181    desired_result = Document.new '<author>Thomas, David; Hunt, Andrew</author>'
182    xpath = '//author'
183    result = XPath.first(doc, xpath)
184    assert_equal desired_result.to_s, result.to_s
185  end
186
187  def test_xpath_whitespace_TobiasReif
188    # same as above, with whitespace in XPath
189    doc = Document.new(XML_STRING_01.dup)
190    desired_result = Document.new('<author>Thomas, David; Hunt, Andrew</author>')
191    xpath = "\/\/author\n \n"
192    result = XPath.first(doc, xpath)
193    failure_message = "\n[[[TR: AFAIK, whitespace should be allowed]]]\n"
194    assert_equal(desired_result.to_s, result.to_s, failure_message)
195  end
196
197  def test_xpath_02_TobiasReif
198    doc = Document.new XML_STRING_01.dup
199    desired_result = Document.new '<author>Thomas, David; Hunt, Andrew</author>'
200    # Could that quirky
201    #  Programmer',&quot;'&quot;,'s
202    # be handled automatically, somehow?
203    # Or is there a simpler way? (the below XPath should match the author element above,
204    # AFAIK; I tested it inside an XSLT)
205    xpath = %q{/biblio/entry[
206    title/text()=concat('Programming Ruby. The Pragmatic Programmer',"'",'s Guide')
207    and
208    year='2000'
209    ]/author}
210    result = XPath.first(doc, xpath)
211    failure_message = "\nHow to handle the apos inside the string inside the XPath?\nXPath = #{xpath}\n"
212    assert_equal desired_result.to_s, result.to_s, failure_message
213  end
214
215  def test_xpath_03_TobiasReif
216    doc = Document.new XML_STRING_02.dup
217    desired_result_string = "<entry type='Book'>
218    <language>english</language>
219    <publisher>Addison-Wesley</publisher>
220    <title>Programming Ruby. The Pragmatic Programmer's Guide</title>
221    <type>Book</type>
222    <year>2000</year>
223  </entry>"
224    Document.new desired_result_string
225    xpath = "/biblio/entry[not(author)]"
226    result = XPath.first(doc, xpath)
227    assert_equal desired_result_string, result.to_s
228  end
229
230  def test_umlaut
231    koln_iso = "K\xf6ln"
232    koln_utf = "K\xc3\xb6ln"
233    source_iso = "<?xml version='1.0' encoding='ISO-8859-1'?><test>#{koln_iso}</test>"
234    source_utf = "<?xml version='1.0' encoding='UTF-8'?><test>#{koln_utf}</test>"
235
236    if String.method_defined? :encode
237      koln_iso.force_encoding('iso-8859-1')
238      koln_utf.force_encoding('utf-8')
239      source_iso.force_encoding('iso-8859-1')
240      source_utf.force_encoding('utf-8')
241    end
242
243    doc = REXML::Document.new(source_iso)
244    assert_equal('ISO-8859-1', doc.xml_decl.encoding)
245    assert_equal(koln_utf, doc.root.text)
246    doc.write(out="")
247    assert_equal(source_iso, out )
248    doc.xml_decl.encoding = 'UTF-8'
249    doc.write(out="")
250    assert_equal(source_utf, out)
251
252    doc = Document.new <<-EOF
253<?xml version="1.0" encoding="ISO-8859-1"?>
254<intranet>
255<position><aktuell datum="01-10-11">Technik</aktuell></position>
256<hauptspalte>
257<headline>Technik</headline>
258Die Technik ist das R\xFCckgrat der meisten Gesch\xFCftsprozesse bei Home of the Brave. Deshalb sollen hier alle relevanten technischen Abl\xFCufe, Daten und Einrichtungen beschrieben werden, damit jeder im Bedarfsfall die n\xFCtigen Informationen, Anweisungen und Verhaltensempfehlungen nachlesen und/oder abrufen kann.
259</hauptspalte>
260<nebenspalte>
261  <link ziel="Flash/">Flash</link><umbruch/>
262  N\xFCtzliches von Flashern f\xFCr Flasher.<umbruch/>
263  <link neu="ja" ziel="Cvs/">CVS-FAQ</link><umbruch/>
264  FAQ zur Benutzung von CVS bei HOB
265</nebenspalte>
266</intranet>
267EOF
268    tn = XPath.first(doc, "//nebenspalte/text()[2]")
269    expected_iso = "N\xFCtzliches von Flashern f\xFCr Flasher."
270    expected_utf = expected_iso.unpack('C*').pack('U*')
271    expected_iso.force_encoding(::Encoding::ISO_8859_1)
272    expected_utf.force_encoding(::Encoding::UTF_8)
273    assert_equal(expected_utf, tn.to_s.strip)
274    f = REXML::Formatters::Default.new
275    f.write( tn, Output.new(o = "", "ISO-8859-1") )
276    assert_equal(expected_iso, o.strip)
277
278    doc = Document.new File.new(fixture_path('xmlfile-bug.xml'))
279    tn = XPath.first(doc, "//nebenspalte/text()[2]")
280    assert_equal(expected_utf, tn.to_s.strip)
281    f.write( tn, Output.new(o = "", "ISO-8859-1") )
282    assert_equal(expected_iso, o.strip)
283  end
284
285  def test_element_cloning_namespace_Chris
286    aDoc = REXML::Document.new '<h1 tpl:content="title" xmlns:tpl="1">Dummy title</h1>'
287
288    anElement = anElement = aDoc.elements[1]
289    elementAttrPrefix = anElement.attributes.get_attribute('content').prefix
290
291    aClone = anElement.clone
292    cloneAttrPrefix = aClone.attributes.get_attribute('content').prefix
293
294    assert_equal( elementAttrPrefix , cloneAttrPrefix )
295  end
296
297  def test_namespaces_in_attlist_tobias
298    in_string = File.open(fixture_path('foo.xml'), 'r') do |file|
299       file.read
300    end
301
302    doc = Document.new in_string
303
304    assert_nil XPath.first(doc,'//leg')
305    assert_equal 'http://www.foo.com/human', doc.root.elements[1].namespace
306    assert_equal 'human leg',
307      XPath.first(doc, '//x:leg/text()', {'x'=>'http://www.foo.com/human'}).to_s
308  end
309
310  #  Alun ap Rhisiart
311  def test_less_than_in_element_content
312    source = File.new(fixture_path('ProductionSupport.xml'))
313    h = Hash.new
314    doc = REXML::Document.new source
315    doc.elements.each("//CommonError") { |el|
316      h[el.elements['Key'].text] = 'okay'
317    }
318    assert(h.include?('MotorInsuranceContract(Object)>>#error:'))
319  end
320
321  # XPaths provided by Thomas Sawyer
322  def test_various_xpath
323    #@doc = REXML::Document.new('<r a="1"><p><c b="2"/></p></r>')
324    doc = REXML::Document.new('<r a="1"><p><c b="2">3</c></p></r>')
325
326    [['/r', REXML::Element],
327     ['/r/p/c', REXML::Element],
328     ['/r/attribute::a', Attribute],
329     ['/r/@a', Attribute],
330     ['/r/attribute::*', Attribute],
331     ['/r/@*', Attribute],
332     ['/r/p/c/attribute::b', Attribute],
333     ['/r/p/c/@b', Attribute],
334     ['/r/p/c/attribute::*', Attribute],
335     ['/r/p/c/@*', Attribute],
336     ['//c/attribute::b', Attribute],
337     ['//c/@b', Attribute],
338     ['//c/attribute::*', Attribute],
339     ['//c/@*', Attribute],
340     ['.//node()', REXML::Node ],
341     ['.//node()[@a]', REXML::Element ],
342     ['.//node()[@a="1"]', REXML::Element ],
343     ['.//node()[@b]', REXML::Element ], # no show, why?
344     ['.//node()[@b="2"]', REXML::Element ]
345    ].each do |xpath,kind|
346      begin
347        REXML::XPath.each( doc, xpath ) do |what|
348          assert_kind_of( kind, what, "\n\nWrong type (#{what.class}) returned for #{xpath} (expected #{kind.name})\n\n" )
349        end
350      rescue Exception
351        puts "PATH WAS: #{xpath}"
352        raise
353      end
354    end
355
356    [
357     ['/r', 'attribute::a', Attribute ],
358     ['/r', '@a', Attribute ],
359     ['/r', 'attribute::*', Attribute ],
360     ['/r', '@*', Attribute ],
361     ['/r/p/c', 'attribute::b', Attribute ],
362     ['/r/p/c', '@b', Attribute ],
363     ['/r/p/c', 'attribute::*', Attribute ],
364     ['/r/p/c', '@*', Attribute ]
365    ].each do |nodepath, xpath, kind|
366      begin
367        context = REXML::XPath.first(doc, nodepath)
368        REXML::XPath.each( context, xpath ) do |what|
369          assert_kind_of kind, what, "Wrong type (#{what.class}) returned for #{xpath} (expected #{kind.name})\n"
370        end
371      rescue Exception
372        puts "PATH WAS: #{xpath}"
373        raise
374      end
375    end
376  end
377
378  def test_entities_Holden_Glova
379    document = <<-EOL
380    <?xml version="1.0" encoding="UTF-8"?>
381    <!DOCTYPE rubynet [
382    <!ENTITY rbconfig.MAJOR "1">
383    <!ENTITY rbconfig.MINOR "7">
384    <!ENTITY rbconfig.TEENY "2">
385    <!ENTITY rbconfig.ruby_version "&rbconfig.MAJOR;.&rbconfig.MINOR;">
386    <!ENTITY rbconfig.arch "i386-freebsd5">
387    <!ENTITY rbconfig.prefix "/usr/local">
388    <!ENTITY rbconfig.libdir "&rbconfig.prefix;/lib">
389    <!ENTITY rbconfig.includedir "&rbconfig.prefix;/include">
390    <!ENTITY rbconfig.sitedir "&rbconfig.prefix;/lib/ruby/site_ruby">
391    <!ENTITY rbconfig.sitelibdir "&rbconfig.sitedir;/&rbconfig.ruby_version;">
392    <!ENTITY rbconfig.sitearchdir "&rbconfig.sitelibdir;/&rbconfig.arch;">
393    ]>
394    <rubynet>
395      <pkg version="version1.0">
396        <files>
397          <file>
398            <filename>uga.rb</filename>
399            <mode>0444</mode>
400            <path>&rbconfig.libdir;/rexml</path>
401            <content encoding="xml">... the file here</content>
402          </file>
403          <file>
404            <filename>booga.h</filename>
405            <mode>0444</mode>
406            <path>&rbconfig.includedir;</path>
407            <content encoding="xml">... the file here</content>
408          </file>
409          <file>
410            <filename>foo.so</filename>
411            <mode>0555</mode>
412            <path>&rbconfig.sitearchdir;/rexml</path>
413            <content encoding="mime64">Li4uIHRoZSBmaWxlIGhlcmU=\n</content>
414          </file>
415        </files>
416      </pkg>
417    </rubynet>
418    EOL
419
420    file_xpath = '/rubynet/pkg/files/file'
421
422    root = REXML::Document.new(document)
423
424    root.elements.each(file_xpath) do |metadata|
425      text = metadata.elements['path'].get_text.value
426      assert text !~ /&rbconfig/, "'#{text}' failed"
427    end
428
429    #Error occurred in test_package_file_opens(TC_PackageInstall):
430    # ArgumentError:
431    #illegal access mode &rbconfig.prefix;/lib/rexml
432    #
433    #[synack@Evergreen] src $ ruby --version
434    #ruby 1.6.7 (2002-03-01) [i686-linux-gnu]
435    #
436    #It looks like it expanded the first entity, but didn't reparse it for more
437    #entities. possible bug - or have I mucked this up?
438  end
439
440  def test_whitespace_after_xml_decl
441    Document.new <<EOL
442<?xml version='1.0'?>
443  <blo>
444    <wak>
445    </wak>
446</blo>
447EOL
448  end
449
450  def test_external_entity
451    xp = '//channel/title'
452
453    %w{working.rss broken.rss}.each do |path|
454      File.open(File.join(fixture_path(path))) do |file|
455        doc = REXML::Document.new file.readlines.join('')
456
457        # check to make sure everything is kosher
458        assert_equal( doc.root.class, REXML::Element )
459        assert_equal( doc.root.elements.class, REXML::Elements )
460
461        # get the title of the feed
462        assert( doc.root.elements[xp].kind_of?( REXML::Element ) )
463      end
464    end
465  end
466
467  def test_maintain_dtd
468    src = %q{<?xml version="1.0" encoding="UTF-8"?>
469<!DOCTYPE ivattacks SYSTEM "../../ivacm.dtd" [
470<!ENTITY % extern-packages SYSTEM "../../ivpackages.dtd">
471<!ENTITY % extern-packages SYSTEM "../../common-declarations.dtd">
472%extern-packages;
473%extern-common;
474]>}
475    doc = Document.new( src )
476    doc.write( out="" )
477    src = src.tr('"', "'")
478    out = out.tr('"', "'")
479    assert_equal( src, out )
480  end
481
482  def test_text_nodes_nomatch
483    source = "<root><child>test</child></root>"
484    d = REXML::Document.new( source )
485    r = REXML::XPath.match( d, %q{/root/child[text()="no-test"]} )
486    assert_equal( 0, r.size )
487  end
488
489  def test_raw_Terje_Elde
490    f = REXML::Formatters::Default.new
491    txt = 'abc&#248;def'
492    a = Text.new( txt,false,nil,true )
493    f.write(a,out="")
494    assert_equal( txt, out )
495
496    txt = '<sean><russell>abc&#248;def</russell></sean>'
497    a = Document.new( txt, { :raw => ["russell"] } )
498    f.write(a,out="")
499    assert_equal( txt, out )
500  end
501
502  def test_indenting_error
503    a=Element.new("test1")
504    b=Element.new("test2")
505    c=Element.new("test3")
506    b << c
507    a << b
508
509    REXML::Formatters::Pretty.new.write(a,"")
510  end
511
512  def test_pos
513    require 'tempfile'
514    testfile = Tempfile.new("tidal")
515    testdata = %Q{<calibration>
516<section name="parameters">
517<param name="barpress">760</param>
518<param name="hertz">50</param>
519</section>
520</calibration>
521}
522
523    testfile.puts testdata
524    testfile.rewind
525    assert_nothing_raised do
526      REXML::Document.new(testfile)
527    end
528    testfile.close(true)
529  end
530
531  def test_deep_clone
532    a = Document.new( '<?xml version="1.0"?><!DOCTYPE html PUBLIC
533    "-//W3C//DTD
534    XHTML 1.0 Transitional//EN"
535    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html
536    xmlns="http:///www.w3.org/1999/xhtml"></html>' )
537    b = a.deep_clone
538    assert_equal a.to_s, b.to_s
539  end
540
541  def test_double_escaping
542    data = '<title>AT&amp;T</title>'
543    xml = "<description><![CDATA[#{data}]]></description>"
544
545    doc = REXML::Document.new(xml)
546    description = doc.find {|e| e.name=="description"}
547    assert_equal data, description.text
548  end
549
550  def test_ticket_12
551    cfg = "<element><anotherelement><child1>a</child1><child2>b</child2></anotherelement></element>"
552
553    config = REXML::Document.new( cfg )
554
555    assert_equal( "a", config.elements[ "//child1" ].text )
556  end
557
558=begin
559 # This is a silly test, and is low priority
560 def test_namespace_serialization_tobi_reif
561   doc = Document.new '<doc xmlns:b="http://www.foo.foo">
562 <b:p/>
563</doc>'
564   ns = 'http://www.foo.foo'
565   ns_declaration={'f'=>ns}
566   returned = XPath.match(doc,'//f:p',ns_declaration)
567   # passes:
568   assert( (returned[0].namespace==ns), 'namespace should be '+ns)
569   serialized = returned.to_s
570   serialized_and_parsed = Document.new(serialized)
571   puts 'serialized: '+serialized
572     # ... currently brings <b:p/>
573     # prefix b is undeclared (!)
574   assert( (serialized_and_parsed.namespace==ns),
575     'namespace should still be '+ns.inspect+
576     ' and not '+serialized_and_parsed.namespace.inspect)
577   # ... currently results in a failure:
578   # 'namespace should still be "http://www.foo.foo" and not ""'
579 end
580=end
581end
582