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 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.2.0'
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
- @reporter.failure(failure)
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
- orig_exp = exp.deep_clone
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}: #{orig_exp.inspect}"
135
+ puts "Error: #{e.message} with: #{klass_name}##{method_name}: #{exp_copy.inspect}"
131
136
  raise e
132
137
  end
133
- @reporter.replacing(klass_name, method_name, src) if @@debug
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, /#{Regexp.escape(rand_string)}/]
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(10) + 1)*((-1)**rand(2))
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 "\nThe following mutations didn't cause test failures:\n"
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
- puts "\n#{failure}\n"
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#{src}\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
@@ -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
- klass_methods = klass.singleton_methods(false).collect {|meth| "self.#{meth}"}
51
+ puts
52
+
44
53
  methods = method_name ? Array(method_name) : klass.instance_methods(false) + klass_methods
45
-
46
- methods.each do |method_name|
47
- self.new(klass_name, method_name).validate
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.2.0
7
- date: 2007-01-15 00:00:00 -08:00
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.1.7
74
+ version: 1.2.0
75
75
  version: