class Shindo::Tests
def after(&block)
def after(&block) @afters.last.push(block) end
def assert(type, expectation, description, &block)
def assert(type, expectation, description, &block) return if Thread.main[:exit] || Thread.current[:reload] description = [@description, description].compact.join(' ') if block_given? begin for before in @befores.flatten.compact before.call end value, success = case type when :raises value = begin instance_eval(&block) rescue Shindo::Pending @pending = true rescue Exception => error error end [value, value.is_a?(expectation)] when :returns [value = instance_eval(&block), value == expectation] end for after in @afters.flatten.compact after.call end rescue Shindo::Pending @pending = true rescue => error success = false value = error end if @pending @pending = false display_pending(description) elsif success display_success(description) else display_failure(description) case value when Exception, Interrupt display_error(value) else @message ||= [ "expected => #{expectation.inspect}", "returned => #{value.inspect}" ] Formatador.indent do Formatador.display_lines([*@message].map {|message| "[red]#{message}[/]"}) end @message = nil end if Thread.current[:interactive] && STDOUT.tty? prompt(description, &block) end end else display_pending(description) end success end
def before(&block)
def before(&block) @befores.last.push(block) end
def display_description(description)
def display_description(description) unless @described Thread.current[:formatador].display(@description_stack.first) print ' ' @described = true end end
def display_description(description)
def display_description(description) unless @inline Formatador.display_line(description) else Formatador.display(description) print ' ' end end
def display_description_stack
def display_description_stack Formatador.indent do @description_stack.length.times do Formatador.display_line(@description_stack.pop) end end end
def display_error(error)
def display_error(error) Thread.current[:totals][:errored] += 1 Formatador.display_lines(['', Thread.current[:file]]) display_description_stack Formatador.display_line("[red]#{error.message} (#{error.class})[/]") unless error.backtrace.empty? Formatador.indent do Formatador.display_lines(error.backtrace.map {|line| "[red]#{line}[/]"}) end end end
def display_error(error)
def display_error(error) Thread.current[:totals][:errored] += 1 Formatador.display_line("[red]#{error.message} (#{error.class})[/]") unless error.backtrace.empty? Formatador.indent do Formatador.display_lines(error.backtrace.map {|line| "[red]#{line}[/]"}) end end end
def display_failure(description)
def display_failure(description) Thread.current[:totals][:failed] += 1 Formatador.display_lines(['', Thread.current[:file]]) display_description_stack Formatador.display_line("[red]- #{description}[/]") end
def display_failure(description)
def display_failure(description) Thread.current[:totals][:failed] += 1 unless @inline Formatador.display_line("[red]- #{description}[/]") else print Formatador.parse("[red]- #{description}[/]\n") end end
def display_pending(description)
def display_pending(description) Thread.current[:totals][:pending] += 1 print Formatador.parse("[yellow]#[/]") end
def display_pending(description)
def display_pending(description) Thread.current[:totals][:pending] += 1 unless @inline Formatador.display_line("[yellow]# #{description}[/]") else print Formatador.parse("[yellow]# #{description}[/]\n") end end
def display_success(description)
def display_success(description) Thread.current[:totals][:succeeded] += 1 print Formatador.parse("[green]+[/]") end
def display_success(description)
def display_success(description) Thread.current[:totals][:succeeded] += 1 unless @inline Formatador.display_line("[green]+ #{description}[/]") else print Formatador.parse("[green]+ #{description}[/]\n") end end
def initialize(description, tags = [], &block)
def initialize(description, tags = [], &block) @afters = [] @befores = [] @description_stack = [] @tag_stack = [] Thread.current[:reload] = false Thread.current[:tags] ||= [] Thread.current[:totals] ||= { :failed => 0, :errored => 0, :pending => 0, :skipped => 0, :succeeded => 0 } @if_tagged = [] @unless_tagged = [] for tag in Thread.current[:tags] case tag[0...1] when '+' @if_tagged << tag[1..-1] when '-' @unless_tagged << tag[1..-1] end end @pending = nil Formatador.display_line tests(description, tags, &block) end
def pending
def pending raise(Shindo::Pending.new) end
def prompt(description, &block)
def prompt(description, &block) return if Thread.main[:exit] || Thread.current[:reload] Formatador.display("Action? [c,q,r,?]? ") choice = STDIN.gets.strip continue = false Formatador.display_line Formatador.indent do case choice when 'c', 'continue' continue = true when 'q', 'quit', 'exit' Formatador.display_line("Exiting...") Thread.main[:exit] = true when 'r', 'reload' Formatador.display_line("Reloading...") Thread.current[:reload] = true when '?', 'help' Formatador.display_lines([ 'c - ignore this error and continue', 'q - quit Shindo', 'r - reload and run the tests again', '? - display help' ]) else Formatador.display_line("[red]#{choice} is not a valid choice, please try again.[/]") end Formatador.display_line end unless continue || Thread.main[:exit] Formatador.display_line("[red]- #{description}[/]") prompt(description, &block) end end
def raises(error, description = "raises #{error.inspect}", &block)
def raises(error, description = "raises #{error.inspect}", &block) assert(:raises, error, description, &block) end
def returns(expectation, description = "returns #{expectation.inspect}", &block)
def returns(expectation, description = "returns #{expectation.inspect}", &block) assert(:returns, expectation, description, &block) end
def test(description = 'returns true', &block)
def test(description = 'returns true', &block) assert(:returns, true, description, &block) end
def tests(description, tags = [], &block)
def tests(description, tags = [], &block) return self if Thread.main[:exit] || Thread.current[:reload] tags = [*tags].collect { |tag| tag.to_s } @tag_stack.push(tags) @befores.push([]) @afters.push([]) @description = nil @inline = false description ||= 'Shindo.tests' description = "[bold]#{description}[normal]" unless tags.empty? description << " (#{tags.join(', ')})" end @description_stack.push(description) # if the test includes +tags and discludes -tags, evaluate it if (@if_tagged.empty? || !(@if_tagged & @tag_stack.flatten).empty?) && (@unless_tagged.empty? || (@unless_tagged & @tag_stack.flatten).empty?) if block_given? begin display_description(description) # HACK: increase indent indent = Thread.current[:formatador].instance_variable_get(:@indent) Thread.current[:formatador].instance_variable_set(:@indent, indent + 1) instance_eval(&block) rescue Shindo::Pending display_pending(description) rescue => error display_error(error) ensure # HACK: decrease indent indent = Thread.current[:formatador].instance_variable_get(:@indent) Thread.current[:formatador].instance_variable_set(:@indent, indent - 1) end else @inline = true display_description(description) end else display_description("[light_black]#{description}[/]") end @description_stack.pop @afters.pop @befores.pop @tag_stack.pop Thread.exit if Thread.main[:exit] || Thread.current[:reload] self end