diff --git a/lib/debug/breakpoint.rb b/lib/debug/breakpoint.rb index 37102ab72..802d230b3 100644 --- a/lib/debug/breakpoint.rb +++ b/lib/debug/breakpoint.rb @@ -263,22 +263,29 @@ class CatchBreakpoint < Breakpoint attr_reader :last_exc include SkipPathHelper - def initialize pat, cond: nil, command: nil + def initialize pat, cond: nil, command: nil, path: nil @pat = pat.freeze @key = [:catch, @pat].freeze @last_exc = nil @cond = cond @command = command + @path = path super() end def setup @tp = TracePoint.new(:raise){|tp| - next if skip_path?(tp.path) exc = tp.raised_exception next if SystemExit === exc + + if @path + next if !tp.path.match?(@path) + elsif skip_path?(tp.path) + next + end + next if !safe_eval(tp.binding, @cond) if @cond should_suspend = false @@ -303,9 +310,10 @@ def description end class CheckBreakpoint < Breakpoint - def initialize expr + def initialize expr, path @expr = expr.freeze @key = [:check, @expr].freeze + @path = path super() end @@ -315,6 +323,7 @@ def setup next if tp.path.start_with? __dir__ next if tp.path.start_with? ' e @@ -646,8 +646,8 @@ def make_breakpoint args bp when :watch - ivar, object, result, cond, command = args[1..] - WatchIVarBreakpoint.new(ivar, object, result, cond: cond, command: command) + ivar, object, result, cond, command, path = args[1..] + WatchIVarBreakpoint.new(ivar, object, result, cond: cond, command: command, path: path) else raise "unknown breakpoint: #{args}" end @@ -890,7 +890,7 @@ def wait_next_action_ bp = make_breakpoint args event! :result, :method_breakpoint, bp when :watch - ivar, cond, command = args[1..] + ivar, cond, command, path = args[1..] result = frame_eval(ivar) if @success_last_eval @@ -900,7 +900,7 @@ def wait_next_action_ else current_frame.self end - bp = make_breakpoint [:watch, ivar, object, result, cond, command] + bp = make_breakpoint [:watch, ivar, object, result, cond, command, path] event! :result, :watch_breakpoint, bp else event! :result, nil diff --git a/test/debug/break_test.rb b/test/debug/break_test.rb index 42945a589..1fcd5bbc3 100644 --- a/test/debug/break_test.rb +++ b/test/debug/break_test.rb @@ -203,6 +203,37 @@ def test_debugger_doesnt_stop_when_other_instance_calls_the_inherited_method type "c" end end + + class PathOptionTest < TestCase + def extra_file + <<~RUBY + Foo.new.bar + RUBY + end + + def program(extra_file_path) + <<~RUBY + 1| class Foo + 2| def bar; end + 3| end + 4| + 5| Foo.new.bar + 6| + 7| load "#{extra_file_path}" + RUBY + end + + def test_break_only_stops_when_path_matches + with_extra_tempfile do |extra_file| + debug_code(program(extra_file.path)) do + type "break Foo#bar path: #{extra_file.path}" + type 'c' + assert_line_text(/#{extra_file.path}/) + type 'c' + end + end + end + end end class BreakAtCMethodsTest < TestCase @@ -275,6 +306,32 @@ def test_break_C_method_with_singleton_method type 'c' end end + + class PathOptionTest < TestCase + def extra_file + <<~RUBY + 1.abs + RUBY + end + + def program(extra_file_path) + <<~RUBY + 1| load "#{extra_file_path}" + 2| 1.abs + RUBY + end + + def test_break_only_stops_when_path_matches + with_extra_tempfile do |extra_file| + debug_code(program(extra_file.path)) do + type "break Integer#abs path: #{extra_file.path}" + type 'c' + assert_line_text(/#{extra_file.path}/) + type 'c' + end + end + end + end end class BreakAtCMethod2Test < TestCase @@ -477,27 +534,6 @@ def test_conditional_breakpoint_stops_for_repeated_iterations end end - def test_conditional_breakpoint_stops_if_condition_is_true - debug_code program do - type 'break if: n == 1' - assert_line_text(/#0 BP - Check n == 1/) - type 'continue' - assert_line_num 8 - type 'quit' - type 'y' - end - end - - def test_conditional_breakpoint_shows_error - debug_code(program) do - type 'break if: xyzzy' - type 'b 23' - type 'c' - assert_debuggee_line_text(/EVAL ERROR/) - type 'c' - end - end - def test_conditional_breakpoint_stops_at_specified_location_if_condition_is_true debug_code(program) do type 'break 16 if: d == 1' @@ -543,4 +579,69 @@ def test_break_with_space_between_file_and_line_stops_at_correct_place end end end + + class ConditionalBreakTest < TestCase + def program + <<~RUBY + 1| a = 1 + 2| a += 1 + 3| a += 2 + 4| a += 3 + 5| + 6| binding.b + RUBY + end + + + def test_conditional_breakpoint_stops_if_condition_is_true + debug_code program do + type 'break if: a == 4' + assert_line_text(/#0 BP - Check a == 4/) + type 'c' + assert_line_num 4 + type 'c' + type 'c' + end + end + + def test_conditional_breakpoint_shows_error + debug_code(program) do + type 'break if: xyzzy' + type 'c' + assert_debuggee_line_text(/EVAL ERROR/) + type 'c' + end + end + + class PathOptionTest < TestCase + def extra_file + <<~RUBY + a = 100 + b = 1 + _ = 0 + RUBY + end + + def program(extra_file_path) + <<~RUBY + 1| a = 200 + 2| b = 1 + 3| _ = 0 + 4| load "#{extra_file_path}" + RUBY + end + + def test_conditional_breakpoint_only_stops_when_path_matches + with_extra_tempfile do |extra_file| + debug_code(program(extra_file.path)) do + type "break if: b == 1 path: #{extra_file.path}" + type 'c' + type 'a + b' + assert_line_text(/101/) + type 'c' + end + end + end + end + end end diff --git a/test/debug/catch_test.rb b/test/debug/catch_test.rb index a01ffd548..18293b7f2 100644 --- a/test/debug/catch_test.rb +++ b/test/debug/catch_test.rb @@ -148,4 +148,57 @@ def test_catch_with_namespace_stops_at_exception end end end + + class PathOptionTest < TestCase + def extra_file + <<~RUBY + def bar + raise "bar" + rescue + end + RUBY + end + + def program(extra_file_path) + <<~RUBY + 1| load "#{extra_file_path}" + 2| + 3| def foo + 4| raise "foo" + 5| rescue + 6| end + 7| + 8| foo + 9| bar + RUBY + end + + def test_catch_only_stops_when_path_matches + with_extra_tempfile do |extra_file| + debug_code(program(extra_file.path)) do + type "catch RuntimeError path: #{extra_file.path}" + type 'c' + assert_line_text(/bar/) + type 'c' + end + end + end + + def test_the_path_option_supersede_skip_path_config + with_extra_tempfile do |extra_file| + debug_code(program(extra_file.path)) do + type "config set skip_path #{extra_file.path}" + type 'c' + end + + debug_code(program(extra_file.path)) do + type "config set skip_path #{extra_file.path}" + type "catch RuntimeError path: #{extra_file.path}" + type 'c' + assert_line_text(/bar/) + type 'c' + end + end + end + end end diff --git a/test/debug/watch_test.rb b/test/debug/watch_test.rb index 940b1117c..e4b4bf892 100644 --- a/test/debug/watch_test.rb +++ b/test/debug/watch_test.rb @@ -103,5 +103,43 @@ def test_condition_is_evaluated_in_the_watched_object end end end + + class PathOptionTest < TestCase + def extra_file + <<~RUBY + STUDENT.age = 25 + _ = 1 # for the debugger to stop + RUBY + end + + def program(extra_file_path) + <<~RUBY + 1| class Student + 2| attr_accessor :age + 3| + 4| def initialize + 5| binding.b(do: "watch @age path: #{extra_file_path}") + 6| end + 7| end + 8| + 9| STUDENT = Student.new + 10| + 11| load "#{extra_file_path}" + 12| + 13| STUDENT.age = 30 + 14| _ = 1 + RUBY + end + + def test_watch_only_stops_when_path_matches + with_extra_tempfile do |extra_file| + debug_code(program(extra_file.path)) do + type 'c' + assert_line_text(/@age = -> 25/) + type 'c' + end + end + end + end end end diff --git a/test/support/test_case.rb b/test/support/test_case.rb index a8740eb95..7baee0504 100644 --- a/test/support/test_case.rb +++ b/test/support/test_case.rb @@ -2,6 +2,7 @@ require 'test/unit' require 'tempfile' +require 'securerandom' require_relative 'utils' require_relative 'assertions' @@ -23,5 +24,15 @@ def remove_temp_file File.unlink(@temp_file) if @temp_file @temp_file = nil end + + def with_extra_tempfile + t = Tempfile.create([SecureRandom.hex(5), '.rb']).tap do |f| + f.write(extra_file) + f.close + end + yield t + ensure + File.unlink t if t + end end end