Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Pulled flat history from subversion.

[git-p4: depot-paths = "//src/heckle/dev/": change = 2769]
  • Loading branch information...
commit 908b2d6da71e790cd3de2375c73794b15ff1fff9 0 parents
@zenspider zenspider authored
19 History.txt
@@ -0,0 +1,19 @@
+== svn
+* 11 major enhancements:
+ * Able to roll back original method after processing.
+ * Can mutate numeric literals.
+ * Can mutate strings.
+ * Can mutate a node at a time.
+ * Can mutate if/unless
+ * Decoupled from Test::Unit
+ * Cleaner output
+ * Can mutate true and false.
+ * Can mutate while and until.
+ * Can mutate regexes, ranges, symbols
+ * Can run against entire classes
+
+== 1.0.0 / 2006-10-22
+
+* 1 major enhancement
+ * Birthday!
+
13 Manifest.txt
@@ -0,0 +1,13 @@
+History.txt
+Manifest.txt
+README.txt
+Rakefile
+bin/heckle
+lib/heckle.rb
+lib/test_unit_heckler.rb
+sample/Rakefile
+sample/changes.log
+sample/lib/heckled.rb
+sample/test/test_heckled.rb
+test/fixtures/heckled.rb
+test/test_heckle.rb
49 README.txt
@@ -0,0 +1,49 @@
+heckle
+ by Ryan Davis and Kevin Clark
+ http://www.rubyforge.org/projects/seattlerb
+
+== DESCRIPTION:
+
+Unit Testing Sadism. More description coming soon. I'm punting to get
+this launched ASAP.
+
+== FEATURES/PROBLEMS:
+
+* needs some love. haha.
+
+== SYNOPSYS:
+
+ FIX
+
+== REQUIREMENTS:
+
++ FIX
+
+== INSTALL:
+
++ sudo gem install heckle
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) 2006 Ryan Davis and Kevin Clark
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 Rakefile
@@ -0,0 +1,17 @@
+# -*- ruby -*-
+
+require 'rubygems'
+require 'hoe'
+require './lib/heckle.rb'
+
+Hoe.new('heckle', Heckle::VERSION) do |p|
+ p.rubyforge_name = 'seattlerb'
+ p.summary = 'Unit Test Sadism'
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
+
+ p.extra_deps << ['ruby2ruby', '>= 1.1.0']
+end
+
+# vim: syntax=Ruby
21 bin/heckle
@@ -0,0 +1,21 @@
+#!/usr/local/bin/ruby
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
+require 'test_unit_heckler'
+
+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]"
+end
+
+load file
+
+if meth
+ heckle = TestUnitHeckler.new(impl, meth)
+ heckle.validate
+else
+ TestUnitHeckler.validate(impl)
+end
334 lib/heckle.rb
@@ -0,0 +1,334 @@
+require 'rubygems'
+require 'parse_tree'
+require 'ruby2ruby'
+require 'logger'
+
+class String
+ def to_class
+ split(/::/).inject(Object) { |klass, name| klass.const_get(name) }
+ end
+end
+
+class Heckle < SexpProcessor
+ VERSION = '1.0.0'
+
+ MUTATABLE_NODES = [:if, :lit, :str, :true, :false, :while, :until]
+
+ attr_accessor :file, :klass_name, :method_name, :klass, :method, :mutatees, :original_tree,
+ :mutation_count, :node_count, :failures
+
+ @@debug = false;
+
+ def self.debug=(value)
+ @@debug = value
+ end
+
+ def self.validate(klass_name)
+ klass_name.to_class.instance_methods(false).each do |meth|
+ heckler = self.new(klass_name, meth)
+ heckler.validate
+ end
+ 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.strict = false
+ self.auto_shift_type = true
+ self.expected = Array
+
+ @mutatees = Hash.new
+ @mutation_count = Hash.new
+ @node_count = Hash.new
+
+ MUTATABLE_NODES.each {|type| @mutatees[type] = [] }
+
+ @failures = []
+
+ @mutated = false
+
+ grab_mutatees
+
+ @original_tree = current_tree.deep_clone
+ @original_mutatees = mutatees.deep_clone
+ end
+
+ ############################################################
+ ### Overwrite test_pass?, load_test_files for your own Heckle runner.
+ def tests_pass?
+ raise NotImplementedError
+ end
+
+ def run_tests
+ if tests_pass? then
+ record_passing_mutation
+ else
+ report_test_failures
+ 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"
+
+ until @mutatees.collect {|k,v| v}.uniq.flatten.empty?
+ reset_tree if current_tree != original_tree
+ process current_tree
+ silence_stream(STDOUT) { run_tests }
+ end
+
+ unless @failures.empty?
+ puts "The following mutations didn't cause test failures:\n"
+ @failures.each {|failure| puts "\n#{failure}\n"}
+ else
+ puts "No mutants survived. Cool!"
+ end
+ else
+ puts "Tests failed... fix and run heckle again"
+ end
+ end
+
+ def record_passing_mutation
+ @failures << current_code
+ 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)
+ end
+
+ ############################################################
+ ### Processing sexps
+
+ def process_defn(exp)
+ self.method = exp.shift
+ result = [:defn, method]
+ result << process(exp.shift) until exp.empty?
+ heckle(result) if method == method_name
+ @mutated = false
+ reset_node_count
+
+ return result
+ end
+
+ def process_lit(exp)
+ mutate_node [:lit, exp.shift]
+ end
+
+ def mutate_lit(exp)
+ case exp[1]
+ when Fixnum, Float, Bignum
+ [:lit, exp[1] + rand(10)]
+ when Symbol
+ [:lit, :"#{rand_string}"]
+ when Regexp
+ [:lit, /#{Regexp.escape(rand_string)}/]
+ when Range
+ [:lit, rand_range]
+ end
+ end
+
+ def process_str(exp)
+ mutate_node [:str, exp.shift]
+ end
+
+ def mutate_str(node)
+ [:str, rand_string]
+ end
+
+ def process_if(exp)
+ mutate_node [:if, process(exp.shift), process(exp.shift), process(exp.shift)]
+ end
+
+ def mutate_if(node)
+ [:if, node[1], node[3], node[2]]
+ end
+
+ def process_true(exp)
+ mutate_node [:true]
+ end
+
+ def mutate_true(node)
+ [:false]
+ end
+
+ def process_false(exp)
+ mutate_node [:false]
+ end
+
+ def mutate_false(node)
+ [:true]
+ end
+
+ def process_while(exp)
+ cond, body, head_controlled = grab_conditional_loop_parts(exp)
+ mutate_node [:while, cond, body, head_controlled]
+ end
+
+ def mutate_while(node)
+ [:until, node[1], node[2], node[3]]
+ end
+
+ def process_until(exp)
+ cond, body, head_controlled = grab_conditional_loop_parts(exp)
+ mutate_node [:until, cond, body, head_controlled]
+ end
+
+ def mutate_until(node)
+ [:while, node[1], node[2], node[3]]
+ end
+
+ def mutate_node(node)
+ raise UnsupportedNodeError unless respond_to? "mutate_#{node.first}"
+ increment_node_count node
+ if should_heckle? node
+ increment_mutation_count node
+ return send("mutate_#{node.first}", node)
+ else
+ node
+ end
+ end
+
+ ############################################################
+ ### Tree operations
+
+ def walk_and_push(node)
+ return unless node.respond_to? :each
+ return if node.is_a? String
+ node.each { |child| walk_and_push(child) }
+ if MUTATABLE_NODES.include? node.first
+ @mutatees[node.first.to_sym].push(node)
+ mutation_count[node] = 0
+ end
+ end
+
+ def grab_mutatees
+ walk_and_push(current_tree)
+ end
+
+ def current_tree
+ ParseTree.translate(klass_name.to_class, method_name)
+ end
+
+ def reset
+ reset_tree
+ reset_mutatees
+ reset_mutation_count
+ end
+
+ def reset_tree
+ return unless original_tree != current_tree
+ @mutated = false
+
+ r2r = RubyToRuby.new
+ src = r2r.process(original_tree.deep_clone)
+ klass_name.to_class.class_eval(src)
+ end
+
+ def reset_mutatees
+ @mutatees = @original_mutatees.deep_clone
+ end
+
+ def reset_mutation_count
+ mutation_count.each {|k,v| mutation_count[k] = 0}
+ end
+
+ def reset_node_count
+ node_count.each {|k,v| node_count[k] = 0}
+ end
+
+ def increment_node_count(node)
+ if node_count[node].nil?
+ node_count[node] = 1
+ else
+ node_count[node] += 1
+ end
+ end
+
+ def increment_mutation_count(node)
+ # So we don't re-mutate this later if the tree is reset
+ mutation_count[node] += 1
+ @mutatees[node.first].delete_at(@mutatees[node.first].index(node))
+ @mutated = true
+ end
+
+ ############################################################
+ ### Convenience methods
+
+ def should_heckle?(exp)
+ return false unless method == method_name
+ mutation_count[exp] = 0 if mutation_count[exp].nil?
+ return false if node_count[exp] <= mutation_count[exp]
+ mutatees[exp.first.to_sym].include?(exp) && !already_mutated?
+ end
+
+ def grab_conditional_loop_parts(exp)
+ cond = process(exp.shift)
+ body = process(exp.shift)
+ head_controlled = exp.shift
+ return cond, body, head_controlled
+ end
+
+ def already_mutated?
+ @mutated
+ end
+
+ def current_code
+ RubyToRuby.translate(klass_name.to_class, method_name)
+ end
+
+ def rand_string
+ size = rand(100)
+ str = ""
+ size.times { str << rand(126).chr }
+ str
+ end
+
+ def rand_range
+ min = rand(50)
+ max = min + rand(50)
+ min..max
+ end
+
+ def abort_and_report_incomplete_tests
+ abort "*** Tests passed again after heckling, your tests are incomplete"
+ end
+
+ def report_test_failures
+ puts "Tests failed -- this is good"
+ end
+
+ # silence_stream taken from Rails ActiveSupport reporting.rb
+
+ # Silences any stream for the duration of the block.
+ #
+ # silence_stream(STDOUT) do
+ # puts 'This will never be seen'
+ # end
+ #
+ # puts 'But this will'
+ def silence_stream(stream)
+ old_stream = stream.dup
+ stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
+ stream.sync = true
+ yield
+ ensure
+ stream.reopen(old_stream)
+ end
+
+end
14 lib/test_unit_heckler.rb
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+
+require 'test/unit/autorunner'
+require 'heckle'
+
+class TestUnitHeckler < Heckle
+ def initialize(klass_name=nil, method_name=nil)
+ super(klass_name, method_name)
+ end
+
+ def tests_pass?
+ Test::Unit::AutoRunner.run
+ end
+end
16 sample/Rakefile
@@ -0,0 +1,16 @@
+# -*- ruby -*-
+require 'rubygems'
+require 'rake/testtask'
+
+task :default => :test
+
+desc "Run basic tests"
+Rake::TestTask.new { |t|
+ t.pattern = 'test/test_*.rb'
+ t.verbose = true
+ t.warning = true
+}
+
+
+
+# vim: syntax=Ruby
91 sample/changes.log
@@ -0,0 +1,91 @@
+# Logfile created on Fri Nov 10 22:52:10 PST 2006 by logger.rb/1.5.2.7
+I, [2006-11-10T22:52:10.310486 #498] INFO -- : Validating
+I, [2006-11-10T22:52:10.326577 #498] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T22:52:10.328115 #498] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "I\rmeo&+eIZ{bD2dj1Z_e\001{Y!](CbI'!`@\020Wlg4,w\t\02181/4J\t\v@][ L!Xy[\r\\MJ[0.+gh}\001ks\026_-BK")
+ (@names << "Hello, Jeff")
+ (@names << "Hi, Frank")
+end
+I, [2006-11-10T22:52:10.344547 #498] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T22:52:10.346021 #498] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "Hello, Robert")
+ (@names << "\030ym+3\f:\031\023`\r:O\a")
+ (@names << "Hi, Frank")
+end
+I, [2006-11-10T22:52:10.356743 #498] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T22:52:10.358234 #498] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "Hello, Robert")
+ (@names << "Hello, Jeff")
+ (@names << "o,NY\020\027V0v\032dEw\010<*4\026uh$\026\010vhl(A\"ybX$sd\"M\006qbH\032TAV/")
+end
+I, [2006-11-10T22:52:10.369174 #498] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T22:52:10.370636 #498] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "Hello, Robert")
+ (@names << "Hello, Jeff")
+ (@names << "Hi, Frank")
+end
+I, [2006-11-10T23:17:54.953429 #569] INFO -- : Validating
+I, [2006-11-10T23:19:05.369315 #570] INFO -- : Validating
+I, [2006-11-10T23:20:01.512545 #571] INFO -- : Validating
+I, [2006-11-10T23:20:08.080030 #572] INFO -- : Validating
+I, [2006-11-10T23:20:08.100363 #572] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T23:20:08.101943 #572] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "\034\000\021[\r\aM!L=qfU'#")
+ (@names << "Hello, Jeff")
+ (@names << "Hi, Frank")
+end
+I, [2006-11-10T23:20:08.120455 #572] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T23:20:08.122176 #572] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "Hello, Robert")
+ (@names << "\r\025 $\n<z\037Z5<VSsja\026/\t>(&6P^X<\000\t..[<QGM\036\ei$\010RL\030J.vEsJ,")
+ (@names << "Hi, Frank")
+end
+I, [2006-11-10T23:20:08.141360 #572] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T23:20:08.143164 #572] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "Hello, Robert")
+ (@names << "Hello, Jeff")
+ (@names << ":\032lTh\031\037sw8\004!d\021\017n8\"\f8ucDs:.H\026n\010Bb|Is?p\027XR<4+%qfQ|^(\022\aN\024<e)\023P_a.j\000(\037bRe\"&xIO\001\f5\021o?\005\a\034)V@WmAb|\001(Z\020\032")
+end
+I, [2006-11-10T23:20:08.162167 #572] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T23:20:08.163769 #572] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "Hello, Robert")
+ (@names << "Hello, Jeff")
+ (@names << "Hi, Frank")
+end
+I, [2006-11-10T23:20:30.047573 #574] INFO -- : Validating
+I, [2006-11-10T23:20:30.064031 #574] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T23:20:30.065826 #574] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "B]ms\026*\025\034\035%\026d_!\023\017c8\f\v\003G\fSAdh\025kC8&!,1sc\032htACl6\023rgq[j\016J>\003$I\025\037\001\027E$/P\025<Uu+#MCN%.mGFw\005qje 1D\037{wQL")
+ (@names << "Hello, Jeff")
+ (@names << "Hi, Frank")
+end
+I, [2006-11-10T23:20:30.076369 #574] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T23:20:30.078079 #574] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "Hello, Robert")
+ (@names << "A\002\tu<:r\021L\n`yArP\010\032]n\n\036+@?sh\e\020\023{\027I<t+QbD\000\006:3{w\f\\XCH\032\026xSogO\036)*B_E\034;\016\\Mj")
+ (@names << "Hi, Frank")
+end
+I, [2006-11-10T23:20:30.089013 #574] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T23:20:30.090484 #574] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "Hello, Robert")
+ (@names << "Hello, Jeff")
+ (@names << "Z\023C#\025qg{Cb++W\r\\dtEbq4P2c")
+end
+I, [2006-11-10T23:20:30.101238 #574] INFO -- : Heckling Heckled#uses_strings
+I, [2006-11-10T23:20:30.102788 #574] INFO -- : Replacing #uses_strings with:
+def uses_strings
+ (@names << "Hello, Robert")
+ (@names << "Hello, Jeff")
+ (@names << "Hi, Frank")
+end
63 sample/lib/heckled.rb
@@ -0,0 +1,63 @@
+class Heckled
+ attr_accessor :names
+
+ def initialize
+ @names = []
+ end
+
+ def uses_while
+ i = 1
+ while i < 10
+ i += 1
+ end
+ i
+ end
+
+ def uses_until
+ i = 1
+ until i >= 10
+ i += 1
+ end
+ i
+ end
+
+ def uses_numeric_literals
+ i = 1
+ i += 10
+ i -= 3.5
+ end
+
+ def uses_strings
+ @names << "Hello, Robert"
+ @names << "Hello, Jeff"
+ @names << "Hi, Frank"
+ end
+
+ def uses_different_types
+ i = 1
+ b = "Hello, Joe"
+ c = 3.3
+ end
+
+ def uses_the_same_literal
+ i = 1
+ i = 1
+ i = 1
+ end
+
+ def uses_if
+ if true
+ if false
+ return
+ end
+ end
+ end
+
+ def uses_unless
+ unless true
+ if false
+ return
+ end
+ end
+ end
+end
19 sample/test/test_heckled.rb
@@ -0,0 +1,19 @@
+#!/usr/bin/env ruby
+#
+# Created by Kevin Clark on 2006-11-10.
+# Copyright (c) 2006. All rights reserved.
+
+require "test/unit"
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
+require "heckled"
+
+class TestHeckled < Test::Unit::TestCase
+ def setup
+ @heckled = Heckled.new
+ end
+ def test_uses_strings
+ @heckled.uses_strings
+ assert_equal ["Hello, Robert", "Hello, Jeff", "Hi, Frank"], @heckled.names
+ end
+end
100 test/fixtures/heckled.rb
@@ -0,0 +1,100 @@
+class Heckled
+ attr_accessor :names
+
+ def initialize
+ @names = []
+ end
+
+ def uses_many_things
+ i = 1
+ while i < 10
+ i += 1
+ until some_func
+ some_other_func
+ end
+ return true if "hi there" == "changeling"
+ return false
+ end
+ i
+ end
+
+ def uses_while
+ while some_func
+ some_other_func
+ end
+ end
+
+ def uses_until
+ until some_func
+ some_other_func
+ end
+ end
+
+ def uses_numeric_literals
+ i = 1
+ i += 2147483648
+ i -= 3.5
+ end
+
+ def uses_strings
+ @names << "Hello, Robert"
+ @names << "Hello, Jeff"
+ @names << "Hi, Frank"
+ end
+
+ def uses_different_types
+ i = 1
+ b = "Hello, Joe"
+ c = 3.3
+ end
+
+ def uses_the_same_literal
+ i = 1
+ i = 1
+ i = 1
+ end
+
+ def uses_if
+ if some_func
+ if some_other_func
+ return
+ end
+ end
+ end
+
+ def uses_boolean
+ true
+ false
+ end
+
+ def uses_unless
+ unless true
+ if false
+ return
+ end
+ end
+ end
+
+ def uses_symbols
+ i = :blah
+ i = :blah
+ i = :and_blah
+ end
+
+ def uses_regexes
+ i = /a.*/
+ i = /c{2,4}+/
+ i = /123/
+ end
+
+ def uses_ranges
+ i = 6..100
+ i = -1..9
+ i = 1..4
+ end
+
+ private
+
+ def some_func; end
+ def some_other_func; end
+end
637 test/test_heckle.rb
@@ -0,0 +1,637 @@
+$:.unshift(File.dirname(__FILE__) + '/fixtures')
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+
+require 'test/unit/testcase'
+require 'test/unit' if $0 == __FILE__
+require 'test_unit_heckler'
+require 'heckled'
+
+class TestHeckle < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_many_things")
+ end
+
+ def test_should_set_original_tree
+ expected = [:defn,
+ :uses_many_things,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 1]],
+ [:while,
+ [:call, [:lvar, :i], :<, [:array, [:lit, 10]]],
+ [:block,
+ [:lasgn, :i, [:call, [:lvar, :i], :+, [:array, [:lit, 1]]]],
+ [:until, [:vcall, :some_func], [:vcall, :some_other_func], true],
+ [:if,
+ [:call, [:str, "hi there"], :==, [:array, [:str, "changeling"]]],
+ [:return, [:true]],
+ nil],
+ [:return, [:false]]],
+ true],
+ [:lvar, :i]]]]
+
+ assert_equal expected, @heckler.original_tree
+ end
+
+ def test_should_grab_mutatees_from_method
+ # expected is from tree of uses_while
+ expected = {
+ :lit=>[[:lit, 1], [:lit, 10], [:lit, 1]],
+ :if=>[[:if,
+ [:call, [:str, "hi there"], :==, [:array, [:str, "changeling"]]],
+ [:return, [:true]],
+ nil]],
+ :str => [[:str, "hi there"], [:str, "changeling"]],
+ :true => [[:true]],
+ :false => [[:false]],
+ :while=>
+ [[:while,
+ [:call, [:lvar, :i], :<, [:array, [:lit, 10]]],
+ [:block,
+ [:lasgn, :i, [:call, [:lvar, :i], :+, [:array, [:lit, 1]]]],
+ [:until, [:vcall, :some_func], [:vcall, :some_other_func], true],
+ [:if,
+ [:call, [:str, "hi there"], :==, [:array, [:str, "changeling"]]],
+ [:return, [:true]],
+ nil],
+ [:return, [:false]]],
+ true]],
+ :until => [[:until, [:vcall, :some_func], [:vcall, :some_other_func], true]]
+ }
+
+ assert_equal expected, @heckler.mutatees
+ end
+
+ def test_reset
+ original_tree = @heckler.current_tree.deep_clone
+ original_mutatees = @heckler.mutatees.deep_clone
+
+ 3.times { @heckler.process(@heckler.current_tree) }
+
+ assert_not_equal original_tree, @heckler.current_tree
+ assert_not_equal original_mutatees, @heckler.mutatees
+
+ @heckler.reset
+ assert_equal original_tree, @heckler.current_tree
+ assert_equal original_mutatees, @heckler.mutatees
+ end
+
+ def test_reset_tree
+ original_tree = @heckler.current_tree.deep_clone
+
+ @heckler.process(@heckler.current_tree)
+ assert_not_equal original_tree, @heckler.current_tree
+
+ @heckler.reset_tree
+ assert_equal original_tree, @heckler.current_tree
+ end
+
+ def test_reset_should_work_over_several_process_calls
+ original_tree = @heckler.current_tree.deep_clone
+ original_mutatees = @heckler.mutatees.deep_clone
+
+ @heckler.process(@heckler.current_tree)
+ assert_not_equal original_tree, @heckler.current_tree
+ assert_not_equal original_mutatees, @heckler.mutatees
+
+ @heckler.reset
+ assert_equal original_tree, @heckler.current_tree
+ assert_equal original_mutatees, @heckler.mutatees
+
+ 3.times { @heckler.process(@heckler.current_tree) }
+ assert_not_equal original_tree, @heckler.current_tree
+ assert_not_equal original_mutatees, @heckler.mutatees
+
+ @heckler.reset
+ assert_equal original_tree, @heckler.current_tree
+ assert_equal original_mutatees, @heckler.mutatees
+ end
+
+ def test_reset_mutatees
+ original_mutatees = @heckler.mutatees.deep_clone
+
+ @heckler.process(@heckler.current_tree)
+ assert_not_equal original_mutatees, @heckler.mutatees
+
+ @heckler.reset_mutatees
+ assert_equal original_mutatees, @heckler.mutatees
+ end
+
+ def teardown
+ @heckler.reset
+ end
+end
+
+class Heckle
+ def rand(*args)
+ 5
+ end
+end
+
+class TestHeckleNumbers < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_numeric_literals")
+ end
+
+ def test_literals_should_flip_one_at_a_time
+ expected = [:defn,
+ :uses_numeric_literals,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 6]],
+ [:lasgn, :i, [:call, [:lvar, :i], :+, [:array, [:lit, 2147483648]]]],
+ [:lasgn, :i, [:call, [:lvar, :i], :-, [:array, [:lit, 3.5]]]]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_numeric_literals,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 1]],
+ [:lasgn, :i, [:call, [:lvar, :i], :+, [:array, [:lit, 2147483653]]]],
+ [:lasgn, :i, [:call, [:lvar, :i], :-, [:array, [:lit, 3.5]]]]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_numeric_literals,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 1]],
+ [:lasgn, :i, [:call, [:lvar, :i], :+, [:array, [:lit, 2147483648]]]],
+ [:lasgn, :i, [:call, [:lvar, :i], :-, [:array, [:lit, 8.5]]]]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+
+ def teardown
+ @heckler.reset
+ end
+end
+
+class TestHeckleSymbols < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_symbols")
+ end
+
+ def test_default_structure
+ expected = [:defn,
+ :uses_symbols,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, :blah]],
+ [:lasgn, :i, [:lit, :blah]],
+ [:lasgn, :i, [:lit, :and_blah]]]]]
+ assert_equal expected, @heckler.current_tree
+ end
+
+
+ def test_should_randomize_symbol
+ expected = [:defn,
+ :uses_symbols,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, :"l33t h4x0r"]],
+ [:lasgn, :i, [:lit, :blah]],
+ [:lasgn, :i, [:lit, :and_blah]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_symbols,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, :blah]],
+ [:lasgn, :i, [:lit, :"l33t h4x0r"]],
+ [:lasgn, :i, [:lit, :and_blah]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_symbols,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, :blah]],
+ [:lasgn, :i, [:lit, :blah]],
+ [:lasgn, :i, [:lit, :"l33t h4x0r"]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+end
+
+class TestHeckleRegexes < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_regexes")
+ end
+
+ def test_default_structure
+ expected = [:defn,
+ :uses_regexes,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, /a.*/]],
+ [:lasgn, :i, [:lit, /c{2,4}+/]],
+ [:lasgn, :i, [:lit, /123/]]]]]
+ assert_equal expected, @heckler.current_tree
+ end
+
+
+ def test_should_randomize_symbol
+ expected = [:defn,
+ :uses_regexes,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, /l33t\ h4x0r/]],
+ [:lasgn, :i, [:lit, /c{2,4}+/]],
+ [:lasgn, :i, [:lit, /123/]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_regexes,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, /a.*/]],
+ [:lasgn, :i, [:lit, /l33t\ h4x0r/]],
+ [:lasgn, :i, [:lit, /123/]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_regexes,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, /a.*/]],
+ [:lasgn, :i, [:lit, /c{2,4}+/]],
+ [:lasgn, :i, [:lit, /l33t\ h4x0r/]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+end
+
+class TestHeckleRanges < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_ranges")
+ end
+
+ def test_default_structure
+ expected = [:defn,
+ :uses_ranges,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 6..100]],
+ [:lasgn, :i, [:lit, -1..9]],
+ [:lasgn, :i, [:lit, 1..4]]]]]
+ assert_equal expected, @heckler.current_tree
+ end
+
+ def test_should_randomize_symbol
+ expected = [:defn,
+ :uses_ranges,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 5..10]],
+ [:lasgn, :i, [:lit, -1..9]],
+ [:lasgn, :i, [:lit, 1..4]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_ranges,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 6..100]],
+ [:lasgn, :i, [:lit, 5..10]],
+ [:lasgn, :i, [:lit, 1..4]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_ranges,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 6..100]],
+ [:lasgn, :i, [:lit, -1..9]],
+ [:lasgn, :i, [:lit, 5..10]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+end
+
+
+class TestHeckleSameLiteral < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_the_same_literal")
+ end
+
+ def teardown
+ @heckler.reset
+ end
+
+ def test_original_tree
+ expected = [:defn,
+ :uses_the_same_literal,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 1]],
+ [:lasgn, :i, [:lit, 1]],
+ [:lasgn, :i, [:lit, 1]]]]]
+
+ assert_equal expected, @heckler.current_tree
+ end
+
+ def test_literals_should_flip_one_at_a_time
+ # structure of uses_numeric_literals with first literal +5 (from stubbed rand)
+ expected = [:defn,
+ :uses_the_same_literal,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 6]],
+ [:lasgn, :i, [:lit, 1]],
+ [:lasgn, :i, [:lit, 1]]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_the_same_literal,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 1]],
+ [:lasgn, :i, [:lit, 6]],
+ [:lasgn, :i, [:lit, 1]]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_the_same_literal,
+ [:scope,
+ [:block,
+ [:args],
+ [:lasgn, :i, [:lit, 1]],
+ [:lasgn, :i, [:lit, 1]],
+ [:lasgn, :i, [:lit, 6]]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+end
+
+class Heckle
+ def rand_string
+ "l33t h4x0r"
+ end
+end
+
+class TestHeckleStrings < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_strings")
+ end
+
+ def teardown
+ @heckler.reset
+ end
+
+ def test_default_structure
+ expected = [:defn,
+ :uses_strings,
+ [:scope,
+ [:block,
+ [:args],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "Hello, Robert"]]],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "Hello, Jeff"]]],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "Hi, Frank"]]]]]]
+ assert_equal expected, @heckler.current_tree
+ end
+
+ def test_should_heckle_string_literals
+ expected = [:defn,
+ :uses_strings,
+ [:scope,
+ [:block,
+ [:args],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "l33t h4x0r"]]],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "Hello, Jeff"]]],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "Hi, Frank"]]]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_strings,
+ [:scope,
+ [:block,
+ [:args],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "Hello, Robert"]]],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "l33t h4x0r"]]],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "Hi, Frank"]]]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_strings,
+ [:scope,
+ [:block,
+ [:args],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "Hello, Robert"]]],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "Hello, Jeff"]]],
+ [:call, [:ivar, :@names], :<<, [:array, [:str, "l33t h4x0r"]]]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+end
+
+class TestHeckleIfs < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_if")
+ end
+
+ def teardown
+ @heckler.reset
+ end
+
+ def test_default_structure
+ expected = [:defn,
+ :uses_if,
+ [:scope,
+ [:block,
+ [:args],
+ [:if,
+ [:vcall, :some_func],
+ [:if, [:vcall, :some_other_func], [:return], nil],
+ nil]]]]
+
+
+ assert_equal expected, @heckler.current_tree
+ end
+
+ def test_should_flip_if_to_unless
+ expected = [:defn,
+ :uses_if,
+ [:scope,
+ [:block,
+ [:args],
+ [:if,
+ [:vcall, :some_func],
+ [:if, [:vcall, :some_other_func], nil, [:return]],
+ nil]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn,
+ :uses_if,
+ [:scope,
+ [:block,
+ [:args],
+ [:if,
+ [:vcall, :some_func],
+ nil,
+ [:if, [:vcall, :some_other_func], [:return], nil]]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+end
+
+class TestHeckleBooleans < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_boolean")
+ end
+
+ def teardown
+ @heckler.reset
+ end
+
+ def test_default_structure
+ expected = [:defn, :uses_boolean, [:scope, [:block, [:args], [:true], [:false]]]]
+ assert_equal expected, @heckler.current_tree
+ end
+
+
+ def test_should_flip_true_to_false_and_false_to_true
+ expected = [:defn, :uses_boolean, [:scope, [:block, [:args], [:false], [:false]]]]
+
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+
+ @heckler.reset_tree
+
+ expected = [:defn, :uses_boolean, [:scope, [:block, [:args], [:true], [:true]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+end
+
+class TestHeckleWhile < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_while")
+ end
+
+ def teardown
+ @heckler.reset
+ end
+
+ def test_default_structure
+ expected = [:defn,
+ :uses_while,
+ [:scope,
+ [:block,
+ [:args],
+ [:while, [:vcall, :some_func], [:vcall, :some_other_func], true]]]]
+ assert_equal expected, @heckler.current_tree
+ end
+
+ def test_flips_while_to_until
+ expected = [:defn,
+ :uses_while,
+ [:scope,
+ [:block,
+ [:args],
+ [:until, [:vcall, :some_func], [:vcall, :some_other_func], true]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+end
+
+class TestHeckleUntil < Test::Unit::TestCase
+ def setup
+ @heckler = Heckle.new("Heckled", "uses_until")
+ end
+
+ def teardown
+ @heckler.reset
+ end
+
+ def test_default_structure
+ expected = [:defn,
+ :uses_until,
+ [:scope,
+ [:block,
+ [:args],
+ [:until, [:vcall, :some_func], [:vcall, :some_other_func], true]]]]
+ assert_equal expected, @heckler.current_tree
+ end
+
+ def test_flips_until_to_while
+ expected = [:defn,
+ :uses_until,
+ [:scope,
+ [:block,
+ [:args],
+ [:while, [:vcall, :some_func], [:vcall, :some_other_func], true]]]]
+ @heckler.process(@heckler.current_tree)
+ assert_equal expected, @heckler.current_tree
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.