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 raises?(expectation, &block) when :returns returns?(expectation, &block) end for after in @afters.flatten.compact after.call end rescue => error success = false value = error end if @pending display_pending(description) @pending = false 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}" ] Thread.current[:formatador].indent do Thread.current[: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(Thread.current[:file]) print ' ' @described = true end end
def display_description(description)
def display_description(description) Thread.current[:formatador].display_line(description) end
def display_description_stack
def display_description_stack return if @description_stack.empty? Thread.current[:formatador].indent do Thread.current[:formatador].display_line(@description_stack.pop) display_description_stack end end
def display_error(error)
def display_error(error) display_description_stack Thread.current[:formatador].indent do Thread.current[:formatador].display_line("[red]#{error.message} (#{error.class})[/]") unless error.backtrace.empty? Thread.current[:formatador].indent do Thread.current[:formatador].display_lines(error.backtrace.map {|line| "[red]#{line}[/]"}) end end end end
def display_error(error)
def display_error(error) Thread.current[:formatador].display_line("[red]#{error.message} (#{error.class})[/]") unless error.backtrace.empty? Thread.current[:formatador].indent do Thread.current[: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 Thread.current[:formatador].display_line display_description_stack Thread.current[:formatador].display_line("[red]- #{description}[/]") end
def display_failure(description)
def display_failure(description) Thread.current[:totals][:failed] += 1 Thread.current[:formatador].display_line("[red]- #{description}[/]") 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 Thread.current[:formatador].display_line("[yellow]# #{description}[/]") 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 Thread.current[:formatador].display_line("[green]+ #{description}[/]") end
def initialize(description, tags = [], &block)
def initialize(description, tags = [], &block) @afters = [] @befores = [] @description_stack = [] @tag_stack = [] Thread.current[:formatador] = Formatador.new Thread.current[:reload] = false Thread.current[:tags] ||= [] Thread.current[:totals] ||= { :failed => 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 Thread.current[:formatador].display_line tests(description, tags, &block) end
def pending
def pending catch(:pending) do @pending = true end throw(:pending) end
def prompt(description, &block)
def prompt(description, &block) return if Thread.main[:exit] || Thread.current[:reload] Thread.current[:formatador].display("Action? [c,e,i,q,r,t,?]? ") choice = STDIN.gets.strip continue = false Thread.current[:formatador].display_line Thread.current[:formatador].indent do case choice when 'c', 'continue' continue = true when /^e .*/, /^eval .*/ begin value = eval(choice[2..-1], @gestalt.bindings.last) if value.nil? value = 'nil' end Thread.current[:formatador].display_line(value) rescue => error display_error(error) end when 'i', 'interactive', 'irb' Thread.current[:formatador].display_line('Starting interactive session...') if @irb.nil? require 'irb' ARGV.clear # Avoid passing args to IRB IRB.setup(nil) @irb = IRB::Irb.new(nil) IRB.conf[:MAIN_CONTEXT] = @irb.context IRB.conf[:PROMPT][:SHINDO] = {} end for key, value in IRB.conf[:PROMPT][:SIMPLE] IRB.conf[:PROMPT][:SHINDO][key] = "#{Thread.current[:formatador].indentation}#{value}" end @irb.context.prompt_mode = :SHINDO @irb.context.workspace = IRB::WorkSpace.new(@gestalt.bindings.last) begin @irb.eval_input rescue SystemExit end when 'q', 'quit', 'exit' Thread.current[:formatador].display_line("Exiting...") Thread.main[:exit] = true when 'r', 'reload' Thread.current[:formatador].display_line("Reloading...") Thread.current[:reload] = true when 't', 'backtrace', 'trace' if @gestalt.calls.empty? Thread.current[:formatador].display_line("[red]No methods were called, so no backtrace was captured.[/]") else @gestalt.display_calls end when '?', 'help' Thread.current[:formatador].display_lines([ 'c - ignore this error and continue', 'i - interactive mode', 'q - quit Shindo', 'r - reload and run the tests again', 't - display backtrace', '? - display help' ]) else Thread.current[:formatador].display_line("[red]#{choice} is not a valid choice, please try again.[/]") end Thread.current[:formatador].display_line end unless continue || Thread.main[:exit] Thread.current[:formatador].display_line("[red]- #{description}[/]") prompt(description, &block) end end
def raises(error, &block)
def raises(error, &block) assert(:raises, error, "raises #{error.inspect}", &block) end
def raises?(expectation, &block)
def raises?(expectation, &block) value = begin instance_eval(&block) rescue => error error end [value, value.is_a?(expectation)] end
def raises?(expectation, &block)
def raises?(expectation, &block) @gestalt = Gestalt.new({'formatador' => Thread.current[:formatador]}) [value = @gestalt.run(&block), value.is_a?(expectation)] end
def returns(expectation, &block)
def returns(expectation, &block) assert(:returns, expectation, "returns #{expectation.inspect}", &block) end
def returns?(expectation, &block)
def returns?(expectation, &block) [value = instance_eval(&block), value == expectation] end
def returns?(expectation, &block)
def returns?(expectation, &block) @gestalt = Gestalt.new({'formatador' => Thread.current[:formatador]}) [value = @gestalt.run(&block), value == expectation] 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] @tag_stack.push(tags) @befores.push([]) @afters.push([]) @description = nil 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) Thread.current[:formatador].indent { instance_eval(&block) } rescue => error display_error(error) end else @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