Skip to content

Instantly share code, notes, and snippets.

@zed-0xff
Created January 27, 2010 21:16
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zed-0xff/288159 to your computer and use it in GitHub Desktop.
Save zed-0xff/288159 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# this tool is similar to "git bisect" one, but for specs.
# it tries to find what spec from list of specs breaks execution of one specified spec.
#
# see more at http://zed.0xff.me/2010/01/28/rspec-bisect
#
# usage example:
# ./rspec-bisect.rb spec/**/*_spec.rb spec/controllers/spaces/tickets_controller_spec.rb
# [.] rspec runner: ./script/spec
# [.] target spec : spec/controllers/spaces/tickets_controller_spec.rb
# [.] 123 candidate specs
# [.] running 62 specs.. Done. ( 19s) (105/0/4) : target OK
# [.] running 63 specs.. Done. ( 64s) (386/2/6) : target FAIL
# [.] running 32 specs.. Done. ( 34s) (157/0/2) : target OK
# [.] running 32 specs.. Done. ( 43s) (238/2/4) : target FAIL
# [.] running 16 specs.. Done. ( 38s) (129/2/1) : target FAIL
# [.] running 8 specs.. Done. ( 27s) (73/0) : target OK
# [.] running 9 specs.. Done. ( 19s) (65/2/1) : target FAIL
# [.] running 5 specs.. Done. ( 19s) (52/2/1) : target FAIL
# [.] running 3 specs.. Done. ( 16s) (33/0) : target OK
# [.] running 3 specs.. Done. ( 18s) (28/2/1) : target FAIL
# [.] running 2 specs.. Done. ( 17s) (27/2/1) : target FAIL
# [*] found matching spec: spec/models/mailman_spec.rb
$rspec_runner = './script/spec'
$target = nil
$shuffle = false
require 'optparse'
op = OptionParser.new do |opts|
opts.banner = "Usage: bisect.rb [options] [specs] [target_spec]"
opts.on("-r", "--runner PATHNAME", "Use specified rspec runner (default: #{$rspec_runner})") do |arg|
$rspec_runner = arg
end
opts.on("-t", "--target TARGET", "Specify target spec (default: last argument)") do |arg|
$target = arg
end
opts.on("-s", "--shuffle", "Shuffle specs before running them (default: sort by name)") do
$shuffle = true
end
end
op.parse!
specs = ARGV
if specs.size < 2
puts "please give me at least 2 specs!"
puts op
exit
end
# TODO: warnings
target = $target || specs.pop
specs.delete(target)
specs.delete("./#{target}")
specs.delete(target.sub(/^\.\//,''))
if $shuffle
specs.shuffle!
else
specs.sort!
end
puts "[.] rspec runner: #{$rspec_runner}"
puts "[.] target spec : #{target}"
puts "[.] #{specs.size} candidate specs"
def run_specs target, specs
# puts "./script/spec #{specs.join(' ')} #{target}"
printf "[.] running %4d specs.. ", specs.size+1
STDOUT.flush
t0 = Time.now
t = `#{$rspec_runner} #{specs.join(' ')} #{target}`
r = false
if t["#{target}:"]
lines = t.strip.split("\n")
lines.each_with_index do |line,idx|
if line["#{target}:"] && idx>0 && !lines[idx-1]['(Not Yet Implemented)']
r = true
break
end
end
end
counts = t[/^\d+ examples, .*$/].to_s.strip.gsub(/[a-z,]/,'').strip.gsub(/ +/,"/")
# puts "Done. (#{(Time.now - t0).to_i}s) (#{counts})\t: target #{r ? 'OK' : 'FAIL'}"
printf("Done. (%3ds) (%s)\t: target %s\n",
(Time.now - t0).to_i,
counts,
r ? 'FAIL' : 'OK'
)
r
end
def process_candidates target, specs
if specs.size <= 1
puts "[*] found matching spec: #{specs.first}"
exit
else
if run_specs target, specs[0...specs.size/2]
process_candidates target, specs[0...specs.size/2]
elsif run_specs target, specs[specs.size/2..-1]
process_candidates target, specs[specs.size/2..-1]
else
puts "[?] all specs ran OK"
# run_specs target, specs
end
end
end
process_candidates target, specs
@ryanong
Copy link

ryanong commented Sep 2, 2014

This is amazing. Small note. If you have any ./ before the test name rspec will run it out of order even if you specify --order defined

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment