summaryrefslogtreecommitdiffstats
path: root/test/lib/spec/matchers
diff options
context:
space:
mode:
Diffstat (limited to 'test/lib/spec/matchers')
-rw-r--r--test/lib/spec/matchers/be.rb161
-rw-r--r--test/lib/spec/matchers/be_close.rb37
-rw-r--r--test/lib/spec/matchers/change.rb120
-rw-r--r--test/lib/spec/matchers/eql.rb43
-rw-r--r--test/lib/spec/matchers/equal.rb43
-rw-r--r--test/lib/spec/matchers/has.rb44
-rw-r--r--test/lib/spec/matchers/have.rb140
-rw-r--r--test/lib/spec/matchers/include.rb50
-rw-r--r--test/lib/spec/matchers/match.rb41
-rw-r--r--test/lib/spec/matchers/raise_error.rb100
-rw-r--r--test/lib/spec/matchers/respond_to.rb35
-rw-r--r--test/lib/spec/matchers/satisfy.rb47
-rw-r--r--test/lib/spec/matchers/throw_symbol.rb75
13 files changed, 936 insertions, 0 deletions
diff --git a/test/lib/spec/matchers/be.rb b/test/lib/spec/matchers/be.rb
new file mode 100644
index 000000000..957f23de8
--- /dev/null
+++ b/test/lib/spec/matchers/be.rb
@@ -0,0 +1,161 @@
+module Spec
+ module Matchers
+
+ class Be #:nodoc:
+ def initialize(expected=nil, *args)
+ @expected = parse_expected(expected)
+ @args = args
+ @comparison = ""
+ end
+
+ def matches?(actual)
+ @actual = actual
+ return true if match_or_compare unless handling_predicate?
+ if handling_predicate?
+ begin
+ return @result = actual.__send__(predicate, *@args)
+ rescue => predicate_error
+ # This clause should be empty, but rcov will not report it as covered
+ # unless something (anything) is executed within the clause
+ rcov_error_report = "http://eigenclass.org/hiki.rb?rcov-0.8.0"
+ end
+
+ # This supports should_exist > target.exists? in the old world.
+ # We should consider deprecating that ability as in the new world
+ # you can't write "should exist" unless you have your own custom matcher.
+ begin
+ return @result = actual.__send__(present_tense_predicate, *@args)
+ rescue
+ raise predicate_error
+ end
+ end
+ return false
+ end
+
+ def failure_message
+ return "expected #{@comparison}#{expected}, got #{@actual.inspect}" unless handling_predicate?
+ return "expected #{predicate}#{args_to_s} to return true, got #{@result.inspect}"
+ end
+
+ def negative_failure_message
+ return "expected not #{expected}, got #{@actual.inspect}" unless handling_predicate?
+ return "expected #{predicate}#{args_to_s} to return false, got #{@result.inspect}"
+ end
+
+ def expected
+ return true if @expected == :true
+ return false if @expected == :false
+ return "nil" if @expected == :nil
+ return @expected.inspect
+ end
+
+ def match_or_compare
+ return @actual == true if @expected == :true
+ return @actual == false if @expected == :false
+ return @actual.nil? if @expected == :nil
+ return @actual < @expected if @less_than
+ return @actual <= @expected if @less_than_or_equal
+ return @actual >= @expected if @greater_than_or_equal
+ return @actual > @expected if @greater_than
+ return @actual.equal?(@expected)
+ end
+
+ def <(expected)
+ @less_than = true
+ @comparison = "< "
+ @expected = expected
+ self
+ end
+
+ def <=(expected)
+ @less_than_or_equal = true
+ @comparison = "<= "
+ @expected = expected
+ self
+ end
+
+ def >=(expected)
+ @greater_than_or_equal = true
+ @comparison = ">= "
+ @expected = expected
+ self
+ end
+
+ def >(expected)
+ @greater_than = true
+ @comparison = "> "
+ @expected = expected
+ self
+ end
+
+ def description
+ "be #{@comparison}#{@expected}"
+ end
+
+ private
+ def parse_expected(expected)
+ if Symbol === expected
+ ["be_an_","be_a_","be_"].each do |prefix|
+ @handling_predicate = true
+ return "#{expected.to_s.sub(prefix,"")}".to_sym if expected.starts_with?(prefix)
+ end
+ end
+ return expected
+ end
+
+ def predicate
+ "#{@expected.to_s}?".to_sym
+ end
+
+ def present_tense_predicate
+ "#{@expected.to_s}s?".to_sym
+ end
+
+ def args_to_s
+ return "" if @args.empty?
+ transformed_args = @args.collect{|a| a.inspect}
+ return "(#{transformed_args.join(', ')})"
+ end
+
+ def handling_predicate?
+ return false if [:true, :false, :nil].include?(@expected)
+ return @handling_predicate
+ end
+ end
+
+ # :call-seq:
+ # should be_true
+ # should be_false
+ # should be_nil
+ # should be_arbitrary_predicate(*args)
+ # should_not be_nil
+ # should_not be_arbitrary_predicate(*args)
+ #
+ # Given true, false, or nil, will pass if actual is
+ # true, false or nil (respectively).
+ #
+ # Predicates are any Ruby method that ends in a "?" and returns true or false.
+ # Given be_ followed by arbitrary_predicate (without the "?"), RSpec will match
+ # convert that into a query against the target object.
+ #
+ # The arbitrary_predicate feature will handle any predicate
+ # prefixed with "be_an_" (e.g. be_an_instance_of), "be_a_" (e.g. be_a_kind_of)
+ # or "be_" (e.g. be_empty), letting you choose the prefix that best suits the predicate.
+ #
+ # == Examples
+ #
+ # target.should be_true
+ # target.should be_false
+ # target.should be_nil
+ # target.should_not be_nil
+ #
+ # collection.should be_empty #passes if target.empty?
+ # "this string".should be_an_intance_of(String)
+ #
+ # target.should_not be_empty #passes unless target.empty?
+ # target.should_not be_old_enough(16) #passes unless target.old_enough?(16)
+ def be(*args)
+ Matchers::Be.new(*args)
+ end
+ end
+end
diff --git a/test/lib/spec/matchers/be_close.rb b/test/lib/spec/matchers/be_close.rb
new file mode 100644
index 000000000..b09e3fd2f
--- /dev/null
+++ b/test/lib/spec/matchers/be_close.rb
@@ -0,0 +1,37 @@
+module Spec
+ module Matchers
+
+ class BeClose #:nodoc:
+ def initialize(expected, delta)
+ @expected = expected
+ @delta = delta
+ end
+
+ def matches?(actual)
+ @actual = actual
+ (@actual - @expected).abs < @delta
+ end
+
+ def failure_message
+ "expected #{@expected} +/- (<#{@delta}), got #{@actual}"
+ end
+
+ def description
+ "be close to #{@expected} (+- #{@delta})"
+ end
+ end
+
+ # :call-seq:
+ # should be_close(expected, delta)
+ # should_not be_close(expected, delta)
+ #
+ # Passes if actual == expected +/- delta
+ #
+ # == Example
+ #
+ # result.should be_close(3.0, 0.5)
+ def be_close(expected, delta)
+ Matchers::BeClose.new(expected, delta)
+ end
+ end
+end \ No newline at end of file
diff --git a/test/lib/spec/matchers/change.rb b/test/lib/spec/matchers/change.rb
new file mode 100644
index 000000000..41a718aca
--- /dev/null
+++ b/test/lib/spec/matchers/change.rb
@@ -0,0 +1,120 @@
+module Spec
+ module Matchers
+
+ #Based on patch from Wilson Bilkovich
+ class Change #:nodoc:
+ def initialize(receiver=nil, message=nil, &block)
+ @receiver = receiver
+ @message = message
+ @block = block
+ end
+
+ def matches?(target, &block)
+ if block
+ raise MatcherError.new(<<-EOF
+block passed to should or should_not change must use {} instead of do/end
+EOF
+)
+ end
+ @target = target
+ execute_change
+ return false if @from && (@from != @before)
+ return false if @to && (@to != @after)
+ return (@before + @amount == @after) if @amount
+ return @before != @after
+ end
+
+ def execute_change
+ @before = @block.nil? ? @receiver.send(@message) : @block.call
+ @target.call
+ @after = @block.nil? ? @receiver.send(@message) : @block.call
+ end
+
+ def failure_message
+ if @to
+ "#{result} should have been changed to #{@to.inspect}, but is now #{@after.inspect}"
+ elsif @from
+ "#{result} should have initially been #{@from.inspect}, but was #{@before.inspect}"
+ elsif @amount
+ "#{result} should have been changed by #{@amount.inspect}, but was changed by #{actual_delta.inspect}"
+ else
+ "#{result} should have changed, but is still #{@before.inspect}"
+ end
+ end
+
+ def result
+ @message || "result"
+ end
+
+ def actual_delta
+ @after - @before
+ end
+
+ def negative_failure_message
+ "#{result} should not have changed, but did change from #{@before.inspect} to #{@after.inspect}"
+ end
+
+ def by(amount)
+ @amount = amount
+ self
+ end
+
+ def to(to)
+ @to = to
+ self
+ end
+
+ def from (from)
+ @from = from
+ self
+ end
+ end
+
+ # :call-seq:
+ # should change(receiver, message, &block)
+ # should change(receiver, message, &block).by(value)
+ # should change(receiver, message, &block).from(old).to(new)
+ # should_not change(receiver, message, &block)
+ #
+ # Allows you to specify that a Proc will cause some value to change.
+ #
+ # == Examples
+ #
+ # lambda {
+ # team.add_player(player)
+ # }.should change(roster, :count)
+ #
+ # lambda {
+ # team.add_player(player)
+ # }.should change(roster, :count).by(1)
+ #
+ # string = "string"
+ # lambda {
+ # string.reverse
+ # }.should change { string }.from("string").to("gnirts")
+ #
+ # lambda {
+ # person.happy_birthday
+ # }.should change(person, :birthday).from(32).to(33)
+ #
+ # lambda {
+ # employee.develop_great_new_social_networking_app
+ # }.should change(employee, :title).from("Mail Clerk").to("CEO")
+ #
+ # Evaluates +receiver.message+ or +block+ before and
+ # after it evaluates the c object (generated by the lambdas in the examples above).
+ #
+ # Then compares the values before and after the +receiver.message+ and
+ # evaluates the difference compared to the expected difference.
+ #
+ # == Warning
+ # +should_not+ +change+ only supports the form with no subsequent calls to
+ # +be+, +to+ or +from+.
+ #
+ # blocks passed to +should+ +change+ and +should_not+ +change+
+ # must use the <tt>{}</tt> form (<tt>do/end</tt> is not supported)
+ def change(target=nil, message=nil, &block)
+ Matchers::Change.new(target, message, &block)
+ end
+ end
+end
diff --git a/test/lib/spec/matchers/eql.rb b/test/lib/spec/matchers/eql.rb
new file mode 100644
index 000000000..caca1f7c6
--- /dev/null
+++ b/test/lib/spec/matchers/eql.rb
@@ -0,0 +1,43 @@
+module Spec
+ module Matchers
+
+ class Eql #:nodoc:
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @actual.eql?(@expected)
+ end
+
+ def failure_message
+ return "expected #{@expected.inspect}, got #{@actual.inspect} (using .eql?)", @expected, @actual
+ end
+
+ def negative_failure_message
+ return "expected #{@actual.inspect} not to equal #{@expected.inspect} (using .eql?)", @expected, @actual
+ end
+
+ def description
+ "eql #{@expected.inspect}"
+ end
+ end
+
+ # :call-seq:
+ # should eql(expected)
+ # should_not eql(expected)
+ #
+ # Passes if actual and expected are of equal value, but not necessarily the same object.
+ #
+ # See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more information about equality in Ruby.
+ #
+ # == Examples
+ #
+ # 5.should eql(5)
+ # 5.should_not eql(3)
+ def eql(expected)
+ Matchers::Eql.new(expected)
+ end
+ end
+end \ No newline at end of file
diff --git a/test/lib/spec/matchers/equal.rb b/test/lib/spec/matchers/equal.rb
new file mode 100644
index 000000000..e987e73cb
--- /dev/null
+++ b/test/lib/spec/matchers/equal.rb
@@ -0,0 +1,43 @@
+module Spec
+ module Matchers
+
+ class Equal #:nodoc:
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @actual.equal?(@expected)
+ end
+
+ def failure_message
+ return "expected #{@expected.inspect}, got #{@actual.inspect} (using .equal?)", @expected, @actual
+ end
+
+ def negative_failure_message
+ return "expected #{@actual.inspect} not to equal #{@expected.inspect} (using .equal?)", @expected, @actual
+ end
+
+ def description
+ "equal #{@expected.inspect}"
+ end
+ end
+
+ # :call-seq:
+ # should equal(expected)
+ # should_not equal(expected)
+ #
+ # Passes if actual and expected are the same object (object identity).
+ #
+ # See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more information about equality in Ruby.
+ #
+ # == Examples
+ #
+ # 5.should equal(5) #Fixnums are equal
+ # "5".should_not equal("5") #Strings that look the same are not the same object
+ def equal(expected)
+ Matchers::Equal.new(expected)
+ end
+ end
+end \ No newline at end of file
diff --git a/test/lib/spec/matchers/has.rb b/test/lib/spec/matchers/has.rb
new file mode 100644
index 000000000..cc5a250b8
--- /dev/null
+++ b/test/lib/spec/matchers/has.rb
@@ -0,0 +1,44 @@
+module Spec
+ module Matchers
+
+ class Has #:nodoc:
+ def initialize(sym, *args)
+ @sym = sym
+ @args = args
+ end
+
+ def matches?(target)
+ @target = target
+ begin
+ return target.send(predicate, *@args)
+ rescue => @error
+ # This clause should be empty, but rcov will not report it as covered
+ # unless something (anything) is executed within the clause
+ rcov_error_report = "http://eigenclass.org/hiki.rb?rcov-0.8.0"
+ end
+ return false
+ end
+
+ def failure_message
+ raise @error if @error
+ "expected ##{predicate}(#{@args[0].inspect}) to return true, got false"
+ end
+
+ def negative_failure_message
+ raise @error if @error
+ "expected ##{predicate}(#{@args[0].inspect}) to return false, got true"
+ end
+
+ def description
+ "have key #{@args[0].inspect}"
+ end
+
+ private
+ def predicate
+ "#{@sym.to_s.sub("have_","has_")}?".to_sym
+ end
+
+ end
+
+ end
+end
diff --git a/test/lib/spec/matchers/have.rb b/test/lib/spec/matchers/have.rb
new file mode 100644
index 000000000..81f9af3e3
--- /dev/null
+++ b/test/lib/spec/matchers/have.rb
@@ -0,0 +1,140 @@
+module Spec
+ module Matchers
+
+ class Have #:nodoc:
+ def initialize(expected, relativity=:exactly)
+ @expected = (expected == :no ? 0 : expected)
+ @relativity = relativity
+ end
+
+ def relativities
+ @relativities ||= {
+ :exactly => "",
+ :at_least => "at least ",
+ :at_most => "at most "
+ }
+ end
+
+ def method_missing(sym, *args, &block)
+ @collection_name = sym
+ @args = args
+ @block = block
+ self
+ end
+
+ def matches?(collection_owner)
+ if collection_owner.respond_to?(collection_name)
+ collection = collection_owner.send(collection_name, *@args, &@block)
+ elsif (collection_owner.respond_to?(:length) || collection_owner.respond_to?(:size))
+ collection = collection_owner
+ else
+ collection_owner.send(collection_name, *@args, &@block)
+ end
+ @actual = collection.length if collection.respond_to?(:length)
+ @actual = collection.size if collection.respond_to?(:size)
+ return @actual >= @expected if @relativity == :at_least
+ return @actual <= @expected if @relativity == :at_most
+ return @actual == @expected
+ end
+
+ def failure_message
+ "expected #{relative_expectation} #{collection_name}, got #{@actual}"
+ end
+
+ def negative_failure_message
+ if @relativity == :exactly
+ return "expected target not to have #{@expected} #{collection_name}, got #{@actual}"
+ elsif @relativity == :at_most
+ return <<-EOF
+Isn't life confusing enough?
+Instead of having to figure out the meaning of this:
+ should_not have_at_most(#{@expected}).#{collection_name}
+We recommend that you use this instead:
+ should have_at_least(#{@expected + 1}).#{collection_name}
+EOF
+ elsif @relativity == :at_least
+ return <<-EOF
+Isn't life confusing enough?
+Instead of having to figure out the meaning of this:
+ should_not have_at_least(#{@expected}).#{collection_name}
+We recommend that you use this instead:
+ should have_at_most(#{@expected - 1}).#{collection_name}
+EOF
+ end
+ end
+
+ def description
+ "have #{relative_expectation} #{collection_name}"
+ end
+
+ private
+ def collection_name
+ @collection_name
+ end
+
+ def relative_expectation
+ "#{relativities[@relativity]}#{@expected}"
+ end
+ end
+
+ # :call-seq:
+ # should have(number).named_collection__or__sugar
+ # should_not have(number).named_collection__or__sugar
+ #
+ # Passes if receiver is a collection with the submitted
+ # number of items OR if the receiver OWNS a collection
+ # with the submitted number of items.
+ #
+ # If the receiver OWNS the collection, you must use the name
+ # of the collection. So if a <tt>Team</tt> instance has a
+ # collection named <tt>#players</tt>, you must use that name
+ # to set the expectation.
+ #
+ # If the receiver IS the collection, you can use any name
+ # you like for <tt>named_collection</tt>. We'd recommend using
+ # either "elements", "members", or "items" as these are all
+ # standard ways of describing the things IN a collection.
+ #
+ # This also works for Strings, letting you set an expectation
+ # about its length
+ #
+ # == Examples
+ #
+ # # Passes if team.players.size == 11
+ # team.should have(11).players
+ #
+ # # Passes if [1,2,3].length == 3
+ # [1,2,3].should have(3).items #"items" is pure sugar
+ #
+ # # Passes if "this string".length == 11
+ # "this string".should have(11).characters #"characters" is pure sugar
+ def have(n)
+ Matchers::Have.new(n)
+ end
+ alias :have_exactly :have
+
+ # :call-seq:
+ # should have_at_least(number).items
+ #
+ # Exactly like have() with >=.
+ #
+ # == Warning
+ #
+ # +should_not+ +have_at_least+ is not supported
+ def have_at_least(n)
+ Matchers::Have.new(n, :at_least)
+ end
+
+ # :call-seq:
+ # should have_at_most(number).items
+ #
+ # Exactly like have() with <=.
+ #
+ # == Warning
+ #
+ # +should_not+ +have_at_most+ is not supported
+ def have_at_most(n)
+ Matchers::Have.new(n, :at_most)
+ end
+ end
+end \ No newline at end of file
diff --git a/test/lib/spec/matchers/include.rb b/test/lib/spec/matchers/include.rb
new file mode 100644
index 000000000..0d387f323
--- /dev/null
+++ b/test/lib/spec/matchers/include.rb
@@ -0,0 +1,50 @@
+module Spec
+ module Matchers
+
+ class Include #:nodoc:
+
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ actual.include?(@expected)
+ end
+
+ def failure_message
+ _message
+ end
+
+ def negative_failure_message
+ _message("not ")
+ end
+
+ def description
+ "include #{@expected.inspect}"
+ end
+
+ private
+ def _message(maybe_not="")
+ "expected #{@actual.inspect} #{maybe_not}to include #{@expected.inspect}"
+ end
+ end
+
+ # :call-seq:
+ # should include(expected)
+ # should_not include(expected)
+ #
+ # Passes if actual includes expected. This works for
+ # collections and Strings
+ #
+ # == Examples
+ #
+ # [1,2,3].should include(3)
+ # [1,2,3].should_not include(4)
+ # "spread".should include("read")
+ # "spread".should_not include("red")
+ def include(expected)
+ Matchers::Include.new(expected)
+ end
+ end
+end
diff --git a/test/lib/spec/matchers/match.rb b/test/lib/spec/matchers/match.rb
new file mode 100644
index 000000000..61ab52429
--- /dev/null
+++ b/test/lib/spec/matchers/match.rb
@@ -0,0 +1,41 @@
+module Spec
+ module Matchers
+
+ class Match #:nodoc:
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ return true if actual =~ @expected
+ return false
+ end
+
+ def failure_message
+ return "expected #{@actual.inspect} to match #{@expected.inspect}", @expected, @actual
+ end
+
+ def negative_failure_message
+ return "expected #{@actual.inspect} not to match #{@expected.inspect}", @expected, @actual
+ end
+
+ def description
+ "match #{@expected.inspect}"
+ end
+ end
+
+ # :call-seq:
+ # should match(regexp)
+ # should_not match(regexp)
+ #
+ # Given a Regexp, passes if actual =~ regexp
+ #
+ # == Examples
+ #
+ # email.should match(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i)
+ def match(regexp)
+ Matchers::Match.new(regexp)
+ end
+ end
+end
diff --git a/test/lib/spec/matchers/raise_error.rb b/test/lib/spec/matchers/raise_error.rb
new file mode 100644
index 000000000..95e82ad5e
--- /dev/null
+++ b/test/lib/spec/matchers/raise_error.rb
@@ -0,0 +1,100 @@
+module Spec
+ module Matchers
+
+ class RaiseError #:nodoc:
+ def initialize(exception=Exception, message=nil)
+ @expected_error = exception
+ @expected_message = message
+ end
+
+ def matches?(proc)
+ @raised_expected_error = false
+ @raised_other = false
+ begin
+ proc.call
+ rescue @expected_error => @actual_error
+ if @expected_message.nil?
+ @raised_expected_error = true
+ else
+ case @expected_message
+ when Regexp
+ if @expected_message =~ @actual_error.message
+ @raised_expected_error = true
+ else
+ @raised_other = true
+ end
+ else
+ if @actual_error.message == @expected_message
+ @raised_expected_error = true
+ else
+ @raised_other = true
+ end
+ end
+ end
+ rescue => @actual_error
+ @raised_other = true
+ ensure
+ return @raised_expected_error
+ end
+ end
+
+ def failure_message
+ return "expected #{expected_error}#{actual_error}" if @raised_other || !@raised_expected_error
+ end
+
+ def negative_failure_message
+ "expected no #{expected_error}#{actual_error}"
+ end
+
+ def description
+ "raise #{expected_error}"
+ end
+
+ private
+ def expected_error
+ case @expected_message
+ when nil
+ @expected_error
+ when Regexp
+ "#{@expected_error} with message matching #{@expected_message.inspect}"
+ else
+ "#{@expected_error} with #{@expected_message.inspect}"
+ end
+ end
+
+ def actual_error
+ @actual_error.nil? ? " but nothing was raised" : ", got #{@actual_error.inspect}"
+ end
+ end
+
+ # :call-seq:
+ # should raise_error()
+ # should raise_error(NamedError)
+ # should raise_error(NamedError, String)
+ # should raise_error(NamedError, Regexp)
+ # should_not raise_error()
+ # should_not raise_error(NamedError)
+ # should_not raise_error(NamedError, String)
+ # should_not raise_error(NamedError, Regexp)
+ #
+ # With no args, matches if any error is raised.
+ # With a named error, matches only if that specific error is raised.
+ # With a named error and messsage specified as a String, matches only if both match.
+ # With a named error and messsage specified as a Regexp, matches only if both match.
+ #
+ # == Examples
+ #
+ # lambda { do_something_risky }.should raise_error
+ # lambda { do_something_risky }.should raise_error(PoorRiskDecisionError)
+ # lambda { do_something_risky }.should raise_error(PoorRiskDecisionError, "that was too risky")
+ # lambda { do_something_risky }.should raise_error(PoorRiskDecisionError, /oo ri/)
+ #
+ # lambda { do_something_risky }.should_not raise_error
+ # lambda { do_something_risky }.should_not raise_error(PoorRiskDecisionError)
+ # lambda { do_something_risky }.should_not raise_error(PoorRiskDecisionError, "that was too risky")
+ # lambda { do_something_risky }.should_not raise_error(PoorRiskDecisionError, /oo ri/)
+ def raise_error(error=Exception, message=nil)
+ Matchers::RaiseError.new(error, message)
+ end
+ end
+end
diff --git a/test/lib/spec/matchers/respond_to.rb b/test/lib/spec/matchers/respond_to.rb
new file mode 100644
index 000000000..013a36f1d
--- /dev/null
+++ b/test/lib/spec/matchers/respond_to.rb
@@ -0,0 +1,35 @@
+module Spec
+ module Matchers
+
+ class RespondTo #:nodoc:
+ def initialize(sym)
+ @sym = sym
+ end
+
+ def matches?(target)
+ return target.respond_to?(@sym)
+ end
+
+ def failure_message
+ "expected target to respond to #{@sym.inspect}"
+ end
+
+ def negative_failure_message
+ "expected target not to respond to #{@sym.inspect}"
+ end
+
+ def description
+ "respond to ##{@sym.to_s}"
+ end
+ end
+
+ # :call-seq:
+ # should respond_to(:sym)
+ # should_not respond_to(:sym)
+ #
+ # Matches if the target object responds to :sym
+ def respond_to(sym)
+ Matchers::RespondTo.new(sym)
+ end
+ end
+end
diff --git a/test/lib/spec/matchers/satisfy.rb b/test/lib/spec/matchers/satisfy.rb
new file mode 100644
index 000000000..6c0ca95bc
--- /dev/null
+++ b/test/lib/spec/matchers/satisfy.rb
@@ -0,0 +1,47 @@
+module Spec
+ module Matchers
+
+ class Satisfy #:nodoc:
+ def initialize(&block)
+ @block = block
+ end
+
+ def matches?(actual, &block)
+ @block = block if block
+ @actual = actual
+ @block.call(actual)
+ end
+
+ def failure_message
+ "expected #{@actual} to satisfy block"
+ end
+
+ def negative_failure_message
+ "expected #{@actual} not to satisfy block"
+ end
+ end
+
+ # :call-seq:
+ # should satisfy {}
+ # should_not satisfy {}
+ #
+ # Passes if the submitted block returns true. Yields target to the
+ # block.
+ #
+ # Generally speaking, this should be thought of as a last resort when
+ # you can't find any other way to specify the behaviour you wish to
+ # specify.
+ #
+ # If you do find yourself in such a situation, you could always write
+ # a custom matcher, which would likely make your specs more expressive.
+ #
+ # == Examples
+ #
+ # 5.should satisfy { |n|
+ # n > 3
+ # }
+ def satisfy(&block)
+ Matchers::Satisfy.new(&block)
+ end
+ end
+end
diff --git a/test/lib/spec/matchers/throw_symbol.rb b/test/lib/spec/matchers/throw_symbol.rb
new file mode 100644
index 000000000..6732f6fed
--- /dev/null
+++ b/test/lib/spec/matchers/throw_symbol.rb
@@ -0,0 +1,75 @@
+module Spec
+ module Matchers
+
+ class ThrowSymbol #:nodoc:
+ def initialize(expected=nil)
+ @expected = expected
+ end
+
+ def matches?(proc)
+ begin
+ proc.call
+ rescue NameError => e
+ @actual = extract_sym_from_name_error(e)
+ ensure
+ if @expected.nil?
+ return @actual.nil? ? false : true
+ else
+ return @actual == @expected
+ end
+ end
+ end
+
+ def failure_message
+ if @actual
+ "expected #{expected}, got #{@actual.inspect}"
+ else
+ "expected #{expected} but nothing was thrown"
+ end
+ end
+
+ def negative_failure_message
+ if @expected
+ "expected #{expected} not to be thrown"
+ else
+ "expected no Symbol, got :#{@actual}"
+ end
+ end
+
+ def description
+ "throw #{expected}"
+ end
+
+ private
+
+ def expected
+ @expected.nil? ? "a Symbol" : @expected.inspect
+ end
+
+ def extract_sym_from_name_error(error)
+ return "#{error.message.split("`").last.split("'").first}".to_sym
+ end
+ end
+
+ # :call-seq:
+ # should throw_symbol()
+ # should throw_symbol(:sym)
+ # should_not throw_symbol()
+ # should_not throw_symbol(:sym)
+ #
+ # Given a Symbol argument, matches if a proc throws the specified Symbol.
+ #
+ # Given no argument, matches if a proc throws any Symbol.
+ #
+ # == Examples
+ #
+ # lambda { do_something_risky }.should throw_symbol
+ # lambda { do_something_risky }.should throw_symbol(:that_was_risky)
+ #
+ # lambda { do_something_risky }.should_not throw_symbol
+ # lambda { do_something_risky }.should_not throw_symbol(:that_was_risky)
+ def throw_symbol(sym=nil)
+ Matchers::ThrowSymbol.new(sym)
+ end
+ end
+end