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>ü</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>ü</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