summaryrefslogtreecommitdiff
path: root/test/irb/yamatanooroti/test_rendering.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/irb/yamatanooroti/test_rendering.rb')
-rw-r--r--test/irb/yamatanooroti/test_rendering.rb657
1 files changed, 496 insertions, 161 deletions
diff --git a/test/irb/yamatanooroti/test_rendering.rb b/test/irb/yamatanooroti/test_rendering.rb
index ac6df30c62..44e07a3a12 100644
--- a/test/irb/yamatanooroti/test_rendering.rb
+++ b/test/irb/yamatanooroti/test_rendering.rb
@@ -2,181 +2,516 @@ require 'irb'
begin
require 'yamatanooroti'
+rescue LoadError, NameError
+ # On Ruby repository, this test suite doesn't run because Ruby repo doesn't
+ # have the yamatanooroti gem.
+ return
+end
- class IRB::TestRendering < Yamatanooroti::TestCase
- def setup
- @pwd = Dir.pwd
- suffix = '%010d' % Random.rand(0..65535)
- @tmpdir = File.join(File.expand_path(Dir.tmpdir), "test_irb_#{$$}_#{suffix}")
- begin
- Dir.mkdir(@tmpdir)
- rescue Errno::EEXIST
- FileUtils.rm_rf(@tmpdir)
- Dir.mkdir(@tmpdir)
- end
- @irbrc_backup = ENV['IRBRC']
- @irbrc_file = ENV['IRBRC'] = File.join(@tmpdir, 'temporaty_irbrc')
- File.unlink(@irbrc_file) if File.exist?(@irbrc_file)
- end
-
- def teardown
+class IRB::RenderingTest < Yamatanooroti::TestCase
+ def setup
+ @original_term = ENV['TERM']
+ @home_backup = ENV['HOME']
+ @xdg_config_home_backup = ENV['XDG_CONFIG_HOME']
+ ENV['TERM'] = "xterm-256color"
+ @pwd = Dir.pwd
+ suffix = '%010d' % Random.rand(0..65535)
+ @tmpdir = File.join(File.expand_path(Dir.tmpdir), "test_irb_#{$$}_#{suffix}")
+ begin
+ Dir.mkdir(@tmpdir)
+ rescue Errno::EEXIST
FileUtils.rm_rf(@tmpdir)
- ENV['IRBRC'] = @irbrc_backup
- ENV.delete('RELINE_TEST_PROMPT') if ENV['RELINE_TEST_PROMPT']
+ Dir.mkdir(@tmpdir)
end
+ @irbrc_backup = ENV['IRBRC']
+ @irbrc_file = ENV['IRBRC'] = File.join(@tmpdir, 'temporaty_irbrc')
+ File.unlink(@irbrc_file) if File.exist?(@irbrc_file)
+ ENV['HOME'] = File.join(@tmpdir, 'home')
+ ENV['XDG_CONFIG_HOME'] = File.join(@tmpdir, 'xdg_config_home')
+ end
- def test_launch
- write_irbrc <<~'LINES'
- puts 'start IRB'
- LINES
- start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
- write(<<~EOC)
- 'Hello, World!'
- EOC
- close
- assert_screen(<<~EOC)
- start IRB
- irb(main):001:0> 'Hello, World!'
- => "Hello, World!"
- irb(main):002:0>
- EOC
- end
+ def teardown
+ FileUtils.rm_rf(@tmpdir)
+ ENV['IRBRC'] = @irbrc_backup
+ ENV['TERM'] = @original_term
+ ENV['HOME'] = @home_backup
+ ENV['XDG_CONFIG_HOME'] = @xdg_config_home_backup
+ end
- def test_multiline_paste
- write_irbrc <<~'LINES'
- puts 'start IRB'
- LINES
- start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
- write(<<~EOC)
- class A
- def inspect; '#<A>'; end
- def a; self; end
- def b; true; end
- end
-
- a = A.new
-
- a
- .a
- .b
- EOC
- close
- assert_screen(<<~EOC)
- start IRB
- irb(main):001:1* class A
- irb(main):002:1* def inspect; '#<A>'; end
- irb(main):003:1* def a; self; end
- irb(main):004:1* def b; true; end
- irb(main):005:0> end
- => :b
- irb(main):006:0>
- irb(main):007:0> a = A.new
- => #<A>
- irb(main):008:0>
- irb(main):009:0> a
- irb(main):010:0> .a
- irb(main):011:0> .b
- => true
- irb(main):012:0>
- EOC
- end
+ def test_launch
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write(<<~EOC)
+ 'Hello, World!'
+ EOC
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001> 'Hello, World!'
+ => "Hello, World!"
+ irb(main):002>
+ EOC
+ end
- def test_evaluate_each_toplevel_statement_by_multiline_paste
- write_irbrc <<~'LINES'
- puts 'start IRB'
- LINES
- start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
- write(<<~EOC)
- class A
- def inspect; '#<A>'; end
- def b; self; end
- def c; true; end
- end
-
- a = A.new
-
- a
- .b
- # aaa
- .c
-
- (a)
- &.b()
-
-
- class A def b; self; end; def c; true; end; end;
- a = A.new
- a
- .b
- # aaa
- .c
- (a)
- &.b()
- EOC
- close
+ def test_configuration_file_is_skipped_with_dash_f
+ write_irbrc <<~'LINES'
+ puts '.irbrc file should be ignored when -f is used'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb -f}, startup_message: '')
+ write(<<~EOC)
+ 'Hello, World!'
+ EOC
+ close
+ assert_screen(<<~EOC)
+ irb(main):001> 'Hello, World!'
+ => "Hello, World!"
+ irb(main):002>
+ EOC
+ end
+
+ def test_configuration_file_is_skipped_with_dash_f_for_nested_sessions
+ write_irbrc <<~'LINES'
+ puts '.irbrc file should be ignored when -f is used'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb -f}, startup_message: '')
+ write(<<~EOC)
+ 'Hello, World!'
+ binding.irb
+ exit!
+ EOC
+ close
+ assert_screen(<<~EOC)
+ irb(main):001> 'Hello, World!'
+ => "Hello, World!"
+ irb(main):002> binding.irb
+ irb(main):003> exit!
+ irb(main):001>
+ EOC
+ end
+
+ def test_nomultiline
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb --nomultiline}, startup_message: 'start IRB')
+ write(<<~EOC)
+ if true
+ if false
+ a = "hello
+ world"
+ puts a
+ end
+ end
+ EOC
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001> if true
+ irb(main):002* if false
+ irb(main):003* a = "hello
+ irb(main):004" world"
+ irb(main):005* puts a
+ irb(main):006* end
+ irb(main):007* end
+ => nil
+ irb(main):008>
+ EOC
+ end
+
+ def test_multiline_paste
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write(<<~EOC)
+ class A
+ def inspect; '#<A>'; end
+ def a; self; end
+ def b; true; end
+ end
+
+ a = A.new
+
+ a
+ .a
+ .b
+ .itself
+ EOC
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001* class A
+ irb(main):002* def inspect; '#<A>'; end
+ irb(main):003* def a; self; end
+ irb(main):004* def b; true; end
+ irb(main):005> end
+ => :b
+ irb(main):006>
+ irb(main):007> a = A.new
+ => #<A>
+ irb(main):008>
+ irb(main):009> a
+ irb(main):010> .a
+ irb(main):011> .b
+ irb(main):012> .itself
+ => true
+ irb(main):013>
+ EOC
+ end
+
+ def test_evaluate_each_toplevel_statement_by_multiline_paste
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write(<<~EOC)
+ class A
+ def inspect; '#<A>'; end
+ def b; self; end
+ def c; true; end
+ end
+
+ a = A.new
+
+ a
+ .b
+ # aaa
+ .c
+
+ (a)
+ &.b()
+
+ class A def b; self; end; def c; true; end; end;
+ a = A.new
+ a
+ .b
+ # aaa
+ .c
+ (a)
+ &.b()
+ .itself
+ EOC
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001* class A
+ irb(main):002* def inspect; '#<A>'; end
+ irb(main):003* def b; self; end
+ irb(main):004* def c; true; end
+ irb(main):005> end
+ => :c
+ irb(main):006>
+ irb(main):007> a = A.new
+ => #<A>
+ irb(main):008>
+ irb(main):009> a
+ irb(main):010> .b
+ irb(main):011> # aaa
+ irb(main):012> .c
+ => true
+ irb(main):013>
+ irb(main):014> (a)
+ irb(main):015> &.b()
+ => #<A>
+ irb(main):016>
+ irb(main):017> class A def b; self; end; def c; true; end; end;
+ irb(main):018> a = A.new
+ => #<A>
+ irb(main):019> a
+ irb(main):020> .b
+ irb(main):021> # aaa
+ irb(main):022> .c
+ => true
+ irb(main):023> (a)
+ irb(main):024> &.b()
+ irb(main):025> .itself
+ => #<A>
+ irb(main):026>
+ EOC
+ end
+
+ def test_symbol_with_backtick
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write(<<~EOC)
+ :`
+ EOC
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001> :`
+ => :`
+ irb(main):002>
+ EOC
+ end
+
+ def test_autocomplete_with_multiple_doc_namespaces
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(3, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("{}.__id_")
+ write("\C-i")
+ sleep 0.2
+ close
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/start\ IRB\nirb\(main\):001> {}\.__id__\n }\.__id__(?:Press )?/, screen)
+ end
+
+ def test_autocomplete_with_showdoc_in_gaps_on_narrow_screen_right
+ rdoc_dir = File.join(@tmpdir, 'rdoc')
+ system("bundle exec rdoc -r -o #{rdoc_dir}")
+ write_irbrc <<~LINES
+ IRB.conf[:EXTRA_DOC_DIRS] = ['#{rdoc_dir}']
+ IRB.conf[:PROMPT][:MY_PROMPT] = {
+ :PROMPT_I => "%03n> ",
+ :PROMPT_S => "%03n> ",
+ :PROMPT_C => "%03n> "
+ }
+ IRB.conf[:PROMPT_MODE] = :MY_PROMPT
+ puts 'start IRB'
+ LINES
+ start_terminal(4, 19, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("IR")
+ write("\C-i")
+ sleep 0.2
+ close
+
+ # This is because on macOS we display different shortcut for displaying the full doc
+ # 'O' is for 'Option' and 'A' is for 'Alt'
+ if RUBY_PLATFORM =~ /darwin/
assert_screen(<<~EOC)
start IRB
- irb(main):001:1* class A
- irb(main):002:1* def inspect; '#<A>'; end
- irb(main):003:1* def b; self; end
- irb(main):004:1* def c; true; end
- irb(main):005:0> end
- => :c
- irb(main):006:0>
- irb(main):007:0> a = A.new
- => #<A>
- irb(main):008:0>
- irb(main):009:0> a
- irb(main):010:0> .b
- irb(main):011:0> # aaa
- irb(main):012:0> .c
- => true
- irb(main):013:0>
- irb(main):014:0> (a)
- irb(main):015:0> &.b()
- => #<A>
- irb(main):016:0>
- irb(main):017:0>
- irb(main):018:0> class A def b; self; end; def c; true; end; end;
- => :c
- irb(main):019:0> a = A.new
- => #<A>
- irb(main):020:0> a
- irb(main):021:0> .b
- irb(main):022:0> # aaa
- irb(main):023:0> .c
- => true
- irb(main):024:0> (a)
- irb(main):025:0> &.b()
- => #<A>
- irb(main):026:0>
- EOC
- end
-
- def test_symbol_with_backtick
- write_irbrc <<~'LINES'
- puts 'start IRB'
- LINES
- start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
- write(<<~EOC)
- :`
+ 001> IRB
+ IRBPress Opti
+ IRB
EOC
- close
+ else
assert_screen(<<~EOC)
start IRB
- irb(main):001:0> :`
- => :`
- irb(main):002:0>
+ 001> IRB
+ IRBPress Alt+
+ IRB
EOC
end
+ end
- private def write_irbrc(content)
- File.open(@irbrc_file, 'w') do |f|
- f.write content
- end
+ def test_autocomplete_with_showdoc_in_gaps_on_narrow_screen_left
+ rdoc_dir = File.join(@tmpdir, 'rdoc')
+ system("bundle exec rdoc -r -o #{rdoc_dir}")
+ write_irbrc <<~LINES
+ IRB.conf[:EXTRA_DOC_DIRS] = ['#{rdoc_dir}']
+ IRB.conf[:PROMPT][:MY_PROMPT] = {
+ :PROMPT_I => "%03n> ",
+ :PROMPT_S => "%03n> ",
+ :PROMPT_C => "%03n> "
+ }
+ IRB.conf[:PROMPT_MODE] = :MY_PROMPT
+ puts 'start IRB'
+ LINES
+ start_terminal(4, 12, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("IR")
+ write("\C-i")
+ sleep 0.2
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ 001> IRB
+ PressIRB
+ IRB
+ EOC
+ end
+
+ def test_assignment_expression_truncate
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ # Assignment expression code that turns into non-assignment expression after evaluation
+ code = "a /'/i if false; a=1; x=1000.times.to_a#'.size"
+ write(code + "\n")
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001> #{code}
+ =>
+ [0,
+ ...
+ irb(main):002>
+ EOC
+ end
+
+ def test_ctrl_c_is_handled
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ # Assignment expression code that turns into non-assignment expression after evaluation
+ write("\C-c")
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001>
+ ^C
+ irb(main):001>
+ EOC
+ end
+
+ def test_show_cmds_with_pager_can_quit_with_ctrl_c
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("help\n")
+ write("G") # move to the end of the screen
+ write("\C-c") # quit pager
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ # IRB::Abort should be rescued
+ assert_not_match(/IRB::Abort/, screen)
+ # IRB should resume
+ assert_match(/foobar/, screen)
+ end
+
+ def test_pager_page_content_pages_output_when_it_does_not_fit_in_the_screen_because_of_total_length
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("IRB::Pager.page_content('a' * (80 * 8))\n")
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/a{80}/, screen)
+ # because pager is invoked, foobar will not be evaluated
+ assert_not_match(/foobar/, screen)
+ end
+
+ def test_pager_page_content_pages_output_when_it_does_not_fit_in_the_screen_because_of_screen_height
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("IRB::Pager.page_content('a\n' * 8)\n")
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/(a\n){8}/, screen)
+ # because pager is invoked, foobar will not be evaluated
+ assert_not_match(/foobar/, screen)
+ end
+
+ def test_pager_page_content_doesnt_page_output_when_it_fits_in_the_screen
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("IRB::Pager.page_content('a' * (80 * 7))\n")
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/a{80}/, screen)
+ # because pager is not invoked, foobar will be evaluated
+ assert_match(/foobar/, screen)
+ end
+
+ def test_long_evaluation_output_is_paged
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("'a' * 80 * 11\n")
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/(a{80}\n){8}/, screen)
+ # because pager is invoked, foobar will not be evaluated
+ assert_not_match(/foobar/, screen)
+ end
+
+ def test_long_evaluation_output_is_preserved_after_paging
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("'a' * 80 * 11\n")
+ write("q") # quit pager
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ # confirm pager has exited
+ assert_match(/foobar/, screen)
+ # confirm output is preserved
+ assert_match(/(a{80}\n){6}/, screen)
+ end
+
+ def test_debug_integration_hints_debugger_commands
+ write_irbrc <<~'LINES'
+ IRB.conf[:USE_COLORIZE] = false
+ LINES
+ script = Tempfile.create(["debug", ".rb"])
+ script.write <<~RUBY
+ puts 'start IRB'
+ binding.irb
+ RUBY
+ script.close
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{script.to_path}}, startup_message: 'start IRB')
+ write("debug\n")
+ write("pp 1\n")
+ write("pp 1")
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ # submitted input shouldn't contain hint
+ assert_include(screen, "irb:rdbg(main):002> pp 1\n")
+ # unsubmitted input should contain hint
+ assert_include(screen, "irb:rdbg(main):003> pp 1 # debug command\n")
+ ensure
+ File.unlink(script) if script
+ end
+
+ def test_debug_integration_doesnt_hint_non_debugger_commands
+ write_irbrc <<~'LINES'
+ IRB.conf[:USE_COLORIZE] = false
+ LINES
+ script = Tempfile.create(["debug", ".rb"])
+ script.write <<~RUBY
+ puts 'start IRB'
+ binding.irb
+ RUBY
+ script.close
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{script.to_path}}, startup_message: 'start IRB')
+ write("debug\n")
+ write("foo")
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_include(screen, "irb:rdbg(main):002> foo\n")
+ ensure
+ File.unlink(script) if script
+ end
+
+ private
+
+ def write_irbrc(content)
+ File.open(@irbrc_file, 'w') do |f|
+ f.write content
end
end
-rescue LoadError, NameError
- # On Ruby repository, this test suit doesn't run because Ruby repo doesn't
- # have the yamatanooroti gem.
end