pretentious 0.0.7 → 0.0.8
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 +64 -3
- data/example.rb +21 -0
- data/lib/pretentious.rb +36 -403
- data/lib/pretentious/generator.rb +460 -0
- data/lib/pretentious/recorded_proc.rb +43 -0
- data/lib/pretentious/rspec_generator.rb +41 -2
- data/lib/pretentious/version.rb +1 -1
- data/run_test.sh +1 -1
- data/spec/fibonacci_spec.rb +0 -2
- data/spec/generator_spec.rb +148 -0
- data/spec/prententious_spec.rb +27 -0
- data/spec/test_class1_spec.rb +14 -15
- data/spec/test_class2_spec.rb +0 -1
- data/spec/test_class3_spec.rb +4 -6
- data/spec/test_class4_spec.rb +3 -4
- data/spec/test_class_for_auto_stub_spec.rb +25 -0
- data/spec/test_class_for_mocks_spec.rb +53 -0
- data/test/test_generator.rb +4 -0
- data/test_classes.rb +96 -0
- metadata +12 -2
@@ -0,0 +1,460 @@
|
|
1
|
+
module Pretentious
|
2
|
+
class Generator
|
3
|
+
|
4
|
+
def self.test_generator=(generator)
|
5
|
+
@test_generator = generator
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.test_generator
|
9
|
+
@test_generator || Pretentious::RspecGenerator
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.impostor_for(module_space, klass)
|
13
|
+
newStandInKlass = Class.new()
|
14
|
+
name = klass.name
|
15
|
+
module_space.const_set "#{name.split('::').last}Impostor", newStandInKlass
|
16
|
+
|
17
|
+
newStandInKlass.class_eval("
|
18
|
+
def setup_instance(*args, &block)
|
19
|
+
@_instance = #{klass.name}_ddt.new(*args, &block)
|
20
|
+
end
|
21
|
+
")
|
22
|
+
|
23
|
+
newStandInKlass.class_eval("
|
24
|
+
class << self
|
25
|
+
def _get_standin_class
|
26
|
+
#{newStandInKlass}
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_class
|
30
|
+
#{klass.name}
|
31
|
+
end
|
32
|
+
|
33
|
+
def _current_old_class
|
34
|
+
#{klass.name}_ddt
|
35
|
+
end
|
36
|
+
end
|
37
|
+
")
|
38
|
+
|
39
|
+
newStandInKlass.class_exec do
|
40
|
+
|
41
|
+
def initialize(*args, &block)
|
42
|
+
|
43
|
+
@_instance_init = {params: [], block: nil}
|
44
|
+
|
45
|
+
self.class.replace_procs_with_recorders(args)
|
46
|
+
|
47
|
+
@_instance_init[:params] = args
|
48
|
+
|
49
|
+
recordedProc = if (block)
|
50
|
+
RecordedProc.new(block, true)
|
51
|
+
else
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
@_instance_init[:block] = recordedProc
|
56
|
+
|
57
|
+
setup_instance(*args, &recordedProc)
|
58
|
+
|
59
|
+
|
60
|
+
@_method_calls = []
|
61
|
+
@_method_calls_by_method = {}
|
62
|
+
@_methods_for_test = []
|
63
|
+
@_let_variables = {}
|
64
|
+
|
65
|
+
|
66
|
+
@_init_let_variables = {}
|
67
|
+
|
68
|
+
caller_context = binding.of_caller(2)
|
69
|
+
v_locals = caller_context.eval('local_variables')
|
70
|
+
|
71
|
+
v_locals.each { |v|
|
72
|
+
variable_value = caller_context.eval("#{v.to_s}")
|
73
|
+
@_init_let_variables[variable_value.object_id] = v
|
74
|
+
}
|
75
|
+
|
76
|
+
self.class._add_instances(self)
|
77
|
+
end
|
78
|
+
|
79
|
+
def _init_arguments
|
80
|
+
@_instance_init
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_class
|
84
|
+
@_instance.class
|
85
|
+
end
|
86
|
+
|
87
|
+
def include_for_tests(method_list = [])
|
88
|
+
@_methods_for_test = @_methods_for_test + method_list
|
89
|
+
end
|
90
|
+
|
91
|
+
def let_variables
|
92
|
+
@_let_variables
|
93
|
+
end
|
94
|
+
|
95
|
+
def init_let_variables
|
96
|
+
@_init_let_variables
|
97
|
+
end
|
98
|
+
|
99
|
+
def method_calls_by_method
|
100
|
+
@_method_calls_by_method
|
101
|
+
end
|
102
|
+
|
103
|
+
def method_calls
|
104
|
+
@_method_calls
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_s
|
108
|
+
@_instance.to_s
|
109
|
+
end
|
110
|
+
|
111
|
+
def ==(other)
|
112
|
+
@_instance==other
|
113
|
+
end
|
114
|
+
|
115
|
+
def kind_of?(klass)
|
116
|
+
@_instance.kind_of? klass
|
117
|
+
end
|
118
|
+
|
119
|
+
def methods
|
120
|
+
@_instance.methods + [:method_calls]
|
121
|
+
end
|
122
|
+
|
123
|
+
def freeze
|
124
|
+
@_instance.freeze
|
125
|
+
end
|
126
|
+
|
127
|
+
def hash
|
128
|
+
@instance.hash
|
129
|
+
end
|
130
|
+
|
131
|
+
def inspect
|
132
|
+
@_instance.inspect
|
133
|
+
end
|
134
|
+
|
135
|
+
def is_a?(something)
|
136
|
+
@_instance.is_a? something
|
137
|
+
end
|
138
|
+
|
139
|
+
def instance_variable_get(sym)
|
140
|
+
@_instance.instance_variable_get(sym)
|
141
|
+
end
|
142
|
+
|
143
|
+
def instance_variable_set(sym, val)
|
144
|
+
@_instance.instance_variable_get(sym, val)
|
145
|
+
end
|
146
|
+
|
147
|
+
class << self
|
148
|
+
|
149
|
+
def replace_procs_with_recorders(args)
|
150
|
+
(0..args.size).each do |index|
|
151
|
+
if (args[index].kind_of? Proc)
|
152
|
+
args[index] = Pretentious::RecordedProc.new(args[index]) {}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def _set_is_stub
|
158
|
+
@_is_stub = true
|
159
|
+
end
|
160
|
+
|
161
|
+
def _is_stub?
|
162
|
+
@_is_stub = @_is_stub || false
|
163
|
+
@_is_stub
|
164
|
+
end
|
165
|
+
|
166
|
+
def _add_instances(instance)
|
167
|
+
@_instances = @_instances || []
|
168
|
+
@_instances << instance unless @_instances.include? instance
|
169
|
+
end
|
170
|
+
|
171
|
+
def let_variables
|
172
|
+
@_let_variables
|
173
|
+
end
|
174
|
+
|
175
|
+
def method_calls_by_method
|
176
|
+
@_method_calls_by_method
|
177
|
+
end
|
178
|
+
|
179
|
+
def method_calls
|
180
|
+
@_method_calls
|
181
|
+
end
|
182
|
+
|
183
|
+
def _add_instances(instance)
|
184
|
+
@_instances = @_instances || []
|
185
|
+
@_instances << instance unless @_instances.include? instance
|
186
|
+
end
|
187
|
+
|
188
|
+
def _instances
|
189
|
+
@_instances
|
190
|
+
end
|
191
|
+
|
192
|
+
def instance_methods
|
193
|
+
methods = super
|
194
|
+
test_class.instance_methods + methods
|
195
|
+
end
|
196
|
+
|
197
|
+
def _call_method(target, method_sym, *arguments, &block)
|
198
|
+
|
199
|
+
klass = _get_standin_class
|
200
|
+
caller_context = binding.of_caller(2)
|
201
|
+
|
202
|
+
is_stub = _is_stub?
|
203
|
+
|
204
|
+
target.instance_exec do
|
205
|
+
@_method_calls = @_method_calls || []
|
206
|
+
@_method_calls_by_method = @_method_calls_by_method || {}
|
207
|
+
@_methods_for_test = @_methods_for_test || []
|
208
|
+
@_let_variables = @_let_variables || {}
|
209
|
+
|
210
|
+
|
211
|
+
#puts \"local_variables\"
|
212
|
+
v_locals = caller_context.eval('local_variables')
|
213
|
+
|
214
|
+
v_locals.each { |v|
|
215
|
+
variable_value = caller_context.eval("#{v.to_s}")
|
216
|
+
@_let_variables[variable_value.object_id] = v
|
217
|
+
}
|
218
|
+
|
219
|
+
klass.replace_procs_with_recorders(arguments)
|
220
|
+
|
221
|
+
info_block = {}
|
222
|
+
info_block[:method] = method_sym
|
223
|
+
info_block[:params] = arguments
|
224
|
+
|
225
|
+
recordedProc = if (block)
|
226
|
+
RecordedProc.new(block, true)
|
227
|
+
else
|
228
|
+
nil
|
229
|
+
end
|
230
|
+
info_block[:block] = recordedProc
|
231
|
+
|
232
|
+
info_block[:names] = @_instance.method(method_sym).parameters
|
233
|
+
|
234
|
+
begin
|
235
|
+
|
236
|
+
unless is_stub
|
237
|
+
current_context = { calls: [] }
|
238
|
+
info_block[:context] = current_context
|
239
|
+
|
240
|
+
Thread.current._push_context(current_context)
|
241
|
+
end
|
242
|
+
|
243
|
+
if (@_instance.methods.include? method_sym)
|
244
|
+
result = @_instance.send(method_sym, *arguments, &recordedProc)
|
245
|
+
else
|
246
|
+
result = @_instance.send(:method_missing, method_sym, *arguments, &recordedProc)
|
247
|
+
end
|
248
|
+
|
249
|
+
Thread.current._pop_context unless is_stub
|
250
|
+
|
251
|
+
# methods that end with = are a special case with return values
|
252
|
+
if method_sym.to_s.end_with? '='
|
253
|
+
info_block[:result] = arguments[0]
|
254
|
+
else
|
255
|
+
info_block[:result] = result
|
256
|
+
end
|
257
|
+
|
258
|
+
rescue Exception=>e
|
259
|
+
info_block[:result] = e
|
260
|
+
rescue StandardError=>e
|
261
|
+
info_block[:result] = e
|
262
|
+
end
|
263
|
+
|
264
|
+
if is_stub
|
265
|
+
info_block[:class] = test_class
|
266
|
+
Thread.current._all_context.each { |mock_context|
|
267
|
+
mock_context[:calls] << info_block if mock_context
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
@_method_calls << info_block
|
272
|
+
|
273
|
+
if (@_method_calls_by_method[method_sym].nil?)
|
274
|
+
@_method_calls_by_method[method_sym] = []
|
275
|
+
end
|
276
|
+
|
277
|
+
@_method_calls_by_method[method_sym] << info_block
|
278
|
+
raise e if (e.kind_of? Exception)
|
279
|
+
result
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
newStandInKlass.class_exec do
|
288
|
+
def method_missing(method_sym, *arguments, &block)
|
289
|
+
#puts "#{method_sym} #{arguments}"
|
290
|
+
self.class._call_method(self, method_sym, *arguments, &block)
|
291
|
+
end
|
292
|
+
|
293
|
+
class << self
|
294
|
+
def method_missing(method_sym, *arguments, &block)
|
295
|
+
#puts "method #{method_sym.to_s}"
|
296
|
+
_add_instances(self)
|
297
|
+
@_instance = _current_old_class
|
298
|
+
_call_method(self, method_sym, *arguments, &block)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
newStandInKlass
|
304
|
+
end
|
305
|
+
|
306
|
+
def self.replace_class(klass, stub = false)
|
307
|
+
klass_name_parts = klass.name.split('::')
|
308
|
+
last_part = klass_name_parts.pop
|
309
|
+
|
310
|
+
module_space = Object
|
311
|
+
|
312
|
+
if (klass_name_parts.size > 0)
|
313
|
+
klass_name_parts.each do |part|
|
314
|
+
module_space = module_space.const_get(part)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
newStandInKlass = impostor_for module_space, klass
|
319
|
+
newStandInKlass._set_is_stub if stub
|
320
|
+
|
321
|
+
module_space.send(:remove_const,last_part.to_sym)
|
322
|
+
module_space.const_set("#{last_part}_ddt", klass)
|
323
|
+
module_space.const_set("#{last_part}", newStandInKlass)
|
324
|
+
|
325
|
+
[module_space, klass, last_part, newStandInKlass]
|
326
|
+
end
|
327
|
+
|
328
|
+
def self.restore_class(module_space, klass, last_part)
|
329
|
+
module_space.send(:remove_const,"#{last_part}Impostor".to_sym)
|
330
|
+
module_space.send(:remove_const,"#{last_part}".to_sym)
|
331
|
+
module_space.const_set(last_part, klass)
|
332
|
+
module_space.send(:remove_const,"#{last_part}_ddt".to_sym)
|
333
|
+
end
|
334
|
+
|
335
|
+
def self.generate_for(*klasses_or_instances, &block)
|
336
|
+
all_results = {}
|
337
|
+
klasses = []
|
338
|
+
mock_dict = {}
|
339
|
+
|
340
|
+
klasses_or_instances.each { |klass_or_instance|
|
341
|
+
klass = klass_or_instance.class == Class ? klass_or_instance : klass_or_instance.class
|
342
|
+
klasses << replace_class(klass)
|
343
|
+
|
344
|
+
mock_klasses = []
|
345
|
+
|
346
|
+
klass._get_mock_classes.each do |mock_klass|
|
347
|
+
mock_klasses << replace_class(mock_klass , true)
|
348
|
+
end unless klass._get_mock_classes.nil?
|
349
|
+
|
350
|
+
mock_dict[klass] = mock_klasses
|
351
|
+
}
|
352
|
+
|
353
|
+
watch_new_instances
|
354
|
+
|
355
|
+
block.call
|
356
|
+
|
357
|
+
unwatch_new_instances
|
358
|
+
|
359
|
+
klasses.each { |module_space, klass, last_part, newStandInKlass|
|
360
|
+
|
361
|
+
#restore the previous class
|
362
|
+
restore_class module_space, klass, last_part
|
363
|
+
|
364
|
+
mock_dict[klass].each do |_module_space, _klass, _last_part, _newStandInKlass|
|
365
|
+
restore_class _module_space, _klass, _last_part
|
366
|
+
end
|
367
|
+
|
368
|
+
generator = test_generator.new
|
369
|
+
generator.begin_spec(klass)
|
370
|
+
num = 1
|
371
|
+
|
372
|
+
newStandInKlass._instances.each do |instance|
|
373
|
+
generator.generate(instance, num)
|
374
|
+
num+=1
|
375
|
+
end unless newStandInKlass._instances.nil?
|
376
|
+
|
377
|
+
generator.end_spec
|
378
|
+
|
379
|
+
result = all_results[klass]
|
380
|
+
if result.nil?
|
381
|
+
all_results[klass] = []
|
382
|
+
end
|
383
|
+
|
384
|
+
all_results[klass] = generator.output
|
385
|
+
|
386
|
+
} unless klasses.nil?
|
387
|
+
|
388
|
+
all_results
|
389
|
+
end
|
390
|
+
|
391
|
+
def self.watch_new_instances
|
392
|
+
Object.class_eval do
|
393
|
+
def _get_init_arguments
|
394
|
+
@_init_arguments
|
395
|
+
end
|
396
|
+
|
397
|
+
def _set_init_arguments(*args, &block)
|
398
|
+
@_init_arguments = @_init_arguments || {}
|
399
|
+
@_init_arguments[:params] = args
|
400
|
+
unless (block.nil?)
|
401
|
+
@_init_arguments[:block] = RecordedProc.new(block) {}
|
402
|
+
end
|
403
|
+
@_variable_names= {}
|
404
|
+
|
405
|
+
index = 0
|
406
|
+
params = method(:initialize).parameters
|
407
|
+
|
408
|
+
args.each do |arg|
|
409
|
+
p = params[index]
|
410
|
+
if p.size > 1
|
411
|
+
@_variable_names[arg.object_id] = p[1].to_s
|
412
|
+
end unless p.nil?
|
413
|
+
index+=1
|
414
|
+
end unless args.nil?
|
415
|
+
|
416
|
+
end
|
417
|
+
|
418
|
+
def _variable_map
|
419
|
+
@_variable_names
|
420
|
+
end
|
421
|
+
|
422
|
+
def _deconstruct
|
423
|
+
Pretentious::Deconstructor.new().deconstruct(self)
|
424
|
+
end
|
425
|
+
|
426
|
+
def _deconstruct_to_ruby(indentation = 0)
|
427
|
+
Pretentious::Deconstructor.new().deconstruct_to_ruby(indentation, _variable_map, {}, self)
|
428
|
+
end
|
429
|
+
|
430
|
+
end
|
431
|
+
|
432
|
+
Class.class_eval do
|
433
|
+
alias_method :_ddt_old_new, :new
|
434
|
+
|
435
|
+
def new(*args, &block)
|
436
|
+
instance = _ddt_old_new(*args, &block)
|
437
|
+
instance._set_init_arguments(*args, &block)
|
438
|
+
instance
|
439
|
+
end
|
440
|
+
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
def self.clean_watches
|
445
|
+
Class.class_eval do
|
446
|
+
remove_method :new
|
447
|
+
alias_method :new, :_ddt_old_new
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def self.unwatch_new_instances
|
452
|
+
Class.class_eval do
|
453
|
+
remove_method :new
|
454
|
+
alias_method :new, :_ddt_old_new
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|