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::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) 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 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_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_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; '#'; 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; '#'; 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 => # 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; '#'; 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; '#'; 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 => # 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() => # irb(main):016> irb(main):017> class A def b; self; end; def c; true; end; end; irb(main):018> a = A.new => # 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 => # 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 001> IRB IRBPress Opti IRB EOC else assert_screen(<<~EOC) start IRB 001> IRB IRBPress Alt+ IRB EOC end 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 end