* 2022-01-18 [ci skip]
[ruby-80x24.org.git] / test / irb / test_context.rb
blob42f82fc37ed8f27e55d83a7cb772d239cc33e10a
1 # frozen_string_literal: false
2 require 'test/unit'
3 require 'tempfile'
4 require 'irb'
5 require 'rubygems' if defined?(Gem)
7 module TestIRB
8   class TestContext < Test::Unit::TestCase
9     class TestInputMethod < ::IRB::InputMethod
10       attr_reader :list, :line_no
12       def initialize(list = [])
13         super("test")
14         @line_no = 0
15         @list = list
16       end
18       def gets
19         @list[@line_no]&.tap {@line_no += 1}
20       end
22       def eof?
23         @line_no >= @list.size
24       end
26       def encoding
27         Encoding.default_external
28       end
30       def reset
31         @line_no = 0
32       end
34       def winsize
35         [10, 20]
36       end
37     end
39     def setup
40       IRB.init_config(nil)
41       IRB.conf[:USE_SINGLELINE] = false
42       IRB.conf[:VERBOSE] = false
43       workspace = IRB::WorkSpace.new(Object.new)
44       @context = IRB::Context.new(nil, workspace, TestInputMethod.new)
46       @get_screen_size = Reline.method(:get_screen_size)
47       Reline.instance_eval { undef :get_screen_size }
48       def Reline.get_screen_size
49         [36, 80]
50       end
51     end
53     def teardown
54       Reline.instance_eval { undef :get_screen_size }
55       Reline.define_singleton_method(:get_screen_size, @get_screen_size)
56     end
58     def test_last_value
59       assert_nil(@context.last_value)
60       assert_nil(@context.evaluate('_', 1))
61       obj = Object.new
62       @context.set_last_value(obj)
63       assert_same(obj, @context.last_value)
64       assert_same(obj, @context.evaluate('_', 1))
65     end
67     def test_evaluate_with_exception
68       assert_nil(@context.evaluate("$!", 1))
69       e = assert_raise_with_message(RuntimeError, 'foo') {
70         @context.evaluate("raise 'foo'", 1)
71       }
72       assert_equal('foo', e.message)
73       assert_same(e, @context.evaluate('$!', 1, exception: e))
74       e = assert_raise(SyntaxError) {
75         @context.evaluate("1,2,3", 1, exception: e)
76       }
77       assert_match(/\A\(irb\):1:/, e.message)
78       assert_not_match(/rescue _\.class/, e.message)
79     end
81     def test_evaluate_with_encoding_error_without_lineno
82       pend if RUBY_ENGINE == 'truffleruby'
83       assert_raise_with_message(EncodingError, /invalid symbol/) {
84         @context.evaluate(%q[{"\xAE": 1}], 1)
85         # The backtrace of this invalid encoding hash doesn't contain lineno.
86       }
87     end
89     def test_evaluate_with_onigmo_warning
90       pend if RUBY_ENGINE == 'truffleruby'
91       assert_warning("(irb):1: warning: character class has duplicated range: /[aa]/\n") do
92         @context.evaluate('/[aa]/', 1)
93       end
94     end
96     def test_eval_input
97       pend if RUBY_ENGINE == 'truffleruby'
98       verbose, $VERBOSE = $VERBOSE, nil
99       input = TestInputMethod.new([
100         "raise 'Foo'\n",
101         "_\n",
102         "0\n",
103         "_\n",
104       ])
105       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
106       out, err = capture_output do
107         irb.eval_input
108       end
109       assert_empty err
110       assert_pattern_list([:*, /\(irb\):1:in `<main>': Foo \(RuntimeError\)\n/,
111                            :*, /#<RuntimeError: Foo>\n/,
112                            :*, /0$/,
113                            :*, /0$/,
114                            /\s*/], out)
115     ensure
116       $VERBOSE = verbose
117     end
119     def test_eval_input_raise2x
120       pend if RUBY_ENGINE == 'truffleruby'
121       input = TestInputMethod.new([
122         "raise 'Foo'\n",
123         "raise 'Bar'\n",
124         "_\n",
125       ])
126       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
127       out, err = capture_output do
128         irb.eval_input
129       end
130       assert_empty err
131       assert_pattern_list([
132           :*, /\(irb\):1:in `<main>': Foo \(RuntimeError\)\n/,
133           :*, /\(irb\):2:in `<main>': Bar \(RuntimeError\)\n/,
134           :*, /#<RuntimeError: Bar>\n/,
135         ], out)
136     end
138     def test_eval_object_without_inspect_method
139       verbose, $VERBOSE = $VERBOSE, nil
140       all_assertions do |all|
141         IRB::Inspector::INSPECTORS.invert.each_value do |mode|
142           all.for(mode) do
143             input = TestInputMethod.new([
144                 "[BasicObject.new, Class.new]\n",
145               ])
146             irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
147             irb.context.inspect_mode = mode
148             out, err = capture_output do
149               irb.eval_input
150             end
151             assert_empty err
152             assert_match(/\(Object doesn't support #inspect\)\n(=> )?\n/, out)
153           end
154         end
155       end
156     ensure
157       $VERBOSE = verbose
158     end
160     def test_default_config
161       assert_equal(true, @context.use_colorize?)
162       assert_equal(true, @context.use_autocomplete?)
163     end
165     def test_assignment_expression
166       input = TestInputMethod.new
167       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
168       [
169         "foo = bar",
170         "@foo = bar",
171         "$foo = bar",
172         "@@foo = bar",
173         "::Foo = bar",
174         "a::Foo = bar",
175         "Foo = bar",
176         "foo.bar = 1",
177         "foo[1] = bar",
178         "foo += bar",
179         "foo -= bar",
180         "foo ||= bar",
181         "foo &&= bar",
182         "foo, bar = 1, 2",
183         "foo.bar=(1)",
184         "foo; foo = bar",
185         "foo; foo = bar; ;\n ;",
186         "foo\nfoo = bar",
187       ].each do |exp|
188         assert(
189           irb.assignment_expression?(exp),
190           "#{exp.inspect}: should be an assignment expression"
191         )
192       end
194       [
195         "foo",
196         "foo.bar",
197         "foo[0]",
198         "foo = bar; foo",
199         "foo = bar\nfoo",
200       ].each do |exp|
201         refute(
202           irb.assignment_expression?(exp),
203           "#{exp.inspect}: should not be an assignment expression"
204         )
205       end
206     end
208     def test_echo_on_assignment
209       input = TestInputMethod.new([
210         "a = 1\n",
211         "a\n",
212         "a, b = 2, 3\n",
213         "a\n",
214         "b\n",
215         "b = 4\n",
216         "_\n"
217       ])
218       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
219       irb.context.return_format = "=> %s\n"
221       # The default
222       irb.context.echo = true
223       irb.context.echo_on_assignment = false
224       out, err = capture_output do
225         irb.eval_input
226       end
227       assert_empty err
228       assert_equal("=> 1\n=> 2\n=> 3\n=> 4\n", out)
230       # Everything is output, like before echo_on_assignment was introduced
231       input.reset
232       irb.context.echo = true
233       irb.context.echo_on_assignment = true
234       out, err = capture_output do
235         irb.eval_input
236       end
237       assert_empty err
238       assert_equal("=> 1\n=> 1\n=> [2, 3]\n=> 2\n=> 3\n=> 4\n=> 4\n", out)
240       # Nothing is output when echo is false
241       input.reset
242       irb.context.echo = false
243       irb.context.echo_on_assignment = false
244       out, err = capture_output do
245         irb.eval_input
246       end
247       assert_empty err
248       assert_equal("", out)
250       # Nothing is output when echo is false even if echo_on_assignment is true
251       input.reset
252       irb.context.echo = false
253       irb.context.echo_on_assignment = true
254       out, err = capture_output do
255         irb.eval_input
256       end
257       assert_empty err
258       assert_equal("", out)
259     end
261     def test_omit_on_assignment
262       IRB.conf[:USE_COLORIZE] = false
263       input = TestInputMethod.new([
264         "a = [1] * 100\n",
265         "a\n",
266       ])
267       value = [1] * 100
268       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
269       irb.context.return_format = "=> %s\n"
271       irb.context.echo = true
272       irb.context.echo_on_assignment = false
273       out, err = capture_output do
274         irb.eval_input
275       end
276       assert_empty err
277       assert_equal("=> \n#{value.pretty_inspect}", out)
279       input.reset
280       irb.context.echo = true
281       irb.context.echo_on_assignment = :truncate
282       out, err = capture_output do
283         irb.eval_input
284       end
285       assert_empty err
286       assert_equal("=> \n#{value.pretty_inspect[0..3]}...\n=> \n#{value.pretty_inspect}", out)
288       input.reset
289       irb.context.echo = true
290       irb.context.echo_on_assignment = true
291       out, err = capture_output do
292         irb.eval_input
293       end
294       assert_empty err
295       assert_equal("=> \n#{value.pretty_inspect}=> \n#{value.pretty_inspect}", out)
297       input.reset
298       irb.context.echo = false
299       irb.context.echo_on_assignment = false
300       out, err = capture_output do
301         irb.eval_input
302       end
303       assert_empty err
304       assert_equal("", out)
306       input.reset
307       irb.context.echo = false
308       irb.context.echo_on_assignment = :truncate
309       out, err = capture_output do
310         irb.eval_input
311       end
312       assert_empty err
313       assert_equal("", out)
315       input.reset
316       irb.context.echo = false
317       irb.context.echo_on_assignment = true
318       out, err = capture_output do
319         irb.eval_input
320       end
321       assert_empty err
322       assert_equal("", out)
323     end
325     def test_omit_multiline_on_assignment
326       IRB.conf[:USE_COLORIZE] = false
327       input = TestInputMethod.new([
328         "class A; def inspect; ([?* * 1000] * 3).join(%{\\n}); end; end; a = A.new\n",
329         "a\n"
330       ])
331       value = ([?* * 1000] * 3).join(%{\n})
332       value_first_line = (?* * 1000).to_s
333       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
334       irb.context.return_format = "=> %s\n"
336       irb.context.echo = true
337       irb.context.echo_on_assignment = false
338       out, err = capture_output do
339         irb.eval_input
340       end
341       assert_empty err
342       assert_equal("=> \n#{value}\n", out)
343       irb.context.evaluate('A.remove_method(:inspect)', 0)
345       input.reset
346       irb.context.echo = true
347       irb.context.echo_on_assignment = :truncate
348       out, err = capture_output do
349         irb.eval_input
350       end
351       assert_empty err
352       assert_equal("=> #{value_first_line[0..(input.winsize.last - 9)]}...\n=> \n#{value}\n", out)
353       irb.context.evaluate('A.remove_method(:inspect)', 0)
355       input.reset
356       irb.context.echo = true
357       irb.context.echo_on_assignment = true
358       out, err = capture_output do
359         irb.eval_input
360       end
361       assert_empty err
362       assert_equal("=> \n#{value}\n=> \n#{value}\n", out)
363       irb.context.evaluate('A.remove_method(:inspect)', 0)
365       input.reset
366       irb.context.echo = false
367       irb.context.echo_on_assignment = false
368       out, err = capture_output do
369         irb.eval_input
370       end
371       assert_empty err
372       assert_equal("", out)
373       irb.context.evaluate('A.remove_method(:inspect)', 0)
375       input.reset
376       irb.context.echo = false
377       irb.context.echo_on_assignment = :truncate
378       out, err = capture_output do
379         irb.eval_input
380       end
381       assert_empty err
382       assert_equal("", out)
383       irb.context.evaluate('A.remove_method(:inspect)', 0)
385       input.reset
386       irb.context.echo = false
387       irb.context.echo_on_assignment = true
388       out, err = capture_output do
389         irb.eval_input
390       end
391       assert_empty err
392       assert_equal("", out)
393       irb.context.evaluate('A.remove_method(:inspect)', 0)
394     end
396     def test_echo_on_assignment_conf
397       # Default
398       IRB.conf[:ECHO] = nil
399       IRB.conf[:ECHO_ON_ASSIGNMENT] = nil
400       IRB.conf[:USE_COLORIZE] = false
401       input = TestInputMethod.new()
402       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
404       assert(irb.context.echo?, "echo? should be true by default")
405       assert_equal(:truncate, irb.context.echo_on_assignment?, "echo_on_assignment? should be :truncate by default")
407       # Explicitly set :ECHO to false
408       IRB.conf[:ECHO] = false
409       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
411       refute(irb.context.echo?, "echo? should be false when IRB.conf[:ECHO] is set to false")
412       assert_equal(:truncate, irb.context.echo_on_assignment?, "echo_on_assignment? should be :truncate by default")
414       # Explicitly set :ECHO_ON_ASSIGNMENT to true
415       IRB.conf[:ECHO] = nil
416       IRB.conf[:ECHO_ON_ASSIGNMENT] = false
417       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
419       assert(irb.context.echo?, "echo? should be true by default")
420       refute(irb.context.echo_on_assignment?, "echo_on_assignment? should be false when IRB.conf[:ECHO_ON_ASSIGNMENT] is set to false")
421     end
423     def test_multiline_output_on_default_inspector
424       main = Object.new
425       def main.inspect
426         "abc\ndef"
427       end
428       IRB.conf[:USE_COLORIZE] = false
429       input = TestInputMethod.new([
430         "self"
431       ])
432       irb = IRB::Irb.new(IRB::WorkSpace.new(main), input)
433       irb.context.return_format = "=> %s\n"
435       # The default
436       irb.context.newline_before_multiline_output = true
437       out, err = capture_output do
438         irb.eval_input
439       end
440       assert_empty err
441       assert_equal("=> \nabc\ndef\n",
442                    out)
444       # No newline before multiline output
445       input.reset
446       irb.context.newline_before_multiline_output = false
447       out, err = capture_output do
448         irb.eval_input
449       end
450       assert_empty err
451       assert_equal("=> abc\ndef\n",
452                    out)
453     end
455     def test_default_return_format
456       IRB.conf[:PROMPT][:MY_PROMPT] = {
457         :PROMPT_I => "%03n> ",
458         :PROMPT_N => "%03n> ",
459         :PROMPT_S => "%03n> ",
460         :PROMPT_C => "%03n> "
461         # without :RETURN
462         # :RETURN => "%s\n"
463       }
464       IRB.conf[:PROMPT_MODE] = :MY_PROMPT
465       input = TestInputMethod.new([
466         "3"
467       ])
468       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
469       out, err = capture_output do
470         irb.eval_input
471       end
472       assert_empty err
473       assert_equal("3\n",
474                    out)
475     end
477     def test_eval_input_with_exception
478       pend if RUBY_ENGINE == 'truffleruby'
479       verbose, $VERBOSE = $VERBOSE, nil
480       input = TestInputMethod.new([
481         "def hoge() fuga; end; def fuga() raise; end; hoge\n",
482       ])
483       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
484       out, err = capture_output do
485         irb.eval_input
486       end
487       assert_empty err
488       if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' && STDOUT.tty?
489         expected = [
490           :*, /Traceback \(most recent call last\):\n/,
491           :*, /\t 2: from \(irb\):1:in `<main>'\n/,
492           :*, /\t 1: from \(irb\):1:in `hoge'\n/,
493           :*, /\(irb\):1:in `fuga': unhandled exception\n/,
494         ]
495       else
496         expected = [
497           :*, /\(irb\):1:in `fuga': unhandled exception\n/,
498           :*, /\tfrom \(irb\):1:in `hoge'\n/,
499           :*, /\tfrom \(irb\):1:in `<main>'\n/,
500           :*
501         ]
502       end
503       assert_pattern_list(expected, out)
504     ensure
505       $VERBOSE = verbose
506     end
508     def test_eval_input_with_invalid_byte_sequence_exception
509       pend if RUBY_ENGINE == 'truffleruby'
510       verbose, $VERBOSE = $VERBOSE, nil
511       input = TestInputMethod.new([
512         %Q{def hoge() fuga; end; def fuga() raise "A\\xF3B"; end; hoge\n},
513       ])
514       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
515       out, err = capture_output do
516         irb.eval_input
517       end
518       assert_empty err
519       if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' && STDOUT.tty?
520         expected = [
521           :*, /Traceback \(most recent call last\):\n/,
522           :*, /\t 2: from \(irb\):1:in `<main>'\n/,
523           :*, /\t 1: from \(irb\):1:in `hoge'\n/,
524           :*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/,
525         ]
526       else
527         expected = [
528           :*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/,
529           :*, /\tfrom \(irb\):1:in `hoge'\n/,
530           :*, /\tfrom \(irb\):1:in `<main>'\n/,
531           :*
532         ]
533       end
534       assert_pattern_list(expected, out)
535     ensure
536       $VERBOSE = verbose
537     end
539     def test_eval_input_with_long_exception
540       pend if RUBY_ENGINE == 'truffleruby'
541       verbose, $VERBOSE = $VERBOSE, nil
542       nesting = 20
543       generated_code = ''
544       nesting.times do |i|
545         generated_code << "def a#{i}() a#{i + 1}; end; "
546       end
547       generated_code << "def a#{nesting}() raise; end; a0\n"
548       input = TestInputMethod.new([
549         generated_code
550       ])
551       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
552       out, err = capture_output do
553         irb.eval_input
554       end
555       assert_empty err
556       if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' && STDOUT.tty?
557         expected = [
558           :*, /Traceback \(most recent call last\):\n/,
559           :*, /\t... \d+ levels...\n/,
560           :*, /\t16: from \(irb\):1:in `a4'\n/,
561           :*, /\t15: from \(irb\):1:in `a5'\n/,
562           :*, /\t14: from \(irb\):1:in `a6'\n/,
563           :*, /\t13: from \(irb\):1:in `a7'\n/,
564           :*, /\t12: from \(irb\):1:in `a8'\n/,
565           :*, /\t11: from \(irb\):1:in `a9'\n/,
566           :*, /\t10: from \(irb\):1:in `a10'\n/,
567           :*, /\t 9: from \(irb\):1:in `a11'\n/,
568           :*, /\t 8: from \(irb\):1:in `a12'\n/,
569           :*, /\t 7: from \(irb\):1:in `a13'\n/,
570           :*, /\t 6: from \(irb\):1:in `a14'\n/,
571           :*, /\t 5: from \(irb\):1:in `a15'\n/,
572           :*, /\t 4: from \(irb\):1:in `a16'\n/,
573           :*, /\t 3: from \(irb\):1:in `a17'\n/,
574           :*, /\t 2: from \(irb\):1:in `a18'\n/,
575           :*, /\t 1: from \(irb\):1:in `a19'\n/,
576           :*, /\(irb\):1:in `a20': unhandled exception\n/,
577         ]
578       else
579         expected = [
580           :*, /\(irb\):1:in `a20': unhandled exception\n/,
581           :*, /\tfrom \(irb\):1:in `a19'\n/,
582           :*, /\tfrom \(irb\):1:in `a18'\n/,
583           :*, /\tfrom \(irb\):1:in `a17'\n/,
584           :*, /\tfrom \(irb\):1:in `a16'\n/,
585           :*, /\tfrom \(irb\):1:in `a15'\n/,
586           :*, /\tfrom \(irb\):1:in `a14'\n/,
587           :*, /\tfrom \(irb\):1:in `a13'\n/,
588           :*, /\tfrom \(irb\):1:in `a12'\n/,
589           :*, /\tfrom \(irb\):1:in `a11'\n/,
590           :*, /\tfrom \(irb\):1:in `a10'\n/,
591           :*, /\tfrom \(irb\):1:in `a9'\n/,
592           :*, /\tfrom \(irb\):1:in `a8'\n/,
593           :*, /\tfrom \(irb\):1:in `a7'\n/,
594           :*, /\tfrom \(irb\):1:in `a6'\n/,
595           :*, /\tfrom \(irb\):1:in `a5'\n/,
596           :*, /\tfrom \(irb\):1:in `a4'\n/,
597           :*, /\t... \d+ levels...\n/,
598         ]
599       end
600       assert_pattern_list(expected, out)
601     ensure
602       $VERBOSE = verbose
603     end
605     def test_lineno
606       input = TestInputMethod.new([
607         "\n",
608         "__LINE__\n",
609         "__LINE__\n",
610         "\n",
611         "\n",
612         "__LINE__\n",
613       ])
614       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
615       out, err = capture_output do
616         irb.eval_input
617       end
618       assert_empty err
619       assert_pattern_list([
620           :*, /\b2\n/,
621           :*, /\b3\n/,
622           :*, /\b6\n/,
623         ], out)
624     end
625   end