pretentious 0.1.6 → 0.1.7
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/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
|