# -*- coding: utf-8 -*-moduleNyanCatmoduleCommonESC="\e["NND="#{ESC}0m"PASS='='PASS_ARY=['-','_']FAIL='*'ERROR='!'PENDING='+'VT100_CODES={:black=>30,:red=>31,:green=>32,:yellow=>33,:blue=>34,:magenta=>35,:cyan=>36,:white=>37,:bold=>1,}VT100_CODE_VALUES=VT100_CODES.invertdefself.included(base)base.class_evaldoattr_reader:current,:example_results,:color_index,:pending_count,:failure_count,:example_countendend# Increments the example count and displays the current progress## @returns nothingdeftick(mark=PASS)@example_results<<mark@current=(@current>@example_count)?@example_count:@current+1dump_progressend# Determine which Ascii Nyan Cat to display. If tests are complete,# Nyan Cat goes to sleep. If there are failing or pending examples,# Nyan Cat is concerned.## @return [String] Nyan Catdefnyan_catifself.failed_or_pending?&&self.finished?ascii_cat('x')[@color_index%2].join("\n")#'~|_(x.x)'elsifself.failed_or_pending?ascii_cat('o')[@color_index%2].join("\n")#'~|_(o.o)'elsifself.finished?ascii_cat('-')[@color_index%2].join("\n")# '~|_(-.-)'elseascii_cat('^')[@color_index%2].join("\n")# '~|_(^.^)'endend# Displays the current progress in all Nyan Cat glory## @return nothingdefdump_progressoutput.print(progress_lines.join("\n")+eol)enddefprogress_lines[nyan_trail.split("\n").each_with_index.inject([])do|result,(trail,index)|value="#{scoreboard[index]}/#{@example_count}:"result<<format("%s %s",value,trail)end].flattenend# Determines how we end the trail line. If complete, return a newline etc.## @return [String]defeolreturn"\n"if@current==@example_countlength=progress_lines.length-1length>0?format("\e[1A"*length+"\r"):"\r"end# Calculates the current flight length## @return [Fixnum]defcurrent_width# padding_width + example_width + cat_lengthpadding_width+(@current*example_width)+cat_lengthend# Gets the padding for the current example count## @return [Fixnum]defpadding_width@example_count.to_s.length*2+6end# A Unix trick using stty to get the console columns## @return [Fixnum]defterminal_widthifdefined?JRUBY_VERSIONdefault_width=80elsedefault_width=`stty size`.split.map{|x|x.to_i}.reverse.first-1end@terminal_width||=default_widthend# Creates a data store of pass, failed, and pending example results# We have to pad the results here because sprintf can't properly pad color## @return [Array]defscoreboard@pending_examples||=[]@failed_examples||=[]padding=@example_count.to_s.length[@current.to_s.rjust(padding),success_color((@current-@pending_examples.size-@failed_examples.size).to_s.rjust(padding)),pending_color(@pending_examples.size.to_s.rjust(padding)),failure_color(@failed_examples.size.to_s.rjust(padding))]end# Creates a rainbow trail## @return [String] the sprintf format of the Nyan catdefnyan_trailmarks=@example_results.each_with_index.map{|mark,i|highlight(mark)*example_width(i)}marks.shift(current_width-terminal_width)ifcurrent_width>=terminal_widthnyan_cat.split("\n").each_with_index.mapdo|line,index|format("%s#{line}",marks.join)end.join("\n")end# Times a mark has to be repeateddefexample_width(item=1)1end# Ascii version of Nyan cat. Two cats in the array allow Nyan to animate running.## @param o [String] Nyan's eye# @return [Array] Nyan catsdefascii_cat(o='^')[["_,------, ","_| /\\_/\\ ","~|_( #{o} .#{o}) "," \"\"\"\" "],["_,------, ","_| /\\_/\\","^|__( #{o} .#{o}) "," \"\"\"\""]]end# Colorizes the string with raindow colors of the rainbow## @params string [String]# @return [String]defrainbowify(string)c=colors[@color_index%colors.size]@color_index+=1"#{ESC}38;5;#{c}m#{string}#{NND}"end# Calculates the colors of the rainbow## @return [Array]defcolors@colors||=(0...(6*7)).mapdo|n|pi_3=Math::PI/3n*=1.0/6r=(3*Math.sin(n)+3).to_ig=(3*Math.sin(n+2*pi_3)+3).to_ib=(3*Math.sin(n+4*pi_3)+3).to_i36*r+6*g+b+16endend# Determines how to color the example. If pass, it is rainbowified, otherwise# we assign red if failed or yellow if an error occurred.## @return [String]defhighlight(mark=PASS)casemarkwhenPASS;rainbowifyPASS_ARY[@color_index%2]whenFAIL;"\e[31m#{mark}\e[0m"whenERROR;"\e[33m#{mark}\e[0m"whenPENDING;"\e[33m#{mark}\e[0m"elsemarkendend# Converts a float of seconds into a minutes/seconds string## @return [String]defformat_duration(duration)seconds=((duration%60)*100.0).round/100.0# 1.8.7 safe .round(2)seconds=seconds.to_iifseconds.to_i==seconds# drop that zero if it's not neededmessage="#{seconds} second#{seconds==1?"":"s"}"message="#{(duration/60).to_i} minute#{(duration/60).to_i==1?"":"s"} and "+messageifduration>=60messageend# Determines if the specs have completed## @returns [Boolean] true if finished; false otherwisedeffinished?(@current==@example_count)end# Determines if the any specs failed or are in pending state## @returns [Boolean] true if failed or pending; false otherwisedeffailed_or_pending?(@failure_count.to_i>0||@pending_count.to_i>0)end# Returns the cat length## @returns [Fixnum]defcat_lengthnyan_cat.split("\n").group_by(&:size).max.firstenddefsuccess_color(text)wrap(text,:success)enddefpending_color(text)wrap(text,:pending)enddeffailure_color(text)wrap(text,:failure)enddefconsole_code_for(code_or_symbol)ifVT100_CODE_VALUES.has_key?(code_or_symbol)code_or_symbolelseVT100_CODES.fetch(code_or_symbol)doconsole_code_for(:white)endendenddefwrap(text,code_or_symbol)ifRSpec.configuration.color_enabled?"\e[#{console_code_for(code_or_symbol)}m#{text}\e[0m"elsetextendendendend