1require "rexml_test_utils"
2require 'rexml/sax2listener'
3require 'rexml/parsers/sax2parser'
4require 'rexml/document'
5
6class SAX2Tester < Test::Unit::TestCase
7  include REXMLTestUtils
8  include REXML
9  def test_characters
10    d = Document.new( "<A>@blah@</A>" )
11    txt = d.root.text
12    p = Parsers::SAX2Parser.new "<A>@blah@</A>"
13    p.listen(:characters) {|x| assert_equal txt, x}
14    p.listen(:characters, ["A"]) {|x| assert_equal txt,x}
15    p.parse
16  end
17
18  def test_entity_replacement
19    source = '<!DOCTYPE foo [
20    <!ENTITY la "1234">
21    <!ENTITY lala "--&la;--">
22    <!ENTITY lalal "&la;&la;">
23    ]><a><la>&la;</la><lala>&lala;</lala></a>'
24    sax = Parsers::SAX2Parser.new( source )
25    results = []
26    sax.listen(:characters) {|x| results << x }
27    sax.parse
28    assert_equal 2, results.size
29    assert_equal '1234', results[0]
30    assert_equal '--1234--', results[1]
31  end
32
33  def test_sax2
34    f = File.new(fixture_path("documentation.xml"))
35    parser = Parsers::SAX2Parser.new( f )
36    # Listen to all events on the following elements
37    count = 0
38    blok = proc { |uri,localname,qname,attributes|
39      assert %w{ bugs todo }.include?(localname),
40      "Mismatched name; we got '#{qname}'\nArgs were:\n\tURI: #{uri}\n\tLOCALNAME: #{localname}\n\tQNAME: #{qname}\n\tATTRIBUTES: #{attributes.inspect}\n\tSELF=#{blok}"
41      count += 1
42    }
43
44    start_document = 0
45    end_document = 0
46    parser.listen( :start_document ) { start_document += 1 }
47    parser.listen( :end_document ) { end_document += 1 }
48    parser.listen( :start_element, %w{ changelog bugs todo }, &blok )
49    # Listen to all events on the following elements.  Synonymous with
50    # listen( :start_element, %w{ ... } )
51    parser.listen( %w{ changelog bugs todo }, &blok )
52    # Listen for all start element events
53    parser.listen( :start_element ) { |uri,localname,qname,attributes|
54    }
55    listener = MySAX2Listener.new
56    # Listen for all events
57    parser.listen( listener )
58    # Listen for all events on the given elements.  Does not include children
59    # events.  Regular expressions work as well!
60    parser.listen( %w{ /change/ bugs todo }, listener )
61    # Test the deafening method
62    blok = proc  { |uri,localname,qname,attributes|
63      assert_fail "This listener should have been deafened!"
64    }
65    parser.listen( %w{ changelog }, &blok )
66    parser.deafen( &blok )
67
68    tc = 0
69    parser.listen( :characters, %w{version} ) {|text|
70      assert(text=~/@ANT_VERSION@/, "version was '#{text}'")
71      tc += 1
72    }
73
74    begin
75      parser.parse
76    rescue => exception
77      if exception.kind_of? Test::Unit::AssertionFailedError
78        raise exception
79      end
80      puts $!
81      puts exception.backtrace
82    end
83    assert_equal 2, count
84    assert_equal 1, tc
85    assert_equal 1, start_document
86    assert_equal 1, end_document
87  end
88
89
90
91  # used by test_simple_doctype_listener
92  # submitted by Jeff Barczewski
93  class SimpleDoctypeListener
94    include REXML::SAX2Listener
95    attr_reader :name, :pub_sys, :long_name, :uri
96
97    def initialize
98      @name = @pub_sys = @long_name = @uri = nil
99    end
100
101    def doctype(name, pub_sys, long_name, uri)
102      @name = name
103      @pub_sys = pub_sys
104      @long_name = long_name
105      @uri = uri
106    end
107  end
108
109  # test simple non-entity doctype in sax listener
110  # submitted by Jeff Barczewski
111  def test_simple_doctype_listener
112    xml = <<-END
113      <?xml version="1.0"?>
114      <!DOCTYPE greeting PUBLIC "Hello Greeting DTD" "http://foo/hello.dtd">
115      <greeting>Hello, world!</greeting>
116    END
117    parser = Parsers::SAX2Parser.new(xml)
118    dtl = SimpleDoctypeListener.new
119    parser.listen(dtl)
120    tname = nil
121    tpub_sys = nil
122    tlong_name = nil
123    turi = nil
124    parser.listen(:doctype) do |name, pub_sys, long_name, uri|
125      tname = name
126      tpub_sys = pub_sys
127      tlong_name = long_name
128      turi = uri
129    end
130    parser.parse
131    assert_equal 'greeting', tname, 'simple doctype block listener failed - incorrect name'
132    assert_equal 'PUBLIC', tpub_sys, 'simple doctype block listener failed - incorrect pub_sys'
133    assert_equal 'Hello Greeting DTD', tlong_name, 'simple doctype block listener failed - incorrect long_name'
134    assert_equal 'http://foo/hello.dtd', turi, 'simple doctype block listener failed - incorrect uri'
135    assert_equal 'greeting', dtl.name, 'simple doctype listener failed - incorrect name'
136    assert_equal 'PUBLIC', dtl.pub_sys, 'simple doctype listener failed - incorrect pub_sys'
137    assert_equal 'Hello Greeting DTD', dtl.long_name, 'simple doctype listener failed - incorrect long_name'
138    assert_equal 'http://foo/hello.dtd', dtl.uri, 'simple doctype listener failed - incorrect uri'
139  end
140
141  # test doctype with missing name, should throw ParseException
142  # submitted by Jeff Barczewseki
143  def test_doctype_with_mising_name_throws_exception
144    xml = <<-END
145      <?xml version="1.0"?>
146      <!DOCTYPE >
147      <greeting>Hello, world!</greeting>
148    END
149    parser = Parsers::SAX2Parser.new(xml)
150    assert_raise(REXML::ParseException, 'doctype missing name did not throw ParseException') do
151      parser.parse
152    end
153  end
154
155
156  class KouListener
157    include REXML::SAX2Listener
158    attr_accessor :sdoc, :edoc
159    attr_reader :selem, :decl, :pi
160    def initialize
161      @sdoc = @edoc = @selem = false
162      @decl = 0
163      @pi = 0
164    end
165    def start_document
166      @sdoc = true
167    end
168    def end_document
169      @edoc = true
170    end
171    def xmldecl( *arg )
172      @decl += 1
173    end
174    def processing_instruction( *arg )
175      @pi += 1
176    end
177    def start_element( *arg )
178      @selem = true
179    end
180  end
181
182  # Submitted by Kou
183  def test_begin_end_document
184    parser = Parsers::SAX2Parser.new("<a/>")
185
186    kl = KouListener.new
187    parser.listen(kl)
188    sd = false
189    ed = false
190    parser.listen(:start_document) { sd = true }
191    parser.listen(:end_document) { ed = true }
192
193    parser.parse
194    assert( sd, ':start_document block failed' )
195    assert( ed, ':end_document block failed' )
196    assert( kl.sdoc, ':start_document listener failed' )
197    assert( kl.edoc, ':end_document listener failed' )
198  end
199
200  # Submitted by Kou
201  def test_listen_before_start
202    # FIXME: the following comment should be a test for validity. (The xml declaration
203    # is invalid).
204    #parser =  Parsers::SAX2Parser.new( "<?xml ?><?pi?><a><?pi?></a>")
205    parser =  Parsers::SAX2Parser.new( "<?xml version='1.0'?><?pi?><a><?pi?></a>")
206    k1 = KouListener.new
207    parser.listen( k1 )
208    xmldecl = false
209    pi = 0
210    parser.listen( :xmldecl ) { xmldecl = true }
211    parser.listen( :processing_instruction ) { pi += 1 }
212
213    parser.parse
214
215    assert( xmldecl, ':xmldecl failed' )
216    assert_equal( 2, pi, ':processing_instruction failed' )
217    assert( k1.decl, 'Listener for xmldecl failed' )
218    assert_equal( 2, k1.pi, 'Listener for processing instruction failed' )
219  end
220
221
222  def test_socket
223    require 'socket'
224
225    server = TCPServer.new('127.0.0.1', 0)
226    socket = TCPSocket.new('127.0.0.1', server.addr[1])
227
228    ok = false
229    session = server.accept
230    session << '<foo>'
231    parser = REXML::Parsers::SAX2Parser.new(socket)
232    Fiber.new do
233      parser.listen(:start_element) do
234        ok = true
235        Fiber.yield
236      end
237      parser.parse
238    end.resume
239    assert(ok)
240  end
241
242  def test_char_ref_sax2()
243    parser = REXML::Parsers::SAX2Parser.new('<ABC>&#252;</ABC>')
244    result = nil
245    parser.listen(:characters) {|text| result = text.unpack('U*')}
246    parser.parse()
247    assert_equal(1, result.size)
248    assert_equal(252, result[0])
249  end
250
251
252  def test_char_ref_dom()
253    doc = REXML::Document.new('<ABC>&#252;</ABC>')
254    result = doc.root.text.unpack('U*')
255    assert_equal(1, result.size)
256    assert_equal(252, result[0])
257  end
258
259  class Ticket68
260    include REXML::SAX2Listener
261  end
262  def test_ticket_68
263    parser = REXML::Parsers::SAX2Parser.new(File.new(fixture_path('ticket_68.xml')))
264    parser.listen( Ticket68.new )
265    begin
266      parser.parse
267    rescue
268      p parser.source.position
269      p parser.source.current_line
270      puts $!.backtrace.join("\n")
271      flunk $!.message
272    end
273  end
274end
275
276class MySAX2Listener
277  include REXML::SAX2Listener
278end
279
280