peeek 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,82 @@
1
+ require 'peeek/hook/singleton'
2
+
3
+ def sample_singleton_linker(method_name = :quote)
4
+ Peeek::Hook::Singleton.new(Regexp, method_name)
5
+ end
6
+
7
+ describe Peeek::Hook::Singleton, '#method_prefix' do
8
+ it 'returns "."' do
9
+ linker = sample_singleton_linker
10
+ linker.method_prefix.should == '.'
11
+ end
12
+ end
13
+
14
+ describe Peeek::Hook::Singleton, '#defined?' do
15
+ it 'is true if the method is defined in the object' do
16
+ linker = sample_singleton_linker
17
+ linker.should be_defined
18
+ end
19
+
20
+ it 'is false if the method is not defined in the object' do
21
+ linker = sample_singleton_linker(:undefined_method)
22
+ linker.should_not be_defined
23
+ end
24
+ end
25
+
26
+ describe Peeek::Hook::Singleton, '#link' do
27
+ before do
28
+ @original_method = Regexp.method(:quote)
29
+ @linker = sample_singleton_linker
30
+ end
31
+
32
+ after do
33
+ @linker.unlink(@original_method)
34
+ end
35
+
36
+ it 'gives appropriate arguments to the block when calling the method' do
37
+ block_args = nil
38
+ @linker.link { |*args| block_args = args }
39
+ Regexp.quote('.')
40
+ block_args[0][0].should =~ %r(spec/peeek/hook/singleton_spec\.rb)
41
+ block_args[1].should == Regexp
42
+ block_args[2].should == ['.']
43
+ end
44
+
45
+ it 'is return value from the block as return value from the method' do
46
+ return_value = 'return_value'
47
+ @linker.link { |*args| return_value }
48
+ Regexp.quote('.').should equal(return_value)
49
+ end
50
+
51
+ it 'is exception from the block as exception from the method' do
52
+ @linker.link { |*args| raise 'exception' }
53
+ lambda { Regexp.quote('.') }.should raise_error('exception')
54
+ end
55
+
56
+ it 'returns the original method' do
57
+ @linker.link { }.should == @original_method
58
+ end
59
+
60
+ it 'raises ArgumentError if a block not given' do
61
+ lambda { @linker.link }.should raise_error(ArgumentError, 'block not supplied')
62
+ end
63
+ end
64
+
65
+ describe Peeek::Hook::Singleton, '#unlink' do
66
+ before do
67
+ @original_method = Regexp.method(:quote)
68
+ @linker = sample_singleton_linker
69
+ @linker.link { fail }
70
+ end
71
+
72
+ it 'defines method with the original method' do
73
+ call = lambda { Regexp.quote('.') }
74
+
75
+ # assert
76
+ call.should raise_error
77
+
78
+ @linker.unlink(@original_method)
79
+ call.should_not raise_error
80
+ call[].should == '\.'
81
+ end
82
+ end
@@ -0,0 +1,380 @@
1
+ require 'spec_helper'
2
+ require 'peeek/hook'
3
+
4
+ def sample_instance_hook(linker = nil)
5
+ Peeek::Hook.create(String, :%).tap do |hook|
6
+ hook.instance_variable_set(:@linker, linker) if linker
7
+ hook.instance_variable_set(:@calls, Array.new(5))
8
+ end
9
+ end
10
+
11
+ def sample_singleton_hook(linker = nil)
12
+ Peeek::Hook.create($stdout, :write).tap do |hook|
13
+ hook.instance_variable_set(:@linker, linker) if linker
14
+ hook.instance_variable_set(:@calls, Array.new(4))
15
+ end
16
+ end
17
+
18
+ describe Peeek::Hook, '.create' do
19
+ it "returns an instance of #{described_class}" do
20
+ hook = sample_instance_hook
21
+ hook.should be_a(described_class)
22
+ end
23
+
24
+ it "returns a hook that corresponds to the object and the method name" do
25
+ hook = sample_instance_hook
26
+ hook.object.should == String
27
+ hook.method_name.should == :%
28
+ end
29
+
30
+ context 'when specify implicitly' do
31
+ it 'returns a hook to instance method if given any module or any class' do
32
+ hook = described_class.create(String, :%)
33
+ hook.object.should == String
34
+ hook.method_name.should == :%
35
+ hook.should be_instance
36
+ end
37
+
38
+ it 'returns a hook to singleton method if given any instance' do
39
+ hook = described_class.create($stdout, :write)
40
+ hook.object.should == $stdout
41
+ hook.method_name.should == :write
42
+ hook.should be_singleton
43
+ end
44
+ end
45
+
46
+ context 'when specify instance method expressly' do
47
+ it 'returns a hook to instance method if given any module or any class' do
48
+ hook = described_class.create(String, '#%')
49
+ hook.object.should == String
50
+ hook.method_name.should == :%
51
+ hook.should be_instance
52
+ end
53
+
54
+ it 'raises ArgumentError if given any instance' do
55
+ lambda { described_class.create($stdout, '#write') }.should raise_error(ArgumentError, "can't create a hook of instance method to an instance of any class")
56
+ end
57
+ end
58
+
59
+ context 'when specify singleton method expressly' do
60
+ it 'returns a hook to singleton method if given any module or any class' do
61
+ hook = described_class.create(String, '.new')
62
+ hook.object.should == String
63
+ hook.method_name.should == :new
64
+ hook.should be_singleton
65
+ end
66
+
67
+ it 'returns a hook to singleton method if given any instance' do
68
+ hook = described_class.create($stdout, '.write')
69
+ hook.object.should == $stdout
70
+ hook.method_name.should == :write
71
+ hook.should be_singleton
72
+ end
73
+ end
74
+ end
75
+
76
+ describe Peeek::Hook, '.any_module?' do
77
+ it 'is true if any module given' do
78
+ described_class.should be_any_module(Enumerable)
79
+ end
80
+
81
+ it 'is true if any class given' do
82
+ described_class.should be_any_module(String)
83
+ end
84
+
85
+ it 'is false if an instance given' do
86
+ described_class.should_not be_any_module('Koyomi')
87
+ end
88
+ end
89
+
90
+ describe Peeek::Hook, '.any_instance?' do
91
+ it 'is true if an instance given' do
92
+ described_class.should be_any_instance('Koyomi')
93
+ end
94
+
95
+ it 'is false if any module given' do
96
+ described_class.should_not be_any_instance(Enumerable)
97
+ end
98
+
99
+ it 'is false if any class given' do
100
+ described_class.should_not be_any_instance(String)
101
+ end
102
+ end
103
+
104
+ describe Peeek::Hook, '#initialize' do
105
+ it 'raises ArgumentError if the linker class is invalid' do
106
+ lambda { described_class.new(String, :%, String) }.should raise_error(ArgumentError, 'invalid as linker class, Peeek::Hook::Instance or Peeek::Hook::Singleton are valid')
107
+ end
108
+ end
109
+
110
+ describe Peeek::Hook, '#object' do
111
+ it 'returns the value when constructed the hook' do
112
+ hook = described_class.new(String, :%, Peeek::Hook::Instance)
113
+ hook.object.should == String
114
+ end
115
+ end
116
+
117
+ describe Peeek::Hook, '#method_name' do
118
+ it 'returns the value when constructed the hook' do
119
+ hook = described_class.new(String, :%, Peeek::Hook::Instance)
120
+ hook.method_name.should == :%
121
+ end
122
+ end
123
+
124
+ describe Peeek::Hook, '#instance?' do
125
+ it 'is true if the hook is for instance method' do
126
+ hook = sample_instance_hook
127
+ hook.should be_instance
128
+ end
129
+
130
+ it 'is false if the hook is for singleton method' do
131
+ hook = sample_singleton_hook
132
+ hook.should_not be_instance
133
+ end
134
+ end
135
+
136
+ describe Peeek::Hook, '#singleton?' do
137
+ it 'is true if the hook is for singleton method' do
138
+ hook = sample_singleton_hook
139
+ hook.should be_singleton
140
+ end
141
+
142
+ it 'is false if the hook is for instance method' do
143
+ hook = sample_instance_hook
144
+ hook.should_not be_singleton
145
+ end
146
+ end
147
+
148
+ describe Peeek::Hook, '#defined?' do
149
+ before do
150
+ @linker, original_method = instance_linker_stub(String, :%)
151
+ @hook = sample_instance_hook(@linker)
152
+ end
153
+
154
+ it "calls #{described_class}::Linker#defined?" do
155
+ @linker.should_receive(:defined?)
156
+ @hook.defined?
157
+ end
158
+
159
+ it "returns return value from #{described_class}::Linker#defined?" do
160
+ @linker.stub!(:defined? => true)
161
+ return_value = @hook.defined?
162
+ return_value.should == true
163
+ end
164
+ end
165
+
166
+ describe Peeek::Hook, '#link' do
167
+ before do
168
+ @linker, @original_method = instance_linker_stub(String, :%)
169
+ @hook = sample_instance_hook(@linker)
170
+ end
171
+
172
+ after do
173
+ @hook.unlink
174
+ end
175
+
176
+ it "calls #{described_class}::Linker#link with the block" do
177
+ @linker.should_receive(:link).with { }.and_return do |&block|
178
+ block.arity.should == 3
179
+ @original_method
180
+ end
181
+
182
+ @hook.link
183
+ end
184
+
185
+ it 'links the hook to the method' do
186
+ @hook.should_not be_linked # assert
187
+ @hook.link
188
+ @hook.should be_linked
189
+ end
190
+
191
+ it 'returns self' do
192
+ @hook.link.should equal(@hook)
193
+ end
194
+
195
+ context 'in linked' do
196
+ before do
197
+ @linker, original_method = instance_linker_stub(String, :%)
198
+ @hook = sample_instance_hook(@linker)
199
+ @hook.link
200
+ end
201
+
202
+ after do
203
+ @hook.unlink
204
+ end
205
+
206
+ it "doesn't call #{described_class}::Linker#link" do
207
+ @linker.should_not_receive(:link)
208
+ @hook.link
209
+ end
210
+
211
+ it 'returns self' do
212
+ @hook.link.should equal(@hook)
213
+ end
214
+ end
215
+ end
216
+
217
+ describe Peeek::Hook, '#unlink' do
218
+ before do
219
+ @linker, @original_method = instance_linker_stub(String, :%)
220
+ @hook = sample_instance_hook(@linker)
221
+ @hook.link
222
+ end
223
+
224
+ it "calls #{described_class}::Linker#unlink with the original method" do
225
+ @linker.should_receive(:unlink).with(@original_method)
226
+ @hook.unlink
227
+ end
228
+
229
+ it 'unlinks the hook from the method' do
230
+ @hook.should be_linked # assert
231
+ @hook.unlink
232
+ @hook.should_not be_linked
233
+ end
234
+
235
+ it 'returns self' do
236
+ @hook.unlink.should equal(@hook)
237
+ end
238
+
239
+ context 'in not linked' do
240
+ before do
241
+ @linker, original_method = instance_linker_stub(String, :%)
242
+ @hook = sample_instance_hook(@linker)
243
+ end
244
+
245
+ it "doesn't call #{described_class}::Linker#unlink" do
246
+ @linker.should_not_receive(:unlink)
247
+ @hook.unlink
248
+ end
249
+
250
+ it 'returns self' do
251
+ @hook.unlink.should equal(@hook)
252
+ end
253
+ end
254
+ end
255
+
256
+ describe Peeek::Hook, '#to_s' do
257
+ context 'for instance method' do
258
+ it 'returns the stringified hook' do
259
+ hook = sample_instance_hook
260
+ hook.to_s.should == 'String#%'
261
+ end
262
+ end
263
+
264
+ context 'for singleton method' do
265
+ it 'returns the stringified hook' do
266
+ hook = sample_singleton_hook
267
+ hook.to_s.should == "#{$stdout.inspect}.write"
268
+ end
269
+ end
270
+ end
271
+
272
+ describe Peeek::Hook, '#inspect' do
273
+ context 'for instance method' do
274
+ it 'inspects the hook' do
275
+ hook = sample_instance_hook
276
+ hook.inspect.should == "#<#{described_class} String#%>"
277
+ end
278
+ end
279
+
280
+ context 'for singleton method' do
281
+ it 'inspects the hook' do
282
+ hook = sample_singleton_hook
283
+ hook.inspect.should == "#<#{described_class} #{$stdout.inspect}.write>"
284
+ end
285
+ end
286
+
287
+ context 'in linked' do
288
+ before do
289
+ @hook = sample_instance_hook
290
+ @hook.link
291
+ end
292
+
293
+ after do
294
+ @hook.unlink
295
+ end
296
+
297
+ it 'inspects the hook' do
298
+ @hook.inspect.should == "#<#{described_class} String#% (linked)>"
299
+ end
300
+ end
301
+ end
302
+
303
+ describe 'recording of a call by', Peeek::Hook do
304
+ before do
305
+ @hook = Peeek::Hook.create(String, :%)
306
+ @hook.link
307
+ end
308
+
309
+ after do
310
+ @hook.unlink
311
+ end
312
+
313
+ it 'sets attributes to the call' do
314
+ line = __LINE__; '%s (%d)' % ['Koyomi', 18]
315
+ call = @hook.calls.first
316
+ call.file.should == __FILE__
317
+ call.line.should == line
318
+ call.receiver.should == '%s (%d)'
319
+ call.arguments.should == [['Koyomi', 18]]
320
+ end
321
+
322
+ context 'if a value returned' do
323
+ it 'sets the return value to the call' do
324
+ '%s (%d)' % ['Koyomi', 18]
325
+ call = @hook.calls.first
326
+ call.should be_returned
327
+ call.return_value.should == 'Koyomi (18)'
328
+ end
329
+
330
+ it 'returns same value when called the method' do
331
+ return_value = '%s (%d)' % ['Koyomi', 18]
332
+ call = @hook.calls.first
333
+ return_value.should equal(call.return_value)
334
+ end
335
+ end
336
+
337
+ context 'if an exception raised' do
338
+ it 'sets the exception to the call' do
339
+ '%s (%d)' % ['Koyomi'] rescue
340
+ call = @hook.calls.first
341
+ call.should be_raised
342
+ call.exception.should be_an(ArgumentError)
343
+ call.exception.message.should == 'too few arguments'
344
+ end
345
+
346
+ it 'raises same exception when called the method' do
347
+ exception = '%s (%d)' % ['Koyomi'] rescue $!
348
+ call = @hook.calls.first
349
+ exception.should equal(call.exception)
350
+ end
351
+
352
+ it 'raises the exception with valid backtrace' do
353
+ line = __LINE__; exception = '%s (%d)' % ['Koyomi'] rescue $!
354
+ exception.backtrace.first.should be_start_with("#{__FILE__}:#{line}")
355
+ end
356
+ end
357
+
358
+ context 'with a block' do
359
+ before do
360
+ @hook.unlink
361
+ end
362
+
363
+ it 'calls the block with the call' do
364
+ line = nil
365
+
366
+ hook = Peeek::Hook.create(String, :%) do |call|
367
+ call.file.should == __FILE__
368
+ call.line.should == line
369
+ call.receiver.should == '%s (%d)'
370
+ call.arguments.should == [['Koyomi', 18]]
371
+ call.should be_returned
372
+ call.return_value.should == 'Koyomi (18)'
373
+ end
374
+
375
+ hook.link
376
+ line = __LINE__; '%s (%d)' % ['Koyomi', 18]
377
+ hook.unlink
378
+ end
379
+ end
380
+ end