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