Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Syncing to latest svn with facelift and all around better heckling.

[git-p4: depot-paths = "//src/heckle/dev/": change = 2773]
  • Loading branch information...
commit 8fda09619c5e91cf55b10368a1611556bb93ae3d 1 parent 908b2d6
@kevinclark kevinclark authored
View
3  History.txt
@@ -1,5 +1,5 @@
== svn
-* 11 major enhancements:
+* 12 major enhancements:
* Able to roll back original method after processing.
* Can mutate numeric literals.
* Can mutate strings.
@@ -11,6 +11,7 @@
* Can mutate while and until.
* Can mutate regexes, ranges, symbols
* Can run against entire classes
+ * Command line options!
== 1.0.0 / 2006-10-22
View
27 bin/heckle
@@ -2,17 +2,34 @@
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
require 'test_unit_heckler'
+require 'optparse'
+
+opts = OptionParser.new do |opts|
+ opts.banner = "Usage: #{File.basename($0)} class_name [method_name]"
+ opts.on( "-v", "--verbose", "Loudly explain heckle run" ) do |opt|
+ Heckle.debug = true
+ end
+
+ opts.on( "-t", "--tests TEST_PATTERN",
+ "Location of tests (glob)" ) do |pattern|
+ Heckle.test_pattern = pattern
+ end
+
+ opts.on( "-h", "--help", "Show this message") do |opt|
+ puts opts
+ end
+end
+
+opts.parse!
-file = ARGV.shift
impl = ARGV.shift
meth = ARGV.shift
-unless file and impl then
- abort "usage: #{File.basename($0)} file.rb impl_class_name [impl_method_name]"
+unless impl then
+ puts opts
+ exit 1
end
-load file
-
if meth
heckle = TestUnitHeckler.new(impl, meth)
heckle.validate
View
93 lib/heckle.rb
@@ -10,7 +10,7 @@ def to_class
end
class Heckle < SexpProcessor
- VERSION = '1.0.0'
+ VERSION = '1.1.0'
MUTATABLE_NODES = [:if, :lit, :str, :true, :false, :while, :until]
@@ -18,26 +18,40 @@ class Heckle < SexpProcessor
:mutation_count, :node_count, :failures
@@debug = false;
+ @@test_pattern = 'test/test_*.rb'
+ @@tests_loaded = false;
def self.debug=(value)
@@debug = value
end
+ def self.test_pattern=(value)
+ @@test_pattern = value
+ end
+
def self.validate(klass_name)
- klass_name.to_class.instance_methods(false).each do |meth|
+ load_test_files
+ klass = klass_name.to_class
+ klass.instance_methods(false).each do |meth|
heckler = self.new(klass_name, meth)
heckler.validate
end
end
+ def self.load_test_files
+ @@tests_loaded = true
+ Dir.glob(@@test_pattern).each {|test| require test}
+ end
+
+
def initialize(klass_name=nil, method_name=nil)
super()
@klass_name, @method_name = klass_name, method_name.intern
@klass = @method = nil
-
- load_test_files
-
+
+ self.class.load_test_files unless @@tests_loaded
+
self.strict = false
self.auto_shift_type = true
self.expected = Array
@@ -72,29 +86,42 @@ def run_tests
end
end
- def load_test_files
- Dir.glob(ENV['TESTS'] || 'test/test_*.rb').each {|test| require test}
- end
-
############################################################
### Running the script
- def validate
- puts "Validating #{file}"
- if tests_pass? then
- puts "Tests passed -- heckling"
+ def validate
+ if silence_stream(STDOUT) { tests_pass? } then
+ if mutations_left == 0
+ puts
+ puts "!"*70
+ puts "!!! #{method_name} has a thick skin. There's nothing to heckle."
+ puts "!"*70
+ puts
+ return
+ end
+
+ puts
+ puts "*"*70
+ puts "*** #{klass_name}\##{method_name} loaded with #{mutations_left} possible mutations"
+ puts "*"*70
+ puts
- until @mutatees.collect {|k,v| v}.uniq.flatten.empty?
- reset_tree if current_tree != original_tree
+ puts "Initial tests pass. Let's rumble."
+
+ until mutations_left == 0
+ puts "#{mutations_left} mutations remaining..."
+ reset_tree
process current_tree
silence_stream(STDOUT) { run_tests }
end
+ reset # in case we're validating again. we should clean up.
+
unless @failures.empty?
- puts "The following mutations didn't cause test failures:\n"
+ puts "\nThe following mutations didn't cause test failures:\n"
@failures.each {|failure| puts "\n#{failure}\n"}
else
- puts "No mutants survived. Cool!"
+ puts "No mutants survived. Cool!\n\n"
end
else
puts "Tests failed... fix and run heckle again"
@@ -106,7 +133,6 @@ def record_passing_mutation
end
def heckle(exp)
- puts "\nHeckling #{klass_name}##{method_name}\n" if @@debug
src = RubyToRuby.new.process(exp)
puts "Replacing #{klass_name}##{method_name} with:\n\n#{src}\n" if @@debug
klass_name.to_class.class_eval(src)
@@ -133,14 +159,14 @@ def process_lit(exp)
def mutate_lit(exp)
case exp[1]
when Fixnum, Float, Bignum
- [:lit, exp[1] + rand(10)]
+ [:lit, exp[1] + rand_number]
when Symbol
- [:lit, :"#{rand_string}"]
+ [:lit, rand_symbol]
when Regexp
[:lit, /#{Regexp.escape(rand_string)}/]
when Range
[:lit, rand_range]
- end
+ end
end
def process_str(exp)
@@ -288,10 +314,18 @@ def already_mutated?
@mutated
end
+ def mutations_left
+ @mutatees.inject(0) {|sum, mut| sum = sum + mut.last.size }
+ end
+
def current_code
RubyToRuby.translate(klass_name.to_class, method_name)
end
+ def rand_number
+ (rand(10) + 1)*((-1)**rand(2))
+ end
+
def rand_string
size = rand(100)
str = ""
@@ -299,6 +333,13 @@ def rand_string
str
end
+ def rand_symbol
+ letters = ('a'..'z').to_a + ('A'..'Z').to_a
+ str = ""
+ rand(100).times { str << letters[rand(letters.size)] }
+ :"#{str}"
+ end
+
def rand_range
min = rand(50)
max = min + rand(50)
@@ -323,12 +364,14 @@ def report_test_failures
#
# puts 'But this will'
def silence_stream(stream)
- old_stream = stream.dup
- stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
- stream.sync = true
+ unless @@debug
+ old_stream = stream.dup
+ stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
+ stream.sync = true
+ end
yield
ensure
- stream.reopen(old_stream)
+ stream.reopen(old_stream) unless @@debug
end
end
View
3  test/fixtures/heckled.rb
@@ -93,6 +93,9 @@ def uses_ranges
i = 1..4
end
+ def uses_nothing
+ end
+
private
def some_func; end
View
15 test/test_heckle.rb
@@ -62,6 +62,10 @@ def test_should_grab_mutatees_from_method
assert_equal expected, @heckler.mutatees
end
+
+ def test_should_count_mutatees_left
+ assert_equal 10, @heckler.mutations_left
+ end
def test_reset
original_tree = @heckler.current_tree.deep_clone
@@ -127,6 +131,10 @@ class Heckle
def rand(*args)
5
end
+
+ def rand_number(*args)
+ 5
+ end
end
class TestHeckleNumbers < Test::Unit::TestCase
@@ -135,6 +143,7 @@ def setup
end
def test_literals_should_flip_one_at_a_time
+ assert_equal 3, @heckler.mutations_left
expected = [:defn,
:uses_numeric_literals,
[:scope,
@@ -181,6 +190,12 @@ def teardown
end
end
+class Heckle
+ def rand_symbol
+ :"l33t h4x0r"
+ end
+end
+
class TestHeckleSymbols < Test::Unit::TestCase
def setup
@heckler = Heckle.new("Heckled", "uses_symbols")
Please sign in to comment.
Something went wrong with that request. Please try again.