heckle 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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