From 85dceedd1335bbed86584f2f65a4c1dd7ca9e0db Mon Sep 17 00:00:00 2001 From: Wesley Beary Date: Fri, 22 Jan 2010 20:52:04 -0800 Subject: [PATCH] nicer spec options/runner --- spec/compact_progress_bar_formatter.rb | 195 +++++++++++++++++++++++++ spec/spec_helper.rb | 6 + 2 files changed, 201 insertions(+) create mode 100644 spec/compact_progress_bar_formatter.rb diff --git a/spec/compact_progress_bar_formatter.rb b/spec/compact_progress_bar_formatter.rb new file mode 100644 index 000000000..266cc5530 --- /dev/null +++ b/spec/compact_progress_bar_formatter.rb @@ -0,0 +1,195 @@ +# 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/266221 +# +# 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 'spec/runner/formatter/base_text_formatter' +require "rubygems" +require 'formatador' + +module Spec + module Runner + module Formatter + 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, message, pending_caller=nil) + immediately_dump_pending(example.description, message, pending_caller) + mark_error_state_pending + increment + end + + def example_failed(example, counter, failure) + immediately_dump_failure(counter, failure) + notify_failure(counter, failure) + mark_error_state_failed + increment + 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) + title = title.gsub(/\s+/, ' ').gsub(/"/,'\"').gsub(/'/, "\'") + message = message.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.header, failure.exception.message, 2 + end + + # stolen and slightly modified from BaseTextFormatter#dump_failure + def immediately_dump_failure(counter, failure) + @formatador.redisplay("#{' ' * progress_bar.length}\n") + @formatador.redisplay("[red]#{counter.to_s})[/]") + @formatador.display_line("[red]#{failure.header}[/]") + @formatador.indent do + @formatador.display_line("[red]#{failure.exception.message}[/]") + @formatador.display_line + failure.exception.backtrace.each do |line| + @formatador.display_line("[red]#{line}[/]") + end + @formatador.display_line + end + end + + # stolen and modified from BaseTextFormatter#dump_pending + def immediately_dump_pending(desc, msg, called_from) + @formatador.indent do + @formatador.redisplay("#{' ' * progress_bar.length}\r") + @formatador.display_line("[yellow]PENDING SPEC:[/] #{desc} (#{msg})\n") + end + # output.puts " Called from #{called_from}" if called_from + 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 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e22283579..b4dff2721 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,6 +5,12 @@ current_directory = File.dirname(__FILE__) require "#{current_directory}/../lib/fog" # Fog.mock! +# inlined spec.opts +require "#{current_directory}/compact_progress_bar_formatter" +Spec::Runner.options.parse_format("Spec::Runner::Formatter::CompactProgressBarFormatter") +Spec::Runner.options.loadby = 'mtime' +Spec::Runner.options.reverse = true + def ec2 Fog::AWS::EC2.new( :aws_access_key_id => Fog.credentials[:aws_access_key_id],