lib/cucumber/core/test/result.rb
# encoding: UTF-8 module Cucumber module Core module Test module Result # Defines predicate methods on a result class with only the given one # returning true def self.status_queries(status) Module.new do [:passed, :failed, :undefined, :unknown, :skipped, :pending].each do |possible_status| define_method("#{possible_status}?") do possible_status == status end end end end # Null object for results. Represents the state where we haven't run anything yet class Unknown include Result.status_queries :unknown def describe_to(visitor, *args) self end end class Passed include Result.status_queries(:passed) attr_accessor :duration def initialize(duration) raise ArgumentError unless duration @duration = duration end def describe_to(visitor, *args) visitor.passed(*args) visitor.duration(duration, *args) self end def to_s "✓" end end class Failed include Result.status_queries(:failed) attr_reader :duration, :exception def initialize(duration, exception) raise ArgumentError unless duration raise ArgumentError unless exception @duration = duration @exception = exception end def describe_to(visitor, *args) visitor.failed(*args) visitor.duration(duration, *args) visitor.exception(exception, *args) if exception self end def to_s "✗" end def with_duration(new_duration) self.class.new(new_duration, exception) end end # Base class for exceptions that can be raised in a step defintion causing # the step to have that result. class Raisable < StandardError attr_reader :message, :duration def initialize(message = "", duration = UnknownDuration.new, backtrace = nil) @message, @duration = message, duration super(message) set_backtrace(backtrace) if backtrace end def with_message(new_message) self.class.new(new_message, duration, backtrace) end def with_duration(new_duration) self.class.new(message, new_duration, backtrace) end end class Undefined < Raisable include Result.status_queries :undefined def describe_to(visitor, *args) visitor.undefined(*args) visitor.duration(duration, *args) self end def to_s "?" end end class Skipped < Raisable include Result.status_queries :skipped def describe_to(visitor, *args) visitor.skipped(*args) visitor.duration(duration, *args) self end def to_s "-" end end class Pending < Raisable include Result.status_queries :pending def describe_to(visitor, *args) visitor.pending(self, *args) visitor.duration(duration, *args) self end def to_s "P" end end # # An object that responds to the description protocol from the results # and collects summary information. # # e.g. # summary = Result::Summary.new # Result::Passed.new(0).describe_to(summary) # puts summary.total_passed # => 1 # class Summary attr_reader :exceptions, :durations def initialize @totals = Hash.new { 0 } @exceptions = [] @durations = [] end def method_missing(name, *args) if name =~ /^total_/ get_total(name) else increment_total(name) end end def exception(exception) @exceptions << exception self end def duration(duration) @durations << duration self end def total @totals.reduce(0) { |total, status| total += status[1] } end private def get_total(method_name) status = method_name.to_s.gsub('total_', '').to_sym return @totals.fetch(status) { 0 } end def increment_total(status) @totals[status] += 1 self end end class Duration attr_reader :nanoseconds def initialize(nanoseconds) @nanoseconds = nanoseconds end end class UnknownDuration def tap(&block) self end def nanoseconds raise "#nanoseconds only allowed to be used in #tap block" end end end end end end