class Minitest::Bisect
Constants
- RUBY
Borrowed from rake
- SHH
- VERSION
Attributes
culprits[RW]
failures[RW]
mode[RW]
seen_bad[RW]
tainted[RW]
tainted?[RW]
Public Class Methods
new()
click to toggle source
# File lib/minitest/bisect.rb, line 64 def initialize self.culprits = [] self.failures = Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } } end
run(files)
click to toggle source
# File lib/minitest/bisect.rb, line 57 def self.run files new.run files rescue => e warn e.message exit 1 end
Public Instance Methods
bisect_files(files)
click to toggle source
# File lib/minitest/bisect.rb, line 102 def bisect_files files self.mode = :files files, flags = files.partition { |arg| File.file? arg } rb_flags, mt_flags = flags.partition { |arg| arg =~ /^-I/ } mt_flags += ["--server", $$] puts "reproducing..." system "#{build_files_cmd files, rb_flags, mt_flags} #{SHH}" abort "Reproduction run passed? Aborting. Try running with MTB_VERBOSE=2 to verify." unless tainted? puts "reproduced" found, count = files.find_minimal_combination_and_count do |test| puts "# of culprit files: #{test.size}" system "#{build_files_cmd test, rb_flags, mt_flags} #{SHH}" self.tainted? end puts puts "Minimal files found in #{count} steps:" puts cmd = build_files_cmd found, rb_flags, mt_flags puts cmd cmd end
bisect_methods(cmd)
click to toggle source
# File lib/minitest/bisect.rb, line 130 def bisect_methods cmd self.mode = :methods time_it "reproducing...", build_methods_cmd(cmd) unless tainted? then $stderr.puts "Reproduction run passed? Aborting." abort "Try running with MTB_VERBOSE=2 to verify." end bad = map_failures raise "Nothing to verify against because every test failed. Aborting." if culprits.empty? && seen_bad time_it "verifying...", build_methods_cmd(cmd, [], bad) new_bad = map_failures if bad == new_bad then warn "Tests fail by themselves. This may not be an ordering issue." end # culprits populated by initial reproduction via minitest/server found, count = culprits.find_minimal_combination_and_count do |test| prompt = "# of culprit methods: #{test.size}" time_it prompt, build_methods_cmd(cmd, test, bad) self.tainted? end puts puts "Minimal methods found in #{count} steps:" puts puts "Culprit methods: %p" % [found] puts cmd = build_methods_cmd cmd, found, bad puts cmd.sub(/--server \d+/, "") puts cmd end
build_files_cmd(culprits, rb, mt)
click to toggle source
# File lib/minitest/bisect.rb, line 188 def build_files_cmd culprits, rb, mt reset tests = culprits.flatten.compact.map { |f| %(require "./#{f}") }.join " ; " %(#{RUBY} #{rb.shelljoin} -e '#{tests}' -- #{mt.map(&:to_s).shelljoin}) end
build_methods_cmd(cmd, culprits = [], bad = nil)
click to toggle source
# File lib/minitest/bisect.rb, line 220 def build_methods_cmd cmd, culprits = [], bad = nil reset if bad then re = build_re culprits + bad cmd += " -n \"#{re}\"" if bad end if ENV["MTB_VERBOSE"].to_i >= 1 then puts puts cmd puts end cmd end
build_re(bad)
click to toggle source
# File lib/minitest/bisect.rb, line 196 def build_re bad re = [] # bad by class, you perv bbc = bad.map { |s| s.split(/#/, 2) }.group_by(&:first) bbc.each do |klass, methods| methods = methods.map(&:last).flatten.uniq.map { |method| re_escape method } methods = methods.join "|" re << /#{re_escape klass}#(?:#{methods})/.to_s[7..-2] # (?-mix:...) end re = re.join("|").to_s.gsub(/-mix/, "") "/^(?:#{re})$/" end
map_failures()
click to toggle source
# File lib/minitest/bisect.rb, line 180 def map_failures # from: {"file.rb"=>{"Class"=>["test_method1", "test_method2"]}} # to: ["Class#test_method1", "Class#test_method2"] failures.values.map { |h| h.map { |k,vs| vs.map { |v| "#{k}##{v}" } } }.flatten.sort end
minitest_result(file, klass, method, fails, assertions, time)
click to toggle source
# File lib/minitest/bisect.rb, line 245 def minitest_result file, klass, method, fails, assertions, time fails.reject! { |fail| Minitest::Skip === fail } if mode == :methods then if fails.empty? then culprits << "#{klass}##{method}" unless seen_bad # UGH else self.seen_bad = true end end return if fails.empty? self.tainted = true self.failures[file][klass] << method end
minitest_start()
click to toggle source
Server Methods:
# File lib/minitest/bisect.rb, line 241 def minitest_start self.failures.clear end
re_escape(str)
click to toggle source
# File lib/minitest/bisect.rb, line 216 def re_escape str str.gsub(/([`'"!?&\[\]\(\)\{\}\|\+])/, '\\\\\1') end
reset()
click to toggle source
# File lib/minitest/bisect.rb, line 69 def reset self.seen_bad = false self.tainted = false failures.clear # not clearing culprits on purpose end
run(args)
click to toggle source
# File lib/minitest/bisect.rb, line 76 def run args Minitest::Server.run self cmd = nil if :until_I_have_negative_filtering_in_minitest != 0 then mt_flags = args.dup expander = Minitest::Bisect::PathExpander.new mt_flags files = expander.process rb_flags = expander.rb_flags mt_flags += ["--server", $$] cmd = bisect_methods build_files_cmd(files, rb_flags, mt_flags) else cmd = bisect_methods bisect_files args end puts "Final reproduction:" puts system cmd.sub(/--server \d+/, "") ensure Minitest::Server.stop end
time_it(prompt, cmd)
click to toggle source
# File lib/minitest/bisect.rb, line 173 def time_it prompt, cmd print prompt t0 = Time.now system "#{cmd} #{SHH}" puts " in %.2f sec" % (Time.now - t0) end