1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
#!/usr/bin/ruby -ws
$d ||= ENV["DELETE"] || false
$t ||= ENV["DELETE_TIMEOUT"] || false
$m ||= ENV["MOVE_TIMEOUT"] || false
$q ||= ENV["QUIET"] || false
$s ||= ENV["SPEED"] || false
require 'rubygems'
require 'ruby_parser'
require 'fileutils'
ARGV.push "-" if ARGV.empty?
class RubyParser
def extract_defs
ss = current.lexer.ss
raise "can't access source. possible encoding issue" unless ss
src = ss.string
pre_error = src[0...ss.pos]
defs = pre_error.lines.grep(/^ *(?:def|it)/)
raise "can't figure out where the bad code starts" unless defs.last
last_def_indent = defs.last[/^ */]
post_error = src[ss.pos..-1]
idx = post_error =~ /^#{last_def_indent}end.*/
raise "can't figure out where the bad code ends" unless idx
src = pre_error + post_error[0..idx+$&.length]
src.scan(/^(( *)(?:def|it) .*?^\2end)/m)
end
def retest_for_errors defs
parser = self.class.new
parser.process(defs.join("\n\n"))
rescue SyntaxError, StandardError
nil
end
end
def expand path
if File.directory? path then
require 'find'
files = []
Find.find(*Dir[path]) do |f|
files << f if File.file? f
end
files.sort
else
Dir.glob path
end
end
def process_error parser
defs = parser.extract_defs
if parser.retest_for_errors defs then
warn "Can't reproduce error with just methods, punting..."
return
end
catch :extract_done do
(1..defs.size).each do |perm_size|
defs.combination(perm_size).each do |trial|
unless parser.retest_for_errors trial then
puts trial.join "\n"
throw :extract_done
end
end
end
end
rescue RuntimeError, Racc::ParseError => e
warn "# process error: #{e.message.strip}"
warn "# #{e.backtrace.first}"
end
def process file
ruby = file == "-" ? $stdin.binread : File.binread(file)
time = (ENV["RP_TIMEOUT"] || 10).to_i
$stderr.print "# Validating #{file}: "
parser = RubyParser.new
t0 = Time.now if $s
parser.process(ruby, file, time)
if $s then
warn "good: #{Time.now - t0}"
else
warn "good"
end
File.unlink file if $d
rescue Timeout::Error
$exit = 1
warn "TIMEOUT parsing #{file}. Skipping."
if $m then
base_dir, *rest = file.split("/")
base_dir.sub!(/\.slow\.?.*/, "")
base_dir += ".slow.#{time}"
new_file = File.join(base_dir, *rest)
FileUtils.mkdir_p File.dirname(new_file)
FileUtils.move file, new_file, verbose:true
elsif $t then
File.unlink file
end
rescue StandardError, SyntaxError, Racc::ParseError => e
$exit = 1
warn ""
warn "# error: #{e.message.strip}" unless $q
warn "# #{e.backtrace.first}"
warn ""
return if $q
process_error parser
end
$exit = 0
$stdout.sync = true
ARGV.each do |path|
expand(path).each do |file|
next unless File.file? file # omg... why would you name a dir support.rb?
process file
end
end
exit $exit
|