pretentious 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -5
- data/bin/ddtgen +13 -19
- data/lib/pretentious.rb +31 -31
- data/lib/pretentious/context.rb +86 -0
- data/lib/pretentious/deconstructor.rb +305 -368
- data/lib/pretentious/generator.rb +122 -149
- data/lib/pretentious/generator_base.rb +70 -9
- data/lib/pretentious/minitest_generator.rb +172 -288
- data/lib/pretentious/recorded_proc.rb +4 -16
- data/lib/pretentious/rspec_generator.rb +151 -262
- data/lib/pretentious/trigger.rb +30 -30
- data/lib/pretentious/version.rb +2 -1
- data/pretentious.gemspec +3 -0
- data/run_test.sh +1 -1
- data/spec/deconstructor_spec.rb +34 -20
- data/spec/fibonacci_spec.rb +7 -14
- data/spec/generator_spec.rb +1 -1
- data/spec/m_d5_spec.rb +3 -7
- data/spec/minitest_generator_spec.rb +2 -2
- data/spec/prententious_spec.rb +14 -4
- data/spec/spec_helper.rb +3 -1
- data/spec/test_class1_spec.rb +23 -38
- data/spec/test_class2_spec.rb +7 -16
- data/spec/test_class3_spec.rb +11 -18
- data/spec/test_class4_spec.rb +4 -7
- data/spec/test_class_for_auto_stub_spec.rb +5 -10
- data/spec/test_class_for_mocks_spec.rb +10 -19
- data/test/test_fibonacci.rb +72 -10
- data/test/test_m_d5.rb +4 -5
- data/test/test_meme.rb +6 -6
- data/test/test_test_class1.rb +36 -29
- data/test/test_test_class2.rb +16 -12
- data/test/test_test_class3.rb +15 -17
- data/test/test_test_class4.rb +6 -7
- data/test/test_test_class_for_mocks.rb +19 -16
- metadata +46 -4
@@ -1,4 +1,5 @@
|
|
1
1
|
module Pretentious
|
2
|
+
# A class that generates specs by analyzing how an object is used
|
2
3
|
class Generator
|
3
4
|
|
4
5
|
def self.test_generator=(generator)
|
@@ -10,24 +11,24 @@ module Pretentious
|
|
10
11
|
end
|
11
12
|
|
12
13
|
def self.impostor_for(module_space, klass)
|
13
|
-
|
14
|
+
new_standin_klass = Class.new
|
14
15
|
name = klass.name
|
15
16
|
|
16
|
-
#return if already an impostor
|
17
|
-
return klass if
|
17
|
+
# return if already an impostor
|
18
|
+
return klass if klass.respond_to?(:test_class)
|
18
19
|
|
19
|
-
module_space.const_set "#{name.split('::').last}Impostor",
|
20
|
+
module_space.const_set "#{name.split('::').last}Impostor", new_standin_klass
|
20
21
|
|
21
|
-
|
22
|
+
new_standin_klass.class_eval("
|
22
23
|
def setup_instance(*args, &block)
|
23
24
|
@_instance = #{klass.name}_ddt.new(*args, &block)
|
24
25
|
end
|
25
26
|
")
|
26
27
|
|
27
|
-
|
28
|
+
new_standin_klass.class_eval("
|
28
29
|
class << self
|
29
30
|
def _get_standin_class
|
30
|
-
#{
|
31
|
+
#{new_standin_klass}
|
31
32
|
end
|
32
33
|
|
33
34
|
def test_class
|
@@ -40,26 +41,18 @@ module Pretentious
|
|
40
41
|
end
|
41
42
|
")
|
42
43
|
|
43
|
-
|
44
|
-
|
44
|
+
new_standin_klass.class_exec do
|
45
45
|
def initialize(*args, &block)
|
46
|
-
|
47
|
-
@_instance_init = {object_id: self.object_id, params: [], block: nil}
|
46
|
+
@_instance_init = { object_id: object_id, params: [], block: nil }
|
48
47
|
|
49
48
|
self.class.replace_procs_with_recorders(args)
|
50
49
|
|
51
50
|
@_instance_init[:params] = args
|
51
|
+
recorded_proc = block ? RecordedProc.new(block, true) : nil
|
52
52
|
|
53
|
+
@_instance_init[:block] = recorded_proc
|
53
54
|
|
54
|
-
|
55
|
-
RecordedProc.new(block, true)
|
56
|
-
else
|
57
|
-
nil
|
58
|
-
end
|
59
|
-
|
60
|
-
@_instance_init[:block] = recordedProc
|
61
|
-
|
62
|
-
setup_instance(*args, &recordedProc)
|
55
|
+
setup_instance(*args, &recorded_proc)
|
63
56
|
param_types = @_instance.method(:initialize).parameters
|
64
57
|
@_instance_init[:params_types] = param_types
|
65
58
|
|
@@ -68,20 +61,19 @@ module Pretentious
|
|
68
61
|
@_methods_for_test = []
|
69
62
|
@_let_variables = {}
|
70
63
|
|
71
|
-
|
72
64
|
@_init_let_variables = {}
|
73
65
|
|
74
66
|
caller_context = binding.of_caller(2)
|
75
67
|
v_locals = caller_context.eval('local_variables')
|
76
68
|
|
77
|
-
v_locals.each
|
69
|
+
v_locals.each do |v|
|
78
70
|
variable_value = caller_context.eval("#{v.to_s}")
|
79
71
|
@_init_let_variables[variable_value.object_id] = v
|
80
|
-
|
72
|
+
end
|
81
73
|
|
82
|
-
args.each_with_index
|
74
|
+
args.each_with_index do |a, index|
|
83
75
|
@_init_let_variables[a.object_id] = param_types[index][1].to_s if param_types.size == 2
|
84
|
-
|
76
|
+
end
|
85
77
|
|
86
78
|
self.class._add_instances(self)
|
87
79
|
end
|
@@ -95,7 +87,7 @@ module Pretentious
|
|
95
87
|
end
|
96
88
|
|
97
89
|
def include_for_tests(method_list = [])
|
98
|
-
@_methods_for_test
|
90
|
+
@_methods_for_test += method_list
|
99
91
|
end
|
100
92
|
|
101
93
|
def let_variables
|
@@ -119,11 +111,11 @@ module Pretentious
|
|
119
111
|
end
|
120
112
|
|
121
113
|
def ==(other)
|
122
|
-
@_instance==other
|
114
|
+
@_instance == other
|
123
115
|
end
|
124
116
|
|
125
117
|
def kind_of?(klass)
|
126
|
-
@_instance.
|
118
|
+
@_instance.is_a? klass
|
127
119
|
end
|
128
120
|
|
129
121
|
def methods
|
@@ -158,7 +150,7 @@ module Pretentious
|
|
158
150
|
|
159
151
|
def replace_procs_with_recorders(args)
|
160
152
|
(0..args.size).each do |index|
|
161
|
-
if
|
153
|
+
if args[index].kind_of? Proc
|
162
154
|
args[index] = Pretentious::RecordedProc.new(args[index]) {}
|
163
155
|
end
|
164
156
|
end
|
@@ -169,12 +161,12 @@ module Pretentious
|
|
169
161
|
end
|
170
162
|
|
171
163
|
def _is_stub?
|
172
|
-
@_is_stub
|
164
|
+
@_is_stub ||= false
|
173
165
|
@_is_stub
|
174
166
|
end
|
175
167
|
|
176
168
|
def _add_instances(instance)
|
177
|
-
@_instances
|
169
|
+
@_instances ||= []
|
178
170
|
@_instances << instance unless @_instances.include? instance
|
179
171
|
end
|
180
172
|
|
@@ -191,7 +183,7 @@ module Pretentious
|
|
191
183
|
end
|
192
184
|
|
193
185
|
def _add_instances(instance)
|
194
|
-
@_instances
|
186
|
+
@_instances ||= []
|
195
187
|
@_instances << instance unless @_instances.include? instance
|
196
188
|
end
|
197
189
|
|
@@ -205,18 +197,18 @@ module Pretentious
|
|
205
197
|
end
|
206
198
|
|
207
199
|
def _call_method(target, method_sym, *arguments, &block)
|
208
|
-
|
209
200
|
klass = nil
|
210
201
|
begin
|
211
202
|
klass = _get_standin_class
|
212
203
|
rescue NameError=>e
|
213
204
|
result = nil
|
214
205
|
target.instance_exec do
|
215
|
-
result = if
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
206
|
+
result = if @_instance.methods.include? method_sym
|
207
|
+
@_instance.send(method_sym, *arguments, &block)
|
208
|
+
else
|
209
|
+
@_instance.send(:method_missing, method_sym,
|
210
|
+
*arguments, &block)
|
211
|
+
end
|
220
212
|
end
|
221
213
|
return result
|
222
214
|
end
|
@@ -226,95 +218,85 @@ module Pretentious
|
|
226
218
|
is_stub = _is_stub?
|
227
219
|
|
228
220
|
target.instance_exec do
|
229
|
-
@_method_calls
|
230
|
-
@_method_calls_by_method
|
231
|
-
@_methods_for_test
|
232
|
-
@_let_variables
|
221
|
+
@_method_calls ||= []
|
222
|
+
@_method_calls_by_method ||= {}
|
223
|
+
@_methods_for_test ||= []
|
224
|
+
@_let_variables ||= {}
|
233
225
|
|
234
226
|
v_locals = caller_context.eval('local_variables')
|
235
227
|
|
236
|
-
v_locals.each
|
237
|
-
variable_value = caller_context.eval("#{v
|
238
|
-
|
239
|
-
|
228
|
+
v_locals.each do |v|
|
229
|
+
variable_value = caller_context.eval("#{v}")
|
230
|
+
@_let_variables[variable_value.object_id] = v
|
231
|
+
end
|
240
232
|
|
241
|
-
|
233
|
+
klass.replace_procs_with_recorders(arguments)
|
242
234
|
|
243
|
-
|
244
|
-
|
245
|
-
|
235
|
+
info_block = {}
|
236
|
+
info_block[:method] = method_sym
|
237
|
+
info_block[:params] = arguments
|
246
238
|
|
247
|
-
|
248
|
-
RecordedProc.new(block, true)
|
249
|
-
else
|
250
|
-
nil
|
251
|
-
end
|
252
|
-
info_block[:block] = recordedProc
|
239
|
+
recorded_proc = block ? RecordedProc.new(block, true) : nil
|
253
240
|
|
254
|
-
|
241
|
+
info_block[:block] = recorded_proc
|
255
242
|
|
256
|
-
|
243
|
+
info_block[:names] = @_instance.method(method_sym).parameters
|
257
244
|
|
258
|
-
|
259
|
-
current_context = { calls: [] }
|
260
|
-
info_block[:context] = current_context
|
245
|
+
begin
|
261
246
|
|
262
|
-
|
263
|
-
|
247
|
+
unless is_stub
|
248
|
+
current_context = { calls: [] }
|
249
|
+
info_block[:context] = current_context
|
264
250
|
|
265
|
-
|
266
|
-
|
267
|
-
else
|
268
|
-
result = @_instance.send(:method_missing, method_sym, *arguments, &recordedProc)
|
269
|
-
end
|
251
|
+
Thread.current._push_context(current_context)
|
252
|
+
end
|
270
253
|
|
271
|
-
|
254
|
+
if @_instance.methods.include? method_sym
|
255
|
+
result = @_instance.send(method_sym, *arguments, &recorded_proc)
|
256
|
+
else
|
257
|
+
result = @_instance.send(:method_missing, method_sym, *arguments, &recorded_proc)
|
258
|
+
end
|
272
259
|
|
273
|
-
|
274
|
-
if method_sym.to_s.end_with? '='
|
275
|
-
info_block[:result] = arguments[0]
|
276
|
-
else
|
277
|
-
info_block[:result] = result
|
278
|
-
end
|
260
|
+
Thread.current._pop_context unless is_stub
|
279
261
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
262
|
+
# methods that end with = are a special case with return values
|
263
|
+
if method_sym.to_s.end_with? '='
|
264
|
+
info_block[:result] = arguments[0]
|
265
|
+
else
|
266
|
+
info_block[:result] = result
|
284
267
|
end
|
268
|
+
rescue StandardError => e
|
269
|
+
info_block[:result] = e
|
270
|
+
end
|
285
271
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
}
|
272
|
+
if is_stub
|
273
|
+
info_block[:class] = test_class
|
274
|
+
Thread.current._all_context.each do |mock_context|
|
275
|
+
mock_context[:calls] << info_block if mock_context
|
291
276
|
end
|
277
|
+
end
|
292
278
|
|
293
|
-
|
279
|
+
@_method_calls << info_block
|
294
280
|
|
295
|
-
|
296
|
-
|
297
|
-
|
281
|
+
if @_method_calls_by_method[method_sym].nil?
|
282
|
+
@_method_calls_by_method[method_sym] = []
|
283
|
+
end
|
298
284
|
|
299
|
-
|
300
|
-
|
301
|
-
|
285
|
+
@_method_calls_by_method[method_sym] << info_block
|
286
|
+
fail e if e.is_a? Exception
|
287
|
+
result
|
302
288
|
end
|
303
289
|
end
|
304
|
-
|
305
290
|
end
|
306
|
-
|
307
291
|
end
|
308
292
|
|
309
|
-
|
293
|
+
new_standin_klass.class_exec do
|
310
294
|
def method_missing(method_sym, *arguments, &block)
|
311
|
-
#puts "#{method_sym} #{arguments}"
|
312
295
|
self.class._call_method(self, method_sym, *arguments, &block)
|
313
296
|
end
|
314
297
|
|
315
298
|
class << self
|
316
299
|
def method_missing(method_sym, *arguments, &block)
|
317
|
-
#puts "method #{method_sym.to_s}"
|
318
300
|
_add_instances(self)
|
319
301
|
@_instance = _current_old_class
|
320
302
|
_call_method(self, method_sym, *arguments, &block)
|
@@ -322,7 +304,7 @@ module Pretentious
|
|
322
304
|
end
|
323
305
|
end
|
324
306
|
|
325
|
-
|
307
|
+
new_standin_klass
|
326
308
|
end
|
327
309
|
|
328
310
|
def self.replace_class(klass, stub = false)
|
@@ -331,27 +313,27 @@ module Pretentious
|
|
331
313
|
|
332
314
|
module_space = Object
|
333
315
|
|
334
|
-
if
|
316
|
+
if klass_name_parts.size > 0
|
335
317
|
klass_name_parts.each do |part|
|
336
318
|
module_space = module_space.const_get(part)
|
337
319
|
end
|
338
320
|
end
|
339
321
|
|
340
|
-
|
341
|
-
|
322
|
+
new_standin_klass = impostor_for module_space, klass
|
323
|
+
new_standin_klass._set_is_stub if stub
|
342
324
|
|
343
|
-
module_space.send(:remove_const,last_part.to_sym)
|
325
|
+
module_space.send(:remove_const, last_part.to_sym)
|
344
326
|
module_space.const_set("#{last_part}_ddt", klass)
|
345
|
-
module_space.const_set("#{last_part}",
|
327
|
+
module_space.const_set("#{last_part}", new_standin_klass)
|
346
328
|
|
347
|
-
[module_space, klass, last_part,
|
329
|
+
[module_space, klass, last_part, new_standin_klass]
|
348
330
|
end
|
349
331
|
|
350
332
|
def self.restore_class(module_space, klass, last_part)
|
351
|
-
module_space.send(:remove_const,"#{last_part}Impostor".to_sym)
|
352
|
-
module_space.send(:remove_const,"#{last_part}".to_sym)
|
333
|
+
module_space.send(:remove_const, "#{last_part}Impostor".to_sym)
|
334
|
+
module_space.send(:remove_const, "#{last_part}".to_sym)
|
353
335
|
module_space.const_set(last_part, klass)
|
354
|
-
module_space.send(:remove_const,"#{last_part}_ddt".to_sym)
|
336
|
+
module_space.send(:remove_const, "#{last_part}_ddt".to_sym)
|
355
337
|
end
|
356
338
|
|
357
339
|
def self.generate_for(*klasses_or_instances, &block)
|
@@ -359,18 +341,18 @@ module Pretentious
|
|
359
341
|
klasses = []
|
360
342
|
mock_dict = {}
|
361
343
|
|
362
|
-
klasses_or_instances.each
|
344
|
+
klasses_or_instances.each do |klass_or_instance|
|
363
345
|
klass = klass_or_instance.class == Class ? klass_or_instance : klass_or_instance.class
|
364
346
|
klasses << replace_class(klass)
|
365
347
|
|
366
348
|
mock_klasses = []
|
367
349
|
|
368
350
|
klass._get_mock_classes.each do |mock_klass|
|
369
|
-
mock_klasses << replace_class(mock_klass
|
351
|
+
mock_klasses << replace_class(mock_klass, true)
|
370
352
|
end unless klass._get_mock_classes.nil?
|
371
353
|
|
372
354
|
mock_dict[klass] = mock_klasses
|
373
|
-
|
355
|
+
end
|
374
356
|
|
375
357
|
watch_new_instances
|
376
358
|
|
@@ -378,34 +360,30 @@ module Pretentious
|
|
378
360
|
|
379
361
|
unwatch_new_instances
|
380
362
|
|
381
|
-
klasses.each
|
382
|
-
|
383
|
-
#restore the previous class
|
363
|
+
klasses.each do |module_space, klass, last_part, new_standin_klass|
|
364
|
+
# restore the previous class
|
384
365
|
restore_class module_space, klass, last_part
|
385
366
|
|
386
|
-
mock_dict[klass].each do |
|
387
|
-
restore_class
|
367
|
+
mock_dict[klass].each do |mock_module_space, mock_klass, mock_last_part, mock_new_standin_klass|
|
368
|
+
restore_class mock_module_space, mock_klass, mock_last_part
|
388
369
|
end
|
389
370
|
|
390
371
|
generator = test_generator.new
|
391
372
|
generator.begin_spec(klass)
|
392
373
|
num = 1
|
393
374
|
|
394
|
-
|
375
|
+
new_standin_klass._instances.each do |instance|
|
395
376
|
generator.generate(instance, num)
|
396
|
-
num+=1
|
397
|
-
end unless
|
377
|
+
num += 1
|
378
|
+
end unless new_standin_klass._instances.nil?
|
398
379
|
|
399
380
|
generator.end_spec
|
400
381
|
|
401
382
|
result = all_results[klass]
|
402
|
-
if result.nil?
|
403
|
-
all_results[klass] = []
|
404
|
-
end
|
383
|
+
all_results[klass] = [] if result.nil?
|
405
384
|
|
406
|
-
all_results[klass] = {output: generator.output, generator: generator.class }
|
407
|
-
|
408
|
-
} unless klasses.nil?
|
385
|
+
all_results[klass] = { output: generator.output, generator: generator.class }
|
386
|
+
end unless klasses.nil?
|
409
387
|
|
410
388
|
all_results
|
411
389
|
end
|
@@ -424,18 +402,16 @@ module Pretentious
|
|
424
402
|
end
|
425
403
|
@_variable_names = {}
|
426
404
|
|
427
|
-
params = if
|
428
|
-
|
429
|
-
|
430
|
-
|
405
|
+
params = if self.respond_to? :test_class
|
406
|
+
test_class.instance_method(:initialize).parameters
|
407
|
+
else
|
408
|
+
self.class.instance_method(:initialize).parameters
|
431
409
|
end
|
432
410
|
@_init_arguments[:params_types] = params
|
433
411
|
|
434
412
|
args.each_with_index do |arg, index|
|
435
413
|
p = params[index]
|
436
|
-
if p.size > 1
|
437
|
-
@_variable_names[arg.object_id] = p[1].to_s
|
438
|
-
end unless p.nil?
|
414
|
+
@_variable_names[arg.object_id] = p[1].to_s if p && p.size > 1
|
439
415
|
end unless args.nil?
|
440
416
|
|
441
417
|
end
|
@@ -445,7 +421,7 @@ module Pretentious
|
|
445
421
|
end
|
446
422
|
|
447
423
|
def _deconstruct
|
448
|
-
Pretentious::Deconstructor.new
|
424
|
+
Pretentious::Deconstructor.new.deconstruct([], self)
|
449
425
|
end
|
450
426
|
|
451
427
|
def _deconstruct_to_ruby(var_name = nil, indentation = 0)
|
@@ -454,45 +430,44 @@ module Pretentious
|
|
454
430
|
caller_context = binding.of_caller(1)
|
455
431
|
v_locals = caller_context.eval('local_variables')
|
456
432
|
|
457
|
-
v_locals.each
|
458
|
-
variable_value = caller_context.eval("#{v
|
459
|
-
if
|
433
|
+
v_locals.each do |v|
|
434
|
+
variable_value = caller_context.eval("#{v}")
|
435
|
+
if object_id == variable_value.object_id
|
460
436
|
variable_names[variable_value.object_id] = v
|
461
437
|
end
|
462
|
-
|
438
|
+
end
|
463
439
|
|
464
|
-
|
465
|
-
|
466
|
-
end
|
440
|
+
context = Pretentious::Context.new(_variable_map.merge!(variable_names))
|
441
|
+
context.register(object_id, var_name) if var_name
|
467
442
|
|
443
|
+
Pretentious::Deconstructor.new.deconstruct_to_ruby(context, indentation, self)
|
444
|
+
end
|
468
445
|
end
|
469
446
|
|
470
|
-
#make sure it is set only once
|
471
|
-
|
447
|
+
# make sure it is set only once
|
448
|
+
unless Class.instance_methods.include?(:_ddt_old_new)
|
472
449
|
Class.class_eval do
|
473
450
|
alias_method :_ddt_old_new, :new
|
474
451
|
|
475
452
|
def new(*args, &block)
|
476
453
|
instance = _ddt_old_new(*args, &block)
|
477
454
|
|
478
|
-
#rescues for handling native objects that don't have standard
|
455
|
+
# rescues for handling native objects that don't have standard methods
|
479
456
|
begin
|
480
|
-
if
|
457
|
+
if instance.respond_to?(:_set_init_arguments)
|
481
458
|
instance._set_init_arguments(*args, &block)
|
482
459
|
end
|
483
|
-
rescue NoMethodError
|
460
|
+
rescue NoMethodError
|
484
461
|
begin
|
485
462
|
instance._set_init_arguments(*args, &block)
|
486
|
-
rescue NoMethodError
|
463
|
+
rescue NoMethodError
|
487
464
|
end
|
488
465
|
end
|
489
466
|
|
490
467
|
instance
|
491
468
|
end
|
492
|
-
|
493
469
|
end
|
494
470
|
end
|
495
|
-
|
496
471
|
end
|
497
472
|
|
498
473
|
def self.clean_watches
|
@@ -500,7 +475,7 @@ module Pretentious
|
|
500
475
|
end
|
501
476
|
|
502
477
|
def self.unwatch_new_instances
|
503
|
-
if
|
478
|
+
if Class.respond_to?(:_ddt_old_new)
|
504
479
|
Class.class_eval do
|
505
480
|
remove_method :new
|
506
481
|
alias_method :new, :_ddt_old_new
|
@@ -508,7 +483,5 @@ module Pretentious
|
|
508
483
|
end
|
509
484
|
end
|
510
485
|
end
|
511
|
-
|
512
486
|
end
|
513
|
-
|
514
487
|
end
|