Skip to content

Instantly share code, notes, and snippets.

@agile
Created September 17, 2010 13:44
Show Gist options
  • Save agile/584249 to your computer and use it in GitHub Desktop.
Save agile/584249 to your computer and use it in GitHub Desktop.
Nice progress bar and immediate feedback for failures, pending and slow specs
# Copyright (c) 2008 Nicholas A. Evans
# http://ekenosen.net/nick/devblog/2008/12/better-progress-bar-for-rspec/
#
# With some tweaks (slow spec profiler, growl support)
# By Nick Zadrozny
# http://gist.github.com/71340
#
# Further tweaks (formatador, elapsed time instead of eta)
# By geemus (Wesley Beary)
# http://gist.github.com/266222
#
# Further tweaks (Rspec2, back to basics formatting, optional notifications)
# by agile (Mike Vincent)
# http://gist.github.com/584249
#
# 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.
require 'rspec/core/formatters/base_text_formatter'
module RSpec
module Core
module Formatters
class CompactProgressBarFormatter < BaseTextFormatter
# Threshold for slow specs, in seconds.
# Anything that takes longer than this will be printed out
# It would be great to get this down to 0.5 or less...
SLOW_SPEC_THRESHOLD = 2.0
# Keep track the slowest specs and print a report at the end
SLOW_SPEC_REPORT = 3
attr_reader :total, :current
def initialize(output)
super(output)
end
def start(example_count)
@current = 0
@started_at = Time.now
@total = example_count
@error_state = :all_passing
@slow_specs = []
# set @enable_notify to true
# to turn on notifications
@enable_notify = false
end
def example_group_started(example_group)
super(example_group)
@start_time = Time.now
end
def example_passed(example)
super(example)
elapsed = Time.now - @start_time
profile_example(example_group.description, example.description, elapsed)
increment
end
def example_pending(example)
super(example)
immediately_dump_pending(example)
mark_error_state_pending
increment
end
def example_failed(example)
super(example)
if example.pending?
example_pending(example)
else
immediately_dump_failure(example)
notify_failure(@current, example)
mark_error_state_failed
increment
end
end
def start_dump
output.puts
report_slow_specs
output.flush
end
def dump_pending(*args)
# no-op; we summarized pending as we went..
end
def dump_failures(*args)
# no-op; we summarized failures as we were running
end
def method_missing(sym, *args)
# ignore
end
def notify(title, message, priority)
return unless @enable_notify
title = title.to_s.gsub(/\s+/, ' ').gsub(/"/,'\"').gsub(/'/, "\'")
message = message.to_s.gsub(/\s+/, ' ').gsub(/"/,'\"').gsub(/'/, "\'").gsub(/`/,'\`')
notify_command = case RUBY_PLATFORM
when /darwin/
"test -x `which growlnotify` && growlnotify -n autotest -p #{priority} -m \"#{message}\" \"#{title}\""
when /linux/
"test -x `which notify-send` && notify-send \"#{title}\" \"#{message}\""
end
# puts notify_command # use this for debugging purposes
system notify_command if notify_command
end
def notify_failure(counter, failure)
notify(failure.full_description, failure.metadata[:exception_encountered], 2)
end
def immediately_dump_failure(example)
wipe
output.puts red("#{@current}) FAIL: #{example.full_description}#{'('+example.metadata[:execution_result][:exception_encoutered].to_s + ')' if example.metadata[:execution_result][:exception_encoutered]}")
output.puts red(" # #{format_caller example.metadata[:location]}")
output.puts
end
def immediately_dump_pending(example)
wipe
output.puts "#{yellow("PENDING SPEC:")} #{example.full_description}"
output.puts blue(" # #{example.metadata[:execution_result][:pending_message]}") if example.metadata[:execution_result][:pending_message]
output.puts blue(" # #{format_caller example.metadata[:location]}") if example.metadata[:location]
end
def increment
@current += 1
output.print "\r\t#{progress_bar}"
output.flush
end
def mark_error_state_failed
@error_state = :some_failed
end
def mark_error_state_pending
@error_state = :some_pending unless @error_state == :some_failed
end
def state_color(args)
case @error_state
when :some_failed
red(args)
when :some_pending
yellow(args)
else
green(args)
end
end
def bar_color(args)
case @error_state
when :some_failed
red(bg_red(args))
when :some_pending
yellow(bg_yellow(args))
else
green(bg_green(args))
end
end
def wipe
output.puts "\r#{' ' * progress_bar.length}\r"
end
def progress_bar
ratio = "#{(' ' * (@total.to_s.size - @current.to_s.size))}#{@current}/#{@total}"
fraction = state_color("#{(' ' * (@total.to_s.size - @current.to_s.size))}#{@current}/#{@total}")
percent = @current.to_f / @total.to_f
progress = "#{bar_color '*' * (percent * 50).ceil}#{bg_white ' ' * (50 - (percent * 50).ceil)}"
microseconds = Time.now - @started_at
minutes = (microseconds / 60).round.to_s
seconds = (microseconds % 60).round.to_s
elapsed = "#{minutes}:#{'0' if seconds.size < 2}#{seconds}"
[fraction, progress, elapsed, ''].join(' ')
end
def profile_example(group, example, elapsed)
@slow_specs = (@slow_specs + [[elapsed, group, example]]).sort.reverse[0, SLOW_SPEC_REPORT]
print_warning_if_really_slow(group, example, elapsed)
end
def print_warning_if_really_slow(group, example, elapsed)
if elapsed > SLOW_SPEC_THRESHOLD
wipe
output.puts yellow("SLOW SPEC (#{sprintf("%.4f", elapsed)}): #{group} #{example}")
end
end
def report_slow_specs
output.puts
output.puts yellow("Top #{@slow_specs.size} slowest specs:")
@slow_specs.each do |elapsed, group, example|
output.puts yellow("#{sprintf('%.4f', elapsed)} #{group} #{example}")
end
end
def bg_green(txt)
color(txt, "\e[42m")
end
def bg_yellow(txt)
color(txt, "\e[43m")
end
def bg_red(txt)
color(txt, "\e[41m")
end
def bg_white(txt)
color(txt, "\e[47m")
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment