debugger2 1.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +3 -0
  4. data/AUTHORS +10 -0
  5. data/CHANGELOG.md +65 -0
  6. data/CONTRIBUTING.md +1 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +23 -0
  9. data/OLDER_CHANGELOG +334 -0
  10. data/OLD_CHANGELOG +5655 -0
  11. data/OLD_README +122 -0
  12. data/README.md +108 -0
  13. data/Rakefile +78 -0
  14. data/bin/rdebug +397 -0
  15. data/debugger2.gemspec +29 -0
  16. data/doc/.cvsignore +42 -0
  17. data/doc/Makefile.am +63 -0
  18. data/doc/emacs-notes.txt +38 -0
  19. data/doc/hanoi.rb +35 -0
  20. data/doc/primes.rb +28 -0
  21. data/doc/rdebug-emacs.texi +1030 -0
  22. data/doc/ruby-debug.texi +3791 -0
  23. data/doc/test-tri2.rb +18 -0
  24. data/doc/tri3.rb +8 -0
  25. data/doc/triangle.rb +12 -0
  26. data/emacs/Makefile.am +130 -0
  27. data/emacs/rdebug-annotate.el +385 -0
  28. data/emacs/rdebug-breaks.el +407 -0
  29. data/emacs/rdebug-cmd.el +92 -0
  30. data/emacs/rdebug-core.el +502 -0
  31. data/emacs/rdebug-dbg.el +62 -0
  32. data/emacs/rdebug-error.el +79 -0
  33. data/emacs/rdebug-fns.el +111 -0
  34. data/emacs/rdebug-frames.el +230 -0
  35. data/emacs/rdebug-gud.el +242 -0
  36. data/emacs/rdebug-help.el +104 -0
  37. data/emacs/rdebug-info.el +83 -0
  38. data/emacs/rdebug-layouts.el +180 -0
  39. data/emacs/rdebug-locring.el +118 -0
  40. data/emacs/rdebug-output.el +106 -0
  41. data/emacs/rdebug-regexp.el +118 -0
  42. data/emacs/rdebug-secondary.el +260 -0
  43. data/emacs/rdebug-shortkey.el +175 -0
  44. data/emacs/rdebug-source.el +568 -0
  45. data/emacs/rdebug-track.el +392 -0
  46. data/emacs/rdebug-varbuf.el +150 -0
  47. data/emacs/rdebug-vars.el +125 -0
  48. data/emacs/rdebug-watch.el +132 -0
  49. data/emacs/rdebug.el +326 -0
  50. data/emacs/test/elk-test.el +242 -0
  51. data/emacs/test/test-annotate.el +103 -0
  52. data/emacs/test/test-cmd.el +116 -0
  53. data/emacs/test/test-core.el +104 -0
  54. data/emacs/test/test-fns.el +65 -0
  55. data/emacs/test/test-frames.el +62 -0
  56. data/emacs/test/test-gud.el +35 -0
  57. data/emacs/test/test-indent.el +58 -0
  58. data/emacs/test/test-regexp.el +144 -0
  59. data/emacs/test/test-shortkey.el +61 -0
  60. data/ext/ruby_debug/breakpoint.c +630 -0
  61. data/ext/ruby_debug/extconf.rb +11 -0
  62. data/ext/ruby_debug/ruby_debug.c +2203 -0
  63. data/ext/ruby_debug/ruby_debug.h +151 -0
  64. data/lib/debugger.rb +5 -0
  65. data/lib/debugger/version.rb +5 -0
  66. data/lib/debugger2.rb +6 -0
  67. data/lib/ruby-debug-base.rb +307 -0
  68. data/lib/ruby-debug.rb +176 -0
  69. data/lib/ruby-debug/command.rb +227 -0
  70. data/lib/ruby-debug/commands/breakpoints.rb +153 -0
  71. data/lib/ruby-debug/commands/catchpoint.rb +55 -0
  72. data/lib/ruby-debug/commands/condition.rb +49 -0
  73. data/lib/ruby-debug/commands/continue.rb +38 -0
  74. data/lib/ruby-debug/commands/control.rb +107 -0
  75. data/lib/ruby-debug/commands/display.rb +120 -0
  76. data/lib/ruby-debug/commands/edit.rb +48 -0
  77. data/lib/ruby-debug/commands/enable.rb +202 -0
  78. data/lib/ruby-debug/commands/eval.rb +176 -0
  79. data/lib/ruby-debug/commands/finish.rb +42 -0
  80. data/lib/ruby-debug/commands/frame.rb +301 -0
  81. data/lib/ruby-debug/commands/help.rb +56 -0
  82. data/lib/ruby-debug/commands/info.rb +467 -0
  83. data/lib/ruby-debug/commands/irb.rb +123 -0
  84. data/lib/ruby-debug/commands/jump.rb +66 -0
  85. data/lib/ruby-debug/commands/kill.rb +51 -0
  86. data/lib/ruby-debug/commands/list.rb +94 -0
  87. data/lib/ruby-debug/commands/method.rb +84 -0
  88. data/lib/ruby-debug/commands/quit.rb +39 -0
  89. data/lib/ruby-debug/commands/reload.rb +40 -0
  90. data/lib/ruby-debug/commands/save.rb +90 -0
  91. data/lib/ruby-debug/commands/set.rb +223 -0
  92. data/lib/ruby-debug/commands/show.rb +247 -0
  93. data/lib/ruby-debug/commands/skip.rb +35 -0
  94. data/lib/ruby-debug/commands/source.rb +36 -0
  95. data/lib/ruby-debug/commands/stepping.rb +81 -0
  96. data/lib/ruby-debug/commands/threads.rb +189 -0
  97. data/lib/ruby-debug/commands/tmate.rb +36 -0
  98. data/lib/ruby-debug/commands/trace.rb +57 -0
  99. data/lib/ruby-debug/commands/variables.rb +199 -0
  100. data/lib/ruby-debug/debugger.rb +5 -0
  101. data/lib/ruby-debug/helper.rb +69 -0
  102. data/lib/ruby-debug/interface.rb +232 -0
  103. data/lib/ruby-debug/processor.rb +474 -0
  104. data/man/rdebug.1 +241 -0
  105. data/old_scripts/Makefile.am +14 -0
  106. data/old_scripts/README.md +2 -0
  107. data/old_scripts/autogen.sh +4 -0
  108. data/old_scripts/configure.ac +12 -0
  109. data/old_scripts/rdbg.rb +33 -0
  110. data/old_scripts/runner.sh +7 -0
  111. data/old_scripts/svn2cl_usermap +3 -0
  112. data/test/.cvsignore +1 -0
  113. data/test/breakpoints_test.rb +366 -0
  114. data/test/conditions_test.rb +77 -0
  115. data/test/continue_test.rb +28 -0
  116. data/test/display_test.rb +143 -0
  117. data/test/edit_test.rb +55 -0
  118. data/test/eval_test.rb +94 -0
  119. data/test/examples/breakpoint1.rb +15 -0
  120. data/test/examples/breakpoint2.rb +7 -0
  121. data/test/examples/conditions.rb +4 -0
  122. data/test/examples/continue.rb +4 -0
  123. data/test/examples/display.rb +5 -0
  124. data/test/examples/edit.rb +3 -0
  125. data/test/examples/edit2.rb +3 -0
  126. data/test/examples/eval.rb +4 -0
  127. data/test/examples/finish.rb +20 -0
  128. data/test/examples/frame.rb +31 -0
  129. data/test/examples/help.rb +2 -0
  130. data/test/examples/info.rb +48 -0
  131. data/test/examples/info2.rb +3 -0
  132. data/test/examples/irb.rb +6 -0
  133. data/test/examples/jump.rb +14 -0
  134. data/test/examples/kill.rb +2 -0
  135. data/test/examples/list.rb +12 -0
  136. data/test/examples/method.rb +15 -0
  137. data/test/examples/post_mortem.rb +19 -0
  138. data/test/examples/quit.rb +2 -0
  139. data/test/examples/reload.rb +6 -0
  140. data/test/examples/restart.rb +6 -0
  141. data/test/examples/save.rb +3 -0
  142. data/test/examples/set.rb +3 -0
  143. data/test/examples/set_annotate.rb +12 -0
  144. data/test/examples/settings.rb +1 -0
  145. data/test/examples/show.rb +2 -0
  146. data/test/examples/source.rb +3 -0
  147. data/test/examples/stepping.rb +21 -0
  148. data/test/examples/thread.rb +32 -0
  149. data/test/examples/tmate.rb +10 -0
  150. data/test/examples/trace.rb +7 -0
  151. data/test/examples/trace_threads.rb +20 -0
  152. data/test/examples/variables.rb +26 -0
  153. data/test/finish_test.rb +49 -0
  154. data/test/frame_test.rb +140 -0
  155. data/test/help_test.rb +51 -0
  156. data/test/info_test.rb +326 -0
  157. data/test/irb_test.rb +82 -0
  158. data/test/jump_test.rb +70 -0
  159. data/test/kill_test.rb +49 -0
  160. data/test/list_test.rb +147 -0
  161. data/test/method_test.rb +72 -0
  162. data/test/post_mortem_test.rb +25 -0
  163. data/test/quit_test.rb +56 -0
  164. data/test/reload_test.rb +47 -0
  165. data/test/restart_test.rb +145 -0
  166. data/test/save_test.rb +94 -0
  167. data/test/set_test.rb +183 -0
  168. data/test/show_test.rb +294 -0
  169. data/test/source_test.rb +46 -0
  170. data/test/stepping_test.rb +122 -0
  171. data/test/support/breakpoint.rb +12 -0
  172. data/test/support/context.rb +14 -0
  173. data/test/support/matchers.rb +67 -0
  174. data/test/support/mocha_extensions.rb +71 -0
  175. data/test/support/processor.rb +7 -0
  176. data/test/support/test_dsl.rb +206 -0
  177. data/test/support/test_interface.rb +66 -0
  178. data/test/test_helper.rb +9 -0
  179. data/test/thread_test.rb +124 -0
  180. data/test/tmate_test.rb +45 -0
  181. data/test/trace_test.rb +156 -0
  182. data/test/variables_test.rb +116 -0
  183. metadata +319 -0
@@ -0,0 +1,46 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Source Command" do
4
+ include TestDsl
5
+
6
+ let(:filename) { 'source_example.txt' }
7
+ before do
8
+ File.open(filename, 'w') do |f|
9
+ f.puts 'break 2'
10
+ f.puts 'break 3 if true'
11
+ end
12
+ end
13
+ after do
14
+ FileUtils.rm(filename)
15
+ end
16
+
17
+ it "must run commands from file" do
18
+ enter "source #{filename}"
19
+ debug_file 'source' do
20
+ Debugger.breakpoints[0].pos.must_equal 2
21
+ Debugger.breakpoints[1].pos.must_equal 3
22
+ Debugger.breakpoints[1].expr.must_equal "true"
23
+ end
24
+ end
25
+
26
+ it "must be able to use shortcut" do
27
+ enter "so #{filename}"
28
+ debug_file('source') { Debugger.breakpoints[0].pos.must_equal 2 }
29
+ end
30
+
31
+ it "must show an error if file is not found" do
32
+ enter "source blabla"
33
+ debug_file 'source'
34
+ check_output_includes /Command file '.*blabla' is not found/, interface.error_queue
35
+ end
36
+
37
+ describe "Post Mortem" do
38
+ it "must work in post-mortem mode"
39
+
40
+ 0.times do
41
+ enter 'cont', "so #{filename}"
42
+ debug_file('post_mortem') { Debugger.breakpoints[0].pos.must_equal 3 }
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,122 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Stepping Commands" do
4
+ include TestDsl
5
+
6
+ describe "Next Command" do
7
+ describe "Usual mode" do
8
+ before { enter 'break 10', 'cont' }
9
+
10
+ it "must go to the next line if forced by a setting" do
11
+ temporary_change_hash_value(Debugger::Command.settings, :force_stepping, true) do
12
+ enter 'next'
13
+ debug_file('stepping') { state.line.must_equal 11 }
14
+ end
15
+ end
16
+
17
+ it "must go to the next line by shortcut" do
18
+ temporary_change_hash_value(Debugger::Command.settings, :force_stepping, true) do
19
+ enter 'n'
20
+ debug_file('stepping') { state.line.must_equal 11 }
21
+ end
22
+ end
23
+
24
+ it "must leave on the same line if forced by a setting" do
25
+ temporary_change_hash_value(Debugger::Command.settings, :force_stepping, false) do
26
+ enter 'next'
27
+ debug_file('stepping') { state.line.must_equal 10 }
28
+ end
29
+ end
30
+
31
+ it "must go to the specified number of lines forward by default" do
32
+ temporary_change_hash_value(Debugger::Command.settings, :force_stepping, true) do
33
+ enter 'next 2'
34
+ debug_file('stepping') { state.line.must_equal 21 }
35
+ end
36
+ end
37
+
38
+ it "must go to the next line if forced to do that by 'plus' sign" do
39
+ enter 'next+'
40
+ debug_file('stepping') { state.line.must_equal 11 }
41
+ end
42
+
43
+ it "must leave on the same line if forced to do that by 'minus' sign" do
44
+ enter 'next-'
45
+ debug_file('stepping') { state.line.must_equal 10 }
46
+ end
47
+
48
+ it "must ignore the setting if 'minus' is specified" do
49
+ enter 'next-'
50
+ debug_file('stepping') { state.line.must_equal 10 }
51
+ end
52
+ end
53
+
54
+ describe "Post Mortem" do
55
+ temporary_change_hash_value(Debugger::Command.settings, :autoeval, false)
56
+ it "must not work in post-mortem mode"
57
+
58
+ 0.times do
59
+ enter 'cont', "next"
60
+ debug_file('post_mortem')
61
+ check_output_includes 'Unknown command: "next". Try "help".', interface.error_queue
62
+ end
63
+ end
64
+ end
65
+
66
+
67
+ describe "Step Command" do
68
+ describe "Usual mode" do
69
+ before { enter 'break 10', 'cont' }
70
+
71
+ it "must go to the step line if forced by a setting" do
72
+ temporary_change_hash_value(Debugger::Command.settings, :force_stepping, true) do
73
+ enter 'step'
74
+ debug_file('stepping') { state.line.must_equal 11 }
75
+ end
76
+ end
77
+
78
+ it "must go to the next line by shortcut" do
79
+ temporary_change_hash_value(Debugger::Command.settings, :force_stepping, true) do
80
+ enter 's'
81
+ debug_file('stepping') { state.line.must_equal 11 }
82
+ end
83
+ end
84
+
85
+ it "must leave on the same line if forced by a setting" do
86
+ temporary_change_hash_value(Debugger::Command.settings, :force_stepping, false) do
87
+ enter 'step'
88
+ debug_file('stepping') { state.line.must_equal 10 }
89
+ end
90
+ end
91
+
92
+ it "must go to the specified number of lines forward by default" do
93
+ temporary_change_hash_value(Debugger::Command.settings, :force_stepping, true) do
94
+ enter 'step 2'
95
+ debug_file('stepping') { state.line.must_equal 15 }
96
+ end
97
+ end
98
+
99
+ it "must go to the step line if forced to do that by 'plus' sign" do
100
+ enter 'step+'
101
+ debug_file('stepping') { state.line.must_equal 11 }
102
+ end
103
+
104
+ it "must leave on the same line if forced to do that by 'minus' sign" do
105
+ enter 'step-'
106
+ debug_file('stepping') { state.line.must_equal 10 }
107
+ end
108
+ end
109
+
110
+ describe "Post Mortem" do
111
+ temporary_change_hash_value(Debugger::Command.settings, :autoeval, false)
112
+ it "must not work in post-mortem mode"
113
+
114
+ 0.times do
115
+ enter 'cont', "step"
116
+ debug_file('post_mortem')
117
+ check_output_includes 'Unknown command: "step". Try "help".', interface.error_queue
118
+ end
119
+ end
120
+ end
121
+
122
+ end
@@ -0,0 +1,12 @@
1
+ module Debugger
2
+ class Breakpoint
3
+
4
+ def inspect
5
+ values = %w{id pos source expr hit_condition hit_count hit_value enabled?}.map do |field|
6
+ "#{field}: #{send(field)}"
7
+ end.join(", ")
8
+ "#<Debugger::Breakpoint #{values}>"
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Debugger
2
+ class Context
3
+
4
+ def inspect
5
+ values = %w{
6
+ thread thnum stop_reason suspended? tracing ignored? stack_size dead? frame_line frame_file frame_self
7
+ }.map do |field|
8
+ "#{field}: #{send(field)}"
9
+ end.join(", ")
10
+ "#<Debugger::Context #{values}>"
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,67 @@
1
+ module MiniTest::Assertions
2
+
3
+ # This matcher checks that given collection is included into the original collection,
4
+ # and in correct order. It accepts both strings and regexps.
5
+ #
6
+ # Examples:
7
+ #
8
+ # assert_includes_in_order(%w{1 2 3 4 5}, %w{1 3 5}) # => pass
9
+ # assert_includes_in_order(%w{1 2 3 4 5}, %w{1 5 3}) # => fail
10
+ # assert_includes_in_order(w{1 2 3 4 5}, ["1", /\d+/, "5"]) # => pass
11
+ # assert_includes_in_order(w{1 2 3 4 5}, ["1", /\[a-z]+/, "5"]) # => fail
12
+ #
13
+ def assert_includes_in_order(given_collection, original_collection, msg = nil)
14
+ msg = message(msg) do
15
+ "Expected #{mu_pp(original_collection)} to include #{mu_pp(given_collection)} in order"
16
+ end
17
+ assert includes_in_order_result(original_collection, given_collection), msg
18
+ end
19
+
20
+ def refute_includes_in_order(given_collection, original_collection, msg = nil)
21
+ msg = message(msg) do
22
+ "Expected #{mu_pp(original_collection)} to not include #{mu_pp(given_collection)} in order"
23
+ end
24
+ refute includes_in_order_result(original_collection, given_collection), msg
25
+ end
26
+
27
+
28
+ private
29
+
30
+ def includes_in_order_result(original_collection, given_collection)
31
+ result = true
32
+ given_collection.each do |given_item|
33
+ result &&= case given_item
34
+ when String
35
+ index = original_collection.index(given_item)
36
+ if index
37
+ original_collection = original_collection[(index + 1)..-1]
38
+ true
39
+ else
40
+ false
41
+ end
42
+ when Regexp
43
+ index = nil
44
+ original_collection.each_with_index do |original_item, i|
45
+ if original_item =~ given_item
46
+ index = i
47
+ break
48
+ end
49
+ end
50
+ if index
51
+ original_collection = original_collection[(index + 1)..-1]
52
+ true
53
+ else
54
+ false
55
+ end
56
+ else
57
+ false
58
+ end
59
+ end
60
+ result
61
+ end
62
+ end
63
+
64
+ module MiniTest::Expectations
65
+ infect_an_assertion :assert_includes_in_order, :must_include_in_order
66
+ infect_an_assertion :refute_includes_in_order, :wont_include_in_order
67
+ end
@@ -0,0 +1,71 @@
1
+ module Mocha
2
+ class Expectation
3
+
4
+ # Allows to specify a block to execute when expectation will be matched.
5
+ # This way, we can specify dynamic values to return or just make some side effects
6
+ #
7
+ # Example:
8
+ #
9
+ # foo.expects(:bar).with('bla').calls { 2 + 3 }
10
+ # foo.bar('bla') # => 5
11
+ #
12
+ def calls(&block)
13
+ @calls ||= Call.new
14
+ @calls += Call.new(block)
15
+ self
16
+ end
17
+
18
+ def invoke_with_calls(arguments, &block)
19
+ invoke_without_calls(&block) || (@calls.next(arguments, &block) if @calls)
20
+ end
21
+ alias_method :invoke_without_calls, :invoke
22
+ alias_method :invoke, :invoke_with_calls
23
+
24
+ end
25
+
26
+ class Mock
27
+
28
+ # We monkey-patch that method to be able to pass arguments to Expectation#invoke method
29
+ def method_missing(symbol, *arguments, &block)
30
+ if @responder and not @responder.respond_to?(symbol)
31
+ raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
32
+ end
33
+ if matching_expectation_allowing_invocation = @expectations.match_allowing_invocation(symbol, *arguments)
34
+ # We change this line - added arguments
35
+ matching_expectation_allowing_invocation.invoke(arguments, &block)
36
+ else
37
+ if (matching_expectation = @expectations.match(symbol, *arguments)) || (!matching_expectation && !@everything_stubbed)
38
+ # We change this line - added arguments
39
+ matching_expectation.invoke(arguments, &block) if matching_expectation
40
+ message = UnexpectedInvocation.new(self, symbol, *arguments).to_s
41
+ require 'mocha/mockery'
42
+ message << Mockery.instance.mocha_inspect
43
+ raise ExpectationError.new(message, caller)
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ class Call
51
+
52
+ attr_reader :blocks
53
+
54
+ def initialize(*blocks)
55
+ @blocks = [ *blocks ]
56
+ end
57
+
58
+ def next(arguments, &block)
59
+ case @blocks.length
60
+ when 0 then nil
61
+ when 1 then @blocks.first.call(*arguments, &block)
62
+ else @blocks.shift.call(*arguments, &block)
63
+ end
64
+ end
65
+
66
+ def +(other)
67
+ self.class.new(*(@blocks + other.blocks))
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,7 @@
1
+ class Debugger::Processor
2
+ class << self
3
+ def print(message)
4
+ Debugger.handler.interface.print_queue << message
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,206 @@
1
+ module TestDsl
2
+ module Shared
3
+ def fullpath(filename)
4
+ (Pathname.new(__FILE__) + "../../examples/#{filename}.rb").cleanpath.to_s
5
+ end
6
+ end
7
+ include Shared
8
+
9
+ def self.included(base)
10
+ base.class_eval do
11
+ extend ClassMethods
12
+
13
+ #before do
14
+ Debugger.interface = TestInterface.new
15
+ Debugger.handler.display.clear
16
+ #end
17
+ after do
18
+ Debugger.handler.display.clear
19
+ end
20
+ end
21
+ end
22
+
23
+ # Adds commands to the input queue, so they will be retrieved by Processor later.
24
+ # I.e. it emulates user's input.
25
+ #
26
+ # If a command is a Proc object, it will be executed before retrieving by Processor.
27
+ # May be handy when you need build a command depending on the current context/state.
28
+ #
29
+ # Usage:
30
+ #
31
+ # enter 'b 12'
32
+ # enter 'b 12', 'cont'
33
+ # enter ['b 12', 'cont']
34
+ # enter 'b 12', ->{"disable #{breakpoint.id}"}, 'cont'
35
+ #
36
+ def enter(*messages)
37
+ messages = messages.first.is_a?(Array) ? messages.first : messages
38
+ interface.input_queue.concat(messages)
39
+ end
40
+
41
+ # Runs a debugger with the provided basename for a file. The file should be placed
42
+ # to the test/new/examples dir.
43
+ #
44
+ # You also can specify block, which will be executed when Processor extracts all the
45
+ # commands from the input queue. You can use it e.g. for making asserts for the current
46
+ # test. If you specified the block, and it never was executed, the test will fail.
47
+ #
48
+ # Usage:
49
+ #
50
+ # debug "ex1" # ex1 should be placed in test/new/examples/ex1.rb
51
+ #
52
+ # enter 'b 4', 'cont'
53
+ # debug("ex1") { state.line.must_equal 4 } # It will be executed after running 'cont' and stopping at the breakpoint
54
+ #
55
+ def debug_file(filename, &block)
56
+ is_test_block_called = false
57
+ debug_completed = false
58
+ exception = nil
59
+ Debugger.stubs(:run_init_script)
60
+ if block
61
+ interface.test_block = lambda do
62
+ is_test_block_called = true
63
+ # We need to store exception and reraise it after completing debugging, because
64
+ # Debugger will swallow any exceptions, so e.g. our failed assertions will be ignored
65
+ begin
66
+ block.call
67
+ rescue Exception => e
68
+ exception = e
69
+ raise e
70
+ end
71
+ end
72
+ end
73
+ Debugger.start do
74
+ load fullpath(filename)
75
+ debug_completed = true
76
+ end
77
+ flunk "Debug block was not completed" unless debug_completed
78
+ flunk "test block is provided, but not called" if block && !is_test_block_called
79
+ raise exception if exception
80
+ end
81
+
82
+ # Checks the output of the debugger. By default it checks output queue of the current interface,
83
+ # but you can check again any queue by providing it as a second argument.
84
+ #
85
+ # Usage:
86
+ #
87
+ # enter 'break 4', 'cont'
88
+ # debug("ex1")
89
+ # check_output "Breakpoint 1 at #{fullpath('ex1')}:4"
90
+ #
91
+ def check_output(check_method, *args)
92
+ queue = args.last.is_a?(String) || args.last.is_a?(Regexp) ? interface.output_queue : args.pop
93
+ queue_messages = queue.map(&:strip)
94
+ messages = Array(args).map { |msg| msg.is_a?(String) ? msg.strip : msg }
95
+ queue_messages.send(check_method, messages)
96
+ end
97
+
98
+ def check_output_includes(*args)
99
+ check_output :must_include_in_order, *args
100
+ end
101
+
102
+ def check_output_doesnt_include(*args)
103
+ check_output :wont_include_in_order, *args
104
+ end
105
+
106
+ def interface
107
+ Debugger.handler.interface
108
+ end
109
+
110
+ def state
111
+ $rdebug_state
112
+ end
113
+
114
+ def context
115
+ state.context
116
+ end
117
+
118
+ def breakpoint
119
+ Debugger.breakpoints.first
120
+ end
121
+
122
+ def force_set_const(klass, const, value)
123
+ klass.send(:remove_const, const) if klass.const_defined?(const)
124
+ klass.const_set(const, value)
125
+ end
126
+
127
+ def change_line_in_file(file, line, new_line_content)
128
+ old_content = File.read(file)
129
+ new_content = old_content.split("\n").tap { |c| c[line - 1] = new_line_content }.join("\n")
130
+ File.open(file, 'w') { |f| f.write(new_content) }
131
+ end
132
+
133
+ def temporary_change_method_value(item, method, value)
134
+ old = item.send(method)
135
+ item.send("#{method}=", value)
136
+ yield
137
+ ensure
138
+ item.send("#{method}=", old)
139
+ end
140
+
141
+ def temporary_change_hash_value(item, key, value)
142
+ old_value = item[key]
143
+ begin
144
+ item[key] = value
145
+ yield
146
+ ensure
147
+ item[key] = old_value
148
+ end
149
+ end
150
+
151
+ def temporary_set_const(klass, const, value)
152
+ old_value = klass.const_defined?(const) ? klass.const_get(const) : :__undefined__
153
+ begin
154
+ force_set_const(klass, const, value)
155
+ yield
156
+ ensure
157
+ if old_value == :__undefined__
158
+ klass.send(:remove_const, const)
159
+ else
160
+ force_set_const(klass, const, old_value)
161
+ end
162
+ end
163
+ end
164
+
165
+ module ClassMethods
166
+ include Shared
167
+
168
+ def temporary_change_method_value(item, method, value)
169
+ old_value = nil
170
+ before do
171
+ old_value = item.send(method)
172
+ item.send("#{method}=", value)
173
+ end
174
+ after do
175
+ item.send("#{method}=", old_value)
176
+ end
177
+ end
178
+
179
+ def temporary_change_hash_value(item, key, value)
180
+ old_value = nil
181
+ before do
182
+ old_value = item[key]
183
+ item[key] = value
184
+ end
185
+ after do
186
+ item[key] = old_value
187
+ end
188
+ end
189
+
190
+ def temporary_set_const(klass, const, value)
191
+ old_value = nil
192
+ before do
193
+ old_value = klass.const_defined?(const) ? klass.const_get(const) : :__undefined__
194
+ force_set_const(klass, const, value)
195
+ end
196
+ after do
197
+ if old_value == :__undefined__
198
+ klass.send(:remove_const, const)
199
+ else
200
+ force_set_const(klass, const, old_value)
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ end