heckle 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +13 -0
- data/bin/heckle +6 -3
- data/lib/heckle.rb +42 -14
- data/lib/test_unit_heckler.rb +38 -11
- metadata +3 -3
data/History.txt
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
== 1.3.0 / 2007-02-12
|
2
|
+
|
3
|
+
* 1 major enhancement:
|
4
|
+
* Unified diffs for mutatated methods
|
5
|
+
* 4 minor enhancements:
|
6
|
+
* Now returns exit status 1 if failed.
|
7
|
+
* Added a simple report at the end.
|
8
|
+
* Runs are now sorted by method.
|
9
|
+
* Autodetects rails and changes test_pattern accordingly.
|
10
|
+
* 2 bug fixes:
|
11
|
+
* Aborts when an unknown method is supplied.
|
12
|
+
* Escapes slashes in random regexps.
|
13
|
+
|
1
14
|
== 1.2.0 / 2007-01-15
|
2
15
|
|
3
16
|
* 2 major enhancements:
|
data/bin/heckle
CHANGED
@@ -9,7 +9,7 @@ opts = OptionParser.new do |opts|
|
|
9
9
|
opts.on( "-v", "--verbose", "Loudly explain heckle run" ) do |opt|
|
10
10
|
TestUnitHeckler.debug = true
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
opts.on( "-V", "--version", "Prints Heckle's version number") do |opt|
|
14
14
|
puts "Heckle #{Heckle::VERSION}"
|
15
15
|
exit 0
|
@@ -28,7 +28,7 @@ opts = OptionParser.new do |opts|
|
|
28
28
|
|
29
29
|
Heckle::MUTATABLE_NODES.replace [:if, :while, :until]
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
opts.on( "-T", "--timeout SECONDS", "The maximum time for a test run in seconds",
|
33
33
|
"Used to catch infinite loops") do |timeout|
|
34
34
|
Heckle.timeout = timeout.to_i
|
@@ -41,6 +41,9 @@ opts = OptionParser.new do |opts|
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
looks_like_rails = test ?f, 'config/environment.rb'
|
45
|
+
TestUnitHeckler.test_pattern = "test/**/*.rb" if looks_like_rails
|
46
|
+
|
44
47
|
opts.parse!
|
45
48
|
|
46
49
|
impl = ARGV.shift
|
@@ -51,4 +54,4 @@ unless impl then
|
|
51
54
|
exit 1
|
52
55
|
end
|
53
56
|
|
54
|
-
TestUnitHeckler.validate(impl, meth)
|
57
|
+
exit TestUnitHeckler.validate(impl, meth) ? 0 : 1
|
data/lib/heckle.rb
CHANGED
@@ -2,6 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'parse_tree'
|
3
3
|
require 'ruby2ruby'
|
4
4
|
require 'timeout'
|
5
|
+
require 'tempfile'
|
5
6
|
|
6
7
|
class String
|
7
8
|
def to_class
|
@@ -10,10 +11,11 @@ class String
|
|
10
11
|
end
|
11
12
|
|
12
13
|
class Heckle < SexpProcessor
|
13
|
-
VERSION = '1.
|
14
|
+
VERSION = '1.3.0'
|
14
15
|
MUTATABLE_NODES = [:if, :lit, :str, :true, :false, :while, :until]
|
15
16
|
WINDOZE = RUBY_PLATFORM =~ /mswin/
|
16
17
|
NULL_PATH = WINDOZE ? 'NUL:' : '/dev/null'
|
18
|
+
DIFF = WINDOZE ? 'diff.exe' : 'diff'
|
17
19
|
|
18
20
|
attr_accessor(:klass_name, :method_name, :klass, :method, :mutatees,
|
19
21
|
:original_tree, :mutation_count, :node_count,
|
@@ -111,10 +113,13 @@ class Heckle < SexpProcessor
|
|
111
113
|
unless @failures.empty?
|
112
114
|
@reporter.no_failures
|
113
115
|
@failures.each do |failure|
|
114
|
-
@
|
116
|
+
original = RubyToRuby.new.process(@original_tree.deep_clone)
|
117
|
+
@reporter.failure(original, failure)
|
115
118
|
end
|
119
|
+
false
|
116
120
|
else
|
117
121
|
@reporter.no_surviving_mutants
|
122
|
+
true
|
118
123
|
end
|
119
124
|
end
|
120
125
|
|
@@ -123,14 +128,16 @@ class Heckle < SexpProcessor
|
|
123
128
|
end
|
124
129
|
|
125
130
|
def heckle(exp)
|
126
|
-
|
131
|
+
exp_copy = exp.deep_clone
|
127
132
|
src = begin
|
128
133
|
RubyToRuby.new.process(exp)
|
129
134
|
rescue => e
|
130
|
-
puts "Error: #{e.message} with: #{klass_name}##{method_name}: #{
|
135
|
+
puts "Error: #{e.message} with: #{klass_name}##{method_name}: #{exp_copy.inspect}"
|
131
136
|
raise e
|
132
137
|
end
|
133
|
-
|
138
|
+
|
139
|
+
original = RubyToRuby.new.process(@original_tree.deep_clone)
|
140
|
+
@reporter.replacing(klass_name, method_name, original, src) if @@debug
|
134
141
|
|
135
142
|
clean_name = method_name.to_s.gsub(/self\./, '')
|
136
143
|
self.count += 1
|
@@ -170,7 +177,7 @@ class Heckle < SexpProcessor
|
|
170
177
|
when Symbol
|
171
178
|
[:lit, rand_symbol]
|
172
179
|
when Regexp
|
173
|
-
[:lit,
|
180
|
+
[:lit, Regexp.new(Regexp.escape(rand_string.gsub(/\//, '\\/')))]
|
174
181
|
when Range
|
175
182
|
[:lit, rand_range]
|
176
183
|
end
|
@@ -344,7 +351,7 @@ class Heckle < SexpProcessor
|
|
344
351
|
end
|
345
352
|
|
346
353
|
def rand_number
|
347
|
-
(rand(
|
354
|
+
(rand(100) + 1)*((-1)**rand(2))
|
348
355
|
end
|
349
356
|
|
350
357
|
def rand_string
|
@@ -404,7 +411,6 @@ class Heckle < SexpProcessor
|
|
404
411
|
end
|
405
412
|
|
406
413
|
def warning(message)
|
407
|
-
puts
|
408
414
|
puts "!" * 70
|
409
415
|
puts "!!! #{message}"
|
410
416
|
puts "!" * 70
|
@@ -412,7 +418,6 @@ class Heckle < SexpProcessor
|
|
412
418
|
end
|
413
419
|
|
414
420
|
def info(message)
|
415
|
-
puts
|
416
421
|
puts "*"*70
|
417
422
|
puts "*** #{message}"
|
418
423
|
puts "*"*70
|
@@ -420,19 +425,42 @@ class Heckle < SexpProcessor
|
|
420
425
|
end
|
421
426
|
|
422
427
|
def no_failures
|
423
|
-
puts
|
428
|
+
puts
|
429
|
+
puts "The following mutations didn't cause test failures:"
|
430
|
+
puts
|
431
|
+
end
|
432
|
+
|
433
|
+
def diff(original, mutation)
|
434
|
+
length = [original.split(/\n/).size, mutation.split(/\n/).size].max
|
435
|
+
|
436
|
+
Tempfile.open("orig") do |a|
|
437
|
+
a.puts(original)
|
438
|
+
a.flush
|
439
|
+
|
440
|
+
Tempfile.open("fail") do |b|
|
441
|
+
b.puts(mutation)
|
442
|
+
b.flush
|
443
|
+
|
444
|
+
diff_flags = " "
|
445
|
+
|
446
|
+
output = `#{Heckle::DIFF} -U #{length} --label original #{a.path} --label mutation #{b.path}`
|
447
|
+
puts output.sub(/^@@.*?\n/, '')
|
448
|
+
puts
|
449
|
+
end
|
450
|
+
end
|
424
451
|
end
|
425
452
|
|
426
|
-
def failure(failure)
|
427
|
-
|
453
|
+
def failure(original, failure)
|
454
|
+
self.diff original, failure
|
428
455
|
end
|
429
456
|
|
430
457
|
def no_surviving_mutants
|
431
458
|
puts "No mutants survived. Cool!\n\n"
|
432
459
|
end
|
433
460
|
|
434
|
-
def replacing(klass_name, method_name, src)
|
435
|
-
puts "Replacing #{klass_name}##{method_name} with:\n\n
|
461
|
+
def replacing(klass_name, method_name, original, src)
|
462
|
+
puts "Replacing #{klass_name}##{method_name} with:\n\n"
|
463
|
+
diff(original, src)
|
436
464
|
end
|
437
465
|
|
438
466
|
def report_test_failures
|
data/lib/test_unit_heckler.rb
CHANGED
@@ -20,32 +20,59 @@ class TestUnitHeckler < Heckle
|
|
20
20
|
def self.validate(klass_name, method_name = nil)
|
21
21
|
load_test_files
|
22
22
|
klass = klass_name.to_class
|
23
|
-
|
23
|
+
|
24
|
+
# Does the method exist?
|
25
|
+
klass_methods = klass.singleton_methods(false).collect {|meth| "self.#{meth}"}
|
26
|
+
if method_name
|
27
|
+
if method_name =~ /self\./
|
28
|
+
abort "Unknown method: #{klass_name}.#{method_name.gsub('self.', '')}" unless klass_methods.include? method_name
|
29
|
+
else
|
30
|
+
abort "Unknown method: #{klass_name}##{method_name}" unless klass.instance_methods(false).include? method_name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
24
34
|
initial_time = Time.now
|
25
35
|
|
26
36
|
unless self.new(klass_name).tests_pass? then
|
27
37
|
abort "Initial run of tests failed... fix and run heckle again"
|
28
38
|
end
|
29
|
-
|
39
|
+
|
30
40
|
if self.guess_timeout?
|
31
41
|
running_time = (Time.now - initial_time)
|
32
42
|
adjusted_timeout = (running_time * 2 < 5) ? 5 : (running_time * 2)
|
33
43
|
self.timeout = adjusted_timeout
|
34
44
|
puts "Setting timeout at #{adjusted_timeout} seconds." if @@debug
|
35
|
-
|
45
|
+
|
36
46
|
end
|
37
|
-
|
38
|
-
puts "Initial tests pass. Let's rumble."
|
47
|
+
|
39
48
|
self.timeout = adjusted_timeout
|
40
|
-
|
49
|
+
|
41
50
|
puts "Initial tests pass. Let's rumble."
|
42
|
-
|
43
|
-
|
51
|
+
puts
|
52
|
+
|
44
53
|
methods = method_name ? Array(method_name) : klass.instance_methods(false) + klass_methods
|
45
|
-
|
46
|
-
|
47
|
-
|
54
|
+
|
55
|
+
counts = Hash.new(0)
|
56
|
+
methods.sort.each do |method_name|
|
57
|
+
result = self.new(klass_name, method_name).validate
|
58
|
+
counts[result] += 1
|
48
59
|
end
|
60
|
+
all_good = counts[false] == 0
|
61
|
+
|
62
|
+
puts "Heckle Results:"
|
63
|
+
puts
|
64
|
+
puts "Passed : %3d" % counts[true]
|
65
|
+
puts "Failed : %3d" % counts[false]
|
66
|
+
puts "Thick Skin: %3d" % counts[nil]
|
67
|
+
puts
|
68
|
+
|
69
|
+
if all_good then
|
70
|
+
puts "All heckling was thwarted! YAY!!!"
|
71
|
+
else
|
72
|
+
puts "Improve the tests and try again."
|
73
|
+
end
|
74
|
+
|
75
|
+
all_good
|
49
76
|
end
|
50
77
|
|
51
78
|
def initialize(klass_name=nil, method_name=nil)
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0.9
|
|
3
3
|
specification_version: 1
|
4
4
|
name: heckle
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.
|
7
|
-
date: 2007-
|
6
|
+
version: 1.3.0
|
7
|
+
date: 2007-02-13 00:00:00 -08:00
|
8
8
|
summary: Unit Test Sadism
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -71,5 +71,5 @@ dependencies:
|
|
71
71
|
requirements:
|
72
72
|
- - ">="
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: 1.
|
74
|
+
version: 1.2.0
|
75
75
|
version:
|