peeek 1.0.1

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.
@@ -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