heckle 1.2.0 → 1.3.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 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: