heckle 1.3.0 → 1.4.0
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.
- data/History.txt +13 -3
- data/bin/heckle +35 -3
- data/lib/heckle.rb +225 -12
- data/lib/test_unit_heckler.rb +21 -5
- data/test/fixtures/heckled.rb +44 -0
- data/test/test_heckle.rb +316 -8
- metadata +2 -2
data/History.txt
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
== 1.4.0 / 2007-05-18
|
2
|
+
|
3
|
+
* 2 major enhancements:
|
4
|
+
* Method calls are now heckled (by removal).
|
5
|
+
* Assignments are now heckled (by value changing).
|
6
|
+
* 3 minor enhancements:
|
7
|
+
* Added --focus to feel the Eye of Sauron (specify unit tests to run).
|
8
|
+
* Specify nodes to be included/excluded in heckle with -n/-x.
|
9
|
+
* Test only assignments with --assignments
|
10
|
+
|
1
11
|
== 1.3.0 / 2007-02-12
|
2
12
|
|
3
13
|
* 1 major enhancement:
|
@@ -10,7 +20,7 @@
|
|
10
20
|
* 2 bug fixes:
|
11
21
|
* Aborts when an unknown method is supplied.
|
12
22
|
* Escapes slashes in random regexps.
|
13
|
-
|
23
|
+
|
14
24
|
== 1.2.0 / 2007-01-15
|
15
25
|
|
16
26
|
* 2 major enhancements:
|
@@ -22,7 +32,7 @@
|
|
22
32
|
* Revamped the tests and reduced size by 60%.
|
23
33
|
* 1 bug fix:
|
24
34
|
* Fixed the infinite loop caused by syntax errors
|
25
|
-
|
35
|
+
|
26
36
|
== 1.1.1 / 2006-12-20
|
27
37
|
|
28
38
|
* 3 bug fixes:
|
@@ -45,7 +55,7 @@
|
|
45
55
|
* Can mutate regexes, ranges, symbols
|
46
56
|
* Can run against entire classes
|
47
57
|
* Command line options!
|
48
|
-
|
58
|
+
|
49
59
|
== 1.0.0 / 2006-10-22
|
50
60
|
|
51
61
|
* 1 major enhancement
|
data/bin/heckle
CHANGED
@@ -4,6 +4,8 @@ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
|
4
4
|
require 'test_unit_heckler'
|
5
5
|
require 'optparse'
|
6
6
|
|
7
|
+
nodes = Heckle::MUTATABLE_NODES
|
8
|
+
|
7
9
|
opts = OptionParser.new do |opts|
|
8
10
|
opts.banner = "Usage: #{File.basename($0)} class_name [method_name]"
|
9
11
|
opts.on( "-v", "--verbose", "Loudly explain heckle run" ) do |opt|
|
@@ -20,13 +22,31 @@ opts = OptionParser.new do |opts|
|
|
20
22
|
TestUnitHeckler.test_pattern = pattern
|
21
23
|
end
|
22
24
|
|
23
|
-
opts.on(
|
25
|
+
opts.on( "--assignments", "Only mutate assignments" ) do |opt|
|
26
|
+
puts "!"*70
|
27
|
+
puts "!!! Heckling assignments only"
|
28
|
+
puts "!"*70
|
29
|
+
puts
|
30
|
+
|
31
|
+
nodes = Heckle::ASGN_NODES
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on( "-b", "--branches", "Only mutate branches" ) do |opt|
|
24
35
|
puts "!"*70
|
25
36
|
puts "!!! Heckling branches only"
|
26
37
|
puts "!"*70
|
27
38
|
puts
|
28
39
|
|
29
|
-
Heckle::
|
40
|
+
nodes = Heckle::BRANCH_NODES
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on( "-f", "--focus", "Apply the eye of sauron" ) do |opt|
|
44
|
+
puts "!"*70
|
45
|
+
puts "!!! Running in focused mode. FEEL THE EYE OF SAURON!!!"
|
46
|
+
puts "!"*70
|
47
|
+
puts
|
48
|
+
|
49
|
+
TestUnitHeckler.focus = true
|
30
50
|
end
|
31
51
|
|
32
52
|
opts.on( "-T", "--timeout SECONDS", "The maximum time for a test run in seconds",
|
@@ -35,6 +55,18 @@ opts = OptionParser.new do |opts|
|
|
35
55
|
puts "Setting timeout at #{timeout} seconds."
|
36
56
|
end
|
37
57
|
|
58
|
+
opts.on( "-n", "--nodes NODES", "Nodes to mutate",
|
59
|
+
"Possible values: #{Heckle::MUTATABLE_NODES.join(',')}" ) do |opt|
|
60
|
+
nodes = opt.split(',').collect {|n| n.to_sym }
|
61
|
+
puts "Mutating nodes: #{nodes.inspect}"
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on( "-x", "--exclude-nodes NODES", "Nodes to exclude") do |opt|
|
65
|
+
exclusions = opt.split(',').collect {|n| n.to_sym }
|
66
|
+
nodes = nodes - exclusions
|
67
|
+
puts "Mutating without nodes: #{exclusions.inspect}"
|
68
|
+
end
|
69
|
+
|
38
70
|
opts.on( "-h", "--help", "Show this message") do |opt|
|
39
71
|
puts opts
|
40
72
|
exit 0
|
@@ -54,4 +86,4 @@ unless impl then
|
|
54
86
|
exit 1
|
55
87
|
end
|
56
88
|
|
57
|
-
exit TestUnitHeckler.validate(impl, meth)
|
89
|
+
exit TestUnitHeckler.validate(impl, meth, nodes)
|
data/lib/heckle.rb
CHANGED
@@ -4,22 +4,76 @@ require 'ruby2ruby'
|
|
4
4
|
require 'timeout'
|
5
5
|
require 'tempfile'
|
6
6
|
|
7
|
-
class String
|
7
|
+
class String # :nodoc:
|
8
8
|
def to_class
|
9
9
|
split(/::/).inject(Object) { |klass, name| klass.const_get(name) }
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
##
|
14
|
+
# Test Unit Sadism
|
15
|
+
|
13
16
|
class Heckle < SexpProcessor
|
14
|
-
|
15
|
-
|
17
|
+
|
18
|
+
##
|
19
|
+
# The version of Heckle you are using.
|
20
|
+
|
21
|
+
VERSION = '1.4.0'
|
22
|
+
|
23
|
+
##
|
24
|
+
# Branch node types.
|
25
|
+
|
26
|
+
BRANCH_NODES = [:if, :until, :while]
|
27
|
+
|
28
|
+
##
|
29
|
+
# Is this platform MS Windows-like?
|
30
|
+
|
16
31
|
WINDOZE = RUBY_PLATFORM =~ /mswin/
|
32
|
+
|
33
|
+
##
|
34
|
+
# Path to the bit bucket.
|
35
|
+
|
17
36
|
NULL_PATH = WINDOZE ? 'NUL:' : '/dev/null'
|
37
|
+
|
38
|
+
##
|
39
|
+
# diff(1) executable
|
40
|
+
|
18
41
|
DIFF = WINDOZE ? 'diff.exe' : 'diff'
|
19
42
|
|
20
|
-
|
21
|
-
|
22
|
-
|
43
|
+
##
|
44
|
+
# Mutation count
|
45
|
+
|
46
|
+
attr_accessor :count
|
47
|
+
|
48
|
+
##
|
49
|
+
# Mutations that caused failures
|
50
|
+
|
51
|
+
attr_accessor :failures
|
52
|
+
|
53
|
+
##
|
54
|
+
# Class being heckled
|
55
|
+
|
56
|
+
attr_accessor :klass
|
57
|
+
|
58
|
+
##
|
59
|
+
# Name of class being heckled
|
60
|
+
|
61
|
+
attr_accessor :klass_name
|
62
|
+
|
63
|
+
##
|
64
|
+
# Method being heckled
|
65
|
+
|
66
|
+
attr_accessor :method
|
67
|
+
|
68
|
+
##
|
69
|
+
# Name of method being heckled
|
70
|
+
|
71
|
+
attr_accessor :method_name
|
72
|
+
|
73
|
+
attr_accessor :mutatees # :nodoc:
|
74
|
+
attr_accessor :mutation_count # :nodoc:
|
75
|
+
attr_accessor :node_count # :nodoc:
|
76
|
+
attr_accessor :original_tree # :nodoc:
|
23
77
|
|
24
78
|
@@debug = false
|
25
79
|
@@guess_timeout = true
|
@@ -38,7 +92,12 @@ class Heckle < SexpProcessor
|
|
38
92
|
@@guess_timeout
|
39
93
|
end
|
40
94
|
|
41
|
-
|
95
|
+
##
|
96
|
+
# Creates a new Heckle that will heckle +klass_name+ and +method_name+,
|
97
|
+
# sending results to +reporter+.
|
98
|
+
|
99
|
+
def initialize(klass_name = nil, method_name = nil,
|
100
|
+
nodes = Heckle::MUTATABLE_NODES, reporter = Reporter.new)
|
42
101
|
super()
|
43
102
|
|
44
103
|
@klass_name = klass_name
|
@@ -58,7 +117,8 @@ class Heckle < SexpProcessor
|
|
58
117
|
@node_count = Hash.new
|
59
118
|
@count = 0
|
60
119
|
|
61
|
-
|
120
|
+
@mutatable_nodes = nodes
|
121
|
+
@mutatable_nodes.each {|type| @mutatees[type] = [] }
|
62
122
|
|
63
123
|
@failures = []
|
64
124
|
|
@@ -70,8 +130,9 @@ class Heckle < SexpProcessor
|
|
70
130
|
@original_mutatees = mutatees.deep_clone
|
71
131
|
end
|
72
132
|
|
73
|
-
|
74
|
-
|
133
|
+
##
|
134
|
+
# Overwrite test_pass? for your own Heckle runner.
|
135
|
+
|
75
136
|
def tests_pass?
|
76
137
|
raise NotImplementedError
|
77
138
|
end
|
@@ -154,6 +215,24 @@ class Heckle < SexpProcessor
|
|
154
215
|
############################################################
|
155
216
|
### Processing sexps
|
156
217
|
|
218
|
+
def process_call(exp)
|
219
|
+
recv = process(exp.shift)
|
220
|
+
meth = exp.shift
|
221
|
+
args = process(exp.shift)
|
222
|
+
|
223
|
+
out = [:call, recv, meth]
|
224
|
+
out << args if args
|
225
|
+
|
226
|
+
mutate_node out
|
227
|
+
end
|
228
|
+
|
229
|
+
##
|
230
|
+
# Replaces the call node with nil.
|
231
|
+
|
232
|
+
def mutate_call(node)
|
233
|
+
[:nil]
|
234
|
+
end
|
235
|
+
|
157
236
|
def process_defn(exp)
|
158
237
|
self.method = exp.shift
|
159
238
|
result = [:defn, method]
|
@@ -166,10 +245,96 @@ class Heckle < SexpProcessor
|
|
166
245
|
reset_node_count
|
167
246
|
end
|
168
247
|
|
248
|
+
def process_asgn(type, exp)
|
249
|
+
var = exp.shift
|
250
|
+
if exp.empty? then
|
251
|
+
mutate_node [type, var]
|
252
|
+
else
|
253
|
+
mutate_node [type, var, process(exp.shift)]
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def mutate_asgn(node)
|
258
|
+
type = node.shift
|
259
|
+
var = node.shift
|
260
|
+
if node.empty? then
|
261
|
+
[:lasgn, :_heckle_dummy]
|
262
|
+
else
|
263
|
+
if node.last.first == :nil then
|
264
|
+
[type, var, [:lit, 42]]
|
265
|
+
else
|
266
|
+
[type, var, [:nil]]
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def process_cvasgn(exp)
|
272
|
+
process_asgn :cvasgn, exp
|
273
|
+
end
|
274
|
+
|
275
|
+
##
|
276
|
+
# Replaces the value of the cvasgn with nil if its some value, and 42 if its
|
277
|
+
# nil.
|
278
|
+
|
279
|
+
alias mutate_cvasgn mutate_asgn
|
280
|
+
|
281
|
+
def process_dasgn(exp)
|
282
|
+
process_asgn :dasgn, exp
|
283
|
+
end
|
284
|
+
|
285
|
+
##
|
286
|
+
# Replaces the value of the dasgn with nil if its some value, and 42 if its
|
287
|
+
# nil.
|
288
|
+
|
289
|
+
alias mutate_dasgn mutate_asgn
|
290
|
+
|
291
|
+
def process_dasgn_curr(exp)
|
292
|
+
process_asgn :dasgn_curr, exp
|
293
|
+
end
|
294
|
+
|
295
|
+
##
|
296
|
+
# Replaces the value of the dasgn_curr with nil if its some value, and 42 if
|
297
|
+
# its nil.
|
298
|
+
|
299
|
+
alias mutate_dasgn_curr mutate_asgn
|
300
|
+
|
301
|
+
def process_iasgn(exp)
|
302
|
+
process_asgn :iasgn, exp
|
303
|
+
end
|
304
|
+
|
305
|
+
##
|
306
|
+
# Replaces the value of the iasgn with nil if its some value, and 42 if its
|
307
|
+
# nil.
|
308
|
+
|
309
|
+
alias mutate_iasgn mutate_asgn
|
310
|
+
|
311
|
+
def process_gasgn(exp)
|
312
|
+
process_asgn :gasgn, exp
|
313
|
+
end
|
314
|
+
|
315
|
+
##
|
316
|
+
# Replaces the value of the gasgn with nil if its some value, and 42 if its
|
317
|
+
# nil.
|
318
|
+
|
319
|
+
alias mutate_gasgn mutate_asgn
|
320
|
+
|
321
|
+
def process_lasgn(exp)
|
322
|
+
process_asgn :lasgn, exp
|
323
|
+
end
|
324
|
+
|
325
|
+
##
|
326
|
+
# Replaces the value of the lasgn with nil if its some value, and 42 if its
|
327
|
+
# nil.
|
328
|
+
|
329
|
+
alias mutate_lasgn mutate_asgn
|
330
|
+
|
169
331
|
def process_lit(exp)
|
170
332
|
mutate_node [:lit, exp.shift]
|
171
333
|
end
|
172
334
|
|
335
|
+
##
|
336
|
+
# Replaces the value of the :lit node with a random value.
|
337
|
+
|
173
338
|
def mutate_lit(exp)
|
174
339
|
case exp[1]
|
175
340
|
when Fixnum, Float, Bignum
|
@@ -187,6 +352,9 @@ class Heckle < SexpProcessor
|
|
187
352
|
mutate_node [:str, exp.shift]
|
188
353
|
end
|
189
354
|
|
355
|
+
##
|
356
|
+
# Replaces the value of the :str node with a random value.
|
357
|
+
|
190
358
|
def mutate_str(node)
|
191
359
|
[:str, rand_string]
|
192
360
|
end
|
@@ -195,6 +363,9 @@ class Heckle < SexpProcessor
|
|
195
363
|
mutate_node [:if, process(exp.shift), process(exp.shift), process(exp.shift)]
|
196
364
|
end
|
197
365
|
|
366
|
+
##
|
367
|
+
# Swaps the then and else parts of the :if node.
|
368
|
+
|
198
369
|
def mutate_if(node)
|
199
370
|
[:if, node[1], node[3], node[2]]
|
200
371
|
end
|
@@ -203,6 +374,9 @@ class Heckle < SexpProcessor
|
|
203
374
|
mutate_node [:true]
|
204
375
|
end
|
205
376
|
|
377
|
+
##
|
378
|
+
# Swaps for a :false node.
|
379
|
+
|
206
380
|
def mutate_true(node)
|
207
381
|
[:false]
|
208
382
|
end
|
@@ -211,6 +385,9 @@ class Heckle < SexpProcessor
|
|
211
385
|
mutate_node [:false]
|
212
386
|
end
|
213
387
|
|
388
|
+
##
|
389
|
+
# Swaps for a :true node.
|
390
|
+
|
214
391
|
def mutate_false(node)
|
215
392
|
[:true]
|
216
393
|
end
|
@@ -220,6 +397,9 @@ class Heckle < SexpProcessor
|
|
220
397
|
mutate_node [:while, cond, body, head_controlled]
|
221
398
|
end
|
222
399
|
|
400
|
+
##
|
401
|
+
# Swaps for a :until node.
|
402
|
+
|
223
403
|
def mutate_while(node)
|
224
404
|
[:until, node[1], node[2], node[3]]
|
225
405
|
end
|
@@ -229,6 +409,9 @@ class Heckle < SexpProcessor
|
|
229
409
|
mutate_node [:until, cond, body, head_controlled]
|
230
410
|
end
|
231
411
|
|
412
|
+
##
|
413
|
+
# Swaps for a :while node.
|
414
|
+
|
232
415
|
def mutate_until(node)
|
233
416
|
[:while, node[1], node[2], node[3]]
|
234
417
|
end
|
@@ -237,7 +420,7 @@ class Heckle < SexpProcessor
|
|
237
420
|
raise UnsupportedNodeError unless respond_to? "mutate_#{node.first}"
|
238
421
|
increment_node_count node
|
239
422
|
|
240
|
-
if should_heckle? node
|
423
|
+
if should_heckle? node then
|
241
424
|
increment_mutation_count node
|
242
425
|
return send("mutate_#{node.first}", node)
|
243
426
|
else
|
@@ -252,7 +435,7 @@ class Heckle < SexpProcessor
|
|
252
435
|
return unless node.respond_to? :each
|
253
436
|
return if node.is_a? String
|
254
437
|
node.each { |child| walk_and_push(child) }
|
255
|
-
if
|
438
|
+
if @mutatable_nodes.include? node.first
|
256
439
|
@mutatees[node.first.to_sym].push(node)
|
257
440
|
mutation_count[node] = 0
|
258
441
|
end
|
@@ -350,10 +533,16 @@ class Heckle < SexpProcessor
|
|
350
533
|
RubyToRuby.translate(klass_name.to_class, method_name)
|
351
534
|
end
|
352
535
|
|
536
|
+
##
|
537
|
+
# Returns a random Fixnum.
|
538
|
+
|
353
539
|
def rand_number
|
354
540
|
(rand(100) + 1)*((-1)**rand(2))
|
355
541
|
end
|
356
542
|
|
543
|
+
##
|
544
|
+
# Returns a random String
|
545
|
+
|
357
546
|
def rand_string
|
358
547
|
size = rand(50)
|
359
548
|
str = ""
|
@@ -361,6 +550,9 @@ class Heckle < SexpProcessor
|
|
361
550
|
str
|
362
551
|
end
|
363
552
|
|
553
|
+
##
|
554
|
+
# Returns a random Symbol
|
555
|
+
|
364
556
|
def rand_symbol
|
365
557
|
letters = ('a'..'z').to_a + ('A'..'Z').to_a
|
366
558
|
str = ""
|
@@ -368,12 +560,18 @@ class Heckle < SexpProcessor
|
|
368
560
|
:"#{str}"
|
369
561
|
end
|
370
562
|
|
563
|
+
##
|
564
|
+
# Returns a random Range
|
565
|
+
|
371
566
|
def rand_range
|
372
567
|
min = rand(50)
|
373
568
|
max = min + rand(50)
|
374
569
|
min..max
|
375
570
|
end
|
376
571
|
|
572
|
+
##
|
573
|
+
# Suppresses output on $stdout and $stderr.
|
574
|
+
|
377
575
|
def silence_stream
|
378
576
|
dead = File.open("/dev/null", "w")
|
379
577
|
|
@@ -467,4 +665,19 @@ class Heckle < SexpProcessor
|
|
467
665
|
puts "Tests failed -- this is good"
|
468
666
|
end
|
469
667
|
end
|
668
|
+
|
669
|
+
##
|
670
|
+
# All nodes that can be mutated by Heckle.
|
671
|
+
|
672
|
+
MUTATABLE_NODES = instance_methods.grep(/mutate_/).sort.map do |meth|
|
673
|
+
meth.sub(/mutate_/, '').intern
|
674
|
+
end - [:asgn, :node] # Ignore these methods
|
675
|
+
|
676
|
+
##
|
677
|
+
# All assignment nodes that can be mutated by Heckle..
|
678
|
+
|
679
|
+
ASGN_NODES = MUTATABLE_NODES.map { |n| n.to_s }.grep(/asgn/).map do |n|
|
680
|
+
n.intern
|
681
|
+
end
|
682
|
+
|
470
683
|
end
|
data/lib/test_unit_heckler.rb
CHANGED
@@ -2,22 +2,30 @@
|
|
2
2
|
|
3
3
|
require 'test/unit/autorunner'
|
4
4
|
require 'heckle'
|
5
|
+
require 'zentest_mapping'
|
6
|
+
|
5
7
|
$: << 'lib' << 'test'
|
6
8
|
|
7
9
|
class TestUnitHeckler < Heckle
|
8
10
|
@@test_pattern = 'test/test_*.rb'
|
9
|
-
@@tests_loaded = false
|
11
|
+
@@tests_loaded = false
|
12
|
+
@@focus = false
|
10
13
|
|
11
14
|
def self.test_pattern=(value)
|
12
15
|
@@test_pattern = value
|
13
16
|
end
|
14
17
|
|
18
|
+
def self.focus=(value)
|
19
|
+
@@focus = value
|
20
|
+
end
|
21
|
+
|
15
22
|
def self.load_test_files
|
16
23
|
@@tests_loaded = true
|
17
24
|
Dir.glob(@@test_pattern).each {|test| require test}
|
18
25
|
end
|
19
26
|
|
20
|
-
def self.validate(klass_name, method_name = nil
|
27
|
+
def self.validate(klass_name, method_name = nil,
|
28
|
+
nodes = Heckle::MUTATABLE_NODES)
|
21
29
|
load_test_files
|
22
30
|
klass = klass_name.to_class
|
23
31
|
|
@@ -54,7 +62,7 @@ class TestUnitHeckler < Heckle
|
|
54
62
|
|
55
63
|
counts = Hash.new(0)
|
56
64
|
methods.sort.each do |method_name|
|
57
|
-
result = self.new(klass_name, method_name).validate
|
65
|
+
result = self.new(klass_name, method_name, nodes).validate
|
58
66
|
counts[result] += 1
|
59
67
|
end
|
60
68
|
all_good = counts[false] == 0
|
@@ -75,14 +83,22 @@ class TestUnitHeckler < Heckle
|
|
75
83
|
all_good
|
76
84
|
end
|
77
85
|
|
78
|
-
def initialize(klass_name=nil, method_name=nil)
|
79
|
-
super
|
86
|
+
def initialize(klass_name=nil, method_name=nil, nodes=Heckle::MUTATABLE_NODES)
|
87
|
+
super
|
80
88
|
self.class.load_test_files unless @@tests_loaded
|
81
89
|
end
|
82
90
|
|
91
|
+
include ZenTestMapping
|
92
|
+
|
83
93
|
def tests_pass?
|
84
94
|
silence_stream do
|
95
|
+
if @@focus and @method_name then
|
96
|
+
name = normal_to_test @method_name.to_s
|
97
|
+
ARGV.clear
|
98
|
+
ARGV << "--name=/#{name}/"
|
99
|
+
end
|
85
100
|
Test::Unit::AutoRunner.run
|
101
|
+
ARGV.clear
|
86
102
|
end
|
87
103
|
end
|
88
104
|
end
|
data/test/fixtures/heckled.rb
CHANGED
@@ -5,6 +5,50 @@ class Heckled
|
|
5
5
|
@names = []
|
6
6
|
end
|
7
7
|
|
8
|
+
def uses_call
|
9
|
+
some_func + some_other_func
|
10
|
+
end
|
11
|
+
|
12
|
+
def uses_cvasgn
|
13
|
+
@@cvar = 5
|
14
|
+
@@cvar = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def uses_dasgn
|
18
|
+
loop do |dvar|
|
19
|
+
loop do
|
20
|
+
dvar = 5
|
21
|
+
dvar = nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def uses_dasgncurr
|
27
|
+
loop do |dvar|
|
28
|
+
dvar = 5
|
29
|
+
dvar = nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def uses_iasgn
|
34
|
+
@ivar = 5
|
35
|
+
@ivar = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def uses_gasgn
|
39
|
+
$gvar = 5
|
40
|
+
$gvar = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def uses_lasgn
|
44
|
+
lvar = 5
|
45
|
+
lvar = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def uses_masgn
|
49
|
+
@a, $b, c = 5, 6, 7
|
50
|
+
end
|
51
|
+
|
8
52
|
def uses_many_things
|
9
53
|
i = 1
|
10
54
|
while i < 10
|
data/test/test_heckle.rb
CHANGED
@@ -27,9 +27,10 @@ end
|
|
27
27
|
class HeckleTestCase < Test::Unit::TestCase
|
28
28
|
undef_method :default_test
|
29
29
|
def setup
|
30
|
+
@nodes ||= Heckle::MUTATABLE_NODES
|
30
31
|
data = self.class.name["TestHeckle".size..-1].gsub(/([A-Z])/, '_\1').downcase
|
31
32
|
data = "_many_things" if data.empty?
|
32
|
-
@heckler = TestHeckler.new("Heckled", "uses#{data}")
|
33
|
+
@heckler = TestHeckler.new("Heckled", "uses#{data}", @nodes)
|
33
34
|
end
|
34
35
|
|
35
36
|
def teardown
|
@@ -38,6 +39,11 @@ class HeckleTestCase < Test::Unit::TestCase
|
|
38
39
|
end
|
39
40
|
|
40
41
|
class LiteralHeckleTestCase < HeckleTestCase
|
42
|
+
def setup
|
43
|
+
@nodes = [:lit, :str]
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
41
47
|
def toggle(value, toggle)
|
42
48
|
toggle ? self.class::TOGGLE_VALUE : value
|
43
49
|
end
|
@@ -93,6 +99,17 @@ class TestHeckle < HeckleTestCase
|
|
93
99
|
def test_should_grab_mutatees_from_method
|
94
100
|
# expected is from tree of uses_while
|
95
101
|
expected = {
|
102
|
+
:call => [[:call, [:lvar, :i], :<, [:array, [:lit, 10]]],
|
103
|
+
[:call, [:lvar, :i], :+, [:array, [:lit, 1]]],
|
104
|
+
[:call, [:str, "hi there"], :==,
|
105
|
+
[:array, [:str, "changeling"]]]],
|
106
|
+
:cvasgn => [], # no cvasgns here
|
107
|
+
:dasgn => [], # no dasgns here
|
108
|
+
:dasgn_curr => [], # no dasgn_currs here
|
109
|
+
:iasgn => [], # no iasgns here
|
110
|
+
:gasgn => [], # no gasgns here
|
111
|
+
:lasgn => [[:lasgn, :i, [:lit, 1]],
|
112
|
+
[:lasgn, :i, [:call, [:lvar, :i], :+, [:array, [:lit, 1]]]]],
|
96
113
|
:lit => [[:lit, 1], [:lit, 10], [:lit, 1]],
|
97
114
|
:if => [[:if,
|
98
115
|
[:call, [:str, "hi there"], :==, [:array, [:str, "changeling"]]],
|
@@ -121,7 +138,7 @@ class TestHeckle < HeckleTestCase
|
|
121
138
|
end
|
122
139
|
|
123
140
|
def test_should_count_mutatees_left
|
124
|
-
assert_equal
|
141
|
+
assert_equal 15, @heckler.mutations_left
|
125
142
|
end
|
126
143
|
|
127
144
|
def test_reset
|
@@ -239,7 +256,6 @@ class TestHeckleRanges < LiteralHeckleTestCase
|
|
239
256
|
end
|
240
257
|
end
|
241
258
|
|
242
|
-
|
243
259
|
class TestHeckleSameLiteral < LiteralHeckleTestCase
|
244
260
|
TOGGLE_VALUE = 6
|
245
261
|
|
@@ -312,6 +328,12 @@ class TestHeckleIf < HeckleTestCase
|
|
312
328
|
end
|
313
329
|
|
314
330
|
class TestHeckleBoolean < HeckleTestCase
|
331
|
+
|
332
|
+
def setup
|
333
|
+
@nodes = [:true, :false]
|
334
|
+
super
|
335
|
+
end
|
336
|
+
|
315
337
|
def toggle(value, toggle)
|
316
338
|
(toggle ? ! value : value).to_s.intern
|
317
339
|
end
|
@@ -386,6 +408,35 @@ class TestHeckleUntil < HeckleTestCase
|
|
386
408
|
end
|
387
409
|
end
|
388
410
|
|
411
|
+
class TestHeckleCall < HeckleTestCase
|
412
|
+
|
413
|
+
def test_call_deleted
|
414
|
+
expected = [:defn, :uses_call,
|
415
|
+
[:scope,
|
416
|
+
[:block,
|
417
|
+
[:args],
|
418
|
+
[:nil]]]]
|
419
|
+
|
420
|
+
@heckler.process(@heckler.current_tree)
|
421
|
+
assert_equal expected, @heckler.current_tree
|
422
|
+
end
|
423
|
+
|
424
|
+
def test_default_structure
|
425
|
+
expected = [:defn, :uses_call,
|
426
|
+
[:fbody,
|
427
|
+
[:scope,
|
428
|
+
[:block,
|
429
|
+
[:args],
|
430
|
+
[:call,
|
431
|
+
[:vcall, :some_func],
|
432
|
+
:+,
|
433
|
+
[:array, [:vcall, :some_other_func]]]]]]]
|
434
|
+
|
435
|
+
assert_equal expected, @heckler.current_tree
|
436
|
+
end
|
437
|
+
|
438
|
+
end
|
439
|
+
|
389
440
|
class TestHeckleClassMethod < Test::Unit::TestCase
|
390
441
|
def setup
|
391
442
|
@heckler = TestHeckler.new("Heckled", "self.is_a_klass_method?")
|
@@ -414,8 +465,265 @@ class TestHeckleClassMethod < Test::Unit::TestCase
|
|
414
465
|
assert_equal expected, @heckler.current_tree
|
415
466
|
end
|
416
467
|
end
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
468
|
+
|
469
|
+
class TestHeckleCvasgn < HeckleTestCase
|
470
|
+
|
471
|
+
def setup
|
472
|
+
@nodes = [:cvasgn]
|
473
|
+
super
|
474
|
+
end
|
475
|
+
|
476
|
+
def test_cvasgn_val
|
477
|
+
expected = [:defn, :uses_cvasgn,
|
478
|
+
[:scope,
|
479
|
+
[:block,
|
480
|
+
[:args],
|
481
|
+
[:cvasgn, :@@cvar, [:nil]],
|
482
|
+
[:cvasgn, :@@cvar, [:nil]]]]]
|
483
|
+
|
484
|
+
@heckler.process(@heckler.current_tree)
|
485
|
+
assert_equal expected, @heckler.current_tree
|
486
|
+
end
|
487
|
+
|
488
|
+
def test_cvasgn_nil
|
489
|
+
expected = [:defn, :uses_cvasgn,
|
490
|
+
[:scope,
|
491
|
+
[:block,
|
492
|
+
[:args],
|
493
|
+
[:cvasgn, :@@cvar, [:lit, 5]],
|
494
|
+
[:cvasgn, :@@cvar, [:lit, 42]]]]]
|
495
|
+
|
496
|
+
@heckler.process(@heckler.current_tree)
|
497
|
+
@heckler.reset_tree
|
498
|
+
@heckler.process(@heckler.current_tree)
|
499
|
+
assert_equal expected, @heckler.current_tree
|
500
|
+
end
|
501
|
+
|
502
|
+
end
|
503
|
+
|
504
|
+
class TestHeckleDasgn < HeckleTestCase
|
505
|
+
|
506
|
+
def setup
|
507
|
+
@nodes = [:dasgn]
|
508
|
+
super
|
509
|
+
end
|
510
|
+
|
511
|
+
def test_dasgn_val
|
512
|
+
expected = [:defn, :uses_dasgn,
|
513
|
+
[:scope,
|
514
|
+
[:block,
|
515
|
+
[:args],
|
516
|
+
[:iter,
|
517
|
+
[:fcall, :loop],
|
518
|
+
[:dasgn_curr, :dvar],
|
519
|
+
[:iter,
|
520
|
+
[:fcall, :loop],
|
521
|
+
nil,
|
522
|
+
[:block,
|
523
|
+
[:dasgn, :dvar, [:nil]],
|
524
|
+
[:dasgn, :dvar, [:nil]]]]]]]]
|
525
|
+
|
526
|
+
@heckler.process(@heckler.current_tree)
|
527
|
+
assert_equal expected, @heckler.current_tree
|
528
|
+
end
|
529
|
+
|
530
|
+
def test_dasgn_nil
|
531
|
+
expected = [:defn, :uses_dasgn,
|
532
|
+
[:scope,
|
533
|
+
[:block,
|
534
|
+
[:args],
|
535
|
+
[:iter,
|
536
|
+
[:fcall, :loop],
|
537
|
+
[:dasgn_curr, :dvar],
|
538
|
+
[:iter,
|
539
|
+
[:fcall, :loop],
|
540
|
+
nil,
|
541
|
+
[:block,
|
542
|
+
[:dasgn, :dvar, [:lit, 5]],
|
543
|
+
[:dasgn, :dvar, [:lit, 42]]]]]]]]
|
544
|
+
|
545
|
+
@heckler.process(@heckler.current_tree)
|
546
|
+
@heckler.reset_tree
|
547
|
+
@heckler.process(@heckler.current_tree)
|
548
|
+
assert_equal expected, @heckler.current_tree
|
549
|
+
end
|
550
|
+
|
551
|
+
end
|
552
|
+
|
553
|
+
class TestHeckleDasgncurr < HeckleTestCase
|
554
|
+
|
555
|
+
def setup
|
556
|
+
@nodes = [:dasgn_curr]
|
557
|
+
super
|
558
|
+
end
|
559
|
+
|
560
|
+
def test_dasgn_curr_val
|
561
|
+
expected = [:defn, :uses_dasgncurr,
|
562
|
+
[:scope,
|
563
|
+
[:block,
|
564
|
+
[:args],
|
565
|
+
[:iter,
|
566
|
+
[:fcall, :loop],
|
567
|
+
[:dasgn_curr, :dvar],
|
568
|
+
[:block,
|
569
|
+
[:dasgn_curr, :dvar, [:nil]],
|
570
|
+
[:dasgn_curr, :dvar, [:nil]]]]]]]
|
571
|
+
|
572
|
+
@heckler.process(@heckler.current_tree)
|
573
|
+
@heckler.reset_tree
|
574
|
+
@heckler.process(@heckler.current_tree)
|
575
|
+
assert_equal expected, @heckler.current_tree
|
576
|
+
end
|
577
|
+
|
578
|
+
def test_dasgn_curr_nil
|
579
|
+
expected = [:defn, :uses_dasgncurr,
|
580
|
+
[:scope,
|
581
|
+
[:block,
|
582
|
+
[:args],
|
583
|
+
[:iter,
|
584
|
+
[:fcall, :loop],
|
585
|
+
[:dasgn_curr, :dvar],
|
586
|
+
[:block,
|
587
|
+
[:dasgn_curr, :dvar, [:lit, 5]],
|
588
|
+
[:dasgn_curr, :dvar, [:lit, 42]]]]]]]
|
589
|
+
|
590
|
+
@heckler.process(@heckler.current_tree)
|
591
|
+
@heckler.reset_tree
|
592
|
+
@heckler.process(@heckler.current_tree)
|
593
|
+
@heckler.reset_tree
|
594
|
+
@heckler.process(@heckler.current_tree)
|
595
|
+
assert_equal expected, @heckler.current_tree
|
596
|
+
end
|
597
|
+
|
598
|
+
end
|
599
|
+
|
600
|
+
class TestHeckleIasgn < HeckleTestCase
|
601
|
+
|
602
|
+
def setup
|
603
|
+
@nodes = [:iasgn]
|
604
|
+
super
|
605
|
+
end
|
606
|
+
|
607
|
+
def test_iasgn_val
|
608
|
+
expected = [:defn, :uses_iasgn,
|
609
|
+
[:scope,
|
610
|
+
[:block,
|
611
|
+
[:args],
|
612
|
+
[:iasgn, :@ivar, [:nil]],
|
613
|
+
[:iasgn, :@ivar, [:nil]]]]]
|
614
|
+
|
615
|
+
@heckler.process(@heckler.current_tree)
|
616
|
+
assert_equal expected, @heckler.current_tree
|
617
|
+
end
|
618
|
+
|
619
|
+
def test_iasgn_nil
|
620
|
+
expected = [:defn, :uses_iasgn,
|
621
|
+
[:scope,
|
622
|
+
[:block,
|
623
|
+
[:args],
|
624
|
+
[:iasgn, :@ivar, [:lit, 5]],
|
625
|
+
[:iasgn, :@ivar, [:lit, 42]]]]]
|
626
|
+
|
627
|
+
@heckler.process(@heckler.current_tree)
|
628
|
+
@heckler.reset_tree
|
629
|
+
@heckler.process(@heckler.current_tree)
|
630
|
+
assert_equal expected, @heckler.current_tree
|
631
|
+
end
|
632
|
+
|
633
|
+
end
|
634
|
+
|
635
|
+
class TestHeckleGasgn < HeckleTestCase
|
636
|
+
|
637
|
+
def setup
|
638
|
+
@nodes = [:gasgn]
|
639
|
+
super
|
640
|
+
end
|
641
|
+
|
642
|
+
def test_gasgn_val
|
643
|
+
expected = [:defn, :uses_gasgn,
|
644
|
+
[:scope,
|
645
|
+
[:block,
|
646
|
+
[:args],
|
647
|
+
[:gasgn, :$gvar, [:nil]],
|
648
|
+
[:gasgn, :$gvar, [:nil]]]]]
|
649
|
+
|
650
|
+
@heckler.process(@heckler.current_tree)
|
651
|
+
assert_equal expected, @heckler.current_tree
|
652
|
+
end
|
653
|
+
|
654
|
+
def test_gasgn_nil
|
655
|
+
expected = [:defn, :uses_gasgn,
|
656
|
+
[:scope,
|
657
|
+
[:block,
|
658
|
+
[:args],
|
659
|
+
[:gasgn, :$gvar, [:lit, 5]],
|
660
|
+
[:gasgn, :$gvar, [:lit, 42]]]]]
|
661
|
+
|
662
|
+
@heckler.process(@heckler.current_tree)
|
663
|
+
@heckler.reset_tree
|
664
|
+
@heckler.process(@heckler.current_tree)
|
665
|
+
assert_equal expected, @heckler.current_tree
|
666
|
+
end
|
667
|
+
|
668
|
+
end
|
669
|
+
|
670
|
+
class TestHeckleLasgn < HeckleTestCase
|
671
|
+
|
672
|
+
def setup
|
673
|
+
@nodes = [:lasgn]
|
674
|
+
super
|
675
|
+
end
|
676
|
+
|
677
|
+
def test_lasgn_val
|
678
|
+
expected = [:defn, :uses_lasgn,
|
679
|
+
[:scope,
|
680
|
+
[:block,
|
681
|
+
[:args],
|
682
|
+
[:lasgn, :lvar, [:nil]],
|
683
|
+
[:lasgn, :lvar, [:nil]]]]]
|
684
|
+
|
685
|
+
@heckler.process(@heckler.current_tree)
|
686
|
+
assert_equal expected, @heckler.current_tree
|
687
|
+
end
|
688
|
+
|
689
|
+
def test_lasgn_nil
|
690
|
+
expected = [:defn, :uses_lasgn,
|
691
|
+
[:scope,
|
692
|
+
[:block,
|
693
|
+
[:args],
|
694
|
+
[:lasgn, :lvar, [:lit, 5]],
|
695
|
+
[:lasgn, :lvar, [:lit, 42]]]]]
|
696
|
+
|
697
|
+
@heckler.process(@heckler.current_tree)
|
698
|
+
@heckler.reset_tree
|
699
|
+
@heckler.process(@heckler.current_tree)
|
700
|
+
assert_equal expected, @heckler.current_tree
|
701
|
+
end
|
702
|
+
|
703
|
+
end
|
704
|
+
|
705
|
+
class TestHeckleMasgn < HeckleTestCase
|
706
|
+
|
707
|
+
def setup
|
708
|
+
@nodes = [:dasgn, :dasgn_curr, :iasgn, :gasgn, :lasgn]
|
709
|
+
super
|
710
|
+
end
|
711
|
+
|
712
|
+
def test_masgn
|
713
|
+
expected = [:defn, :uses_masgn,
|
714
|
+
[:scope,
|
715
|
+
[:block,
|
716
|
+
[:args],
|
717
|
+
[:masgn,
|
718
|
+
[:array,
|
719
|
+
[:lasgn, :_heckle_dummy],
|
720
|
+
[:gasgn, :$b],
|
721
|
+
[:lasgn, :c]],
|
722
|
+
[:array, [:lit, 5], [:lit, 6], [:lit, 7]]]]]]
|
723
|
+
|
724
|
+
@heckler.process(@heckler.current_tree)
|
725
|
+
assert_equal expected, @heckler.current_tree
|
726
|
+
end
|
727
|
+
|
728
|
+
end
|
729
|
+
|
metadata
CHANGED