Permalink
Browse files

! Hot damn! It works with ruby_parser!!! (phiggins)

[git-p4: depot-paths = "//src/heckle/dev/": change = 7807]
  • Loading branch information...
1 parent 1c50905 commit 5d88142ed589e9ba8006d225b4975f188ab8d1a1 @zenspider zenspider committed Oct 3, 2012
View
@@ -12,8 +12,6 @@ Hoe.add_include_dirs("../../ParseTree/dev/lib",
"../../ruby_parser/2.3.1/lib",
"lib")
-Hoe.plugin :seattlerb
-
Hoe.spec 'heckle' do
developer 'Ryan Davis', 'ryand-ruby@zenspider.com'
developer 'Eric Hodel', 'drbrain@segment7.net'
@@ -28,6 +26,10 @@ Hoe.spec 'heckle' do
dependency 'ZenTest', '~> 4.7.0'
multiruby_skip << "1.9" << "trunk"
+
+ self.test_globs = ["test/test_*.rb"]
+
+ self.testlib = :minitest
end
# vim: syntax=ruby
View
@@ -1,96 +1,8 @@
-#!/usr/local/bin/ruby
+#!/usr/bin/env ruby
-$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
-require 'test_unit_heckler'
-require 'optparse'
+$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
+$LOAD_PATH << 'lib'
-force = false
-nodes = Heckle::MUTATABLE_NODES
-
-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|
- TestUnitHeckler.debug = true
- end
-
- opts.on("-V", "--version", "Prints Heckle's version number") do |opt|
- puts "Heckle #{Heckle::VERSION}"
- exit 0
- end
-
- opts.on("-t", "--tests TEST_PATTERN",
- "Location of tests (glob)") do |pattern|
- TestUnitHeckler.test_pattern = pattern
- end
-
- opts.on("-F", "--force", "Ignore initial test failures",
- "Best used with --focus") do |opt|
- force = true
- end
-
- opts.on( "--assignments", "Only mutate assignments") do |opt|
- puts "!"*70
- puts "!!! Heckling assignments only"
- puts "!"*70
- puts
-
- nodes = Heckle::ASGN_NODES
- end
-
- opts.on("-b", "--branches", "Only mutate branches") do |opt|
- puts "!"*70
- puts "!!! Heckling branches only"
- puts "!"*70
- puts
-
- nodes = Heckle::BRANCH_NODES
- end
-
- opts.on("-f", "--focus", "Apply the eye of sauron") do |opt|
- puts "!"*70
- puts "!!! Running in focused mode. FEEL THE EYE OF SAURON!!!"
- puts "!"*70
- puts
-
- TestUnitHeckler.focus = true
- end
-
- opts.on("-T", "--timeout SECONDS", "The maximum time for a test run in seconds",
- "Used to catch infinite loops") do |timeout|
- Heckle.timeout = timeout.to_i
- puts "Setting timeout at #{timeout} seconds."
- end
-
- opts.on("-n", "--nodes NODES", "Nodes to mutate",
- "Possible values: #{Heckle::MUTATABLE_NODES.join(',')}") do |opt|
- nodes = opt.split(',').collect {|n| n.to_sym }
- puts "Mutating nodes: #{nodes.inspect}"
- end
-
- opts.on("-x", "--exclude-nodes NODES", "Nodes to exclude") do |opt|
- exclusions = opt.split(',').collect {|n| n.to_sym }
- nodes = nodes - exclusions
- puts "Mutating without nodes: #{exclusions.inspect}"
- end
-
- opts.on("-h", "--help", "Show this message") do |opt|
- puts opts
- exit 0
- end
-end
-
-looks_like_rails = test ?f, 'config/environment.rb'
-TestUnitHeckler.test_pattern = "test/**/*.rb" if looks_like_rails
-
-opts.parse!
-
-impl = ARGV.shift
-meth = ARGV.shift
-
-unless impl then
- puts opts
- exit 1
-end
-
-exit TestUnitHeckler.validate(impl, meth, nodes, force)
+require 'heckle_runner'
+exit HeckleRunner.run
View
@@ -13,14 +13,6 @@ def to_class
class Sexp
# REFACTOR: move to sexp.rb
- def deep_each(&block)
- self.each_sexp do |sexp|
- block[sexp]
- sexp.deep_each(&block)
- end
- end
-
- # REFACTOR: move to sexp.rb
def each_sexp
self.each do |sexp|
next unless Sexp === sexp
@@ -150,7 +142,7 @@ def initialize(klass_name = nil, method_name = nil,
@mutated = false
- @original_tree = rewrite find_class_and_method
+ @original_tree = rewrite find_scope_and_method
@current_tree = @original_tree.deep_clone
grab_mutatees
@@ -526,8 +518,8 @@ def current_tree
end
# Copied from Flay#process
- def find_class_and_method
- expand_dirs_to_files('.').each do |file|
+ def find_scope_and_method
+ expand_dirs_to_files.each do |file|
#warn "Processing #{file}" if option[:verbose]
ext = File.extname(file).sub(/^\./, '')
@@ -550,7 +542,7 @@ def find_class_and_method
next unless sexp
- found = process_sexp sexp
+ found = find_scope sexp
return found if found
rescue SyntaxError => e
@@ -565,31 +557,55 @@ def process_rb file
RubyParser.new.process(File.read(file), file)
end
- def process_sexp pt
+ def find_scope sexp, nesting=nil
+ nesting ||= klass_name.split("::").map {|k| k.to_sym }
+ current, *nesting = nesting
+
+ sexp = s(:block, sexp) unless sexp.first == :block
+
+ sexp.each_sexp do |node|
+ next unless [:class, :module].include? node.first
+ next unless node[1] == current
+
+ block = node.detect {|s| Sexp === s && s[0] == :scope }[1]
+
+ if nesting.empty?
+ return sexp if method_name.nil?
+
+ m = find_method block
+
+ return m if m
+ else
+ s = find_scope block, nesting
+
+ return s if s
+ end
+ end
+
+ nil
+ end
+
+ def find_method sexp
class_method = method_name.to_s =~ /^self\./
clean_name = method_name.to_s.sub(/^self\./, '').to_sym
- if pt[0] == :class && pt[1] == klass_name.to_sym
- return pt if method_name.nil?
+ sexp = s(:block, sexp) unless sexp.first == :block
- pt.deep_each do |node|
- if class_method
- return node if node[0] == :defs && node[2] == clean_name
- else
- return node if node[0] == :defn && node[1] == clean_name
- end
+ sexp.each_sexp do |node|
+ if class_method
+ return node if node[0] == :defs && node[2] == clean_name
+ else
+ return node if node[0] == :defn && node[1] == clean_name
end
end
nil
end
- def expand_dirs_to_files *dirs
- extensions = ['rb'] # + Flay.load_plugins
-
- dirs.flatten.map { |p|
+ def expand_dirs_to_files(dirs='.')
+ Array(dirs).flatten.map { |p|
if File.directory? p then
- Dir[File.join(p, '**', "*.{#{extensions.join(',')}}")]
+ Dir[File.join(p, '**', "*.rb")]
else
p
end
@@ -690,7 +706,7 @@ def mutations_left
end
def current_code
- Ruby2Ruby.new.process ParseTree.translate(klass_name.to_class, method_name)
+ Ruby2Ruby.new.process current_tree
end
##
View
@@ -0,0 +1,113 @@
+require 'optparse'
+require 'heckle'
+require 'minitest_heckler'
+
+class HeckleRunner
+ def self.run argv=ARGV
+ options = parse_args argv
+
+ class_or_module, method = argv.shift, argv.shift
+
+ new(class_or_module, method, options).run
+ end
+
+ def self.parse_args argv
+ options = {
+ :force => false,
+ :nodes => Heckle::MUTATABLE_NODES,
+ :debug => false,
+ :focus => false,
+ :timeout => 5,
+ :test_pattern => 'test/test_*.rb',
+ }
+
+ OptionParser.new do |opts|
+ opts.version = Heckle::VERSION
+ opts.program_name = File.basename $0
+ opts.banner = "Usage: #{opts.program_name} class_name [method_name]"
+ opts.on("-v", "--verbose", "Loudly explain heckle run") do
+ options[:debug] = true
+ end
+
+ opts.on("-t", "--tests TEST_PATTERN",
+ "Location of tests (glob)") do |pattern|
+ options[:test_pattern] = pattern
+ end
+
+ opts.on("-F", "--force", "Ignore initial test failures",
+ "Best used with --focus") do
+ options[:force] = true
+ end
+
+ opts.on( "--assignments", "Only mutate assignments") do
+ puts "!"*70
+ puts "!!! Heckling assignments only"
+ puts "!"*70
+ puts
+
+ options[:nodes] = Heckle::ASGN_NODES
+ end
+
+ opts.on("-b", "--branches", "Only mutate branches") do
+ puts "!"*70
+ puts "!!! Heckling branches only"
+ puts "!"*70
+ puts
+
+ options[:nodes] = Heckle::BRANCH_NODES
+ end
+
+ opts.on("-f", "--focus", "Apply the eye of sauron") do
+ puts "!"*70
+ puts "!!! Running in focused mode. FEEL THE EYE OF SAURON!!!"
+ puts "!"*70
+ puts
+
+ options[:focus] = true
+ end
+
+ opts.on("-T", "--timeout SECONDS", "The maximum time for a test run in seconds",
+ "Used to catch infinite loops") do |timeout|
+ timeout = timeout.to_i
+ puts "Setting timeout at #{timeout} seconds."
+ options[:timeout] = timeout
+ end
+
+ opts.on("-n", "--nodes NODES", "Nodes to mutate",
+ "Possible values: #{Heckle::MUTATABLE_NODES.join(',')}") do |opt|
+ nodes = opt.split(',').collect {|n| n.to_sym }
+ options[:nodes] = nodes
+ puts "Mutating nodes: #{nodes.inspect}"
+ end
+
+ opts.on("-x", "--exclude-nodes NODES", "Nodes to exclude") do |opt|
+ exclusions = opt.split(',').collect {|n| n.to_sym }
+ options[:nodes] = options[:nodes] - exclusions
+ puts "Mutating without nodes: #{exclusions.inspect}"
+ end
+ end.parse! argv
+
+ p options if options[:debug]
+
+ # TODO: Pass options to Heckle's initializer instead.
+ Heckle.debug = options[:debug]
+ Heckle.timeout = options[:timeout]
+
+ options
+ end
+
+ # TODO: this sucks
+ def heckler
+ MiniTestHeckler
+ end
+
+ def initialize class_or_module, method, options={}
+ @class_or_module = class_or_module
+ @method = method
+ @options = options
+ end
+
+ def run
+ heckler.new(@class_or_module, @method, @options).validate
+ end
+end
View
@@ -0,0 +1,33 @@
+class MiniTestHeckler < Heckle
+ def initialize(class_or_module, method, options)
+ Dir.glob(options[:test_pattern]).each {|t| load File.expand_path(t) }
+
+ super(class_or_module, method, options[:nodes])
+ end
+
+ def tests_pass?
+ silence do
+ MiniTest::Unit.runner = nil
+
+ MiniTest::Unit.new.run
+
+ runner = MiniTest::Unit.runner
+
+ runner.failures == 0 && runner.errors == 0
+ end
+ end
+
+ # TODO: Windows.
+ def silence
+ return yield if Heckle.debug
+
+ begin
+ original = MiniTest::Unit.output
+ MiniTest::Unit.output = File.open("/dev/null", "w")
+
+ yield
+ ensure
+ MiniTest::Unit.output = original
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 5d88142

Please sign in to comment.