lib/ae/assertor.rb in ae-1.7.4 vs lib/ae/assertor.rb in ae-1.8.0
- old
+ new
@@ -1,8 +1,8 @@
require 'ae/assertion'
require 'ae/basic_object'
-require 'ansi/diff'
+require 'ae/ansi'
module AE
# Assertor is the underlying class of the whole system. It implements
# the flutent assertion notation.
@@ -89,11 +89,10 @@
# NOT HAPPENING
## Is ::Assay defined. This is used for integration of the Assay library.
#def self.assay?
# @_assay ||= defined?(::Assay)
#end
-
#
#def self.message(sym, neg, *args, &blk)
# if method = Message.lookup(sym)
# method = "non_#{method}" if neg
# Message.send(method, *args, &blk)
@@ -114,15 +113,14 @@
@message = opts[:message]
@backtrace = opts[:backtrace] || caller #[1..-1]
@negated = !!opts[:negated]
end
+ # TODO: Should #not return a new Assertor instead of in place negation?
+
# Negate the meaning of the assertion.
#
- #--
- # TODO: Should this return a new Assertor instead of in place negation?
- #++
def not(msg=nil)
@negated = !@negated
@message = msg if msg
self
end
@@ -139,68 +137,64 @@
# instead of
#
# assert something, parameter
#
# Returns +true+ or +false+ based on assertions success.
- #--
- # The use of #to_proc and #matches? as sepcial cases is not
- # a robust solution.
- #++
+ #
def assert(*args, &block)
- return self if args.empty? && !block
+ return self if !block && args.empty?
- target = block || args.shift
+ target = args.shift unless block
error = nil
- # Lambda
- if ::Proc === target || target.respond_to?(:to_proc)
- block = target.to_proc
+ # Block
+ if block
match = args.shift
result = block.arity > 0 ? block.call(@delegate) : block.call
if match
pass = (match == result)
error = @message || "#{match.inspect} == #{result.inspect}"
else
pass = result
error = @message || block.inspect # "#{result.inspect}"
end
- # Matcher
- elsif target.respond_to?(:matches?) # Matchers
- pass = target.matches?(@delegate)
- error = @message || matcher_message(target) #|| target.inspect
- if target.respond_to?(:exception)
- #error_class = target.failure_class
- error = target.exception #(:backtrace=>@backtrace, :negated=>@negated)
- end
+ # Proc-style
+ elsif proc_assertion?(target)
+ pass, error = proc_apply(target)
+ # Assay-style assertions
+ #elsif assay_assertion?(target)
+ # pass, error = assay_assertion_apply(target)
+
+ # RSpec-style matchers
+ elsif rspec_matcher?(target)
+ pass, error = rspec_matcher_apply(target)
+
# Truthiness
else
pass = target # truthiness
error = args.shift # optional message for TestUnit compatiability
end
__assert__(pass, error)
end
+ # TODO: Should we deprecate the receiver matches in favor of #expected ?
+ # In other words, should <code>|| @delegate</code> be dropped?
+
# Internal expect, provides all functionality associated
# with external #expect method. (See Expect#expect)
#
- #--
- # TODO: Should we deprecate the receiver matches in favor of #expected ?
- # In other words, should the <code>|| @delegate</code> be dropped?
- #++
def expect(*args, &block)
- return self if args.empty? && !block # same as #assert
+ return self if !block && args.empty? # same as #assert
- target = block || args.shift
- error = nil
+ pass = false
+ error = nil
- # Lambda
- if ::Proc === target #|| target.respond_to?(:to_proc)
- #block = target.to_proc
- match = args.shift || @delegate
+ if block
+ match = args.shift || @delegate # TODO: see above
if exception?(match)
$DEBUG, debug = false, $DEBUG # b/c it always spits-out a NameError
begin
block.arity > 0 ? block.call(@delegate) : block.call
pass = false
@@ -218,23 +212,24 @@
result = block.arity > 0 ? block.call(@delegte) : block.call
pass = (match === result)
error = @message || "#{match.inspect} === #{result.inspect}"
end
- # Matcher
- elsif target.respond_to?(:matches?)
- pass = target.matches?(@delegate)
- error = @message || matcher_message(target) #|| target.inspect
- if target.respond_to?(:exception)
- #error_class = target.failure_class
- error = target.exception #failure(:backtrace=>@backtrace, :negated=>@negated)
- end
+ ## Matcher
+ #elsif target.respond_to?(:matches?)
+ # pass = target.matches?(@delegate)
+ # error = @message || matcher_message(target) #|| target.inspect
+ # if target.respond_to?(:exception)
+ # #error_class = target.failure_class
+ # error = target.exception #failure(:backtrace=>@backtrace, :negated=>@negated)
+ # end
- # Case Equals
+ # Case Equality
else
- pass = (target === @delegate)
- error = @message || "#{target.inspect} === #{@delegate.inspect}"
+ target = args.shift
+ pass = (target === @delegate)
+ error = @message || "#{target.inspect} === #{@delegate.inspect}"
end
__assert__(pass, error)
end
@@ -258,65 +253,126 @@
#
def inspect
@delegate.inspect
end
- private
+ private
- # Is the +object+ an Exception or an instance of one?
- #--
+ # AE-STYLE ASSERTIONS
+
+ #
+ def proc_assertion?(target)
+ ::Proc === target || target.respond_to?(:call) || target.respond_to?(:to_proc)
+ end
+
+ #
+ def proc_apply(target)
+ call = target.method(:call) rescue target.to_proc
+ pass = call.arity != 0 ? call.call(@delegate) : call.call
+ error = @message || (
+ to_s = target.method(:to_s)
+ to_s.arity == 0 ? to_s.call : to_s.call(@negated)
+ )
+ return pass, error
+ end
+
+ # ASSAY-STYLE ASSERTIONS
+ # (not yet supported b/c api is not 100%)
+
+ # Is the `assertion` object an assay-style assertion?
+ def assay_assertion?(assertion)
+ assertion.respond_to?(:exception) && assertion.respond_to?(:pass?)
+ end
+
+ #
+ def assay_assertion_apply(assay)
+ if @negated
+ pass = assay.fail?(@delegate)
+ error = assay #.exception(@message || )
+ else
+ pass = assay.pass?(@delegate)
+ error = assay #.exception(@message || )
+ end
+ return pass, error
+ end
+
+ # RSPEC-STYLE MATCHERS
+
+ # Is `target` an Rspec-style Matcher?
+ def rspec_matcher?(target)
+ target.respond_to?(:matches?)
+ end
+
+ #
+ def rspec_matcher_apply(matcher)
+ pass = matcher.matches?(@delegate)
+ error = @message || rspec_matcher_message(matcher)
+ return pass, error
+ end
+
+ # TODO: Is there anything to be done with matcher.description?
+
+ #
+ def rspec_matcher_message(matcher)
+ if @negated
+ if matcher.respond_to?(:failure_message_for_should_not)
+ return matcher.failure_message_for_should_not
+ end
+ if matcher.respond_to?(:negative_failure_message)
+ return matcher.negative_failure_message
+ end
+ end
+
+ if matcher.respond_to?(:failure_message_for_should)
+ return matcher.failure_message_for_should
+ end
+ if matcher.respond_to?(:failure_message)
+ return matcher.failure_message
+ end
+
+ return matcher.to_s # TODO: or just `nil` ?
+ end
+
# TODO: Should we use a more libreral determination of exception.
# e.g. <code>respond_to?(:exception)</code>.
- #++
+
+ # Is the +object+ an Exception or an instance of one?
def exception?(object)
::Exception === object or ::Class === object and object.ancestors.include?(::Exception)
end
- # Converts a missing method into an Assertion.
- #
# TODO: In future should probably be `@delegate.public_send(sym, *a, &b)`.
+
+ # Converts a missing method into an Assertion.
def method_missing(sym, *args, &block)
error = @message || compare_message(sym, *args, &block) || generic_message(sym, *args, &block)
pass = @delegate.__send__(sym, *args, &block)
__assert__(pass, error)
end
+ # TODO: Can the handling of the message be simplified/improved?
# Simple assert.
- #--
- # TODO: Can the handling of the message be simplified/improved?
- #++
def __assert__(pass, error=nil)
Assertor.assert(pass, error, @negated, @backtrace)
end
#
- def matcher_message(matcher)
- if @negated
- if matcher.respond_to?(:negative_failure_message)
- return matcher.failure_message
- end
- else
- if matcher.respond_to?(:failure_message)
- return matcher.failure_message
- end
- end
- return nil
- end
-
COMPARISON_OPERATORS = { :"==" => :"!=" }
# Message to use when making a comparion assertion.
#
# NOTE: This message utilizes the ANSI gem to produce colorized
# comparisons. If you need to remove color output (for non-ANSI
# terminals) you can either set `AE.ansi = false` or use the
# ANSI library's master switch to deactive all ANSI codes,
# which can be set in your test helper.
- #
+ #
+ # @param operator [Symbol] operator/method
+ #
# @see http://rubyworks.github.com/ansi
def compare_message(operator, *args, &blk)
return nil unless COMPARISON_OPERATORS.key?(operator)
prefix = ""
a, b = @delegate.inspect, args.first.inspect
@@ -340,10 +396,13 @@
end
end
# Puts together a suitable error message.
#
+ # @param op [Symbol] operator/method
+ #
+ # @return [String] message
def generic_message(op, *a, &b)
inspection = @delegate.send(:inspect)
if @negated
"! #{inspection} #{op} #{a.collect{|x| x.inspect}.join(',')}"
else
@@ -364,6 +423,6 @@
#class BasicObject
# def assert
# end
#end
-# Copyright (c) 2008,2009 Thomas Sawyer
+# Copyright (c) 2008 Thomas Sawyer