# 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) # by agile (Mike Vincent) # http://gist.github.com/583551 # # 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' require "rubygems" require 'formatador' 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 start(example_count) @current = 0 @started_at = Time.now @total = example_count @error_state = :all_passing @slow_specs = [] @formatador = Formatador.new @formatador.display_line end def example_started(example) super @start_time = Time.now end def example_passed(example) elapsed = Time.now - @start_time profile_example(example_group.description, example.description, elapsed) increment end # third param is optional, because earlier versions of rspec sent only two args def example_pending(example) immediately_dump_pending(example) mark_error_state_pending increment end #def example_failed(example, counter, failure) def example_failed(example) if example.pending? #|| !example.metadata[:execution_result][:exception_encoutered] example_pending(example) else immediately_dump_failure(example) notify_failure(@current, example) mark_error_state_failed increment end end def start_dump @formatador.display_line("\n") report_slow_specs end def dump_failure(*args) # no-op; we summarized failures as we were running end def method_missing(sym, *args) # ignore end def notify(title, message, priority) # I already have something that does this, that I like better.. # # 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 # stolen and slightly modified from BaseTextFormatter#dump_failure def immediately_dump_failure(example) @formatador.redisplay("#{' ' * progress_bar.length}\n") @formatador.display_line("[red]#{@current}) FAIL: #{example.full_description}#{'('+example.metadata[:execution_result][:exception_encoutered].to_s + ')' if example.metadata[:execution_result][:exception_encoutered]}[/]") @formatador.indent do @formatador.display_line("[red]# #{format_caller example.metadata[:location]}[/]") @formatador.display_line end end # stolen and modified from BaseTextFormatter#dump_pending def immediately_dump_pending(example) @formatador.indent do @formatador.redisplay("#{' ' * progress_bar.length}\r") @formatador.display_line("[yellow]PENDING SPEC:[/] #{example.full_description}") @formatador.indent do @formatador.display_line("[blue]# #{example.metadata[:execution_result][:pending_message]}[/]") if example.metadata[:execution_result][:pending_message] @formatador.display_line("[blue]# #{format_caller example.metadata[:location]}[/]") if example.metadata[:location] end end end def increment @current += 1 @formatador.redisplay(progress_bar) 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 progress_bar color = case @error_state when :some_failed 'red' when :some_pending 'yellow' else 'green' end ratio = "#{(' ' * (@total.to_s.size - @current.to_s.size))}#{@current}/#{@total}" fraction = "[#{color}]#{(' ' * (@total.to_s.size - @current.to_s.size))}#{@current}/#{@total}[/]" percent = @current.to_f / @total.to_f progress = "[_white_]|[/][#{color}][_#{color}_]#{'*' * (percent * 50).ceil}[/]#{' ' * (50 - (percent * 50).ceil)}[_white_]|[/]" 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 @formatador.indent do @formatador.redisplay("#{' ' * progress_bar.length}\r") @formatador.display_line("[yellow]SLOW SPEC (#{sprintf("%.4f", elapsed)})[/]: #{group} #{example}\n") end end end def report_slow_specs @formatador.display_line("[yellow]Top #{@slow_specs.size} slowest specs:[/]") @slow_specs.each do |elapsed, group, example| @formatador.display_line("[yellow] #{yellow(sprintf('%.4f', elapsed))} #{group} #{example}[/]") end end end end end end