New Upstream Snapshot - ruby-rr

Ready changes

Summary

Merged new upstream version: 3.1.0+git20220714.1.80043b2 (was: 3.1.0).

Resulting package

Built on 2022-11-10T03:30 (took 6m57s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-snapshots ruby-rr

Lintian Result

Diff

diff --git a/.rspec b/.rspec
new file mode 100644
index 0000000..84362ae
--- /dev/null
+++ b/.rspec
@@ -0,0 +1,3 @@
+--require ./spec/custom_formatter_for_rspec_2
+--format CustomFormatterForRSpec2
+--color
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..5f6541d
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,35 @@
+notifications:
+  webhooks:
+    - https://webhook.commit-email.info/
+language: ruby
+before_install:
+  - gem update bundler
+script: bundle exec rake ${TARGET}
+matrix:
+  include:
+  - rvm: 2.0.0
+    gemfile: Gemfile.rspec
+    env:
+    - SPEC_OPTS="--require $PWD/spec/custom_formatter_for_rspec_2 --format CustomFormatterForRSpec2
+      --backtrace"
+    - TARGET="spec:rspec_2"
+  - rvm: 2.5
+    env:
+    - TARGET="test"
+  - rvm: 2.6
+    env:
+    - TARGET="test"
+  - rvm: 2.7
+    env:
+    - TARGET="test"
+  - rvm: 3.0
+    env:
+    - TARGET="test"
+  - rvm: ruby-head
+    env:
+    - TARGET="test"
+  allow_failures:
+  - rvm: ruby-head
+    env:
+    - TARGET="test"
+  fast_finish: true
diff --git a/FOR_DEVELOPERS.md b/FOR_DEVELOPERS.md
new file mode 100644
index 0000000..f04711d
--- /dev/null
+++ b/FOR_DEVELOPERS.md
@@ -0,0 +1,46 @@
+# Instructions for developers
+
+## Running tests
+
+In addition to the instructions in the README, you can run all of the tests for
+all rubies with:
+
+    script/run_full_test_suite
+
+You can also install all appraisals for all rubies with:
+
+    script/regenerate_appraisals
+
+
+## Updating .travis.yml
+
+If you ever need to regenerate .travis.yml for some reason, don't edit it
+directly -- instead, edit spec/suites.yml, then run
+`rake travis:regenerate_config`, and commit both files.
+
+
+## Releasing a new version
+
+To release a new version of RR, update the version in VERSION and update
+CHANGES.md with changes made since the previous release. Commit and push, and
+then run `rake release` to push to RubyGems.
+
+## Notes on Debian and Fedora packaging
+
+Keep in mind that Debian and Fedora both have wrapper packages for RR. We want
+to make sure to stay on good terms with them.
+
+Although it is usually not necessary we include the Rakefile and tests in the
+gem itself specifically for these package maintainers.
+
+### Debian
+
+* [How Ruby gems are packaged](http://wiki.debian.org/Teams/Ruby/Packaging)
+* [How packaged Ruby gems are tested](http://wiki.debian.org/Teams/Ruby/Packaging/Tests)
+* [Page for the ruby-rr package](http://packages.qa.debian.org/r/ruby-rr.html)
+* [Git repo for the ruby-rr package](http://anonscm.debian.org/gitweb/?p=pkg-ruby-extras/ruby-rr.git;a=summary)
+
+### Fedora
+
+* [How Ruby gems are packaged and tested](http://fedoraproject.org/wiki/Packaging_talk:Ruby)
+* [Git repo for the rubygem-rr package](http://pkgs.fedoraproject.org/cgit/rubygem-rr.git)
diff --git a/Gemfile.rspec b/Gemfile.rspec
new file mode 100644
index 0000000..d9160f2
--- /dev/null
+++ b/Gemfile.rspec
@@ -0,0 +1,10 @@
+source 'https://rubygems.org'
+
+gemspec
+
+gem 'rake', '~> 10.0'
+gem 'simplecov', '~> 0.7'
+gem 'appraisal', '~> 0.5'
+gem 'posix-spawn', :platforms => :mri
+gem 'open4', :platforms => :mri
+gem 'rspec', '~> 2.14'
diff --git a/benchmarks/rr_benchmark.rb b/benchmarks/rr_benchmark.rb
new file mode 100644
index 0000000..3e04896
--- /dev/null
+++ b/benchmarks/rr_benchmark.rb
@@ -0,0 +1,32 @@
+dir = File.dirname(__FILE__)
+require File.expand_path("#{dir}/../lib/rr")
+require "benchmark"
+
+o = Object.new
+
+Benchmark.bm do |x|
+  x.report do
+    1000.times do
+      RR.mock(o).foobar.returns("baz")
+      o.foobar
+      RR.reset
+    end
+  end
+end
+
+#require "ruby-prof"
+#RubyProf.start
+#
+##RR.mock(o).foobar.returns("baz")
+##o.foobar
+#10.times do
+#  RR.mock(o).foobar.returns("baz")
+#  o.foobar
+#  RR.reset
+#end
+#
+#result = RubyProf.stop
+#
+## Print a flat profile to text
+#printer = RubyProf::FlatPrinter.new(result)
+#printer.print(STDOUT, 0)
\ No newline at end of file
diff --git a/benchmarks/rspec_benchmark.rb b/benchmarks/rspec_benchmark.rb
new file mode 100644
index 0000000..65c54eb
--- /dev/null
+++ b/benchmarks/rspec_benchmark.rb
@@ -0,0 +1,14 @@
+require "rubygems"
+require "spec/mocks"
+require "benchmark"
+
+o = Object.new
+
+Benchmark.bm do |x|
+  x.report do
+    1000.times do
+      o.should_receive(:foobar).and_return("baz")
+      o.foobar
+    end
+  end
+end
diff --git a/debian/changelog b/debian/changelog
index 484fa68..77d6b6b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ruby-rr (3.1.0+git20220714.1.80043b2-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 10 Nov 2022 03:26:07 -0000
+
 ruby-rr (3.1.0-1) unstable; urgency=medium
 
   * New upstream version 3.1.0
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..f3ab181
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,9 @@
+version: "3.7"
+
+services:
+  spec:
+    image: ruby:2.0
+    volumes:
+      - .:/rr:delegated
+    command:
+      /rr/docker/run-spec.sh
diff --git a/docker/run-spec.sh b/docker/run-spec.sh
new file mode 100755
index 0000000..8db1f7b
--- /dev/null
+++ b/docker/run-spec.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -eux
+
+cd /rr
+export BUNDLE_GEMFILE=$PWD/Gemfile.rspec
+gem install bundler -v 1.16.6
+bundle _1.16.6_ install
+bundle exec rake spec:rspec_2
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..ab928b5
Binary files /dev/null and b/icon.png differ
diff --git a/icon.svg b/icon.svg
new file mode 100644
index 0000000..7d85d54
--- /dev/null
+++ b/icon.svg
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16mm"
+   height="16mm"
+   viewBox="0 0 56.692913 56.692913"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="icon.svg"
+   inkscape:export-filename="icon.png"
+   inkscape:export-xdpi="600.07501"
+   inkscape:export-ydpi="600.07501">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8"
+     inkscape:cx="46.673183"
+     inkscape:cy="34.925888"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="971"
+     inkscape:window-height="419"
+     inkscape:window-x="2737"
+     inkscape:window-y="400"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="レイヤー 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-306.73429,-319.55216)">
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:40px;line-height:125%;font-family:'TeX Gyre Chorus';-inkscape-font-specification:'TeX Gyre Chorus, Medium';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ff0000;fill-opacity:1;stroke:#676767;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.25779036"
+       x="305.71429"
+       y="357.50507"
+       id="text4136"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4138"
+         x="305.71429"
+         y="357.50507">RR</tspan></text>
+  </g>
+</svg>
diff --git a/lib/rr/version.rb b/lib/rr/version.rb
index 07cda66..82393e5 100644
--- a/lib/rr/version.rb
+++ b/lib/rr/version.rb
@@ -1,4 +1,4 @@
 module RR
-  VERSION = '3.1.0'.freeze
+  VERSION = '3.1.1'.freeze
   def self.version; VERSION; end
 end
diff --git a/script/TARGETED_VERSIONS b/script/TARGETED_VERSIONS
new file mode 100644
index 0000000..4873436
--- /dev/null
+++ b/script/TARGETED_VERSIONS
@@ -0,0 +1 @@
+1.8.7-p374 1.9.3-p392 2.0.0-p195 jruby-1.7.4
diff --git a/script/build_and_unpack_gem b/script/build_and_unpack_gem
new file mode 100755
index 0000000..b0fdbd3
--- /dev/null
+++ b/script/build_and_unpack_gem
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+rm -rf pkg && \
+  rbenv gemset delete $(rbenv version | cut -f 1 -d " ") rr && \
+  rake build && \
+  pushd pkg && \
+  gem unpack *.gem && \
+  rm *.gem && \
+  mv rr-* rr && \
+  pushd rr && \
+  echo rr > .rbenv-gemsets
diff --git a/script/regenerate_appraisals b/script/regenerate_appraisals
new file mode 100755
index 0000000..a4529b7
--- /dev/null
+++ b/script/regenerate_appraisals
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+for version in $(<script/TARGETED_VERSIONS); do
+  cmd="rake appraisal:install"
+  echo -e "\033[1;33m[$version] $cmd\033[0m"
+  export RBENV_VERSION=$version
+  eval $cmd || exit $?
+  echo
+done
diff --git a/script/run_full_test_suite b/script/run_full_test_suite
new file mode 100755
index 0000000..53e35b2
--- /dev/null
+++ b/script/run_full_test_suite
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+exitstatus=0
+for version in $(<script/TARGETED_VERSIONS); do
+  cmd="bundle install && rake"
+  echo
+  echo -e "\033[1;33m[$version] $cmd\033[0m"
+  echo
+  export RBENV_VERSION=$version
+  eval $cmd
+  if [ $? -ne 0 ]; then exitstatus=1; fi
+done
+
+if [ $exitstatus -eq 0 ]; then
+  echo -e "\033[1;33mTest suite passed!\033[0m"
+else
+  echo -e "\033[1;33mTest suite failed!\033[0m"
+fi
+echo
diff --git a/test/core_ext/test_array.rb b/test/core_ext/test_array.rb
new file mode 100644
index 0000000..8d9db71
--- /dev/null
+++ b/test/core_ext/test_array.rb
@@ -0,0 +1,9 @@
+class TestArray < Test::Unit::TestCase
+  sub_test_case "#wildcard_match?" do
+    test "too much elements" do
+      assert do
+        ![:a].wildcard_match?([:a, :b])
+      end
+    end
+  end
+end
diff --git a/test/core_ext/test_hash.rb b/test/core_ext/test_hash.rb
new file mode 100644
index 0000000..d2bbec9
--- /dev/null
+++ b/test/core_ext/test_hash.rb
@@ -0,0 +1,28 @@
+class TestHash < Test::Unit::TestCase
+  sub_test_case "#wildcard_match?" do
+    test "is_a" do
+      assert do
+        {is_a(Symbol) => 1}.wildcard_match?(:a => 1)
+      end
+    end
+
+    test "too much keys" do
+      assert do
+        !{:a => 1}.wildcard_match?(:a => 1, :b => 2)
+      end
+    end
+
+    test "different order" do
+      assert do
+        {a: 1, b: 2}.wildcard_match?(b: 2, a: 1)
+      end
+    end
+
+    test "different order with wildcard" do
+      pattern = {:a => 1, :b => 2, is_a(Symbol) => 3, is_a(Symbol) => 1}
+      assert do
+        pattern.wildcard_match?(:d => 1, :c => 3, :b => 2, :a => 1)
+      end
+    end
+  end
+end
diff --git a/test/integration/test_minitest.rb b/test/integration/test_minitest.rb
new file mode 100644
index 0000000..5ac9461
--- /dev/null
+++ b/test/integration/test_minitest.rb
@@ -0,0 +1,23 @@
+class TestIntegerationMinitest < Test::Unit::TestCase
+  setup do
+    omit("Require Minitest") unless defined?(::Minitest)
+    omit("Require not Active Support") if defined?(::ActiveSupport::TestCase)
+  end
+
+  test("verify") do
+    test_class = Class.new(Minitest::Test) do
+      def test_verify
+        object = Object.new
+        mock(object).hello("Alice") do |name|
+          "Hello #{name}!"
+        end
+      end
+    end
+    result = test_class.new(:test_verify).run
+    assert_equal([<<-MESSAGE.chomp], result.failures.collect(&:to_s))
+hello("Alice")
+Called 0 times.
+Expected 1 times.
+    MESSAGE
+  end
+end
diff --git a/test/integration/test_minitest_active_support.rb b/test/integration/test_minitest_active_support.rb
new file mode 100644
index 0000000..e80ad64
--- /dev/null
+++ b/test/integration/test_minitest_active_support.rb
@@ -0,0 +1,22 @@
+class TestIntegerationMinitestActiveSupport < Test::Unit::TestCase
+  setup do
+    omit("Require Active Support") unless defined?(::ActiveSupport::TestCase)
+  end
+
+  test("verify") do
+    test_class = Class.new(ActiveSupport::TestCase) do
+      def test_verify
+        object = Object.new
+        mock(object).hello("Alice") do |name|
+          "Hello #{name}!"
+        end
+      end
+    end
+    result = test_class.new(:test_verify).run
+    assert_equal([<<-MESSAGE.chomp], result.failures.collect(&:to_s))
+hello("Alice")
+Called 0 times.
+Expected 1 times.
+    MESSAGE
+  end
+end
diff --git a/test/mock/test_block.rb b/test/mock/test_block.rb
new file mode 100644
index 0000000..38d853b
--- /dev/null
+++ b/test/mock/test_block.rb
@@ -0,0 +1,10 @@
+class TestMockBlock < Test::Unit::TestCase
+  test "anything" do
+    object = Object.new
+    anything_ = anything
+    mock(object) do
+      hello(anything_).once
+    end
+    object.hello("world")
+  end
+end
diff --git a/test/mock/test_open_struct.rb b/test/mock/test_open_struct.rb
new file mode 100644
index 0000000..d96c2e7
--- /dev/null
+++ b/test/mock/test_open_struct.rb
@@ -0,0 +1,12 @@
+require "ostruct"
+
+class TestMockOpenStruct < Test::Unit::TestCase
+  test "read twice" do
+    open_struct = OpenStruct.new(:key => :value)
+    mock(open_struct).dummy_key {:dummy_value}
+    open_struct.dummy_key
+
+    assert_equal(:value, open_struct.key)
+    assert_equal(:value, open_struct.key)
+  end
+end
diff --git a/test/mock/test_proxy.rb b/test/mock/test_proxy.rb
new file mode 100644
index 0000000..3608a44
--- /dev/null
+++ b/test/mock/test_proxy.rb
@@ -0,0 +1,19 @@
+class TestMockProxy < Test::Unit::TestCase
+  test "keyword arguments" do
+    object = Object.new
+    def object.hello(name: "Alice")
+      "Hello #{name}!"
+    end
+    mock.proxy(object).hello(name: "Bob").once
+    object.hello(name: "Bob")
+  end
+
+  test "ordered arguments + keyword arguments" do
+    object = Object.new
+    def object.hello(name, comment: "Yay!")
+      "Hello #{name}! #{comment}."
+    end
+    mock.proxy(object).hello("Bob", comment: "Wow!").once
+    object.hello("Bob", comment: "Wow!")
+  end
+end
diff --git a/test/mock/test_proxy_kwargs.rb b/test/mock/test_proxy_kwargs.rb
new file mode 100644
index 0000000..0e5e87f
--- /dev/null
+++ b/test/mock/test_proxy_kwargs.rb
@@ -0,0 +1,100 @@
+class TestMockProxyKwargs < Test::Unit::TestCase
+  def call_method(object, style)
+    case style
+    when :kwargs
+      object.call(1, a: 2)
+    when :hash
+      object.call(1, {a: 2})
+    when :kwrest
+      object.call(1, **{a: 2})
+    end
+  end
+
+  data(:style, [:kwargs, :hash, :kwrest], keep: true)
+  test "a, b" do
+    klass = Class.new do
+      def call(a, b)
+        [a, b]
+      end
+    end
+    obj1 = klass.new
+    obj2 = klass.new
+    proxy.mock(obj2).call.with_any_args
+    assert_equal(call_method(obj1, data[:style]),
+                 call_method(obj2, data[:style]))
+  end
+
+  test "a, **b" do
+    klass = Class.new do
+      def call(a, **b)
+        [a, b]
+      end
+    end
+    obj1 = klass.new
+    obj2 = klass.new
+    proxy.mock(obj2).call.with_any_args
+    if data[:style] == :hash && RR::KeywordArguments.fully_supported?
+      assert_raise(ArgumentError) do
+        obj1.call(1, {a: 2})
+      end
+      assert_raise(ArgumentError) do
+        obj2.call(1, {a: 2})
+      end
+    else
+      assert_equal(call_method(obj1, data[:style]),
+                   call_method(obj2, data[:style]))
+    end
+  end
+
+  test "*a" do
+    klass = Class.new do
+      def call(*a)
+        [a]
+      end
+    end
+    obj1 = klass.new
+    obj2 = klass.new
+    proxy.mock(obj2).call.with_any_args
+    assert_equal(call_method(obj1, data[:style]),
+                 call_method(obj2, data[:style]))
+  end
+
+  test "*a, **b" do
+    klass = Class.new do
+      def call(*a, **b)
+        [a, b]
+      end
+    end
+    obj1 = klass.new
+    obj2 = klass.new
+    proxy.mock(obj2).call.with_any_args
+    assert_equal(call_method(obj1, data[:style]),
+                 call_method(obj2, data[:style]))
+  end
+
+  test "a, *b" do
+    klass = Class.new do
+      def call(a, *b)
+        [a, b]
+      end
+    end
+    obj1 = klass.new
+    obj2 = klass.new
+    proxy.mock(obj2).call.with_any_args
+    assert_equal(call_method(obj1, data[:style]),
+                 call_method(obj2, data[:style]))
+  end
+
+  test "*a, b" do
+    klass = Class.new do
+      def call(*a, b)
+        [a, b]
+      end
+    end
+    obj1 = klass.new
+    obj2 = klass.new
+    proxy.mock(obj2).call.with_any_args
+    assert_equal(call_method(obj1, data[:style]),
+                 call_method(obj2, data[:style]))
+  end
+end
diff --git a/test/run-test.rb b/test/run-test.rb
new file mode 100755
index 0000000..bf9243a
--- /dev/null
+++ b/test/run-test.rb
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+
+$VERBOSE = true
+
+base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
+lib_dir = File.join(base_dir, "lib")
+test_dir = File.join(base_dir, "test")
+
+$LOAD_PATH.unshift(lib_dir)
+
+case ENV["RR_INTEGRATION"]
+when "minitest"
+  require "minitest"
+  require "rr"
+when "minitest-active-support"
+  require "active_support"
+  require "active_support/test_case"
+  require "rr"
+end
+
+require "test-unit"
+require "test/unit/rr"
+
+exit(Test::Unit::AutoRunner.run(true, test_dir))
diff --git a/test/test_space.rb b/test/test_space.rb
new file mode 100644
index 0000000..f4d48be
--- /dev/null
+++ b/test/test_space.rb
@@ -0,0 +1,156 @@
+class TestSpace < Test::Unit::TestCase
+  sub_test_case "#reset" do
+    sub_test_case "existent" do
+      test "respond_to?: instance method" do
+        subject = Object.new
+        def subject.hello
+          :original_hello
+        end
+        stub(subject).hello {:stub_hello}
+        assert_equal(:stub_hello, subject.hello)
+        assert do
+          subject.respond_to?(:hello)
+        end
+        RR.reset
+        assert_equal(:original_hello, subject.hello)
+        assert do
+          subject.respond_to?(:hello)
+        end
+      end
+
+      test "respond_to?: class method" do
+        subject = Class.new do
+          def self.hello
+            :original_hello
+          end
+        end
+        stub(subject).hello {:stub_hello}
+        assert_equal(:stub_hello, subject.hello)
+        assert do
+          subject.respond_to?(:hello)
+        end
+        RR.reset
+        assert_equal(:original_hello, subject.hello)
+        assert do
+          subject.respond_to?(:hello)
+        end
+      end
+    end
+
+    sub_test_case "nonexistent" do
+      test "respond_to?: instance method" do
+        subject = Object.new
+        stub(subject).stub_predicate? {true}
+        assert do
+          subject.stub_predicate?
+        end
+        assert do
+          subject.respond_to?(:stub_predicate?)
+        end
+        RR.reset
+        assert_raise(NoMethodError) do
+          subject.stub_predicate?
+        end
+        assert do
+          not subject.respond_to?(:stub_predicate?)
+        end
+      end
+
+      test "respond_to?: class method" do
+        subject = Class.new
+        stub(subject).stub_predicate? {true}
+        assert do
+          subject.stub_predicate?
+        end
+        assert do
+          subject.respond_to?(:stub_predicate?)
+        end
+        RR.reset
+        assert_raise(NoMethodError) do
+          subject.stub_predicate?
+        end
+        assert do
+          not subject.respond_to?(:stub_predicate?)
+        end
+      end
+    end
+
+    sub_test_case "lazy defined" do
+      test "respond_to?: instance method" do
+        subject = Object.new
+        def subject.method_missing(method_name, *args, &block)
+          if method_name.to_sym == :hello
+            def self.hello
+              :original_hello
+            end
+            hello
+          else
+            super
+          end
+        end
+        stub(subject).hello {:stub_hello}
+        assert_equal(:stub_hello, subject.hello)
+        assert do
+          subject.respond_to?(:hello)
+        end
+        RR.reset
+        assert_equal(:original_hello, subject.hello)
+        assert do
+          subject.respond_to?(:hello)
+        end
+      end
+
+      test "respond_to?: class method" do
+        subject = Class.new do
+          def self.method_missing(method_name, *args, &block)
+            if method_name.to_sym == :hello
+              def self.hello
+                :original_hello
+              end
+              hello
+            else
+              super
+            end
+          end
+        end
+        stub(subject).hello {:stub_hello}
+        assert_equal(:stub_hello, subject.hello)
+        assert do
+          subject.respond_to?(:hello)
+        end
+        RR.reset
+        assert_equal(:original_hello, subject.hello)
+        assert do
+          subject.respond_to?(:hello)
+        end
+      end
+    end
+
+    sub_test_case "#any_instance_of" do
+      test "inherit" do
+        parent_class = Class.new
+        subject_class = Class.new(parent_class)
+        subject_to_s_method = subject_class.instance_method(:to_s)
+
+        any_instance_of(parent_class, :to_s => "Parent is stubbed")
+        any_instance_of(parent_class, :stubbed => lambda {:value})
+
+        any_instance_of(subject_class, :to_s => "Subject is stubbed")
+
+        assert_equal("Parent is stubbed", parent_class.new.to_s)
+
+        subject = subject_class.new
+        assert_equal("Subject is stubbed", subject.to_s)
+        assert_equal(:value, subject.stubbed)
+
+        RR.reset
+
+        assert_equal(subject_to_s_method.bind(subject).call,
+                     subject.to_s)
+        assert_raise(NoMethodError) do
+          subject.stubbed
+        end
+      end
+    end
+  end
+end
diff --git a/test/test_stub.rb b/test/test_stub.rb
new file mode 100644
index 0000000..d4fc220
--- /dev/null
+++ b/test/test_stub.rb
@@ -0,0 +1,46 @@
+class TestStub < Test::Unit::TestCase
+  sub_test_case("ordered arguments + keyword arguments") do
+    test("without implementation") do
+      object = Object.new
+      stub(object).hello("Alice", "Bob", comment: "Wow!").once
+      object.hello("Alice", "Bob", comment: "Wow!")
+    end
+
+    test("with implementation") do
+      klass = Class.new do
+        def hello(from, to, comment:)
+        end
+      end
+      object = klass.new
+      stub(object).hello("Alice", "Bob", comment: "Wow!").once
+      object.hello("Alice", "Bob", comment: "Wow!")
+    end
+  end
+
+  sub_test_case("keyword arguments") do
+    test("without implementation") do
+      object = Object.new
+      stub(object).hello(to: "Alice").once
+      object.hello(to: "Alice")
+    end
+
+    test("with implementation") do
+      klass = Class.new do
+        def hello(to:)
+        end
+      end
+      object = klass.new
+      stub(object).hello(to: "Alice").once
+      object.hello(to: "Alice")
+    end
+
+    test("assert_received") do
+      object = Object.new
+      stub(object).hello(to: "Alice").once
+      object.hello(to: "Alice")
+      assert_received(object) do |target|
+        target.hello(to: "Alice")
+      end
+    end
+  end
+end

More details

Full run details