1# frozen_string_literal: false
2require "test/unit/testcase"
3
4require 'rexml/document'
5require 'rexml/entity'
6require 'rexml/source'
7
8module REXMLTests
9  class EntityTester < Test::Unit::TestCase
10    def test_parse_general_decl
11      simple = "<!ENTITY foo 'bar'>"
12      simple =~ /#{REXML::Entity::GEDECL}/
13      assert $&
14      assert_equal simple, $&
15
16      REXML::Entity::ENTITYDECL =~ simple
17      assert REXML::Entity::matches?(simple)
18      match = REXML::Entity::ENTITYDECL.match(simple)
19      assert_equal 'foo', match[1]
20      assert_equal "'bar'", match[2]
21
22      simple = '<!ENTITY Pub-Status
23      "This is a pre-release of the specification.">'
24      assert REXML::Entity::matches?(simple)
25      match = REXML::Entity::ENTITYDECL.match(simple)
26      assert_equal 'Pub-Status', match[1]
27      assert_equal '"This is a pre-release of the specification."', match[2]
28
29      txt = '"This is a
30      pre-release of <the> specification."'
31      simple = "<!ENTITY     Pub-Status
32      #{txt}>"
33      assert REXML::Entity::matches?(simple)
34      match = REXML::Entity::ENTITYDECL.match(simple)
35      assert_equal 'Pub-Status', match[1]
36      assert_equal txt, match[2]
37    end
38
39    def test_parse_external_decl
40      zero = '<!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml" >'
41      one = '<!ENTITY open-hatch
42                SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">'
43      two = '<!ENTITY open-hatch
44                PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN"
45                "http://www.textuality.com/boilerplate/OpenHatch.xml">'
46      three = '<!ENTITY hatch-pic
47                SYSTEM "../grafix/OpenHatch.gif"
48                NDATA gif >'
49      assert REXML::Entity::matches?(zero)
50      assert REXML::Entity::matches?(one)
51      assert REXML::Entity::matches?(two)
52      assert REXML::Entity::matches?(three)
53    end
54
55    def test_parse_entity
56      one = %q{<!ENTITY % YN '"Yes"'>}
57      two = %q{<!ENTITY WhatHeSaid "He said %YN;">}
58      assert REXML::Entity::matches?(one)
59      assert REXML::Entity::matches?(two)
60    end
61
62    def test_constructor
63      one = [ %q{<!ENTITY % YN '"Yes"'>},
64        %q{<!ENTITY % YN2 "Yes">},
65        %q{<!ENTITY WhatHeSaid "He said %YN;">},
66        '<!ENTITY open-hatch
67                SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">',
68        '<!ENTITY open-hatch2
69                PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN"
70                "http://www.textuality.com/boilerplate/OpenHatch.xml">',
71        '<!ENTITY hatch-pic
72                SYSTEM "../grafix/OpenHatch.gif"
73                NDATA gif>' ]
74      source = %q{<!DOCTYPE foo [
75        <!ENTITY % YN '"Yes"'>
76        <!ENTITY % YN2 "Yes">
77        <!ENTITY WhatHeSaid "He said %YN;">
78        <!ENTITY open-hatch
79                SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
80        <!ENTITY open-hatch2
81                PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN"
82                "http://www.textuality.com/boilerplate/OpenHatch.xml">
83        <!ENTITY hatch-pic
84                SYSTEM "../grafix/OpenHatch.gif"
85                NDATA gif>
86      ]>}
87
88      d = REXML::Document.new( source )
89      dt = d.doctype
90      c = 0
91      dt.each do |child|
92        if child.kind_of? REXML::Entity
93          str = one[c].tr("\r\n\t", '   ').squeeze(" ")
94          assert_equal str, child.to_s
95          c+=1
96        end
97      end
98    end
99
100    def test_replace_entities
101      source = "<!DOCTYPE blah [\n<!ENTITY foo \"bar\">\n]><a>&foo;</a>"
102      doc = REXML::Document.new(source)
103      assert_equal 'bar', doc.root.text
104      out = ''
105      doc.write out
106      assert_equal source, out
107    end
108
109    def test_entity_string_limit
110      template = '<!DOCTYPE bomb [ <!ENTITY a "^" > ]> <bomb>$</bomb>'
111      len      = 5120 # 5k per entity
112      template.sub!(/\^/, "B" * len)
113
114      # 10k is OK
115      entities = '&a;' * 2 # 5k entity * 2 = 10k
116      xmldoc = REXML::Document.new(template.sub(/\$/, entities))
117      assert_equal(len * 2, xmldoc.root.text.bytesize)
118
119      # above 10k explodes
120      entities = '&a;' * 3 # 5k entity * 2 = 15k
121      xmldoc = REXML::Document.new(template.sub(/\$/, entities))
122      assert_raise(RuntimeError) do
123        xmldoc.root.text
124      end
125    end
126
127    def test_entity_string_limit_for_parameter_entity
128      template = '<!DOCTYPE bomb [ <!ENTITY % a "^" > <!ENTITY bomb "$" > ]><root/>'
129      len      = 5120 # 5k per entity
130      template.sub!(/\^/, "B" * len)
131
132      # 10k is OK
133      entities = '%a;' * 2 # 5k entity * 2 = 10k
134      REXML::Document.new(template.sub(/\$/, entities))
135
136      # above 10k explodes
137      entities = '%a;' * 3 # 5k entity * 2 = 15k
138      assert_raise(REXML::ParseException) do
139        REXML::Document.new(template.sub(/\$/, entities))
140      end
141    end
142
143    def test_raw
144      source = '<!DOCTYPE foo [
145<!ENTITY ent "replace">
146]><a>replace &ent;</a>'
147      doc = REXML::Document.new( source, {:raw=>:all})
148      assert_equal('replace &ent;', doc.root.get_text.to_s)
149      assert_equal(source, doc.to_s)
150    end
151
152    def test_lazy_evaluation
153      source = '<!DOCTYPE foo [
154<!ENTITY ent "replace">
155]><a>replace &ent;</a>'
156      doc = REXML::Document.new( source )
157      assert_equal(source, doc.to_s)
158      assert_equal("replace replace", doc.root.text)
159      assert_equal(source, doc.to_s)
160    end
161
162    # Contributed (not only test, but bug fix!!) by Kouhei Sutou
163    def test_entity_replacement
164      source = %q{<!DOCTYPE foo [
165      <!ENTITY % YN '"Yes"'>
166      <!ENTITY WhatHeSaid "He said %YN;">]>
167      <a>&WhatHeSaid;</a>}
168
169      d = REXML::Document.new( source )
170      dt = d.doctype
171      assert_equal( '"Yes"', dt.entities[ "YN" ].value )
172      assert_equal( 'He said "Yes"', dt.entities[ "WhatHeSaid" ].value )
173      assert_equal( 'He said "Yes"', d.elements[1].text )
174    end
175
176    # More unit tests from Kouhei.  I looove users who give me unit tests.
177    def test_entity_insertions
178      assert_equal("&amp;", REXML::Text.new("&amp;", false, nil, true).to_s)
179      #assert_equal("&", REXML::Text.new("&amp;", false, false).to_s)
180    end
181
182    def test_single_pass_unnormalization # ticket 123
183      assert_equal '&amp;&', REXML::Text::unnormalize('&#38;amp;&amp;')
184    end
185
186    def test_entity_filter
187      document = REXML::Document.new(<<-XML)
188<!DOCTYPE root [
189<!ENTITY copy "(c)">
190<!ENTITY release-year "2013">
191]>
192<root/>
193XML
194      respect_whitespace = false
195      parent = document.root
196      raw = false
197      entity_filter = ["copy"]
198      assert_equal("(c) &release-year;",
199                   REXML::Text.new("(c) 2013",
200                                   respect_whitespace,
201                                   parent,
202                                   raw,
203                                   entity_filter).to_s)
204    end
205  end
206end
207