rtinspect 0.0.1 → 0.0.19

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.
@@ -1,9 +1,42 @@
1
+ # Contains the TestRuntimeInspectionThread.
2
+ #
3
+ # Copyright (c) 2006-2007 Brad Robel-Forrest.
4
+ #
5
+ # This software can be distributed under the terms of the Ruby license
6
+ # as detailed in the accompanying LICENSE[link:files/LICENSE.html]
7
+ # file.
8
+
9
+ require 'thread'
10
+
11
+ # Extend the Thread class so we can know exactly where a given thread
12
+ # was created to validate that there aren't any leftover in various
13
+ # spots.
14
+ class Thread
15
+ alias orig_initialize initialize
16
+ def initialize( *args, &block )
17
+ orig_initialize( *args, &block )
18
+ self[:caller] = caller
19
+ end
20
+ end
21
+
1
22
  require 'timeout'
2
23
  require 'expect'
3
24
  require 'test/unit'
4
25
 
5
26
  require 'rtinspect'
6
27
 
28
+ if ARGV.include?( '-show_trace' )
29
+ RuntimeInspection.show_trace = true
30
+ end
31
+
32
+ $mybp_line = __LINE__ + 2
33
+ class MyBreakpoint
34
+ def mybreakpoint( i )
35
+ a = 1
36
+ b = 2
37
+ end
38
+ end
39
+
7
40
  class TestRuntimeInspectionThread < Test::Unit::TestCase
8
41
 
9
42
  class Tracker
@@ -11,12 +44,13 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
11
44
  @@assert_equal = block
12
45
  end
13
46
 
14
- def initialize( &assert_block )
47
+ def initialize
15
48
  $expect_verbose = $DEBUG
16
49
  @sock = TCPSocket.new( 'localhost', 56789 )
17
50
  @cmd_count = 1
18
51
  @block_count = 0
19
52
  @at_prompt = false
53
+ @prompt_name = File.basename( $0, '.rb' )
20
54
  end
21
55
 
22
56
  def assert( cmd=nil, *expected )
@@ -38,7 +72,7 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
38
72
  timeout( 5 ) do
39
73
  out += @sock.expect( prompt ).first
40
74
  end
41
- out.sub!( /test:\d+:\d> $/, '' )
75
+ out.sub!( /#{@prompt_name}:\d+:\d> $/, '' )
42
76
  end
43
77
 
44
78
  if @block_count > 0
@@ -48,13 +82,23 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
48
82
  timeout( 5 ) do
49
83
  out += @sock.expect( prompt ).first
50
84
  end
51
- out.sub!( /test:\d+:\d> $/, '' )
85
+ out.sub!( /#{@prompt_name}:\d+:\d> $/, '' )
52
86
  end
53
87
 
54
88
  @at_prompt = true
55
89
 
56
90
  out.chomp!
57
- @@assert_equal.call( expected.join("\n") + "\n", out )
91
+ if expected.empty?
92
+ return out
93
+ else
94
+ if expected.last == :no_newline
95
+ expected.pop
96
+ nl = ''
97
+ else
98
+ nl = "\n"
99
+ end
100
+ @@assert_equal.call( expected.join("\n") + nl, out )
101
+ end
58
102
  end
59
103
 
60
104
  def puts( msg )
@@ -86,7 +130,7 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
86
130
  private
87
131
 
88
132
  def prompt
89
- "test:#{'%03d'%@cmd_count}:#{@block_count}> "
133
+ "#{@prompt_name}:#{'%03d'%@cmd_count}:#{@block_count}> "
90
134
  end
91
135
  end
92
136
 
@@ -96,9 +140,22 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
96
140
  end
97
141
  end
98
142
 
143
+ def teardown
144
+ # Ensure that the RTI is not running so if one test fails,
145
+ # future ones won't just because it can't take the port.
146
+ rti_th = nil
147
+ Thread.list.each do |th|
148
+ if th.kind_of?( RuntimeInspection::Thread )
149
+ rti_th = th
150
+ break
151
+ end
152
+ end
153
+ rti_th.kill if rti_th
154
+ end
155
+
99
156
  def test_prompt
100
157
  threads = Thread.list.dup
101
- r = RuntimeInspectionThread.new
158
+ r = RuntimeInspection::Thread.new
102
159
  assert_not_equal( threads, Thread.list )
103
160
  t = Tracker.new
104
161
  t.assert
@@ -112,33 +169,37 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
112
169
  end
113
170
 
114
171
  def test_locals
115
- r = RuntimeInspectionThread.new
172
+ r = RuntimeInspection::Thread.new
116
173
  t = Tracker.new
117
174
 
118
175
  t.assert( 'rti.state.use_yaml = true', "=> --- true" )
119
176
 
120
- t.assert( 'local_variables',
121
- '=> --- ',
122
- '- rti' )
177
+ # As in the case of running within the GEM framework, the
178
+ # toplevel binding is not clean. Thus, we can only check that
179
+ # at least rti is around.
180
+ out = t.assert( 'local_variables' )
181
+ out.sub!( /^=> /, '' )
182
+ assert( YAML.load( out ).include?( 'rti' ), 'Missing rti local' )
123
183
 
124
184
  r.kill
125
185
  end
126
186
 
187
+ RTI_LIST = ["backtrace", "bp_add", "bp_attach", "bp_continue", "bp_delete",
188
+ "bp_finish", "bp_list", "bp_next", "bp_start", "bp_step",
189
+ "bp_stop", "get_object", "list", "name2class", "start_world",
190
+ "state", "stop_world", "threads"]
191
+
127
192
  def test_list
128
- r = RuntimeInspectionThread.new
193
+ r = RuntimeInspection::Thread.new
129
194
  t = Tracker.new
130
195
 
131
- t.assert( 'rti.list',
132
- '=> ["bp_add", "bp_continue", "bp_del", "bp_fin", "bp_list", ' +
133
- '"bp_next", "bp_start", "bp_step", "bp_stop", "get_object", ' +
134
- '"initialize", "list", "name2class", "start_world", "state", ' +
135
- '"stop_world"]' )
196
+ t.assert( 'rti.list', "=> #{RTI_LIST.inspect}" )
136
197
 
137
198
  r.kill
138
199
  end
139
200
 
140
201
  def test_lookup
141
- r = RuntimeInspectionThread.new
202
+ r = RuntimeInspection::Thread.new
142
203
  t = Tracker.new
143
204
 
144
205
  Struct.new( 'Foo', :one, :two, :three )
@@ -159,4 +220,174 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
159
220
  r.kill
160
221
  end
161
222
 
223
+ def test_signal
224
+ assert_raise( Errno::ECONNREFUSED ) do
225
+ t = Tracker.new
226
+ end
227
+
228
+ RuntimeInspection.on_signal
229
+
230
+ Process.kill( 'USR1', $$ )
231
+ t = Tracker.new
232
+
233
+ t.assert( 'rti.list', "=> #{RTI_LIST.inspect}" )
234
+
235
+ Process.kill( 'USR1', $$ )
236
+
237
+ assert_raise( Errno::ECONNREFUSED ) do
238
+ t = Tracker.new
239
+ end
240
+ end
241
+
242
+ def test_breakpoints
243
+ r = RuntimeInspection::Thread.new
244
+ t = Tracker.new
245
+
246
+ # Try some invalid breakpoints.
247
+ t.assert( '.bp_add Foo#bar',
248
+ '=> "Unable to find matching module or class for Foo"' )
249
+ t.assert( '.bp_add MyBreakpoint#doesnotexist',
250
+ '=> "Unable to find doesnotexist method in MyBreakpoint"' )
251
+ t.assert( '.bp_add bar',
252
+ '=> "Unable to find containing class or module for bar"' )
253
+
254
+ # Now let's run through a simple breakpoint analysis.
255
+ t.assert( '.bp_add MyBreakpoint#mybreakpoint',
256
+ '=> "Added breakpoint 1"' )
257
+
258
+ line = __LINE__
259
+ th = Thread.new do
260
+ bp = MyBreakpoint.new
261
+ 5.times do |i|
262
+ sleep 0.3
263
+ bp.mybreakpoint( i )
264
+ end
265
+ end
266
+
267
+ begin
268
+ t.assert( '.bp_start',
269
+ '=> nil', '',
270
+ 'Breakpoint 1 in MyBreakpoint#mybreakpoint ' +
271
+ "from #{__FILE__}:#{$mybp_line} (call) in #{th}",
272
+ :no_newline )
273
+
274
+ t.assert( 'local_variables',
275
+ '=> ["i", "a", "b", "rti"]', '',
276
+ 'Breakpoint 1 in MyBreakpoint#mybreakpoint ' +
277
+ "from #{__FILE__}:#{$mybp_line} (call) in #{th}",
278
+ :no_newline )
279
+
280
+ t.assert( '.bp_next',
281
+ '=> nil', '',
282
+ 'Stopped in MyBreakpoint#mybreakpoint ' +
283
+ "from #{__FILE__}:#{$mybp_line+1} (line) in #{th}",
284
+ :no_newline )
285
+
286
+ t.assert( 'rti.backtrace',
287
+ "=> [\"#{__FILE__}:#{$mybp_line+1}:in `mybreakpoint'\"," +
288
+ " \"#{__FILE__}:#{line+5}:in `test_breakpoints'\", " +
289
+ "\"#{__FILE__}:#{line+3}:in `test_breakpoints'\", " +
290
+ "\"#{__FILE__}:17:in `initialize'\", " +
291
+ "\"#{__FILE__}:#{line+1}:in `test_breakpoints'\"]",
292
+ '',
293
+ 'Stopped in MyBreakpoint#mybreakpoint ' +
294
+ "from #{__FILE__}:#{$mybp_line+1} (line) in #{th}",
295
+ :no_newline )
296
+
297
+ out = t.assert( 'rti.threads' )
298
+ assert_match( / :main sleep/, out )
299
+ assert_no_match( /RuntimeInspection::Thread:/, out )
300
+ assert_no_match( /:rti_/, out )
301
+
302
+ t.assert( '.bp_continue',
303
+ '=> nil', '',
304
+ 'Breakpoint 1 in MyBreakpoint#mybreakpoint ' +
305
+ "from #{__FILE__}:#{$mybp_line} (call) in #{th}",
306
+ :no_newline )
307
+ ensure
308
+ t.assert( '.bp_stop', '=> nil' )
309
+ th.kill
310
+ end
311
+
312
+ r.kill
313
+ end
314
+
315
+ def test_attach
316
+ r = RuntimeInspection::Thread.new
317
+ t = Tracker.new
318
+
319
+ line = __LINE__ + 4
320
+ th = Thread.new do
321
+ Thread.current[:thread_name] = :mybreakpoint
322
+ 5.times do
323
+ sleep( 0.1 )
324
+ end
325
+ end
326
+
327
+ begin
328
+ t.assert( '.bp_attach :mybreakpoint',
329
+ '=> nil', '',
330
+ 'Breakpoint 1 in TestRuntimeInspectionThread#test_attach ' +
331
+ "from #{__FILE__}:#{line} (line) in #{th}",
332
+ :no_newline )
333
+ ensure
334
+ t.assert( '.bp_stop', '=> nil' )
335
+ th.kill
336
+ end
337
+
338
+ r.kill
339
+ end
340
+
341
+ def test_client_close
342
+ r = RuntimeInspection::Thread.new
343
+ t = Tracker.new
344
+
345
+ # Have the client disconnect while waiting in the bp_start
346
+ # method.
347
+
348
+ t.assert( '.bp_add MyBreakpoint#mybreakpoint',
349
+ '=> "Added breakpoint 1"' )
350
+
351
+ th = Thread.new do
352
+ t.assert( '.bp_start' )
353
+ end
354
+
355
+ sleep 0.2
356
+
357
+ th.kill
358
+ t.close
359
+
360
+ def r.breakpoints
361
+ @breakpoints
362
+ end
363
+
364
+ def r.clients
365
+ @clients
366
+ end
367
+
368
+ sleep 0.2
369
+
370
+ assert_equal( {}, r.breakpoints )
371
+ assert_equal( [], r.clients )
372
+
373
+ found = {}
374
+ Thread.list.each do |th|
375
+ if th.kind_of?( RuntimeInspection::Thread )
376
+ found[:rti] = true
377
+ elsif name = th[:thread_name]
378
+ if name == :rti_gc
379
+ found[:rti_gc] = true
380
+ end
381
+ elsif th == Thread.main
382
+ found[:main] = true
383
+ else
384
+ flunk( "Found unknown thread (#{th.inspect}): " +
385
+ "#{th[:caller].inspect}" )
386
+ end
387
+ end
388
+ assert_equal( {:rti=>true, :rti_gc=>true, :main=>true}, found )
389
+
390
+ r.kill
391
+ end
392
+
162
393
  end
metadata CHANGED
@@ -3,19 +3,19 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: rtinspect
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2007-01-01 00:00:00 -08:00
6
+ version: 0.0.19
7
+ date: 2007-01-09 00:00:00 -08:00
8
8
  summary: A thread that will enable remote access capabilities in a running Ruby process for debugging and inspection.
9
9
  require_paths:
10
10
  - lib
11
11
  email: bradrf@gigglewax.com
12
12
  homepage: http://rtinspect.rubyforge.org/
13
- rubyforge_project:
13
+ rubyforge_project: rtinspect
14
14
  description: This is a class that will enable remote access capabilities in a running Ruby process, exposing the ability to enable breakpoints, walk code (e.g. step/next/continue), inspect variables, modify codepaths, and many other debugging actions.
15
15
  autorequire:
16
16
  default_executable:
17
17
  bindir: bin
18
- has_rdoc: false
18
+ has_rdoc: true
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
21
  - - ">"
@@ -30,11 +30,17 @@ authors:
30
30
  - Brad Robel-Forrest
31
31
  files:
32
32
  - lib/rtinspect.rb
33
+ - lib/rti/manager.rb
34
+ - lib/rti/redirect.rb
35
+ - lib/rti/state.rb
36
+ - lib/rti/thread.rb
37
+ - lib/rti/tracer.rb
33
38
  - test/test.rb
34
39
  - README
35
40
  - LICENSE
36
41
  - COPYING
37
42
  - TODO
43
+ - VERSION
38
44
  test_files:
39
45
  - test/test.rb
40
46
  rdoc_options: []
@@ -44,8 +50,9 @@ extra_rdoc_files:
44
50
  - LICENSE
45
51
  - COPYING
46
52
  - TODO
47
- executables: []
48
-
53
+ - VERSION
54
+ executables:
55
+ - rti
49
56
  extensions: []
50
57
 
51
58
  requirements: []