From 78a8aa88342de97c2be04a26485ba5467c98e457 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Fri, 2 Dec 2022 01:05:07 +0000 Subject: [PATCH] Test debug commands without yamatanooroti (#464) * Add debug command tests that don't require yamatanooroti * Remove debug command related yamatanooroti tests As discussed in https://github.com/ruby/irb/pull/449#pullrequestreview-1187255149, we should avoid adding new tests that need yamatanooroti because it's not maintained by the Ruby org. And since debug commands are now tested in `test/irb/test_debug_cmd.rb`, we don't need these tests anymore. * Test against latest debug gem --- Gemfile | 2 +- test/irb/test_debug_cmd.rb | 258 +++++++++++++++++++++++ test/irb/yamatanooroti/test_rendering.rb | 223 -------------------- 3 files changed, 259 insertions(+), 224 deletions(-) create mode 100644 test/irb/test_debug_cmd.rb diff --git a/Gemfile b/Gemfile index 0b985d1e4..6bc172208 100644 --- a/Gemfile +++ b/Gemfile @@ -11,5 +11,5 @@ group :development do gem "stackprof" if is_unix && !is_truffleruby gem "test-unit" gem "reline", github: "ruby/reline" if ENV["WITH_LATEST_RELINE"] == "true" - gem "debug" + gem "debug", github: "ruby/debug" end diff --git a/test/irb/test_debug_cmd.rb b/test/irb/test_debug_cmd.rb new file mode 100644 index 000000000..50ce90465 --- /dev/null +++ b/test/irb/test_debug_cmd.rb @@ -0,0 +1,258 @@ +# frozen_string_literal: true + +require "pty" unless RUBY_ENGINE == 'truffleruby' +require "tempfile" +require "tmpdir" + +require_relative "helper" + +module TestIRB + class DebugCommandTestCase < TestCase + IRB_AND_DEBUGGER_OPTIONS = { + "RUBY_DEBUG_NO_RELINE" => "true", "NO_COLOR" => "true", "RUBY_DEBUG_HISTORY_FILE" => '' + } + + def test_backtrace + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'RUBY' + def foo + binding.irb + end + foo + RUBY + + output = run_ruby_file do + type "backtrace" + type "q!" + end + + assert_match(/\(rdbg:irb\) backtrace/, output) + assert_match(/Object#foo at #{@ruby_file.to_path}/, output) + end + + def test_debug + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'ruby' + binding.irb + puts "hello" + ruby + + output = run_ruby_file do + type "debug" + type "next" + type "continue" + end + + assert_match(/\(rdbg\) next/, output) + assert_match(/=> 2\| puts "hello"/, output) + end + + def test_next + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'ruby' + binding.irb + puts "hello" + ruby + + output = run_ruby_file do + type "next" + type "continue" + end + + assert_match(/\(rdbg:irb\) next/, output) + assert_match(/=> 2\| puts "hello"/, output) + end + + def test_break + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'RUBY' + binding.irb + puts "Hello" + RUBY + + output = run_ruby_file do + type "break 2" + type "continue" + type "continue" + end + + assert_match(/\(rdbg:irb\) break/, output) + assert_match(/=> 2\| puts "Hello"/, output) + end + + def test_delete + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'RUBY' + binding.irb + puts "Hello" + binding.irb + puts "World" + RUBY + + output = run_ruby_file do + type "break 4" + type "continue" + type "delete 0" + type "continue" + end + + assert_match(/\(rdbg:irb\) delete/, output) + assert_match(/deleted: #0 BP - Line/, output) + end + + def test_step + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'RUBY' + def foo + puts "Hello" + end + binding.irb + foo + RUBY + + output = run_ruby_file do + type "step" + type "continue" + end + + assert_match(/\(rdbg:irb\) step/, output) + assert_match(/=> 2| puts "Hello"/, output) + end + + def test_continue + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'RUBY' + binding.irb + puts "Hello" + binding.irb + puts "World" + RUBY + + output = run_ruby_file do + type "continue" + type "continue" + end + + assert_match(/\(rdbg:irb\) continue/, output) + assert_match(/=> 3: binding.irb/, output) + end + + def test_finish + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'RUBY' + def foo + binding.irb + puts "Hello" + end + foo + RUBY + + output = run_ruby_file do + type "finish" + type "continue" + end + + assert_match(/\(rdbg:irb\) finish/, output) + assert_match(/=> 4\| end/, output) + end + + def test_info + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'RUBY' + def foo + a = "He" + "llo" + binding.irb + end + foo + RUBY + + output = run_ruby_file do + type "info" + type "continue" + end + + assert_match(/\(rdbg:irb\) info/, output) + assert_match(/%self = main/, output) + assert_match(/a = "Hello"/, output) + end + + def test_catch + omit if RUBY_ENGINE == 'truffleruby' + write_ruby <<~'RUBY' + binding.irb + 1 / 0 + RUBY + + output = run_ruby_file do + type "catch ZeroDivisionError" + type "continue" + type "continue" + end + + assert_match(/\(rdbg:irb\) catch/, output) + assert_match(/Stop by #0 BP - Catch "ZeroDivisionError"/, output) + end + + private + + def run_ruby_file(&block) + cmd = "ruby -Ilib #{@ruby_file.to_path}" + tmp_dir = Dir.mktmpdir + rc_file = File.open(File.join(tmp_dir, ".irbrc"), "w+") + rc_file.write("IRB.conf[:USE_SINGLELINE] = true") + rc_file.close + + @commands = [] + lines = [] + + yield + + PTY.spawn(IRB_AND_DEBUGGER_OPTIONS.merge("IRBRC" => rc_file.to_path), cmd) do |read, write, pid| + Timeout.timeout(3) do + while line = safe_gets(read) + lines << line + + # means the breakpoint is triggered + if line.match?(/binding\.irb/) + while command = @commands.shift + write.puts(command) + end + end + end + end + end + + lines.join + rescue Timeout::Error + message = <<~MSG + Test timedout. + + #{'=' * 30} OUTPUT #{'=' * 30} + #{lines.map { |l| " #{l}" }.join} + #{'=' * 27} END OF OUTPUT #{'=' * 27} + MSG + assert_block(message) { false } + ensure + File.unlink(@ruby_file) if @ruby_file + FileUtils.remove_entry tmp_dir + end + + # read.gets could raise exceptions on some platforms + # https://github.com/ruby/ruby/blob/master/ext/pty/pty.c#L729-L736 + def safe_gets(read) + read.gets + rescue Errno::EIO + nil + end + + def type(command) + @commands << command + end + + def write_ruby(program) + @ruby_file = Tempfile.create(%w{irb- .rb}) + @ruby_file.write(program) + @ruby_file.close + end + end +end diff --git a/test/irb/yamatanooroti/test_rendering.rb b/test/irb/yamatanooroti/test_rendering.rb index 6d1a0a1d7..9e0994886 100644 --- a/test/irb/yamatanooroti/test_rendering.rb +++ b/test/irb/yamatanooroti/test_rendering.rb @@ -17,8 +17,6 @@ def setup @irbrc_backup = ENV['IRBRC'] @irbrc_file = ENV['IRBRC'] = File.join(@tmpdir, 'temporaty_irbrc') File.unlink(@irbrc_file) if File.exist?(@irbrc_file) - @ruby_file = File.join(@tmpdir, 'ruby_file.rb') - File.unlink(@ruby_file) if File.exist?(@ruby_file) end def teardown @@ -237,234 +235,13 @@ def test_assignment_expression_truncate EOC end - def test_debug - write_ruby <<~'RUBY' - puts "start IRB" - binding.irb - puts "Hello" - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("debug\n") - write("next\n") - close - assert_include_screen(<<~EOC) - (rdbg) next # command - [1, 3] in #{@ruby_file} - 1| puts "start IRB" - 2| binding.irb - => 3| puts "Hello" - EOC - end - - def test_break - write_ruby <<~'RUBY' - puts "start IRB" - binding.irb - puts "Hello" - puts "World" - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("break 3\n") - write("continue\n") - close - assert_include_screen(<<~EOC) - (rdbg:irb) break 3 - #0 BP - Line #{@ruby_file}:3 (line) - EOC - assert_include_screen(<<~EOC) - (rdbg) continue # command - [1, 4] in #{@ruby_file} - 1| puts "start IRB" - 2| binding.irb - => 3| puts "Hello" - 4| puts "World" - =>#0
at #{@ruby_file}:3 - - Stop by #0 BP - Line #{@ruby_file}:3 (line) - EOC - end - - def test_delete - write_ruby <<~'RUBY' - puts "start IRB" - binding.irb - puts "Hello" - binding.irb - puts "World" - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("break 5\n") - write("continue\n") - write("delete 0\n") - close - assert_include_screen(<<~EOC.strip) - (rdbg:irb) delete 0 - deleted: #0 BP - Line - EOC - end - - def test_next - write_ruby <<~'RUBY' - puts "start IRB" - binding.irb - puts "Hello" - puts "World" - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("next\n") - close - assert_include_screen(<<~EOC) - (rdbg:irb) next - [1, 4] in #{@ruby_file} - 1| puts "start IRB" - 2| binding.irb - => 3| puts "Hello" - 4| puts "World" - =>#0
at #{@ruby_file}:3 - EOC - end - - def test_step - write_ruby <<~'RUBY' - puts "start IRB" - def foo - puts "Hello" - end - binding.irb - foo - puts "World" - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("step\n") - close - assert_include_screen(<<~EOC) - (rdbg:irb) step - [1, 7] in #{@ruby_file} - 1| puts "start IRB" - 2| def foo - => 3| puts "Hello" - 4| end - 5| binding.irb - EOC - end - - def test_continue - write_ruby <<~'RUBY' - puts "start IRB" - binding.irb - puts "Hello" - binding.irb - puts "World" - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("continue\n") - close - assert_include_screen(<<~EOC) - (rdbg:irb) continue - Hello - - From: #{@ruby_file} @ line 4 : - - 1: puts "start IRB" - 2: binding.irb - 3: puts "Hello" - => 4: binding.irb - 5: puts "World" - EOC - end - - def test_finish - write_ruby <<~'RUBY' - puts "start IRB" - def foo - binding.irb - puts "Hello" - end - foo - puts "World" - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("finish\n") - close - assert_include_screen(<<~EOC) - (rdbg:irb) finish - Hello - [1, 7] in #{@ruby_file} - 1| puts "start IRB" - 2| def foo - 3| binding.irb - 4| puts "Hello" - => 5| end - 6| foo - EOC - end - - def test_backtrace - write_ruby <<~'RUBY' - puts "start IRB" - def foo - binding.irb - end - foo - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("backtrace\n") - close - assert_include_screen(<<~EOC) - (rdbg:irb) backtrace - =>#0 Object#foo at #{@ruby_file}:3 - #1
at #{@ruby_file}:5 - EOC - end - - def test_info - write_ruby <<~'RUBY' - puts "start IRB" - a = 1 - binding.irb - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("info\n") - close - assert_include_screen(<<~EOC) - (rdbg:irb) info - %self = main - a = 1 - EOC - end - - def test_catch - write_ruby <<~'RUBY' - puts "start IRB" - binding.irb - raise NotImplementedError - RUBY - start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@ruby_file}}, startup_message: 'start IRB') - write("catch NotImplementedError\n") - write("continue\n") - close - assert_include_screen(<<~EOC) - Stop by #0 BP - Catch "NotImplementedError" - EOC - end - private - def assert_include_screen(expected) - assert_include(result.join("\n"), expected) - end - def write_irbrc(content) File.open(@irbrc_file, 'w') do |f| f.write content end end - - def write_ruby(content) - File.open(@ruby_file, 'w') do |f| - f.write content - end - end end rescue LoadError, NameError # On Ruby repository, this test suit doesn't run because Ruby repo doesn't