pry-stack_explorer 0.6.1 → 0.6.2
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.
- checksums.yaml +4 -4
- data/.gemtest +0 -0
- data/.gitignore +9 -0
- data/.rspec +5 -0
- data/.travis.yml +25 -0
- data/.yardopts +1 -0
- data/CHANGELOG +22 -0
- data/Gemfile +2 -0
- data/Rakefile +114 -0
- data/bin/rspec +29 -0
- data/examples/example.rb +45 -0
- data/examples/example2.rb +18 -0
- data/examples/example3.rb +46 -0
- data/lib/pry-stack_explorer/version.rb +1 -1
- data/pry-stack_explorer.gemspec +26 -0
- data/test/commands_test.rb +353 -0
- data/test/frame_manager_test.rb +68 -0
- data/test/stack_explorer_test.rb +374 -0
- data/test/support/bingbong.rb +11 -0
- data/test/support/input_tester.rb +17 -0
- data/test/support/io_utils.rb +49 -0
- data/test/support/reset_helper.rb +57 -0
- data/test/test_helper.rb +27 -0
- metadata +40 -19
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
require_relative 'test_helper'
|
|
2
|
+
|
|
3
|
+
class Top
|
|
4
|
+
attr_accessor :method_list, :middle
|
|
5
|
+
def initialize method_list
|
|
6
|
+
@method_list = method_list
|
|
7
|
+
end
|
|
8
|
+
def bing
|
|
9
|
+
@middle = Middle.new method_list
|
|
10
|
+
@middle.bong
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Middle
|
|
15
|
+
attr_accessor :method_list, :bottom
|
|
16
|
+
def initialize method_list
|
|
17
|
+
@method_list = method_list
|
|
18
|
+
end
|
|
19
|
+
def bong
|
|
20
|
+
@bottom = Bottom.new method_list
|
|
21
|
+
@bottom.bang
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Bottom
|
|
26
|
+
attr_accessor :method_list
|
|
27
|
+
def initialize method_list
|
|
28
|
+
@method_list = method_list
|
|
29
|
+
end
|
|
30
|
+
def bang
|
|
31
|
+
Pry.start(binding)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
describe "Commands" do
|
|
37
|
+
let(:bingbong){ BingBong.new }
|
|
38
|
+
|
|
39
|
+
include ResetHelper
|
|
40
|
+
|
|
41
|
+
before do
|
|
42
|
+
method_list = []
|
|
43
|
+
@top = Top.new method_list
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
describe "stack" do
|
|
48
|
+
it "outputs the call stack" do
|
|
49
|
+
output = issue_pry_commands("stack"){ bingbong.bing }
|
|
50
|
+
|
|
51
|
+
expect(output).to match(/bang.*?bong.*?bing/m)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "supports 'show-stack' as an alias" do
|
|
55
|
+
output = issue_pry_commands("show-stack"){ bingbong.bing }
|
|
56
|
+
|
|
57
|
+
expect(output).to match(/bang.*?bong.*?bing/m)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe "up" do
|
|
62
|
+
it 'should move up the call stack one frame at a time' do
|
|
63
|
+
redirect_pry_io(InputTester.new("@methods << __method__",
|
|
64
|
+
"up",
|
|
65
|
+
"@methods << __method__",
|
|
66
|
+
"up",
|
|
67
|
+
"@methods << __method__",
|
|
68
|
+
"exit-all"), out=StringIO.new) do
|
|
69
|
+
bingbong.bing
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
expect(bingbong.methods).to eq [:bang, :bong, :bing]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'should move up the call stack two frames at a time' do
|
|
76
|
+
redirect_pry_io(InputTester.new("@methods << __method__",
|
|
77
|
+
"up 2",
|
|
78
|
+
"@methods << __method__",
|
|
79
|
+
"exit-all"), out=StringIO.new) do
|
|
80
|
+
bingbong.bing
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
expect(bingbong.methods).to eq [:bang, :bing]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe "by method name regex" do
|
|
87
|
+
it 'should move to the method name that matches the regex' do
|
|
88
|
+
redirect_pry_io(InputTester.new("@methods << __method__",
|
|
89
|
+
"up bi",
|
|
90
|
+
"@methods << __method__",
|
|
91
|
+
"exit-all"), out=StringIO.new) do
|
|
92
|
+
bingbong.bing
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
expect(bingbong.methods).to eq [:bang, :bing]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'should move through all methods that match regex in order' do
|
|
99
|
+
redirect_pry_io(InputTester.new("@methods << __method__",
|
|
100
|
+
"up b",
|
|
101
|
+
"@methods << __method__",
|
|
102
|
+
"up b",
|
|
103
|
+
"@methods << __method__",
|
|
104
|
+
"exit-all"), out=StringIO.new) do
|
|
105
|
+
bingbong.bing
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
expect(bingbong.methods).to eq [:bang, :bong, :bing]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'should error if it cant find frame to match regex' do
|
|
112
|
+
redirect_pry_io(InputTester.new("up conrad_irwin",
|
|
113
|
+
"exit-all"), out=StringIO.new) do
|
|
114
|
+
bingbong.bing
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
expect(out.string).to match(/Error: No frame that matches/)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
describe 'by Class#method name regex' do
|
|
123
|
+
it 'should move to the method and class that matches the regex' do
|
|
124
|
+
redirect_pry_io(InputTester.new("@method_list << self.class.to_s + '#' + __method__.to_s",
|
|
125
|
+
'up Middle#bong',
|
|
126
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
|
127
|
+
"exit-all"), out=StringIO.new) do
|
|
128
|
+
@top.bing
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
expect(@top.method_list).to eq(['Bottom#bang', 'Middle#bong'])
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
### ????? ###
|
|
135
|
+
# it 'should be case sensitive' do
|
|
136
|
+
# end
|
|
137
|
+
### ????? ###
|
|
138
|
+
|
|
139
|
+
it 'should allow partial class names' do
|
|
140
|
+
redirect_pry_io(InputTester.new("@method_list << self.class.to_s + '#' + __method__.to_s",
|
|
141
|
+
'up Mid#bong',
|
|
142
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
|
143
|
+
"exit-all"), out=StringIO.new) do
|
|
144
|
+
@top.bing
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
expect(@top.method_list).to eq(['Bottom#bang', 'Middle#bong'])
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'should allow partial method names' do
|
|
151
|
+
redirect_pry_io(InputTester.new("@method_list << self.class.to_s + '#' + __method__.to_s",
|
|
152
|
+
'up Middle#bo',
|
|
153
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
|
154
|
+
"exit-all"), out=StringIO.new) do
|
|
155
|
+
@top.bing
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
expect(@top.method_list).to eq(['Bottom#bang', 'Middle#bong'])
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it 'should error if it cant find frame to match regex' do
|
|
162
|
+
redirect_pry_io(InputTester.new('up Conrad#irwin',
|
|
163
|
+
"exit-all"), out=StringIO.new) do
|
|
164
|
+
@top.bing
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
expect(out.string).to match(/Error: No frame that matches/)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
describe "down" do
|
|
173
|
+
it 'should move down the call stack one frame at a time' do
|
|
174
|
+
def bingbong.bang() Pry.start(binding, :initial_frame => 1) end
|
|
175
|
+
|
|
176
|
+
redirect_pry_io(InputTester.new("@methods << __method__",
|
|
177
|
+
"down",
|
|
178
|
+
"@methods << __method__",
|
|
179
|
+
"exit-all"), out=StringIO.new) do
|
|
180
|
+
bingbong.bing
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
expect(bingbong.methods).to eq [:bong, :bang]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it 'should move down the call stack two frames at a time' do
|
|
187
|
+
def bingbong.bang() Pry.start(binding, :initial_frame => 2) end
|
|
188
|
+
|
|
189
|
+
redirect_pry_io(InputTester.new("@methods << __method__",
|
|
190
|
+
"down 2",
|
|
191
|
+
"@methods << __method__",
|
|
192
|
+
"exit-all"), out=StringIO.new) do
|
|
193
|
+
bingbong.bing
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
expect(bingbong.methods).to eq [:bing, :bang]
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
describe "by method name regex" do
|
|
200
|
+
it 'should move to the method name that matches the regex' do
|
|
201
|
+
redirect_pry_io(InputTester.new("frame -1",
|
|
202
|
+
"down bo",
|
|
203
|
+
"@methods << __method__",
|
|
204
|
+
"exit-all"), out=StringIO.new) do
|
|
205
|
+
bingbong.bing
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
expect(bingbong.methods[0]).to eq(:bong)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it 'should move through all methods that match regex in order' do
|
|
212
|
+
redirect_pry_io(InputTester.new("frame bing",
|
|
213
|
+
"@methods << __method__",
|
|
214
|
+
"down b",
|
|
215
|
+
"@methods << __method__",
|
|
216
|
+
"down b",
|
|
217
|
+
"@methods << __method__",
|
|
218
|
+
"exit-all"), out=StringIO.new) do
|
|
219
|
+
bingbong.bing
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
expect(bingbong.methods).to eq [:bing, :bong, :bang]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it 'should error if it cant find frame to match regex' do
|
|
226
|
+
redirect_pry_io(InputTester.new("frame -1",
|
|
227
|
+
"down conrad_irwin",
|
|
228
|
+
"exit-all"), out=StringIO.new) do
|
|
229
|
+
bingbong.bing
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
expect(out.string).to match(/Error: No frame that matches/)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
describe 'by Class#method name regex' do
|
|
237
|
+
it 'should move to the method and class that matches the regex' do
|
|
238
|
+
redirect_pry_io(InputTester.new('frame Top#bing',
|
|
239
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
|
240
|
+
'down Middle#bong',
|
|
241
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
|
242
|
+
"exit-all"), out=StringIO.new) do
|
|
243
|
+
@top.bing
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
expect(@top.method_list).to eq(['Top#bing', 'Middle#bong'])
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
### ????? ###
|
|
250
|
+
# it 'should be case sensitive' do
|
|
251
|
+
# end
|
|
252
|
+
### ????? ###
|
|
253
|
+
|
|
254
|
+
it 'should error if it cant find frame to match regex' do
|
|
255
|
+
redirect_pry_io(InputTester.new('down Conrad#irwin',
|
|
256
|
+
"exit-all"), out=StringIO.new) do
|
|
257
|
+
@top.bing
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
expect(out.string).to match(/Error: No frame that matches/)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
describe "frame" do
|
|
267
|
+
describe "by method name regex" do
|
|
268
|
+
it 'should jump to correct stack frame when given method name' do
|
|
269
|
+
redirect_pry_io(InputTester.new("frame bi",
|
|
270
|
+
"@methods << __method__",
|
|
271
|
+
"exit-all"), out=StringIO.new) do
|
|
272
|
+
bingbong.bing
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
expect(bingbong.methods[0]).to eq(:bing)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
it 'should NOT jump to frames lower down stack when given method name' do
|
|
279
|
+
redirect_pry_io(InputTester.new("frame -1",
|
|
280
|
+
"frame bang",
|
|
281
|
+
"exit-all"), out=StringIO.new) do
|
|
282
|
+
bingbong.bing
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
expect(out.string).to match(/Error: No frame that matches/)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
it 'should move to the given frame in the call stack' do
|
|
291
|
+
redirect_pry_io(InputTester.new("frame 2",
|
|
292
|
+
"@methods << __method__",
|
|
293
|
+
"exit-all"), out=StringIO.new) do
|
|
294
|
+
bingbong.bing
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
expect(bingbong.methods[0]).to eq(:bing)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it 'should return info on current frame when given no parameters' do
|
|
301
|
+
redirect_pry_io(InputTester.new("frame",
|
|
302
|
+
"exit-all"), out=StringIO.new) do
|
|
303
|
+
bingbong.bing
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
expect(out.string).to match(/\#0.*?bang/)
|
|
307
|
+
expect(out.string).not_to match(/\#1/)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
describe "negative indices" do
|
|
311
|
+
class AlphaBetaGamma
|
|
312
|
+
attr_accessor :frame, :frame_number
|
|
313
|
+
|
|
314
|
+
def alpha; binding; end
|
|
315
|
+
def beta; binding; end
|
|
316
|
+
def gamma; binding; end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
let(:alphabetagamma){ AlphaBetaGamma.new }
|
|
320
|
+
|
|
321
|
+
it 'should work with negative frame numbers' do
|
|
322
|
+
o = AlphaBetaGamma.new
|
|
323
|
+
|
|
324
|
+
call_stack = [o.alpha, o.beta, o.gamma]
|
|
325
|
+
method_names = call_stack.map { |v| v.eval('__method__') }.reverse
|
|
326
|
+
(1..3).each_with_index do |v, idx|
|
|
327
|
+
redirect_pry_io(InputTester.new("frame -#{v}",
|
|
328
|
+
"@frame = __method__",
|
|
329
|
+
"exit-all"), out=StringIO.new) do
|
|
330
|
+
Pry.start(o, :call_stack => call_stack)
|
|
331
|
+
end
|
|
332
|
+
expect(o.frame).to eq(method_names[idx])
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
it 'should convert negative indices to their positive counterparts' do
|
|
337
|
+
o = AlphaBetaGamma.new
|
|
338
|
+
|
|
339
|
+
call_stack = [o.alpha, o.beta, o.gamma]
|
|
340
|
+
|
|
341
|
+
(1..3).each_with_index do |v, idx|
|
|
342
|
+
issue_pry_commands(
|
|
343
|
+
"frame -#{v}",
|
|
344
|
+
"@frame_number = PryStackExplorer.frame_manager(pry_instance).binding_index",
|
|
345
|
+
"exit-all"
|
|
346
|
+
){ Pry.start(o, call_stack: call_stack) }
|
|
347
|
+
|
|
348
|
+
expect(o.frame_number).to eq(call_stack.size - v)
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require_relative 'test_helper'
|
|
2
|
+
|
|
3
|
+
describe PryStackExplorer::FrameManager do
|
|
4
|
+
before :all do
|
|
5
|
+
redirect_pry_output!
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
@pry_instance = Pry.new
|
|
10
|
+
@bindings = [binding, binding, binding, binding]
|
|
11
|
+
@bindings.each_with_index { |v, i| v.eval("x = #{i}") }
|
|
12
|
+
@pry_instance.binding_stack.push @bindings.last
|
|
13
|
+
@frame_manager = PE::FrameManager.new(@bindings, @pry_instance)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "creation" do
|
|
17
|
+
it "should make bindings accessible via 'bindings' method" do
|
|
18
|
+
expect(@frame_manager.bindings).to eq(@bindings)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should set binding_index to 0" do
|
|
22
|
+
expect(@frame_manager.binding_index).to eq(0)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should set current_frame to first frame" do
|
|
26
|
+
expect(@frame_manager.current_frame).to eq(@bindings.first)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "FrameManager#change_frame_to" do
|
|
31
|
+
it 'should change the frame to the given one' do
|
|
32
|
+
@frame_manager.change_frame_to(1)
|
|
33
|
+
|
|
34
|
+
expect(@frame_manager.binding_index).to eq(1)
|
|
35
|
+
expect(@frame_manager.current_frame).to eq(@bindings[1])
|
|
36
|
+
expect(@pry_instance.binding_stack.last).to eq(@frame_manager.current_frame)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'should accept negative indices when specifying frame' do
|
|
40
|
+
@frame_manager.change_frame_to(-1)
|
|
41
|
+
|
|
42
|
+
# negative index is converted to a positive one inside change_frame_to
|
|
43
|
+
expect(@frame_manager.binding_index).to eq(@bindings.size - 1)
|
|
44
|
+
expect(@frame_manager.current_frame).to eq(@bindings[-1])
|
|
45
|
+
expect(@pry_instance.binding_stack.last).to eq(@frame_manager.current_frame)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe "FrameManager#refresh_frame" do
|
|
50
|
+
it 'should change the Pry frame to the active one in the FrameManager' do
|
|
51
|
+
@frame_manager.binding_index = 2
|
|
52
|
+
@frame_manager.refresh_frame
|
|
53
|
+
|
|
54
|
+
expect(@pry_instance.binding_stack.last).to eq(@frame_manager.current_frame)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe "FrameManager is Enumerable" do
|
|
59
|
+
it 'should perform an Enumerable#map on the frames' do
|
|
60
|
+
result = @frame_manager.map { |v| v.eval("x") }
|
|
61
|
+
|
|
62
|
+
expect(result).to eq(
|
|
63
|
+
(0..(@bindings.size - 1)).to_a
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|