* 2022-01-18 [ci skip]
[ruby-80x24.org.git] / test / erb / test_erb.rb
blobfb5e9b611ec1500ee8809e3d4ab6da3a16dcadf1
1 # -*- coding: us-ascii -*-
2 # frozen_string_literal: false
3 require 'test/unit'
4 require 'erb'
5 require 'stringio'
7 class TestERB < Test::Unit::TestCase
8   class MyError < RuntimeError ; end
10   def test_without_filename
11     erb = ERB.new("<% raise ::TestERB::MyError %>")
12     e = assert_raise(MyError) {
13       erb.result
14     }
15     assert_match(/\A\(erb\):1\b/, e.backtrace[0])
16   end
18   def test_with_filename
19     erb = ERB.new("<% raise ::TestERB::MyError %>")
20     erb.filename = "test filename"
21     e = assert_raise(MyError) {
22       erb.result
23     }
24     assert_match(/\Atest filename:1\b/, e.backtrace[0])
25   end
27   # [deprecated] This will be removed later
28   def test_without_filename_with_safe_level
29     erb = EnvUtil.suppress_warning do
30       ERB.new("<% raise ::TestERB::MyError %>", 1)
31     end
32     e = assert_raise(MyError) {
33       erb.result
34     }
35     assert_match(/\A\(erb\):1\b/, e.backtrace[0])
36   end
38   # [deprecated] This will be removed later
39   def test_with_filename_and_safe_level
40     erb = EnvUtil.suppress_warning do
41       ERB.new("<% raise ::TestERB::MyError %>", 1)
42     end
43     erb.filename = "test filename"
44     e = assert_raise(MyError) {
45       erb.result
46     }
47     assert_match(/\Atest filename:1\b/, e.backtrace[0])
48   end
50   def test_with_filename_lineno
51     erb = ERB.new("<% raise ::TestERB::MyError %>")
52     erb.filename = "test filename"
53     erb.lineno = 100
54     e = assert_raise(MyError) {
55       erb.result
56     }
57     assert_match(/\Atest filename:101\b/, e.backtrace[0])
58   end
60   def test_with_location
61     erb = ERB.new("<% raise ::TestERB::MyError %>")
62     erb.location = ["test filename", 200]
63     e = assert_raise(MyError) {
64       erb.result
65     }
66     assert_match(/\Atest filename:201\b/, e.backtrace[0])
67   end
69   def test_html_escape
70     assert_equal(" !&quot;\#$%&amp;&#39;()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
71                  ERB::Util.html_escape(" !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"))
73     assert_equal("", ERB::Util.html_escape(""))
74     assert_equal("abc", ERB::Util.html_escape("abc"))
75     assert_equal("&lt;&lt;", ERB::Util.html_escape("<\<"))
77     assert_equal("", ERB::Util.html_escape(nil))
78     assert_equal("123", ERB::Util.html_escape(123))
79   end
81   def test_concurrent_default_binding
82     template1 = 'one <%= ERB.new(template2).result %>'
84     eval 'template2 = "two"', TOPLEVEL_BINDING
86     bug7046 = '[ruby-core:47638]'
87     assert_equal("one two", ERB.new(template1).result, bug7046)
88   end
89 end
91 class TestERBCore < Test::Unit::TestCase
92   def setup
93     @erb = ERB
94   end
96   def test_version
97     assert_equal(String, @erb.version.class)
98   end
100   def test_core
101     # [deprecated] Fix initializer later
102     EnvUtil.suppress_warning do
103       _test_core(nil)
104       _test_core(0)
105       _test_core(1)
106     end
107   end
109   def _test_core(safe)
110     erb = @erb.new("hello")
111     assert_equal("hello", erb.result)
113     erb = @erb.new("hello", safe, 0)
114     assert_equal("hello", erb.result)
116     erb = @erb.new("hello", safe, 1)
117     assert_equal("hello", erb.result)
119     erb = @erb.new("hello", safe, 2)
120     assert_equal("hello", erb.result)
122     src = <<EOS
123 %% hi
124 = hello
125 <% 3.times do |n| %>
126 % n=0
127 * <%= n %>
128 <% end %>
131     ans = <<EOS
132 %% hi
133 = hello
135 % n=0
136 * 0
138 % n=0
139 * 1
141 % n=0
142 * 2
145     erb = @erb.new(src)
146     assert_equal(ans, erb.result)
147     erb = @erb.new(src, safe, 0)
148     assert_equal(ans, erb.result)
149     erb = @erb.new(src, safe, '')
150     assert_equal(ans, erb.result)
152     ans = <<EOS
153 %% hi
154 = hello
155 % n=0
156 * 0% n=0
157 * 1% n=0
158 * 2
160     erb = @erb.new(src, safe, 1)
161     assert_equal(ans.chomp, erb.result)
162     erb = @erb.new(src, safe, '>')
163     assert_equal(ans.chomp, erb.result)
165     ans  = <<EOS
166 %% hi
167 = hello
168 % n=0
169 * 0
170 % n=0
171 * 1
172 % n=0
173 * 2
176     erb = @erb.new(src, safe, 2)
177     assert_equal(ans, erb.result)
178     erb = @erb.new(src, safe, '<>')
179     assert_equal(ans, erb.result)
181     ans = <<EOS
182 % hi
183 = hello
185 * 0
187 * 0
189 * 0
192     erb = @erb.new(src, safe, '%')
193     assert_equal(ans, erb.result)
195     ans = <<EOS
196 % hi
197 = hello
198 * 0* 0* 0
200     erb = @erb.new(src, safe, '%>')
201     assert_equal(ans.chomp, erb.result)
203     ans = <<EOS
204 % hi
205 = hello
206 * 0
207 * 0
208 * 0
210     erb = @erb.new(src, safe, '%<>')
211     assert_equal(ans, erb.result)
212   end
214   def test_trim_line1_with_carriage_return
215     erb = @erb.new("<% 3.times do %>\r\nline\r\n<% end %>\r\n", trim_mode: '>')
216     assert_equal("line\r\n" * 3, erb.result)
218     erb = @erb.new("<% 3.times do %>\r\nline\r\n<% end %>\r\n", trim_mode: '%>')
219     assert_equal("line\r\n" * 3, erb.result)
220   end
222   def test_trim_line2_with_carriage_return
223     erb = @erb.new("<% 3.times do %>\r\nline\r\n<% end %>\r\n", trim_mode: '<>')
224     assert_equal("line\r\n" * 3, erb.result)
226     erb = @erb.new("<% 3.times do %>\r\nline\r\n<% end %>\r\n", trim_mode: '%<>')
227     assert_equal("line\r\n" * 3, erb.result)
228   end
230   def test_explicit_trim_line_with_carriage_return
231     erb = @erb.new("<%- 3.times do -%>\r\nline\r\n<%- end -%>\r\n", trim_mode: '-')
232     assert_equal("line\r\n" * 3, erb.result)
234     erb = @erb.new("<%- 3.times do -%>\r\nline\r\n<%- end -%>\r\n", trim_mode: '%-')
235     assert_equal("line\r\n" * 3, erb.result)
236   end
238   def test_invalid_trim_mode
239     assert_warning(/#{__FILE__}:#{__LINE__ + 1}/) do
240       @erb.new("", trim_mode: 'abc-def')
241     end
243     assert_warning(/Invalid ERB trim mode/) do
244       @erb.new("", trim_mode: 'abc-def')
245     end
247     assert_warning(/Invalid ERB trim mode/) do
248       @erb.new("", trim_mode: '%<')
249     end
251     assert_warning(/Invalid ERB trim mode/) do
252       @erb.new("", trim_mode: '%<>-')
253     end
255     assert_warning(/Invalid ERB trim mode/) do
256       @erb.new("", trim_mode: 3)
257     end
258   end
260   def test_run
261     out = StringIO.new
262     orig, $stdout = $stdout, out
264     num = 3
265     @erb.new('<%= num * 3 %>').run(binding)
267     $stdout = orig
268     out.rewind
269     assert_equal('9', out.read)
270     return unless num               # to remove warning
271   end
273   class Foo; end
275   def test_def_class
276     erb = @erb.new('hello')
277     cls = erb.def_class
278     assert_equal(Object, cls.superclass)
279     assert_respond_to(cls.new, 'result')
280     cls = erb.def_class(Foo)
281     assert_equal(Foo, cls.superclass)
282     assert_respond_to(cls.new, 'result')
283     cls = erb.def_class(Object, 'erb')
284     assert_equal(Object, cls.superclass)
285     assert_respond_to(cls.new, 'erb')
286   end
288   def test_percent
289     src = <<EOS
290 %n = 1
291 <%= n%>
293     assert_equal("1\n", ERB.new(src, trim_mode: '%').result(binding))
295     src = <<EOS
299     ans = "\n"
300     assert_equal(ans, ERB.new(src, trim_mode: '%').result(binding))
302     src = "<%\n%>"
303     # ans = "\n"
304     ans = ""
305     assert_equal(ans, ERB.new(src, trim_mode: '%').result(binding))
307     src = <<EOS
309 n = 1
310 %><%= n%>
312     assert_equal("1\n", ERB.new(src, trim_mode: '%').result(binding))
314     src = <<EOS
315 %n = 1
316 %% <% n = 2
317 n.times do |i|%>
318 %% %%><%%<%= i%><%
319 end%>
322     ans = <<EOS
324 % %%><%0
325 % %%><%1
328     assert_equal(ans, ERB.new(src, trim_mode: '%').result(binding))
329   end
331   def test_def_erb_method
332     klass = Class.new
333     klass.module_eval do
334       extend ERB::DefMethod
335       fname = File.join(File.dirname(File.expand_path(__FILE__)), 'hello.erb')
336       def_erb_method('hello', fname)
337     end
338     assert_respond_to(klass.new, 'hello')
340     assert_not_respond_to(klass.new, 'hello_world')
341     erb = @erb.new('hello, world')
342     klass.module_eval do
343       def_erb_method('hello_world', erb)
344     end
345     assert_respond_to(klass.new, 'hello_world')
346   end
348   def test_def_method_without_filename
349     klass = Class.new
350     erb = ERB.new("<% raise ::TestERB::MyError %>")
351     erb.filename = "test filename"
352     assert_not_respond_to(klass.new, 'my_error')
353     erb.def_method(klass, 'my_error')
354     e = assert_raise(::TestERB::MyError) {
355        klass.new.my_error
356     }
357     assert_match(/\A\(ERB\):1\b/, e.backtrace[0])
358   end
360   def test_def_method_with_fname
361     klass = Class.new
362     erb = ERB.new("<% raise ::TestERB::MyError %>")
363     erb.filename = "test filename"
364     assert_not_respond_to(klass.new, 'my_error')
365     erb.def_method(klass, 'my_error', 'test fname')
366     e = assert_raise(::TestERB::MyError) {
367        klass.new.my_error
368     }
369     assert_match(/\Atest fname:1\b/, e.backtrace[0])
370   end
372   def test_def_module
373     klass = Class.new
374     klass.include ERB.new('<%= val %>').def_module('render(val)')
375     assert_equal('1', klass.new.render(1))
376   end
378   def test_escape
379     src = <<EOS
380 1.<%% : <%="<%%"%>
381 2.%%> : <%="%%>"%>
383 % x = "foo"
384 <%=x%>
386 %% print "foo"
388 %% <%="foo"%>
389 6.<%="
390 % print 'foo'
392 7.<%="
393 %% print 'foo'
396     ans = <<EOS
397 1.<% : <%%
398 2.%%> : %>
402 % print "foo"
404 % foo
406 % print 'foo'
409 %% print 'foo'
412     assert_equal(ans, ERB.new(src, trim_mode: '%').result)
413   end
415   def test_keep_lineno
416     src = <<EOS
417 Hello,\s
418 % x = "World"
419 <%= x%>
420 % raise("lineno")
423     erb = ERB.new(src, trim_mode: '%')
424     e = assert_raise(RuntimeError) {
425       erb.result
426     }
427     assert_match(/\A\(erb\):4\b/, e.backtrace[0].to_s)
429     src = <<EOS
431 Hello,\s
432 <% x = "World%%>
434 <%= x%>
437     ans = <<EOS
438 %>Hello,\s
439 World%>
441     assert_equal(ans, ERB.new(src, trim_mode: '>').result)
443     ans = <<EOS
445 Hello,\s
447 World%>
449     assert_equal(ans, ERB.new(src, trim_mode: '<>').result)
451     ans = <<EOS
453 Hello,\s
455 World%>
458     assert_equal(ans, ERB.new(src).result)
460     src = <<EOS
461 Hello,\s
462 <% x = "World%%>
464 <%= x%>
465 <% raise("lineno") %>
468     erb = ERB.new(src)
469     e = assert_raise(RuntimeError) {
470       erb.result
471     }
472     assert_match(/\A\(erb\):5\b/, e.backtrace[0].to_s)
474     erb = ERB.new(src, trim_mode: '>')
475     e = assert_raise(RuntimeError) {
476       erb.result
477     }
478     assert_match(/\A\(erb\):5\b/, e.backtrace[0].to_s)
480     erb = ERB.new(src, trim_mode: '<>')
481     e = assert_raise(RuntimeError) {
482       erb.result
483     }
484     assert_match(/\A\(erb\):5\b/, e.backtrace[0].to_s)
486     src = <<EOS
487 % y = 'Hello'
488 <%- x = "World%%>
489 "-%>
490 <%= x %><%- x = nil -%>\s
491 <% raise("lineno") %>
494     erb = ERB.new(src, trim_mode: '-')
495     e = assert_raise(RuntimeError) {
496       erb.result
497     }
498     assert_match(/\A\(erb\):5\b/, e.backtrace[0].to_s)
500     erb = ERB.new(src, trim_mode: '%-')
501     e = assert_raise(RuntimeError) {
502       erb.result
503     }
504     assert_match(/\A\(erb\):5\b/, e.backtrace[0].to_s)
505   end
507   def test_explicit
508     src = <<EOS
509 <% x = %w(hello world) -%>
510 NotSkip <%- y = x -%> NotSkip
511 <% x.each do |w| -%>
512   <%- up = w.upcase -%>
513   * <%= up %>
514 <% end -%>
515  <%- z = nil -%> NotSkip <%- z = x %>
516  <%- z.each do |w| -%>
517    <%- down = w.downcase -%>
518    * <%= down %>
519    <%- up = w.upcase -%>
520    * <%= up %>
521  <%- end -%>
522 KeepNewLine <%- z = nil -%>\s
525    ans = <<EOS
526 NotSkip  NotSkip
527   * HELLO
528   * WORLD
529  NotSkip\s
530    * hello
531    * HELLO
532    * world
533    * WORLD
534 KeepNewLine \s
536    assert_equal(ans, ERB.new(src, trim_mode: '-').result)
537    assert_equal(ans, ERB.new(src, trim_mode: '-%').result)
538   end
540   def test_url_encode
541     assert_equal("Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide",
542                  ERB::Util.url_encode("Programming Ruby:  The Pragmatic Programmer's Guide"))
544     assert_equal("%A5%B5%A5%F3%A5%D7%A5%EB",
545                  ERB::Util.url_encode("\xA5\xB5\xA5\xF3\xA5\xD7\xA5\xEB".force_encoding("EUC-JP")))
547     assert_equal("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",
548                  ERB::Util.url_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"),
549                  "should not escape any unreserved characters, as per RFC3986 Section 2.3")
550   end
552   def test_percent_after_etag
553     assert_equal("1%", @erb.new("<%= 1 %>%", trim_mode: "%").result)
554   end
556   def test_token_extension
557     extended_erb = Class.new(ERB)
558     extended_erb.module_eval do
559       def make_compiler(trim_mode)
560         compiler = Class.new(ERB::Compiler)
561         compiler.module_eval do
562           def compile_stag(stag, out, scanner)
563             case stag
564             when '<%=='
565               scanner.stag = stag
566               add_put_cmd(out, content) if content.size > 0
567               self.content = ''
568             else
569               super
570             end
571           end
573           def compile_content(stag, out)
574             case stag
575             when '<%=='
576               out.push("#{@insert_cmd}(::ERB::Util.html_escape(#{content}))")
577             else
578               super
579             end
580           end
582           def make_scanner(src)
583             scanner = Class.new(ERB::Compiler::SimpleScanner)
584             scanner.module_eval do
585               def stags
586                 ['<%=='] + super
587               end
588             end
589             scanner.new(src, @trim_mode, @percent)
590           end
591         end
592         compiler.new(trim_mode)
593       end
594     end
596     src = <<~EOS
597       <% tag = '<>' \%>
598       <\%= tag \%>
599       <\%== tag \%>
600     EOS
601     ans = <<~EOS
603       <>
604       &lt;&gt;
605     EOS
606     assert_equal(ans, extended_erb.new(src).result)
607   end
609   def test_frozen_string_literal
610     bug12031 = '[ruby-core:73561] [Bug #12031]'
611     e = @erb.new("<%#encoding: us-ascii%>a")
612     e.src.sub!(/\A#(?:-\*-)?(.*)(?:-\*-)?/) {
613       '# -*- \1; frozen-string-literal: true -*-'
614     }
615     assert_equal("a", e.result, bug12031)
617     %w(false true).each do |flag|
618       erb = @erb.new("<%#frozen-string-literal: #{flag}%><%=''.frozen?%>")
619       assert_equal(flag, erb.result)
620     end
621   end
623   def test_result_with_hash
624     erb = @erb.new("<%= foo %>")
625     assert_equal("1", erb.result_with_hash(foo: "1"))
626   end
628   def test_result_with_hash_does_not_use_caller_local_variables
629     erb = @erb.new("<%= foo %>")
630     foo = 1
631     assert_raise(NameError) { erb.result_with_hash({}) }
632     assert_equal("1", erb.result_with_hash(foo: foo))
633   end
635   def test_result_with_hash_does_not_modify_caller_binding
636     erb = @erb.new("<%= foo %>")
637     erb.result_with_hash(foo: "1")
638     assert_equal(false, binding.local_variable_defined?(:foo))
639   end
641   def test_result_with_hash_does_not_modify_toplevel_binding
642     erb = @erb.new("<%= foo %>")
643     erb.result_with_hash(foo: "1")
644     assert_equal(false, TOPLEVEL_BINDING.local_variable_defined?(:foo))
645     TOPLEVEL_BINDING.eval 'template2 = "two"'
646     erb = @erb.new("<%= template2 %>")
647     erb.result_with_hash(template2: "TWO")
648     assert_equal "two", TOPLEVEL_BINDING.local_variable_get("template2")
649   end
651   # This depends on the behavior that #local_variable_set raises TypeError by invalid key.
652   def test_result_with_hash_with_invalid_keys_raises_type_error
653     erb = @erb.new("<%= 1 %>")
654     assert_raise(TypeError) { erb.result_with_hash({ 1 => "1" }) }
655   end
657   # Bug#14243
658   def test_half_working_comment_backward_compatibility
659     assert_nothing_raised do
660       @erb.new("<% # comment %>\n").result
661     end
662   end
664   # [deprecated] These interfaces will be removed later
665   def test_deprecated_interface_warnings
666     [nil, 0, 1, 2].each do |safe|
667       assert_warn(/2nd argument of ERB.new is deprecated/) do
668         ERB.new('', safe)
669       end
670     end
672     [nil, '', '%', '%<>'].each do |trim|
673       assert_warn(/3rd argument of ERB.new is deprecated/) do
674         ERB.new('', nil, trim)
675       end
676     end
678     [nil, '_erbout', '_hamlout'].each do |eoutvar|
679       assert_warn(/4th argument of ERB.new is deprecated/) do
680         ERB.new('', nil, nil, eoutvar)
681       end
682     end
683   end
685   def test_prohibited_marshal_dump
686     erb = ERB.new("")
687     assert_raise(TypeError) {Marshal.dump(erb)}
688   end
690   def test_prohibited_marshal_load
691     erb = ERB.allocate
692     erb.instance_variable_set(:@src, "")
693     erb.instance_variable_set(:@lineno, 1)
694     erb.instance_variable_set(:@_init, true)
695     erb = Marshal.load(Marshal.dump(erb))
696     assert_raise(ArgumentError) {erb.result}
697   end
700 class TestERBCoreWOStrScan < TestERBCore
701   def setup
702     @save_map = ERB::Compiler::Scanner.instance_variable_get('@scanner_map')
703     map = {[nil, false]=>ERB::Compiler::SimpleScanner}
704     ERB::Compiler::Scanner.instance_variable_set('@scanner_map', map)
705     super
706   end
708   def teardown
709     ERB::Compiler::Scanner.instance_variable_set('@scanner_map', @save_map)
710   end