# TapTapSpec -- small RSpec clone for taptap. # # Derived from Bacon: # "Truth will sooner come out from error than from confusion." ---Francis Bacon # Copyright (C) 2007 Christian Neukirchen # # TapTapSpec is freely distributable under the terms of an MIT-style license. # See COPYING or http://www.opensource.org/licenses/mit-license.php. require 'taptap/simple' module TapTap::Spec VERSION = "0.2" ErrorLog = "" Shared = Hash.new { |_, name| raise NameError, "no such context: #{name.inspect}" } RestrictName = // unless defined? RestrictName RestrictContext = // unless defined? RestrictContext class Error < RuntimeError attr_accessor :count_as def initialize(count_as, message) @count_as = count_as super message end end class Context include TapTap::Simple def initialize(name, &block) @before = [] @after = [] @name = name return unless name =~ RestrictContext instance_eval(&block) end def before(&block); @before << block; end def after(&block); @after << block; end def behaves_like(name) instance_eval(&Shared[name]) end def it(description, &block) return unless description =~ RestrictName testing description do @before.each { |block| instance_eval(&block) } instance_eval(&block) @after.each { |block| instance_eval(&block) } end end def raise?(*args, &block); block.raise?(*args); end def throw?(*args, &block); block.throw?(*args); end end end class Object def true?; false; end def false?; false; end end class TrueClass def true?; true; end end class FalseClass def false?; true; end end class Proc def raise?(*exceptions) call rescue *(exceptions.empty? ? RuntimeError : exceptions) => e e else # do not rescue other exceptions. false end def throw?(sym) catch(sym) { call return false } return true end end class Numeric def close?(to, delta) (to.to_f - self).abs <= delta.to_f rescue false end end class Object def should(*args, &block) TapTap::Spec::Should.new(self).be(*args, &block) end end module Kernel private def describe(name, &block) TapTap::Spec::Context.new(name, &block) end def shared(name, &block) TapTap::Spec::Shared[name] = block end end class TapTap::Spec::Should include TapTap::Simple # Kills ==, ===, =~, eql?, equal?, frozen?, instance_of?, is_a?, # kind_of?, nil?, respond_to?, tainted? instance_methods.each { |method| undef_method method if method =~ /\?|^\W+$/ } def initialize(object) @object = object @negated = false end def not(*args, &block) @negated = !@negated if args.empty? self else be(*args, &block) end end def be(*args, &block) if args.empty? self else block = args.shift unless block_given? satisfy(*args, &block) end end alias a be alias an be def satisfy(*args, &block) if args.size == 1 && String === args.first description = args.shift else description = "" end r = yield(@object, *args) ok(@negated ^ r) @negated ^ r ? r : false end def method_missing(name, *args, &block) name = "#{name}?" if name.to_s =~ /\w[^?]\z/ desc = @negated ? "not " : "" desc << @object.inspect << "." << name.to_s desc << "(" << args.map{|x|x.inspect}.join(", ") << ") failed" satisfy(desc) { |x| x.__send__(name, *args, &block) } end def equal(value); self == value; end def match(value); self =~ value; end def identical_to(value); self.equal? value; end alias same_as identical_to def flunk(reason="Flunked") ok false, reason end end