module Assert; end module Assert::Result class Base; end class Pass < Base; end class Ignore < Base; end class Fail < Base; end class Error < Base; end class Skip < Base; end def self.types { :pass => Pass, :fail => Fail, :ignore => Ignore, :skip => Skip, :error => Error } end class Base attr_reader :test, :message, :backtrace def initialize(test, message, backtrace=nil) @test = test @backtrace = Backtrace.new(backtrace) @message = message && !message.empty? ? message : nil end Assert::Result.types.keys.each do |meth| define_method("#{meth}?") { false } end def test_name @test.name end def to_sym; nil; end def to_s [ "#{self.name.upcase}: #{self.test_name}", self.message, self.trace ].compact.join("\n") end def name "" end def trace (self.backtrace.filtered.first || self.test.context_info.called_from).to_s end def ==(other) self.class == other.class && self.message == other.message end def inspect "#<#{self.class} @message=#{self.message.inspect}>" end end class Pass < Base def pass?; true; end def to_sym; :pass; end def name "Pass" end end class Ignore < Base def ignore?; true; end def to_sym; :ignore; end def name "Ignore" end end # raised by the 'fail' context helper to break test execution TestFailure = Class.new(RuntimeError) class Fail < Base # fail results can be generated manually or by raising Assert::Result::TestFailure def initialize(test, message_or_exception, backtrace=nil) if message_or_exception.kind_of?(TestFailure) super(test, message_or_exception.message, message_or_exception.backtrace || []) elsif message_or_exception.kind_of?(Exception) raise ArgumentError, "generate fail results by raising Assert::Result::TestFailure" else super(test, message_or_exception, backtrace) end end def fail?; true; end def to_sym; :fail; end def name "Fail" end end # raised by the 'skip' context helper to break test execution class TestSkipped < RuntimeError; end class Skip < Base # skip results are generated by raising Assert::Result::TestSkipped def initialize(test, exception) if exception.kind_of?(TestSkipped) super(test, exception.message, exception.backtrace || []) else raise ArgumentError, "generate skip results by raising Assert::Result::TestSkipped" end end def skip?; true; end def to_sym; :skip; end def name "Skip" end end class Error < Base # error results are generated by raising exceptions in tests def initialize(test, exception) if exception.kind_of?(Exception) super(test, "#{exception.message} (#{exception.class.name})", exception.backtrace || []) else raise ArgumentError, "generate error results by raising an exception" end end def error?; true; end def to_sym; :error; end def name "Error" end # override of the base, always show the full unfiltered backtrace for errors def trace self.backtrace.to_s end end # Utility Classes class Set < ::Array attr_accessor :callback def initialize(callback=nil) @callback = callback super() end def <<(result) super @callback.call(result) if @callback end end class Backtrace < ::Array def initialize(value=nil) super(value || ["No backtrace"]) end def to_s; self.join("\n"); end def filtered self.class.new(self.reject { |line| filter_out?(line) }) end protected # filter a line out if it's an assert lib/bin line def filter_out?(line) # './lib' in project dir, or '/usr/local/blahblah' if installed assert_lib_path = File.expand_path('../..', __FILE__) assert_bin_regex = /bin\/assert\:/ line.rindex(assert_lib_path, 0) || line =~ assert_bin_regex end end end