summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-05-17 06:26:51 +0000
committerhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-05-17 06:26:51 +0000
commitf8c6a5dc02439bb99c8403602288c2937b9356cc (patch)
tree263e3a008f86de66e7d93f5aa4be8b708cc20020 /test
parentc466dcc05bd9b95717dba424be9535e2e75822bc (diff)
* test/runner.rb: remove dependency test-unit and minitest
from stdlib when running with test-all. [Feature #9711][ruby-core:61890] * test/testunit/*.rb: ditto. * test/lib: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@45970 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'test')
-rw-r--r--test/lib/minitest/.document2
-rw-r--r--test/lib/minitest/README.txt457
-rw-r--r--test/lib/minitest/autorun.rb19
-rw-r--r--test/lib/minitest/benchmark.rb423
-rw-r--r--test/lib/minitest/hell.rb20
-rw-r--r--test/lib/minitest/mock.rb200
-rw-r--r--test/lib/minitest/parallel_each.rb80
-rw-r--r--test/lib/minitest/pride.rb119
-rw-r--r--test/lib/minitest/spec.rb551
-rw-r--r--test/lib/minitest/unit.rb1422
-rw-r--r--test/lib/test/unit.rb880
-rw-r--r--test/lib/test/unit/assertions.rb461
-rw-r--r--test/lib/test/unit/parallel.rb190
-rw-r--r--test/lib/test/unit/test-unit.gemspec18
-rw-r--r--test/lib/test/unit/testcase.rb34
-rw-r--r--test/runner.rb6
-rw-r--r--test/testunit/test4test_hideskip.rb2
-rw-r--r--test/testunit/test4test_redefinition.rb2
-rw-r--r--test/testunit/test4test_sorting.rb2
-rw-r--r--test/testunit/test_parallel.rb3
-rw-r--r--test/testunit/test_rake_integration.rb1
-rw-r--r--test/testunit/tests_for_parallel/runner.rb3
22 files changed, 4891 insertions, 4 deletions
diff --git a/test/lib/minitest/.document b/test/lib/minitest/.document
new file mode 100644
index 0000000000..6850e5befb
--- /dev/null
+++ b/test/lib/minitest/.document
@@ -0,0 +1,2 @@
+# Ignore README.txt, it is included in the minitest documentation.
+*.rb
diff --git a/test/lib/minitest/README.txt b/test/lib/minitest/README.txt
new file mode 100644
index 0000000000..368cc3aa4e
--- /dev/null
+++ b/test/lib/minitest/README.txt
@@ -0,0 +1,457 @@
+= minitest/{unit,spec,mock,benchmark}
+
+home :: https://github.com/seattlerb/minitest
+rdoc :: http://docs.seattlerb.org/minitest
+vim :: https://github.com/sunaku/vim-ruby-minitest
+
+== DESCRIPTION:
+
+minitest provides a complete suite of testing facilities supporting
+TDD, BDD, mocking, and benchmarking.
+
+ "I had a class with Jim Weirich on testing last week and we were
+ allowed to choose our testing frameworks. Kirk Haines and I were
+ paired up and we cracked open the code for a few test
+ frameworks...
+
+ I MUST say that minitest is *very* readable / understandable
+ compared to the 'other two' options we looked at. Nicely done and
+ thank you for helping us keep our mental sanity."
+
+ -- Wayne E. Seguin
+
+minitest/unit is a small and incredibly fast unit testing framework.
+It provides a rich set of assertions to make your tests clean and
+readable.
+
+minitest/spec is a functionally complete spec engine. It hooks onto
+minitest/unit and seamlessly bridges test assertions over to spec
+expectations.
+
+minitest/benchmark is an awesome way to assert the performance of your
+algorithms in a repeatable manner. Now you can assert that your newb
+co-worker doesn't replace your linear algorithm with an exponential
+one!
+
+minitest/mock by Steven Baker, is a beautifully tiny mock (and stub)
+object framework.
+
+minitest/pride shows pride in testing and adds coloring to your test
+output. I guess it is an example of how to write IO pipes too. :P
+
+minitest/unit is meant to have a clean implementation for language
+implementors that need a minimal set of methods to bootstrap a working
+test suite. For example, there is no magic involved for test-case
+discovery.
+
+ "Again, I can't praise enough the idea of a testing/specing
+ framework that I can actually read in full in one sitting!"
+
+ -- Piotr Szotkowski
+
+Comparing to rspec:
+
+ rspec is a testing DSL. minitest is ruby.
+
+ -- Adam Hawkins, "Bow Before MiniTest"
+
+minitest doesn't reinvent anything that ruby already provides, like:
+classes, modules, inheritance, methods. This means you only have to
+learn ruby to use minitest and all of your regular OO practices like
+extract-method refactorings still apply.
+
+== FEATURES/PROBLEMS:
+
+* minitest/autorun - the easy and explicit way to run all your tests.
+* minitest/unit - a very fast, simple, and clean test system.
+* minitest/spec - a very fast, simple, and clean spec system.
+* minitest/mock - a simple and clean mock/stub system.
+* minitest/benchmark - an awesome way to assert your algorithm's performance.
+* minitest/pride - show your pride in testing!
+* Incredibly small and fast runner, but no bells and whistles.
+
+== RATIONALE:
+
+See design_rationale.rb to see how specs and tests work in minitest.
+
+== SYNOPSIS:
+
+Given that you'd like to test the following class:
+
+ class Meme
+ def i_can_has_cheezburger?
+ "OHAI!"
+ end
+
+ def will_it_blend?
+ "YES!"
+ end
+ end
+
+=== Unit tests
+
+ require 'minitest/autorun'
+
+ class TestMeme < MiniTest::Unit::TestCase
+ def setup
+ @meme = Meme.new
+ end
+
+ def test_that_kitty_can_eat
+ assert_equal "OHAI!", @meme.i_can_has_cheezburger?
+ end
+
+ def test_that_it_will_not_blend
+ refute_match /^no/i, @meme.will_it_blend?
+ end
+
+ def test_that_will_be_skipped
+ skip "test this later"
+ end
+ end
+
+=== Specs
+
+ require 'minitest/autorun'
+
+ describe Meme do
+ before do
+ @meme = Meme.new
+ end
+
+ describe "when asked about cheeseburgers" do
+ it "must respond positively" do
+ @meme.i_can_has_cheezburger?.must_equal "OHAI!"
+ end
+ end
+
+ describe "when asked about blending possibilities" do
+ it "won't say no" do
+ @meme.will_it_blend?.wont_match /^no/i
+ end
+ end
+ end
+
+For matchers support check out:
+
+https://github.com/zenspider/minitest-matchers
+
+=== Benchmarks
+
+Add benchmarks to your regular unit tests. If the unit tests fail, the
+benchmarks won't run.
+
+ # optionally run benchmarks, good for CI-only work!
+ require 'minitest/benchmark' if ENV["BENCH"]
+
+ class TestMeme < MiniTest::Unit::TestCase
+ # Override self.bench_range or default range is [1, 10, 100, 1_000, 10_000]
+ def bench_my_algorithm
+ assert_performance_linear 0.9999 do |n| # n is a range value
+ @obj.my_algorithm(n)
+ end
+ end
+ end
+
+Or add them to your specs. If you make benchmarks optional, you'll
+need to wrap your benchmarks in a conditional since the methods won't
+be defined.
+
+ describe Meme do
+ if ENV["BENCH"] then
+ bench_performance_linear "my_algorithm", 0.9999 do |n|
+ 100.times do
+ @obj.my_algorithm(n)
+ end
+ end
+ end
+ end
+
+outputs something like:
+
+ # Running benchmarks:
+
+ TestBlah 100 1000 10000
+ bench_my_algorithm 0.006167 0.079279 0.786993
+ bench_other_algorithm 0.061679 0.792797 7.869932
+
+Output is tab-delimited to make it easy to paste into a spreadsheet.
+
+=== Mocks
+
+ class MemeAsker
+ def initialize(meme)
+ @meme = meme
+ end
+
+ def ask(question)
+ method = question.tr(" ","_") + "?"
+ @meme.__send__(method)
+ end
+ end
+
+ require 'minitest/autorun'
+
+ describe MemeAsker do
+ before do
+ @meme = MiniTest::Mock.new
+ @meme_asker = MemeAsker.new @meme
+ end
+
+ describe "#ask" do
+ describe "when passed an unpunctuated question" do
+ it "should invoke the appropriate predicate method on the meme" do
+ @meme.expect :will_it_blend?, :return_value
+ @meme_asker.ask "will it blend"
+ @meme.verify
+ end
+ end
+ end
+ end
+
+=== Stubs
+
+ def test_stale_eh
+ obj_under_test = Something.new
+
+ refute obj_under_test.stale?
+
+ Time.stub :now, Time.at(0) do # stub goes away once the block is done
+ assert obj_under_test.stale?
+ end
+ end
+
+A note on stubbing: In order to stub a method, the method must
+actually exist prior to stubbing. Use a singleton method to create a
+new non-existing method:
+
+ def obj_under_test.fake_method
+ ...
+ end
+
+=== Customizable Test Runner Types:
+
+MiniTest::Unit.runner=(runner) provides an easy way of creating custom
+test runners for specialized needs. Justin Weiss provides the
+following real-world example to create an alternative to regular
+fixture loading:
+
+ class MiniTestWithHooks::Unit < MiniTest::Unit
+ def before_suites
+ end
+
+ def after_suites
+ end
+
+ def _run_suites(suites, type)
+ begin
+ before_suites
+ super(suites, type)
+ ensure
+ after_suites
+ end
+ end
+
+ def _run_suite(suite, type)
+ begin
+ suite.before_suite
+ super(suite, type)
+ ensure
+ suite.after_suite
+ end
+ end
+ end
+
+ module MiniTestWithTransactions
+ class Unit < MiniTestWithHooks::Unit
+ include TestSetupHelper
+
+ def before_suites
+ super
+ setup_nested_transactions
+ # load any data we want available for all tests
+ end
+
+ def after_suites
+ teardown_nested_transactions
+ super
+ end
+ end
+ end
+
+ MiniTest::Unit.runner = MiniTestWithTransactions::Unit.new
+
+== FAQ
+
+=== How to test SimpleDelegates?
+
+The following implementation and test:
+
+ class Worker < SimpleDelegator
+ def work
+ end
+ end
+
+ describe Worker do
+ before do
+ @worker = Worker.new(Object.new)
+ end
+
+ it "must respond to work" do
+ @worker.must_respond_to :work
+ end
+ end
+
+outputs a failure:
+
+ 1) Failure:
+ Worker#test_0001_must respond to work [bug11.rb:16]:
+ Expected #<Object:0x007f9e7184f0a0> (Object) to respond to #work.
+
+Worker is a SimpleDelegate which in 1.9+ is a subclass of BasicObject.
+Expectations are put on Object (one level down) so the Worker
+(SimpleDelegate) hits `method_missing` and delegates down to the
+`Object.new` instance. That object doesn't respond to work so the test
+fails.
+
+You can bypass `SimpleDelegate#method_missing` by extending the worker
+with `MiniTest::Expectations`. You can either do that in your setup at
+the instance level, like:
+
+ before do
+ @worker = Worker.new(Object.new)
+ @worker.extend MiniTest::Expectations
+ end
+
+or you can extend the Worker class (within the test file!), like:
+
+ class Worker
+ include ::MiniTest::Expectations
+ end
+
+== Known Extensions:
+
+capybara_minitest_spec :: Bridge between Capybara RSpec matchers and MiniTest::Spec expectations (e.g. page.must_have_content('Title')).
+minispec-metadata :: Metadata for describe/it blocks
+ (e.g. `it 'requires JS driver', js: true do`)
+minitest-ansi :: Colorize minitest output with ANSI colors.
+minitest-around :: Around block for minitest. An alternative to setup/teardown dance.
+minitest-capistrano :: Assertions and expectations for testing Capistrano recipes
+minitest-capybara :: Capybara matchers support for minitest unit and spec
+minitest-chef-handler :: Run Minitest suites as Chef report handlers
+minitest-ci :: CI reporter plugin for MiniTest.
+minitest-colorize :: Colorize MiniTest output and show failing tests instantly.
+minitest-context :: Defines contexts for code reuse in MiniTest
+ specs that share common expectations.
+minitest-debugger :: Wraps assert so failed assertions drop into
+ the ruby debugger.
+minitest-display :: Patches MiniTest to allow for an easily configurable output.
+minitest-emoji :: Print out emoji for your test passes, fails, and skips.
+minitest-english :: Semantically symmetric aliases for assertions and expectations.
+minitest-excludes :: Clean API for excluding certain tests you
+ don't want to run under certain conditions.
+minitest-firemock :: Makes your MiniTest mocks more resilient.
+minitest-great_expectations :: Generally useful additions to minitest's assertions and expectations
+minitest-growl :: Test notifier for minitest via growl.
+minitest-implicit-subject :: Implicit declaration of the test subject.
+minitest-instrument :: Instrument ActiveSupport::Notifications when
+ test method is executed
+minitest-instrument-db :: Store information about speed of test
+ execution provided by minitest-instrument in database
+minitest-libnotify :: Test notifier for minitest via libnotify.
+minitest-macruby :: Provides extensions to minitest for macruby UI testing.
+minitest-matchers :: Adds support for RSpec-style matchers to minitest.
+minitest-metadata :: Annotate tests with metadata (key-value).
+minitest-mongoid :: Mongoid assertion matchers for MiniTest
+minitest-must_not :: Provides must_not as an alias for wont in MiniTest
+minitest-nc :: Test notifier for minitest via Mountain Lion's Notification Center
+minitest-predicates :: Adds support for .predicate? methods
+minitest-rails :: MiniTest integration for Rails 3.x
+minitest-rails-capybara :: Capybara integration for MiniTest::Rails
+minitest-reporters :: Create customizable MiniTest output formats
+minitest-should_syntax :: RSpec-style +x.should == y+ assertions for MiniTest
+minitest-shouldify :: Adding all manner of shoulds to MiniTest (bad idea)
+minitest-spec-context :: Provides rspec-ish context method to MiniTest::Spec
+minitest-spec-magic :: Minitest::Spec extensions for Rails and beyond
+minitest-spec-rails :: Drop in MiniTest::Spec superclass for ActiveSupport::TestCase.
+minitest-stub-const :: Stub constants for the duration of a block
+minitest-tags :: add tags for minitest
+minitest-wscolor :: Yet another test colorizer.
+minitest_owrapper :: Get tests results as a TestResult object.
+minitest_should :: Shoulda style syntax for minitest test::unit.
+minitest_tu_shim :: minitest_tu_shim bridges between test/unit and minitest.
+mongoid-minitest :: MiniTest matchers for Mongoid.
+pry-rescue :: A pry plugin w/ minitest support. See pry-rescue/minitest.rb.
+
+== Unknown Extensions:
+
+Authors... Please send me a pull request with a description of your minitest extension.
+
+* assay-minitest
+* detroit-minitest
+* em-minitest-spec
+* flexmock-minitest
+* guard-minitest
+* guard-minitest-decisiv
+* minitest-activemodel
+* minitest-ar-assertions
+* minitest-capybara-unit
+* minitest-colorer
+* minitest-deluxe
+* minitest-extra-assertions
+* minitest-rails-shoulda
+* minitest-spec
+* minitest-spec-should
+* minitest-sugar
+* minitest_should
+* mongoid-minitest
+* spork-minitest
+
+== REQUIREMENTS:
+
+* Ruby 1.8, maybe even 1.6 or lower. No magic is involved.
+
+== INSTALL:
+
+ sudo gem install minitest
+
+On 1.9, you already have it. To get newer candy you can still install
+the gem, but you'll need to activate the gem explicitly to use it:
+
+ require 'rubygems'
+ gem 'minitest' # ensures you're using the gem, and not the built in MT
+ require 'minitest/autorun'
+
+ # ... usual testing stuffs ...
+
+DO NOTE: There is a serious problem with the way that ruby 1.9/2.0
+packages their own gems. They install a gem specification file, but
+don't install the gem contents in the gem path. This messes up
+Gem.find_files and many other things (gem which, gem contents, etc).
+
+Just install minitest as a gem for real and you'll be happier.
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) Ryan Davis, seattle.rb
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/test/lib/minitest/autorun.rb b/test/lib/minitest/autorun.rb
new file mode 100644
index 0000000000..cb4a3a0e5d
--- /dev/null
+++ b/test/lib/minitest/autorun.rb
@@ -0,0 +1,19 @@
+# encoding: utf-8
+######################################################################
+# This file is imported from the minitest project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis.
+######################################################################
+
+begin
+ require 'rubygems'
+ gem 'minitest'
+rescue Gem::LoadError
+ # do nothing
+end
+
+require 'minitest/unit'
+require 'minitest/spec'
+require 'minitest/mock'
+
+MiniTest::Unit.autorun
diff --git a/test/lib/minitest/benchmark.rb b/test/lib/minitest/benchmark.rb
new file mode 100644
index 0000000000..e233282b0a
--- /dev/null
+++ b/test/lib/minitest/benchmark.rb
@@ -0,0 +1,423 @@
+# encoding: utf-8
+######################################################################
+# This file is imported from the minitest project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis.
+######################################################################
+
+require 'minitest/unit'
+require 'minitest/spec'
+
+class MiniTest::Unit # :nodoc:
+ def run_benchmarks # :nodoc:
+ _run_anything :benchmark
+ end
+
+ def benchmark_suite_header suite # :nodoc:
+ "\n#{suite}\t#{suite.bench_range.join("\t")}"
+ end
+
+ class TestCase
+ ##
+ # Returns a set of ranges stepped exponentially from +min+ to
+ # +max+ by powers of +base+. Eg:
+ #
+ # bench_exp(2, 16, 2) # => [2, 4, 8, 16]
+
+ def self.bench_exp min, max, base = 10
+ min = (Math.log10(min) / Math.log10(base)).to_i
+ max = (Math.log10(max) / Math.log10(base)).to_i
+
+ (min..max).map { |m| base ** m }.to_a
+ end
+
+ ##
+ # Returns a set of ranges stepped linearly from +min+ to +max+ by
+ # +step+. Eg:
+ #
+ # bench_linear(20, 40, 10) # => [20, 30, 40]
+
+ def self.bench_linear min, max, step = 10
+ (min..max).step(step).to_a
+ rescue LocalJumpError # 1.8.6
+ r = []; (min..max).step(step) { |n| r << n }; r
+ end
+
+ ##
+ # Returns the benchmark methods (methods that start with bench_)
+ # for that class.
+
+ def self.benchmark_methods # :nodoc:
+ public_instance_methods(true).grep(/^bench_/).map { |m| m.to_s }.sort
+ end
+
+ ##
+ # Returns all test suites that have benchmark methods.
+
+ def self.benchmark_suites
+ TestCase.test_suites.reject { |s| s.benchmark_methods.empty? }
+ end
+
+ ##
+ # Specifies the ranges used for benchmarking for that class.
+ # Defaults to exponential growth from 1 to 10k by powers of 10.
+ # Override if you need different ranges for your benchmarks.
+ #
+ # See also: ::bench_exp and ::bench_linear.
+
+ def self.bench_range
+ bench_exp 1, 10_000
+ end
+
+ ##
+ # Runs the given +work+, gathering the times of each run. Range
+ # and times are then passed to a given +validation+ proc. Outputs
+ # the benchmark name and times in tab-separated format, making it
+ # easy to paste into a spreadsheet for graphing or further
+ # analysis.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # validation = proc { |x, y| ... }
+ # assert_performance validation do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance validation, &work
+ range = self.class.bench_range
+
+ io.print "#{__name__}"
+
+ times = []
+
+ range.each do |x|
+ GC.start
+ t0 = Time.now
+ instance_exec(x, &work)
+ t = Time.now - t0
+
+ io.print "\t%9.6f" % t
+ times << t
+ end
+ io.puts
+
+ validation[range, times]
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered fit to
+ # match a constant rate (eg, linear slope == 0) within a given
+ # +threshold+. Note: because we're testing for a slope of 0, R^2
+ # is not a good determining factor for the fit, so the threshold
+ # is applied against the slope itself. As such, you probably want
+ # to tighten it from the default.
+ #
+ # See http://www.graphpad.com/curvefit/goodness_of_fit.htm for
+ # more details.
+ #
+ # Fit is calculated by #fit_linear.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_constant 0.9999 do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance_constant threshold = 0.99, &work
+ validation = proc do |range, times|
+ a, b, rr = fit_linear range, times
+ assert_in_delta 0, b, 1 - threshold
+ [a, b, rr]
+ end
+
+ assert_performance validation, &work
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered fit to
+ # match a exponential curve within a given error +threshold+.
+ #
+ # Fit is calculated by #fit_exponential.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_exponential 0.9999 do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance_exponential threshold = 0.99, &work
+ assert_performance validation_for_fit(:exponential, threshold), &work
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered fit to
+ # match a logarithmic curve within a given error +threshold+.
+ #
+ # Fit is calculated by #fit_logarithmic.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_logarithmic 0.9999 do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance_logarithmic threshold = 0.99, &work
+ assert_performance validation_for_fit(:logarithmic, threshold), &work
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered fit to
+ # match a straight line within a given error +threshold+.
+ #
+ # Fit is calculated by #fit_linear.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_linear 0.9999 do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance_linear threshold = 0.99, &work
+ assert_performance validation_for_fit(:linear, threshold), &work
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered curve
+ # fit to match a power curve within a given error +threshold+.
+ #
+ # Fit is calculated by #fit_power.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_power 0.9999 do |x|
+ # @obj.algorithm
+ # end
+ # end
+
+ def assert_performance_power threshold = 0.99, &work
+ assert_performance validation_for_fit(:power, threshold), &work
+ end
+
+ ##
+ # Takes an array of x/y pairs and calculates the general R^2 value.
+ #
+ # See: http://en.wikipedia.org/wiki/Coefficient_of_determination
+
+ def fit_error xys
+ y_bar = sigma(xys) { |x, y| y } / xys.size.to_f
+ ss_tot = sigma(xys) { |x, y| (y - y_bar) ** 2 }
+ ss_err = sigma(xys) { |x, y| (yield(x) - y) ** 2 }
+
+ 1 - (ss_err / ss_tot)
+ end
+
+ ##
+ # To fit a functional form: y = ae^(bx).
+ #
+ # Takes x and y values and returns [a, b, r^2].
+ #
+ # See: http://mathworld.wolfram.com/LeastSquaresFittingExponential.html
+
+ def fit_exponential xs, ys
+ n = xs.size
+ xys = xs.zip(ys)
+ sxlny = sigma(xys) { |x,y| x * Math.log(y) }
+ slny = sigma(xys) { |x,y| Math.log(y) }
+ sx2 = sigma(xys) { |x,y| x * x }
+ sx = sigma xs
+
+ c = n * sx2 - sx ** 2
+ a = (slny * sx2 - sx * sxlny) / c
+ b = ( n * sxlny - sx * slny ) / c
+
+ return Math.exp(a), b, fit_error(xys) { |x| Math.exp(a + b * x) }
+ end
+
+ ##
+ # To fit a functional form: y = a + b*ln(x).
+ #
+ # Takes x and y values and returns [a, b, r^2].
+ #
+ # See: http://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html
+
+ def fit_logarithmic xs, ys
+ n = xs.size
+ xys = xs.zip(ys)
+ slnx2 = sigma(xys) { |x,y| Math.log(x) ** 2 }
+ slnx = sigma(xys) { |x,y| Math.log(x) }
+ sylnx = sigma(xys) { |x,y| y * Math.log(x) }
+ sy = sigma(xys) { |x,y| y }
+
+ c = n * slnx2 - slnx ** 2
+ b = ( n * sylnx - sy * slnx ) / c
+ a = (sy - b * slnx) / n
+
+ return a, b, fit_error(xys) { |x| a + b * Math.log(x) }
+ end
+
+
+ ##
+ # Fits the functional form: a + bx.
+ #
+ # Takes x and y values and returns [a, b, r^2].
+ #
+ # See: http://mathworld.wolfram.com/LeastSquaresFitting.html
+
+ def fit_linear xs, ys
+ n = xs.size
+ xys = xs.zip(ys)
+ sx = sigma xs
+ sy = sigma ys
+ sx2 = sigma(xs) { |x| x ** 2 }
+ sxy = sigma(xys) { |x,y| x * y }
+
+ c = n * sx2 - sx**2
+ a = (sy * sx2 - sx * sxy) / c
+ b = ( n * sxy - sx * sy ) / c
+
+ return a, b, fit_error(xys) { |x| a + b * x }
+ end
+
+ ##
+ # To fit a functional form: y = ax^b.
+ #
+ # Takes x and y values and returns [a, b, r^2].
+ #
+ # See: http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
+
+ def fit_power xs, ys
+ n = xs.size
+ xys = xs.zip(ys)
+ slnxlny = sigma(xys) { |x, y| Math.log(x) * Math.log(y) }
+ slnx = sigma(xs) { |x | Math.log(x) }
+ slny = sigma(ys) { | y| Math.log(y) }
+ slnx2 = sigma(xs) { |x | Math.log(x) ** 2 }
+
+ b = (n * slnxlny - slnx * slny) / (n * slnx2 - slnx ** 2);
+ a = (slny - b * slnx) / n
+
+ return Math.exp(a), b, fit_error(xys) { |x| (Math.exp(a) * (x ** b)) }
+ end
+
+ ##
+ # Enumerates over +enum+ mapping +block+ if given, returning the
+ # sum of the result. Eg:
+ #
+ # sigma([1, 2, 3]) # => 1 + 2 + 3 => 7
+ # sigma([1, 2, 3]) { |n| n ** 2 } # => 1 + 4 + 9 => 14
+
+ def sigma enum, &block
+ enum = enum.map(&block) if block
+ enum.inject { |sum, n| sum + n }
+ end
+
+ ##
+ # Returns a proc that calls the specified fit method and asserts
+ # that the error is within a tolerable threshold.
+
+ def validation_for_fit msg, threshold
+ proc do |range, times|
+ a, b, rr = send "fit_#{msg}", range, times
+ assert_operator rr, :>=, threshold
+ [a, b, rr]
+ end
+ end
+ end
+end
+
+class MiniTest::Spec
+ ##
+ # This is used to define a new benchmark method. You usually don't
+ # use this directly and is intended for those needing to write new
+ # performance curve fits (eg: you need a specific polynomial fit).
+ #
+ # See ::bench_performance_linear for an example of how to use this.
+
+ def self.bench name, &block
+ define_method "bench_#{name.gsub(/\W+/, '_')}", &block
+ end
+
+ ##
+ # Specifies the ranges used for benchmarking for that class.
+ #
+ # bench_range do
+ # bench_exp(2, 16, 2)
+ # end
+ #
+ # See Unit::TestCase.bench_range for more details.
+
+ def self.bench_range &block
+ return super unless block
+
+ meta = (class << self; self; end)
+ meta.send :define_method, "bench_range", &block
+ end
+
+ ##
+ # Create a benchmark that verifies that the performance is linear.
+ #
+ # describe "my class" do
+ # bench_performance_linear "fast_algorithm", 0.9999 do |n|
+ # @obj.fast_algorithm(n)
+ # end
+ # end
+
+ def self.bench_performance_linear name, threshold = 0.99, &work
+ bench name do
+ assert_performance_linear threshold, &work
+ end
+ end
+
+ ##
+ # Create a benchmark that verifies that the performance is constant.
+ #
+ # describe "my class" do
+ # bench_performance_constant "zoom_algorithm!" do |n|
+ # @obj.zoom_algorithm!(n)
+ # end
+ # end
+
+ def self.bench_performance_constant name, threshold = 0.99, &work
+ bench name do
+ assert_performance_constant threshold, &work
+ end
+ end
+
+ ##
+ # Create a benchmark that verifies that the performance is exponential.
+ #
+ # describe "my class" do
+ # bench_performance_exponential "algorithm" do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def self.bench_performance_exponential name, threshold = 0.99, &work
+ bench name do
+ assert_performance_exponential threshold, &work
+ end
+ end
+end
diff --git a/test/lib/minitest/hell.rb b/test/lib/minitest/hell.rb
new file mode 100644
index 0000000000..827bf0e320
--- /dev/null
+++ b/test/lib/minitest/hell.rb
@@ -0,0 +1,20 @@
+# encoding: utf-8
+######################################################################
+# This file is imported from the minitest project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis.
+######################################################################
+
+require "minitest/parallel_each"
+
+# :stopdoc:
+class Minitest::Unit::TestCase
+ class << self
+ alias :old_test_order :test_order
+
+ def test_order
+ :parallel
+ end
+ end
+end
+# :startdoc:
diff --git a/test/lib/minitest/mock.rb b/test/lib/minitest/mock.rb
new file mode 100644
index 0000000000..a5b0f602f5
--- /dev/null
+++ b/test/lib/minitest/mock.rb
@@ -0,0 +1,200 @@
+# encoding: utf-8
+######################################################################
+# This file is imported from the minitest project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis.
+######################################################################
+
+class MockExpectationError < StandardError; end # :nodoc:
+
+##
+# A simple and clean mock object framework.
+
+module MiniTest # :nodoc:
+
+ ##
+ # All mock objects are an instance of Mock
+
+ class Mock
+ alias :__respond_to? :respond_to?
+
+ skip_methods = %w(object_id respond_to_missing? inspect === to_s)
+
+ instance_methods.each do |m|
+ undef_method m unless skip_methods.include?(m.to_s) || m =~ /^__/
+ end
+
+ def initialize # :nodoc:
+ @expected_calls = Hash.new { |calls, name| calls[name] = [] }
+ @actual_calls = Hash.new { |calls, name| calls[name] = [] }
+ end
+
+ ##
+ # Expect that method +name+ is called, optionally with +args+ or a
+ # +blk+, and returns +retval+.
+ #
+ # @mock.expect(:meaning_of_life, 42)
+ # @mock.meaning_of_life # => 42
+ #
+ # @mock.expect(:do_something_with, true, [some_obj, true])
+ # @mock.do_something_with(some_obj, true) # => true
+ #
+ # @mock.expect(:do_something_else, true) do |a1, a2|
+ # a1 == "buggs" && a2 == :bunny
+ # end
+ #
+ # +args+ is compared to the expected args using case equality (ie, the
+ # '===' operator), allowing for less specific expectations.
+ #
+ # @mock.expect(:uses_any_string, true, [String])
+ # @mock.uses_any_string("foo") # => true
+ # @mock.verify # => true
+ #
+ # @mock.expect(:uses_one_string, true, ["foo"]
+ # @mock.uses_one_string("bar") # => true
+ # @mock.verify # => raises MockExpectationError
+
+ def expect(name, retval, args=[], &blk)
+ if block_given?
+ raise ArgumentError, "args ignored when block given" unless args.empty?
+ @expected_calls[name] << { :retval => retval, :block => blk }
+ else
+ raise ArgumentError, "args must be an array" unless Array === args
+ @expected_calls[name] << { :retval => retval, :args => args }
+ end
+ self
+ end
+
+ def __call name, data # :nodoc:
+ case data
+ when Hash then
+ "#{name}(#{data[:args].inspect[1..-2]}) => #{data[:retval].inspect}"
+ else
+ data.map { |d| __call name, d }.join ", "
+ end
+ end
+
+ ##
+ # Verify that all methods were called as expected. Raises
+ # +MockExpectationError+ if the mock object was not called as
+ # expected.
+
+ def verify
+ @expected_calls.each do |name, calls|
+ calls.each do |expected|
+ msg1 = "expected #{__call name, expected}"
+ msg2 = "#{msg1}, got [#{__call name, @actual_calls[name]}]"
+
+ raise MockExpectationError, msg2 if
+ @actual_calls.has_key?(name) and
+ not @actual_calls[name].include?(expected)
+
+ raise MockExpectationError, msg1 unless
+ @actual_calls.has_key?(name) and
+ @actual_calls[name].include?(expected)
+ end
+ end
+ true
+ end
+
+ def method_missing(sym, *args) # :nodoc:
+ unless @expected_calls.has_key?(sym) then
+ raise NoMethodError, "unmocked method %p, expected one of %p" %
+ [sym, @expected_calls.keys.sort_by(&:to_s)]
+ end
+
+ index = @actual_calls[sym].length
+ expected_call = @expected_calls[sym][index]
+
+ unless expected_call then
+ raise MockExpectationError, "No more expects available for %p: %p" %
+ [sym, args]
+ end
+
+ expected_args, retval, val_block =
+ expected_call.values_at(:args, :retval, :block)
+
+ if val_block then
+ raise MockExpectationError, "mocked method %p failed block w/ %p" %
+ [sym, args] unless val_block.call(args)
+
+ # keep "verify" happy
+ @actual_calls[sym] << expected_call
+ return retval
+ end
+
+ if expected_args.size != args.size then
+ raise ArgumentError, "mocked method %p expects %d arguments, got %d" %
+ [sym, expected_args.size, args.size]
+ end
+
+ fully_matched = expected_args.zip(args).all? { |mod, a|
+ mod === a or mod == a
+ }
+
+ unless fully_matched then
+ raise MockExpectationError, "mocked method %p called with unexpected arguments %p" %
+ [sym, args]
+ end
+
+ @actual_calls[sym] << {
+ :retval => retval,
+ :args => expected_args.zip(args).map { |mod, a| mod === a ? mod : a }
+ }
+
+ retval
+ end
+
+ def respond_to?(sym, include_private = false) # :nodoc:
+ return true if @expected_calls.has_key?(sym.to_sym)
+ return __respond_to?(sym, include_private)
+ end
+ end
+end
+
+class Object # :nodoc:
+
+ ##
+ # Add a temporary stubbed method replacing +name+ for the duration
+ # of the +block+. If +val_or_callable+ responds to #call, then it
+ # returns the result of calling it, otherwise returns the value
+ # as-is. Cleans up the stub at the end of the +block+. The method
+ # +name+ must exist before stubbing.
+ #
+ # def test_stale_eh
+ # obj_under_test = Something.new
+ # refute obj_under_test.stale?
+ #
+ # Time.stub :now, Time.at(0) do
+ # assert obj_under_test.stale?
+ # end
+ # end
+
+ def stub name, val_or_callable, &block
+ new_name = "__minitest_stub__#{name}"
+
+ metaclass = class << self; self; end
+
+ if respond_to? name and not methods.map(&:to_s).include? name.to_s then
+ metaclass.send :define_method, name do |*args|
+ super(*args)
+ end
+ end
+
+ metaclass.send :alias_method, new_name, name
+
+ metaclass.send :define_method, name do |*args|
+ if val_or_callable.respond_to? :call then
+ val_or_callable.call(*args)
+ else
+ val_or_callable
+ end
+ end
+
+ yield self
+ ensure
+ metaclass.send :undef_method, name
+ metaclass.send :alias_method, name, new_name
+ metaclass.send :undef_method, new_name
+ end
+end
diff --git a/test/lib/minitest/parallel_each.rb b/test/lib/minitest/parallel_each.rb
new file mode 100644
index 0000000000..e1020b35a0
--- /dev/null
+++ b/test/lib/minitest/parallel_each.rb
@@ -0,0 +1,80 @@
+# encoding: utf-8
+######################################################################
+# This file is imported from the minitest project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis.
+######################################################################
+
+##
+# Provides a parallel #each that lets you enumerate using N threads.
+# Use environment variable N to customize. Defaults to 2. Enumerable,
+# so all the goodies come along (tho not all are wrapped yet to
+# return another ParallelEach instance).
+
+class ParallelEach
+ require 'thread'
+ include Enumerable
+
+ ##
+ # How many Threads to use for this parallel #each.
+
+ N = (ENV['N'] || 2).to_i
+
+ ##
+ # Create a new ParallelEach instance over +list+.
+
+ def initialize list
+ @queue = Queue.new # *sigh*... the Queue api sucks sooo much...
+
+ list.each { |i| @queue << i }
+ N.times { @queue << nil }
+ end
+
+ def grep pattern # :nodoc:
+ self.class.new super
+ end
+
+ def select(&block) # :nodoc:
+ self.class.new super
+ end
+
+ alias find_all select # :nodoc:
+
+ ##
+ # Starts N threads that yield each element to your block. Joins the
+ # threads at the end.
+
+ def each
+ threads = N.times.map {
+ Thread.new do
+ Thread.current.abort_on_exception = true
+ while job = @queue.pop
+ yield job
+ end
+ end
+ }
+ threads.map(&:join)
+ end
+
+ def count
+ [@queue.size - N, 0].max
+ end
+
+ alias_method :size, :count
+end
+
+class MiniTest::Unit
+ alias _old_run_suites _run_suites
+
+ ##
+ # Runs all the +suites+ for a given +type+. Runs suites declaring
+ # a test_order of +:parallel+ in parallel, and everything else
+ # serial.
+
+ def _run_suites suites, type
+ parallel, serial = suites.partition { |s| s.test_order == :parallel }
+
+ ParallelEach.new(parallel).map { |suite| _run_suite suite, type } +
+ serial.map { |suite| _run_suite suite, type }
+ end
+end
diff --git a/test/lib/minitest/pride.rb b/test/lib/minitest/pride.rb
new file mode 100644
index 0000000000..40c35394fa
--- /dev/null
+++ b/test/lib/minitest/pride.rb
@@ -0,0 +1,119 @@
+# encoding: utf-8
+######################################################################
+# This file is imported from the minitest project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis.
+######################################################################
+
+require "minitest/unit"
+
+##
+# Show your testing pride!
+
+class PrideIO
+
+ # Start an escape sequence
+ ESC = "\e["
+
+ # End the escape sequence
+ NND = "#{ESC}0m"
+
+ # The IO we're going to pipe through.
+ attr_reader :io
+
+ def initialize io # :nodoc:
+ @io = io
+ # stolen from /System/Library/Perl/5.10.0/Term/ANSIColor.pm
+ # also reference http://en.wikipedia.org/wiki/ANSI_escape_code
+ @colors ||= (31..36).to_a
+ @size = @colors.size
+ @index = 0
+ # io.sync = true
+ end
+
+ ##
+ # Wrap print to colorize the output.
+
+ def print o
+ case o
+ when "." then
+ io.print pride o
+ when "E", "F" then
+ io.print "#{ESC}41m#{ESC}37m#{o}#{NND}"
+ else
+ io.print o
+ end
+ end
+
+ def puts(*o) # :nodoc:
+ o.map! { |s|
+ s.to_s.sub(/Finished tests/) {
+ @index = 0
+ 'Fabulous tests'.split(//).map { |c|
+ pride(c)
+ }.join
+ }
+ }
+
+ super
+ end
+
+ ##
+ # Color a string.
+
+ def pride string
+ string = "*" if string == "."
+ c = @colors[@index % @size]
+ @index += 1
+ "#{ESC}#{c}m#{string}#{NND}"
+ end
+
+ def method_missing msg, *args # :nodoc:
+ io.send(msg, *args)
+ end
+end
+
+##
+# If you thought the PrideIO was colorful...
+#
+# (Inspired by lolcat, but with clean math)
+
+class PrideLOL < PrideIO
+ PI_3 = Math::PI / 3 # :nodoc:
+
+ def initialize io # :nodoc:
+ # walk red, green, and blue around a circle separated by equal thirds.
+ #
+ # To visualize, type this into wolfram-alpha:
+ #
+ # plot (3*sin(x)+3), (3*sin(x+2*pi/3)+3), (3*sin(x+4*pi/3)+3)
+
+ # 6 has wide pretty gradients. 3 == lolcat, about half the width
+ @colors = (0...(6 * 7)).map { |n|
+ n *= 1.0 / 6
+ r = (3 * Math.sin(n ) + 3).to_i
+ g = (3 * Math.sin(n + 2 * PI_3) + 3).to_i
+ b = (3 * Math.sin(n + 4 * PI_3) + 3).to_i
+
+ # Then we take rgb and encode them in a single number using base 6.
+ # For some mysterious reason, we add 16... to clear the bottom 4 bits?
+ # Yes... they're ugly.
+
+ 36 * r + 6 * g + b + 16
+ }
+
+ super
+ end
+
+ ##
+ # Make the string even more colorful. Damnit.
+
+ def pride string
+ c = @colors[@index % @size]
+ @index += 1
+ "#{ESC}38;5;#{c}m#{string}#{NND}"
+ end
+end
+
+klass = ENV['TERM'] =~ /^xterm|-256color$/ ? PrideLOL : PrideIO
+MiniTest::Unit.output = klass.new(MiniTest::Unit.output)
diff --git a/test/lib/minitest/spec.rb b/test/lib/minitest/spec.rb
new file mode 100644
index 0000000000..d91fccf5bc
--- /dev/null
+++ b/test/lib/minitest/spec.rb
@@ -0,0 +1,551 @@
+# encoding: utf-8
+######################################################################
+# This file is imported from the minitest project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis.
+######################################################################
+
+#!/usr/bin/ruby -w
+
+require 'minitest/unit'
+
+class Module # :nodoc:
+ def infect_an_assertion meth, new_name, dont_flip = false # :nodoc:
+ # warn "%-22p -> %p %p" % [meth, new_name, dont_flip]
+ self.class_eval <<-EOM
+ def #{new_name} *args
+ case
+ when Proc === self then
+ MiniTest::Spec.current.#{meth}(*args, &self)
+ when #{!!dont_flip} then
+ MiniTest::Spec.current.#{meth}(self, *args)
+ else
+ MiniTest::Spec.current.#{meth}(args.first, self, *args[1..-1])
+ end
+ end
+ EOM
+ end
+
+ ##
+ # infect_with_assertions has been removed due to excessive clever.
+ # Use infect_an_assertion directly instead.
+
+ def infect_with_assertions(pos_prefix, neg_prefix,
+ skip_re,
+ dont_flip_re = /\c0/,
+ map = {})
+ abort "infect_with_assertions is dead. Use infect_an_assertion directly"
+ end
+end
+
+module Kernel # :nodoc:
+ ##
+ # Describe a series of expectations for a given target +desc+.
+ #
+ # TODO: find good tutorial url.
+ #
+ # Defines a test class subclassing from either MiniTest::Spec or
+ # from the surrounding describe's class. The surrounding class may
+ # subclass MiniTest::Spec manually in order to easily share code:
+ #
+ # class MySpec < MiniTest::Spec
+ # # ... shared code ...
+ # end
+ #
+ # class TestStuff < MySpec
+ # it "does stuff" do
+ # # shared code available here
+ # end
+ # describe "inner stuff" do
+ # it "still does stuff" do
+ # # ...and here
+ # end
+ # end
+ # end
+
+ def describe desc, additional_desc = nil, &block # :doc:
+ stack = MiniTest::Spec.describe_stack
+ name = [stack.last, desc, additional_desc].compact.join("::")
+ sclas = stack.last || if Class === self && is_a?(MiniTest::Spec::DSL) then
+ self
+ else
+ MiniTest::Spec.spec_type desc
+ end
+
+ cls = sclas.create name, desc
+
+ stack.push cls
+ cls.class_eval(&block)
+ stack.pop
+ cls
+ end
+ private :describe
+end
+
+##
+# MiniTest::Spec -- The faster, better, less-magical spec framework!
+#
+# For a list of expectations, see MiniTest::Expectations.
+
+class MiniTest::Spec < MiniTest::Unit::TestCase
+
+ ##
+ # Oh look! A MiniTest::Spec::DSL module! Eat your heart out DHH.
+
+ module DSL
+ ##
+ # Contains pairs of matchers and Spec classes to be used to
+ # calculate the superclass of a top-level describe. This allows for
+ # automatically customizable spec types.
+ #
+ # See: register_spec_type and spec_type
+
+ TYPES = [[//, MiniTest::Spec]]
+
+ ##
+ # Register a new type of spec that matches the spec's description.
+ # This method can take either a Regexp and a spec class or a spec
+ # class and a block that takes the description and returns true if
+ # it matches.
+ #
+ # Eg:
+ #
+ # register_spec_type(/Controller$/, MiniTest::Spec::Rails)
+ #
+ # or:
+ #
+ # register_spec_type(MiniTest::Spec::RailsModel) do |desc|
+ # desc.superclass == ActiveRecord::Base
+ # end
+
+ def register_spec_type(*args, &block)
+ if block then
+ matcher, klass = block, args.first
+ else
+ matcher, klass = *args
+ end
+ TYPES.unshift [matcher, klass]
+ end
+
+ ##
+ # Figure out the spec class to use based on a spec's description. Eg:
+ #
+ # spec_type("BlahController") # => MiniTest::Spec::Rails
+
+ def spec_type desc
+ TYPES.find { |matcher, klass|
+ if matcher.respond_to? :call then
+ matcher.call desc
+ else
+ matcher === desc.to_s
+ end
+ }.last
+ end
+
+ def describe_stack # :nodoc:
+ Thread.current[:describe_stack] ||= []
+ end
+
+ ##
+ # Returns the children of this spec.
+
+ def children
+ @children ||= []
+ end
+
+ def nuke_test_methods! # :nodoc:
+ self.public_instance_methods.grep(/^test_/).each do |name|
+ self.send :undef_method, name
+ end
+ end
+
+ ##
+ # Define a 'before' action. Inherits the way normal methods should.
+ #
+ # NOTE: +type+ is ignored and is only there to make porting easier.
+ #
+ # Equivalent to MiniTest::Unit::TestCase#setup.
+
+ def before type = nil, &block
+ define_method :setup do
+ super()
+ self.instance_eval(&block)
+ end
+ end
+
+ ##
+ # Define an 'after' action. Inherits the way normal methods should.
+ #
+ # NOTE: +type+ is ignored and is only there to make porting easier.
+ #
+ # Equivalent to MiniTest::Unit::TestCase#teardown.
+
+ def after type = nil, &block
+ define_method :teardown do
+ self.instance_eval(&block)
+ super()
+ end
+ end
+
+ ##
+ # Define an expectation with name +desc+. Name gets morphed to a
+ # proper test method name. For some freakish reason, people who
+ # write specs don't like class inheritance, so this goes way out of
+ # its way to make sure that expectations aren't inherited.
+ #
+ # This is also aliased to #specify and doesn't require a +desc+ arg.
+ #
+ # Hint: If you _do_ want inheritence, use minitest/unit. You can mix
+ # and match between assertions and expectations as much as you want.
+
+ def it desc = "anonymous", &block
+ block ||= proc { skip "(no tests defined)" }
+
+ @specs ||= 0
+ @specs += 1
+
+ name = "test_%04d_%s" % [ @specs, desc ]
+
+ define_method name, &block
+
+ self.children.each do |mod|
+ mod.send :undef_method, name if mod.public_method_defined? name
+ end
+
+ name
+ end
+
+ ##
+ # Essentially, define an accessor for +name+ with +block+.
+ #
+ # Why use let instead of def? I honestly don't know.
+
+ def let name, &block
+ define_method name do
+ @_memoized ||= {}
+ @_memoized.fetch(name) { |k| @_memoized[k] = instance_eval(&block) }
+ end
+ end
+
+ ##
+ # Another lazy man's accessor generator. Made even more lazy by
+ # setting the name for you to +subject+.
+
+ def subject &block
+ let :subject, &block
+ end
+
+ def create name, desc # :nodoc:
+ cls = Class.new(self) do
+ @name = name
+ @desc = desc
+
+ nuke_test_methods!
+ end
+
+ children << cls
+
+ cls
+ end
+
+ def name # :nodoc:
+ defined?(@name) ? @name : super
+ end
+
+ def to_s # :nodoc:
+ name # Can't alias due to 1.8.7, not sure why
+ end
+
+ # :stopdoc:
+ attr_reader :desc
+ alias :specify :it
+ # :startdoc:
+ end
+
+ extend DSL
+
+ TYPES = DSL::TYPES # :nodoc:
+end
+
+##
+# It's where you hide your "assertions".
+
+module MiniTest::Expectations
+ ##
+ # See MiniTest::Assertions#assert_empty.
+ #
+ # collection.must_be_empty
+ #
+ # :method: must_be_empty
+
+ infect_an_assertion :assert_empty, :must_be_empty, :unary
+
+ ##
+ # See MiniTest::Assertions#assert_equal
+ #
+ # a.must_equal b
+ #
+ # :method: must_equal
+
+ infect_an_assertion :assert_equal, :must_equal
+
+ ##
+ # See MiniTest::Assertions#assert_in_delta
+ #
+ # n.must_be_close_to m [, delta]
+ #
+ # :method: must_be_close_to
+
+ infect_an_assertion :assert_in_delta, :must_be_close_to
+
+ alias :must_be_within_delta :must_be_close_to # :nodoc:
+
+ ##
+ # See MiniTest::Assertions#assert_in_epsilon
+ #
+ # n.must_be_within_epsilon m [, epsilon]
+ #
+ # :method: must_be_within_epsilon
+
+ infect_an_assertion :assert_in_epsilon, :must_be_within_epsilon
+
+ ##
+ # See MiniTest::Assertions#assert_includes
+ #
+ # collection.must_include obj
+ #
+ # :method: must_include
+
+ infect_an_assertion :assert_includes, :must_include, :reverse
+
+ ##
+ # See MiniTest::Assertions#assert_instance_of
+ #
+ # obj.must_be_instance_of klass
+ #
+ # :method: must_be_instance_of
+
+ infect_an_assertion :assert_instance_of, :must_be_instance_of
+
+ ##
+ # See MiniTest::Assertions#assert_kind_of
+ #
+ # obj.must_be_kind_of mod
+ #
+ # :method: must_be_kind_of
+
+ infect_an_assertion :assert_kind_of, :must_be_kind_of
+
+ ##
+ # See MiniTest::Assertions#assert_match
+ #
+ # a.must_match b
+ #
+ # :method: must_match
+
+ infect_an_assertion :assert_match, :must_match
+
+ ##
+ # See MiniTest::Assertions#assert_nil
+ #
+ # obj.must_be_nil
+ #
+ # :method: must_be_nil
+
+ infect_an_assertion :assert_nil, :must_be_nil, :unary
+
+ ##
+ # See MiniTest::Assertions#assert_operator
+ #
+ # n.must_be :<=, 42
+ #
+ # This can also do predicates:
+ #
+ # str.must_be :empty?
+ #
+ # :method: must_be
+
+ infect_an_assertion :assert_operator, :must_be, :reverse
+
+ ##
+ # See MiniTest::Assertions#assert_output
+ #
+ # proc { ... }.must_output out_or_nil [, err]
+ #
+ # :method: must_output
+
+ infect_an_assertion :assert_output, :must_output
+
+ ##
+ # See MiniTest::Assertions#assert_raises
+ #
+ # proc { ... }.must_raise exception
+ #
+ # :method: must_raise
+
+ infect_an_assertion :assert_raises, :must_raise
+
+ ##
+ # See MiniTest::Assertions#assert_respond_to
+ #
+ # obj.must_respond_to msg
+ #
+ # :method: must_respond_to
+
+ infect_an_assertion :assert_respond_to, :must_respond_to, :reverse
+
+ ##
+ # See MiniTest::Assertions#assert_same
+ #
+ # a.must_be_same_as b
+ #
+ # :method: must_be_same_as
+
+ infect_an_assertion :assert_same, :must_be_same_as
+
+ ##
+ # See MiniTest::Assertions#assert_send
+ # TODO: remove me
+ #
+ # a.must_send
+ #
+ # :method: must_send
+
+ infect_an_assertion :assert_send, :must_send
+
+ ##
+ # See MiniTest::Assertions#assert_silent
+ #
+ # proc { ... }.must_be_silent
+ #
+ # :method: must_be_silent
+
+ infect_an_assertion :assert_silent, :must_be_silent
+
+ ##
+ # See MiniTest::Assertions#assert_throws
+ #
+ # proc { ... }.must_throw sym
+ #
+ # :method: must_throw
+
+ infect_an_assertion :assert_throws, :must_throw
+
+ ##
+ # See MiniTest::Assertions#refute_empty
+ #
+ # collection.wont_be_empty
+ #
+ # :method: wont_be_empty
+
+ infect_an_assertion :refute_empty, :wont_be_empty, :unary
+
+ ##
+ # See MiniTest::Assertions#refute_equal
+ #
+ # a.wont_equal b
+ #
+ # :method: wont_equal
+
+ infect_an_assertion :refute_equal, :wont_equal
+
+ ##
+ # See MiniTest::Assertions#refute_in_delta
+ #
+ # n.wont_be_close_to m [, delta]
+ #
+ # :method: wont_be_close_to
+
+ infect_an_assertion :refute_in_delta, :wont_be_close_to
+
+ alias :wont_be_within_delta :wont_be_close_to # :nodoc:
+
+ ##
+ # See MiniTest::Assertions#refute_in_epsilon
+ #
+ # n.wont_be_within_epsilon m [, epsilon]
+ #
+ # :method: wont_be_within_epsilon
+
+ infect_an_assertion :refute_in_epsilon, :wont_be_within_epsilon
+
+ ##
+ # See MiniTest::Assertions#refute_includes
+ #
+ # collection.wont_include obj
+ #
+ # :method: wont_include
+
+ infect_an_assertion :refute_includes, :wont_include, :reverse
+
+ ##
+ # See MiniTest::Assertions#refute_instance_of
+ #
+ # obj.wont_be_instance_of klass
+ #
+ # :method: wont_be_instance_of
+
+ infect_an_assertion :refute_instance_of, :wont_be_instance_of
+
+ ##
+ # See MiniTest::Assertions#refute_kind_of
+ #
+ # obj.wont_be_kind_of mod
+ #
+ # :method: wont_be_kind_of
+
+ infect_an_assertion :refute_kind_of, :wont_be_kind_of
+
+ ##
+ # See MiniTest::Assertions#refute_match
+ #
+ # a.wont_match b
+ #
+ # :method: wont_match
+
+ infect_an_assertion :refute_match, :wont_match
+
+ ##
+ # See MiniTest::Assertions#refute_nil
+ #
+ # obj.wont_be_nil
+ #
+ # :method: wont_be_nil
+
+ infect_an_assertion :refute_nil, :wont_be_nil, :unary
+
+ ##
+ # See MiniTest::Assertions#refute_operator
+ #
+ # n.wont_be :<=, 42
+ #
+ # This can also do predicates:
+ #
+ # str.wont_be :empty?
+ #
+ # :method: wont_be
+
+ infect_an_assertion :refute_operator, :wont_be, :reverse
+
+ ##
+ # See MiniTest::Assertions#refute_respond_to
+ #
+ # obj.wont_respond_to msg
+ #
+ # :method: wont_respond_to
+
+ infect_an_assertion :refute_respond_to, :wont_respond_to, :reverse
+
+ ##
+ # See MiniTest::Assertions#refute_same
+ #
+ # a.wont_be_same_as b
+ #
+ # :method: wont_be_same_as
+
+ infect_an_assertion :refute_same, :wont_be_same_as
+end
+
+class Object # :nodoc:
+ include MiniTest::Expectations unless ENV["MT_NO_EXPECTATIONS"]
+end
diff --git a/test/lib/minitest/unit.rb b/test/lib/minitest/unit.rb
new file mode 100644
index 0000000000..465e5b4c98
--- /dev/null
+++ b/test/lib/minitest/unit.rb
@@ -0,0 +1,1422 @@
+# encoding: utf-8
+######################################################################
+# This file is imported from the minitest project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis.
+######################################################################
+
+require "optparse"
+require "rbconfig"
+
+##
+# Minimal (mostly drop-in) replacement for test-unit.
+#
+# :include: README.txt
+
+module MiniTest
+
+ def self.const_missing name # :nodoc:
+ case name
+ when :MINI_DIR then
+ msg = "MiniTest::MINI_DIR was removed. Don't violate other's internals."
+ warn "WAR\NING: #{msg}"
+ warn "WAR\NING: Used by #{caller.first}."
+ const_set :MINI_DIR, "bad value"
+ else
+ super
+ end
+ end
+
+ ##
+ # Assertion base class
+
+ class Assertion < Exception; end
+
+ ##
+ # Assertion raised when skipping a test
+
+ class Skip < Assertion; end
+
+ class << self
+ ##
+ # Filter object for backtraces.
+
+ attr_accessor :backtrace_filter
+ end
+
+ class BacktraceFilter # :nodoc:
+ def filter bt
+ return ["No backtrace"] unless bt
+
+ new_bt = []
+
+ unless $DEBUG then
+ bt.each do |line|
+ break if line =~ /lib\/minitest/
+ new_bt << line
+ end
+
+ new_bt = bt.reject { |line| line =~ /lib\/minitest/ } if new_bt.empty?
+ new_bt = bt.dup if new_bt.empty?
+ else
+ new_bt = bt.dup
+ end
+
+ new_bt
+ end
+ end
+
+ self.backtrace_filter = BacktraceFilter.new
+
+ def self.filter_backtrace bt # :nodoc:
+ backtrace_filter.filter bt
+ end
+
+ ##
+ # MiniTest Assertions. All assertion methods accept a +msg+ which is
+ # printed if the assertion fails.
+
+ module Assertions
+ UNDEFINED = Object.new # :nodoc:
+
+ def UNDEFINED.inspect # :nodoc:
+ "UNDEFINED" # again with the rdoc bugs... :(
+ end
+
+ ##
+ # Returns the diff command to use in #diff. Tries to intelligently
+ # figure out what diff to use.
+
+ def self.diff
+ @diff = if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ &&
+ system("diff.exe", __FILE__, __FILE__)) then
+ "diff.exe -u"
+ elsif Minitest::Unit::Guard.maglev? then # HACK
+ "diff -u"
+ elsif system("gdiff", __FILE__, __FILE__)
+ "gdiff -u" # solaris and kin suck
+ elsif system("diff", __FILE__, __FILE__)
+ "diff -u"
+ else
+ nil
+ end unless defined? @diff
+
+ @diff
+ end
+
+ ##
+ # Set the diff command to use in #diff.
+
+ def self.diff= o
+ @diff = o
+ end
+
+ ##
+ # Returns a diff between +exp+ and +act+. If there is no known
+ # diff command or if it doesn't make sense to diff the output
+ # (single line, short output), then it simply returns a basic
+ # comparison between the two.
+
+ def diff exp, act
+ require "tempfile"
+
+ expect = mu_pp_for_diff exp
+ butwas = mu_pp_for_diff act
+ result = nil
+
+ need_to_diff =
+ MiniTest::Assertions.diff &&
+ (expect.include?("\n") ||
+ butwas.include?("\n") ||
+ expect.size > 30 ||
+ butwas.size > 30 ||
+ expect == butwas)
+
+ return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
+ need_to_diff
+
+ Tempfile.open("expect") do |a|
+ a.puts expect
+ a.flush
+
+ Tempfile.open("butwas") do |b|
+ b.puts butwas
+ b.flush
+
+ result = `#{MiniTest::Assertions.diff} #{a.path} #{b.path}`
+ result.sub!(/^\-\-\- .+/, "--- expected")
+ result.sub!(/^\+\+\+ .+/, "+++ actual")
+
+ if result.empty? then
+ klass = exp.class
+ result = [
+ "No visible difference in the #{klass}#inspect output.\n",
+ "You should look at the implementation of #== on ",
+ "#{klass} or its members.\n",
+ expect,
+ ].join
+ end
+ end
+ end
+
+ result
+ end
+
+ ##
+ # This returns a human-readable version of +obj+. By default
+ # #inspect is called. You can override this to use #pretty_print
+ # if you want.
+
+ def mu_pp obj
+ s = obj.inspect
+ s = s.encode Encoding.default_external if defined? Encoding
+ s
+ end
+
+ ##
+ # This returns a diff-able human-readable version of +obj+. This
+ # differs from the regular mu_pp because it expands escaped
+ # newlines and makes hex-values generic (like object_ids). This
+ # uses mu_pp to do the first pass and then cleans it up.
+
+ def mu_pp_for_diff obj
+ mu_pp(obj).gsub(/\\n/, "\n").gsub(/:0x[a-fA-F0-9]{4,}/m, ':0xXXXXXX')
+ end
+
+ def _assertions= n # :nodoc:
+ @_assertions = n
+ end
+
+ def _assertions # :nodoc:
+ @_assertions ||= 0
+ end
+
+ ##
+ # Fails unless +test+ is a true value.
+
+ def assert test, msg = nil
+ msg ||= "Failed assertion, no message given."
+ self._assertions += 1
+ unless test then
+ msg = msg.call if Proc === msg
+ raise MiniTest::Assertion, msg
+ end
+ true
+ end
+
+ ##
+ # Fails unless +obj+ is empty.
+
+ def assert_empty obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" }
+ assert_respond_to obj, :empty?
+ assert obj.empty?, msg
+ end
+
+ ##
+ # Fails unless <tt>exp == act</tt> printing the difference between
+ # the two, if possible.
+ #
+ # If there is no visible difference but the assertion fails, you
+ # should suspect that your #== is buggy, or your inspect output is
+ # missing crucial details.
+ #
+ # For floats use assert_in_delta.
+ #
+ # See also: MiniTest::Assertions.diff
+
+ def assert_equal exp, act, msg = nil
+ msg = message(msg, "") { diff exp, act }
+ assert exp == act, msg
+ end
+
+ ##
+ # For comparing Floats. Fails unless +exp+ and +act+ are within +delta+
+ # of each other.
+ #
+ # assert_in_delta Math::PI, (22.0 / 7.0), 0.01
+
+ def assert_in_delta exp, act, delta = 0.001, msg = nil
+ n = (exp - act).abs
+ msg = message(msg) {
+ "Expected |#{exp} - #{act}| (#{n}) to be <= #{delta}"
+ }
+ assert delta >= n, msg
+ end
+
+ ##
+ # For comparing Floats. Fails unless +exp+ and +act+ have a relative
+ # error less than +epsilon+.
+
+ def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
+ assert_in_delta a, b, [a.abs, b.abs].min * epsilon, msg
+ end
+
+ ##
+ # Fails unless +collection+ includes +obj+.
+
+ def assert_includes collection, obj, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}"
+ }
+ assert_respond_to collection, :include?
+ assert collection.include?(obj), msg
+ end
+
+ ##
+ # Fails unless +obj+ is an instance of +cls+.
+
+ def assert_instance_of cls, obj, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}"
+ }
+
+ assert obj.instance_of?(cls), msg
+ end
+
+ ##
+ # Fails unless +obj+ is a kind of +cls+.
+
+ def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
+
+ assert obj.kind_of?(cls), msg
+ end
+
+ ##
+ # Fails unless +matcher+ <tt>=~</tt> +obj+.
+
+ def assert_match matcher, obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" }
+ assert_respond_to matcher, :"=~"
+ matcher = Regexp.new Regexp.escape matcher if String === matcher
+ assert matcher =~ obj, msg
+ end
+
+ ##
+ # Fails unless +obj+ is nil
+
+ def assert_nil obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
+ assert obj.nil?, msg
+ end
+
+ ##
+ # For testing with binary operators.
+ #
+ # assert_operator 5, :<=, 4
+
+ def assert_operator o1, op, o2 = UNDEFINED, msg = nil
+ return assert_predicate o1, op, msg if UNDEFINED == o2
+ msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
+ assert o1.__send__(op, o2), msg
+ end
+
+ ##
+ # Fails if stdout or stderr do not output the expected results.
+ # Pass in nil if you don't care about that streams output. Pass in
+ # "" if you require it to be silent. Pass in a regexp if you want
+ # to pattern match.
+ #
+ # NOTE: this uses #capture_io, not #capture_subprocess_io.
+ #
+ # See also: #assert_silent
+
+ def assert_output stdout = nil, stderr = nil
+ out, err = capture_io do
+ yield
+ end
+
+ err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr
+ out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout
+
+ y = send err_msg, stderr, err, "In stderr" if err_msg
+ x = send out_msg, stdout, out, "In stdout" if out_msg
+
+ (!stdout || x) && (!stderr || y)
+ end
+
+ ##
+ # For testing with predicates.
+ #
+ # assert_predicate str, :empty?
+ #
+ # This is really meant for specs and is front-ended by assert_operator:
+ #
+ # str.must_be :empty?
+
+ def assert_predicate o1, op, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op}" }
+ assert o1.__send__(op), msg
+ end
+
+ ##
+ # Fails unless the block raises one of +exp+. Returns the
+ # exception matched so you can check the message, attributes, etc.
+
+ def assert_raises *exp
+ msg = "#{exp.pop}.\n" if String === exp.last
+
+ begin
+ yield
+ rescue MiniTest::Skip => e
+ return e if exp.include? MiniTest::Skip
+ raise e
+ rescue Exception => e
+ expected = exp.any? { |ex|
+ if ex.instance_of? Module then
+ e.kind_of? ex
+ else
+ e.instance_of? ex
+ end
+ }
+
+ assert expected, proc {
+ exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not")
+ }
+
+ return e
+ end
+
+ exp = exp.first if exp.size == 1
+
+ flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised."
+ end
+
+ ##
+ # Fails unless +obj+ responds to +meth+.
+
+ def assert_respond_to obj, meth, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
+ }
+ assert obj.respond_to?(meth), msg
+ end
+
+ ##
+ # Fails unless +exp+ and +act+ are #equal?
+
+ def assert_same exp, act, msg = nil
+ msg = message(msg) {
+ data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
+ "Expected %s (oid=%d) to be the same as %s (oid=%d)" % data
+ }
+ assert exp.equal?(act), msg
+ end
+
+ ##
+ # +send_ary+ is a receiver, message and arguments.
+ #
+ # Fails unless the call returns a true value
+ # TODO: I should prolly remove this from specs
+
+ def assert_send send_ary, m = nil
+ recv, msg, *args = send_ary
+ m = message(m) {
+ "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
+ assert recv.__send__(msg, *args), m
+ end
+
+ ##
+ # Fails if the block outputs anything to stderr or stdout.
+ #
+ # See also: #assert_output
+
+ def assert_silent
+ assert_output "", "" do
+ yield
+ end
+ end
+
+ ##
+ # Fails unless the block throws +sym+
+
+ def assert_throws sym, msg = nil
+ default = "Expected #{mu_pp(sym)} to have been thrown"
+ caught = true
+ catch(sym) do
+ begin
+ yield
+ rescue ThreadError => e # wtf?!? 1.8 + threads == suck
+ default += ", not \:#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
+ rescue ArgumentError => e # 1.9 exception
+ default += ", not #{e.message.split(/ /).last}"
+ rescue NameError => e # 1.8 exception
+ default += ", not #{e.name.inspect}"
+ end
+ caught = false
+ end
+
+ assert caught, message(msg) { default }
+ end
+
+ ##
+ # Captures $stdout and $stderr into strings:
+ #
+ # out, err = capture_io do
+ # puts "Some info"
+ # warn "You did a bad thing"
+ # end
+ #
+ # assert_match %r%info%, out
+ # assert_match %r%bad%, err
+ #
+ # NOTE: For efficiency, this method uses StringIO and does not
+ # capture IO for subprocesses. Use #capture_subprocess_io for
+ # that.
+
+ def capture_io
+ require 'stringio'
+
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
+
+ synchronize do
+ orig_stdout, orig_stderr = $stdout, $stderr
+ $stdout, $stderr = captured_stdout, captured_stderr
+
+ begin
+ yield
+ ensure
+ $stdout = orig_stdout
+ $stderr = orig_stderr
+ end
+ end
+
+ return captured_stdout.string, captured_stderr.string
+ end
+
+ ##
+ # Captures $stdout and $stderr into strings, using Tempfile to
+ # ensure that subprocess IO is captured as well.
+ #
+ # out, err = capture_subprocess_io do
+ # system "echo Some info"
+ # system "echo You did a bad thing 1>&2"
+ # end
+ #
+ # assert_match %r%info%, out
+ # assert_match %r%bad%, err
+ #
+ # NOTE: This method is approximately 10x slower than #capture_io so
+ # only use it when you need to test the output of a subprocess.
+
+ def capture_subprocess_io
+ require 'tempfile'
+
+ captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err")
+
+ synchronize do
+ orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
+ $stdout.reopen captured_stdout
+ $stderr.reopen captured_stderr
+
+ begin
+ yield
+
+ $stdout.rewind
+ $stderr.rewind
+
+ [captured_stdout.read, captured_stderr.read]
+ ensure
+ captured_stdout.unlink
+ captured_stderr.unlink
+ $stdout.reopen orig_stdout
+ $stderr.reopen orig_stderr
+ end
+ end
+ end
+
+ ##
+ # Returns details for exception +e+
+
+ def exception_details e, msg
+ [
+ "#{msg}",
+ "Class: <#{e.class}>",
+ "Message: <#{e.message.inspect}>",
+ "---Backtrace---",
+ "#{MiniTest::filter_backtrace(e.backtrace).join("\n")}",
+ "---------------",
+ ].join "\n"
+ end
+
+ ##
+ # Fails with +msg+
+
+ def flunk msg = nil
+ msg ||= "Epic Fail!"
+ assert false, msg
+ end
+
+ ##
+ # Returns a proc that will output +msg+ along with the default message.
+
+ def message msg = nil, ending = ".", &default
+ proc {
+ msg = msg.call.chomp(".") if Proc === msg
+ custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
+ "#{custom_message}#{default.call}#{ending}"
+ }
+ end
+
+ ##
+ # used for counting assertions
+
+ def pass msg = nil
+ assert true
+ end
+
+ ##
+ # Fails if +test+ is a true value
+
+ def refute test, msg = nil
+ msg ||= "Failed refutation, no message given"
+ not assert(! test, msg)
+ end
+
+ ##
+ # Fails if +obj+ is empty.
+
+ def refute_empty obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" }
+ assert_respond_to obj, :empty?
+ refute obj.empty?, msg
+ end
+
+ ##
+ # Fails if <tt>exp == act</tt>.
+ #
+ # For floats use refute_in_delta.
+
+ def refute_equal exp, act, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}"
+ }
+ refute exp == act, msg
+ end
+
+ ##
+ # For comparing Floats. Fails if +exp+ is within +delta+ of +act+.
+ #
+ # refute_in_delta Math::PI, (22.0 / 7.0)
+
+ def refute_in_delta exp, act, delta = 0.001, msg = nil
+ n = (exp - act).abs
+ msg = message(msg) {
+ "Expected |#{exp} - #{act}| (#{n}) to not be <= #{delta}"
+ }
+ refute delta >= n, msg
+ end
+
+ ##
+ # For comparing Floats. Fails if +exp+ and +act+ have a relative error
+ # less than +epsilon+.
+
+ def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
+ refute_in_delta a, b, a * epsilon, msg
+ end
+
+ ##
+ # Fails if +collection+ includes +obj+.
+
+ def refute_includes collection, obj, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}"
+ }
+ assert_respond_to collection, :include?
+ refute collection.include?(obj), msg
+ end
+
+ ##
+ # Fails if +obj+ is an instance of +cls+.
+
+ def refute_instance_of cls, obj, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} to not be an instance of #{cls}"
+ }
+ refute obj.instance_of?(cls), msg
+ end
+
+ ##
+ # Fails if +obj+ is a kind of +cls+.
+
+ def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
+ refute obj.kind_of?(cls), msg
+ end
+
+ ##
+ # Fails if +matcher+ <tt>=~</tt> +obj+.
+
+ def refute_match matcher, obj, msg = nil
+ msg = message(msg) {"Expected #{mu_pp matcher} to not match #{mu_pp obj}"}
+ assert_respond_to matcher, :"=~"
+ matcher = Regexp.new Regexp.escape matcher if String === matcher
+ refute matcher =~ obj, msg
+ end
+
+ ##
+ # Fails if +obj+ is nil.
+
+ def refute_nil obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
+ refute obj.nil?, msg
+ end
+
+ ##
+ # Fails if +o1+ is not +op+ +o2+. Eg:
+ #
+ # refute_operator 1, :>, 2 #=> pass
+ # refute_operator 1, :<, 2 #=> fail
+
+ def refute_operator o1, op, o2 = UNDEFINED, msg = nil
+ return refute_predicate o1, op, msg if UNDEFINED == o2
+ msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}"}
+ refute o1.__send__(op, o2), msg
+ end
+
+ ##
+ # For testing with predicates.
+ #
+ # refute_predicate str, :empty?
+ #
+ # This is really meant for specs and is front-ended by refute_operator:
+ #
+ # str.wont_be :empty?
+
+ def refute_predicate o1, op, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op}" }
+ refute o1.__send__(op), msg
+ end
+
+ ##
+ # Fails if +obj+ responds to the message +meth+.
+
+ def refute_respond_to obj, meth, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
+
+ refute obj.respond_to?(meth), msg
+ end
+
+ ##
+ # Fails if +exp+ is the same (by object identity) as +act+.
+
+ def refute_same exp, act, msg = nil
+ msg = message(msg) {
+ data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
+ "Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data
+ }
+ refute exp.equal?(act), msg
+ end
+
+ ##
+ # Skips the current test. Gets listed at the end of the run but
+ # doesn't cause a failure exit code.
+
+ def skip msg = nil, bt = caller
+ msg ||= "Skipped, no message given"
+ @skip = true
+ raise MiniTest::Skip, msg, bt
+ end
+
+ ##
+ # Was this testcase skipped? Meant for #teardown.
+
+ def skipped?
+ defined?(@skip) and @skip
+ end
+
+ ##
+ # Takes a block and wraps it with the runner's shared mutex.
+
+ def synchronize
+ Minitest::Unit.runner.synchronize do
+ yield
+ end
+ end
+ end
+
+ class Unit # :nodoc:
+ VERSION = "4.7.5" # :nodoc:
+
+ attr_accessor :report, :failures, :errors, :skips # :nodoc:
+ attr_accessor :assertion_count # :nodoc:
+ attr_writer :test_count # :nodoc:
+ attr_accessor :start_time # :nodoc:
+ attr_accessor :help # :nodoc:
+ attr_accessor :verbose # :nodoc:
+ attr_writer :options # :nodoc:
+
+ ##
+ # :attr:
+ #
+ # if true, installs an "INFO" signal handler (only available to BSD and
+ # OS X users) which prints diagnostic information about the test run.
+ #
+ # This is auto-detected by default but may be overridden by custom
+ # runners.
+
+ attr_accessor :info_signal
+
+ ##
+ # Lazy accessor for options.
+
+ def options
+ @options ||= {}
+ end
+
+ @@installed_at_exit ||= false
+ @@out = $stdout
+ @@after_tests = []
+
+ ##
+ # A simple hook allowing you to run a block of code after _all_ of
+ # the tests are done. Eg:
+ #
+ # MiniTest::Unit.after_tests { p $debugging_info }
+
+ def self.after_tests &block
+ @@after_tests << block
+ end
+
+ ##
+ # Registers MiniTest::Unit to run tests at process exit
+
+ def self.autorun
+ at_exit {
+ # don't run if there was a non-exit exception
+ next if $! and not $!.kind_of? SystemExit
+
+ # the order here is important. The at_exit handler must be
+ # installed before anyone else gets a chance to install their
+ # own, that way we can be assured that our exit will be last
+ # to run (at_exit stacks).
+ exit_code = nil
+
+ at_exit {
+ @@after_tests.reverse_each(&:call)
+ exit false if exit_code && exit_code != 0
+ }
+
+ exit_code = MiniTest::Unit.new.run ARGV
+ } unless @@installed_at_exit
+ @@installed_at_exit = true
+ end
+
+ ##
+ # Returns the stream to use for output.
+
+ def self.output
+ @@out
+ end
+
+ ##
+ # Sets MiniTest::Unit to write output to +stream+. $stdout is the default
+ # output
+
+ def self.output= stream
+ @@out = stream
+ end
+
+ ##
+ # Tells MiniTest::Unit to delegate to +runner+, an instance of a
+ # MiniTest::Unit subclass, when MiniTest::Unit#run is called.
+
+ def self.runner= runner
+ @@runner = runner
+ end
+
+ ##
+ # Returns the MiniTest::Unit subclass instance that will be used
+ # to run the tests. A MiniTest::Unit instance is the default
+ # runner.
+
+ def self.runner
+ @@runner ||= self.new
+ end
+
+ ##
+ # Return all plugins' run methods (methods that start with "run_").
+
+ def self.plugins
+ @@plugins ||= (["run_tests"] +
+ public_instance_methods(false).
+ grep(/^run_/).map { |s| s.to_s }).uniq
+ end
+
+ ##
+ # Return the IO for output.
+
+ def output
+ self.class.output
+ end
+
+ def puts *a # :nodoc:
+ output.puts(*a)
+ end
+
+ def print *a # :nodoc:
+ output.print(*a)
+ end
+
+ def test_count # :nodoc:
+ @test_count ||= 0
+ end
+
+ ##
+ # Runner for a given +type+ (eg, test vs bench).
+
+ def _run_anything type
+ suites = TestCase.send "#{type}_suites"
+ return if suites.empty?
+
+ start = Time.now
+
+ puts
+ puts "# Running #{type}s:"
+ puts
+
+ @test_count, @assertion_count = 0, 0
+ sync = output.respond_to? :"sync=" # stupid emacs
+ old_sync, output.sync = output.sync, true if sync
+
+ results = _run_suites suites, type
+
+ @test_count = results.inject(0) { |sum, (tc, _)| sum + tc }
+ @assertion_count = results.inject(0) { |sum, (_, ac)| sum + ac }
+
+ output.sync = old_sync if sync
+
+ t = Time.now - start
+
+ puts
+ puts
+ puts "Finished #{type}s in %.6fs, %.4f tests/s, %.4f assertions/s." %
+ [t, test_count / t, assertion_count / t]
+
+ report.each_with_index do |msg, i|
+ puts "\n%3d) %s" % [i + 1, msg]
+ end
+
+ puts
+
+ status
+ end
+
+ ##
+ # Runs all the +suites+ for a given +type+.
+ #
+ # NOTE: this method is redefined in parallel_each.rb, which is
+ # loaded if a test-suite calls parallelize_me!.
+
+ def _run_suites suites, type
+ suites.map { |suite| _run_suite suite, type }
+ end
+
+ ##
+ # Run a single +suite+ for a given +type+.
+
+ def _run_suite suite, type
+ header = "#{type}_suite_header"
+ puts send(header, suite) if respond_to? header
+
+ filter = options[:filter] || '/./'
+ filter = Regexp.new $1 if filter =~ /\/(.*)\//
+
+ all_test_methods = suite.send "#{type}_methods"
+
+ filtered_test_methods = all_test_methods.find_all { |m|
+ filter === m || filter === "#{suite}##{m}"
+ }
+
+ assertions = filtered_test_methods.map { |method|
+ inst = suite.new method
+ inst._assertions = 0
+
+ print "#{suite}##{method} = " if @verbose
+
+ start_time = Time.now if @verbose
+ result = inst.run self
+
+ print "%.2f s = " % (Time.now - start_time) if @verbose
+ print result
+ puts if @verbose
+
+ inst._assertions
+ }
+
+ return assertions.size, assertions.inject(0) { |sum, n| sum + n }
+ end
+
+ ##
+ # Record the result of a single test. Makes it very easy to gather
+ # information. Eg:
+ #
+ # class StatisticsRecorder < MiniTest::Unit
+ # def record suite, method, assertions, time, error
+ # # ... record the results somewhere ...
+ # end
+ # end
+ #
+ # MiniTest::Unit.runner = StatisticsRecorder.new
+ #
+ # NOTE: record might be sent more than once per test. It will be
+ # sent once with the results from the test itself. If there is a
+ # failure or error in teardown, it will be sent again with the
+ # error or failure.
+
+ def record suite, method, assertions, time, error
+ end
+
+ def location e # :nodoc:
+ last_before_assertion = ""
+ e.backtrace.reverse_each do |s|
+ break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
+ last_before_assertion = s
+ end
+ last_before_assertion.sub(/:in .*$/, '')
+ end
+
+ ##
+ # Writes status for failed test +meth+ in +klass+ which finished with
+ # exception +e+
+
+ def puke klass, meth, e
+ e = case e
+ when MiniTest::Skip then
+ @skips += 1
+ return "S" unless @verbose
+ "Skipped:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n"
+ when MiniTest::Assertion then
+ @failures += 1
+ "Failure:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n"
+ else
+ @errors += 1
+ bt = MiniTest::filter_backtrace(e.backtrace).join "\n "
+ "Error:\n#{klass}##{meth}:\n#{e.class}: #{e.message}\n #{bt}\n"
+ end
+ @report << e
+ e[0, 1]
+ end
+
+ def initialize # :nodoc:
+ @report = []
+ @errors = @failures = @skips = 0
+ @verbose = false
+ @mutex = defined?(Mutex) ? Mutex.new : nil
+ @info_signal = Signal.list['INFO']
+ end
+
+ def synchronize # :nodoc:
+ if @mutex then
+ @mutex.synchronize { yield }
+ else
+ yield
+ end
+ end
+
+ def process_args args = [] # :nodoc:
+ options = {}
+ orig_args = args.dup
+
+ OptionParser.new do |opts|
+ opts.banner = 'minitest options:'
+ opts.version = MiniTest::Unit::VERSION
+
+ opts.on '-h', '--help', 'Display this help.' do
+ puts opts
+ exit
+ end
+
+ opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
+ options[:seed] = m.to_i
+ end
+
+ opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
+ options[:verbose] = true
+ end
+
+ opts.on '-n', '--name PATTERN', "Filter test names on pattern (e.g. /foo/)" do |a|
+ options[:filter] = a
+ end
+
+ opts.parse! args
+ orig_args -= args
+ end
+
+ unless options[:seed] then
+ srand
+ options[:seed] = srand % 0xFFFF
+ orig_args << "--seed" << options[:seed].to_s
+ end
+
+ srand options[:seed]
+
+ self.verbose = options[:verbose]
+ @help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
+
+ options
+ end
+
+ ##
+ # Begins the full test run. Delegates to +runner+'s #_run method.
+
+ def run args = []
+ self.class.runner._run(args)
+ end
+
+ ##
+ # Top level driver, controls all output and filtering.
+
+ def _run args = []
+ args = process_args args # ARGH!! blame test/unit process_args
+ self.options.merge! args
+
+ puts "Run options: #{help}"
+
+ self.class.plugins.each do |plugin|
+ send plugin
+ break unless report.empty?
+ end
+
+ return failures + errors if self.test_count > 0 # or return nil...
+ rescue Interrupt
+ abort 'Interrupted'
+ end
+
+ ##
+ # Runs test suites matching +filter+.
+
+ def run_tests
+ _run_anything :test
+ end
+
+ ##
+ # Writes status to +io+
+
+ def status io = self.output
+ format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
+ io.puts format % [test_count, assertion_count, failures, errors, skips]
+ end
+
+ ##
+ # Provides a simple set of guards that you can use in your tests
+ # to skip execution if it is not applicable. These methods are
+ # mixed into TestCase as both instance and class methods so you
+ # can use them inside or outside of the test methods.
+ #
+ # def test_something_for_mri
+ # skip "bug 1234" if jruby?
+ # # ...
+ # end
+ #
+ # if windows? then
+ # # ... lots of test methods ...
+ # end
+
+ module Guard
+
+ ##
+ # Is this running on jruby?
+
+ def jruby? platform = RUBY_PLATFORM
+ "java" == platform
+ end
+
+ ##
+ # Is this running on mri?
+
+ def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
+ "maglev" == platform
+ end
+
+ module_function :maglev?
+
+ ##
+ # Is this running on mri?
+
+ def mri? platform = RUBY_DESCRIPTION
+ /^ruby/ =~ platform
+ end
+
+ ##
+ # Is this running on rubinius?
+
+ def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
+ "rbx" == platform
+ end
+
+ ##
+ # Is this running on windows?
+
+ def windows? platform = RUBY_PLATFORM
+ /mswin|mingw/ =~ platform
+ end
+ end
+
+ ##
+ # Provides before/after hooks for setup and teardown. These are
+ # meant for library writers, NOT for regular test authors. See
+ # #before_setup for an example.
+
+ module LifecycleHooks
+ ##
+ # Runs before every test, after setup. This hook is meant for
+ # libraries to extend minitest. It is not meant to be used by
+ # test developers.
+ #
+ # See #before_setup for an example.
+
+ def after_setup; end
+
+ ##
+ # Runs before every test, before setup. This hook is meant for
+ # libraries to extend minitest. It is not meant to be used by
+ # test developers.
+ #
+ # As a simplistic example:
+ #
+ # module MyMinitestPlugin
+ # def before_setup
+ # super
+ # # ... stuff to do before setup is run
+ # end
+ #
+ # def after_setup
+ # # ... stuff to do after setup is run
+ # super
+ # end
+ #
+ # def before_teardown
+ # super
+ # # ... stuff to do before teardown is run
+ # end
+ #
+ # def after_teardown
+ # # ... stuff to do after teardown is run
+ # super
+ # end
+ # end
+ #
+ # class MiniTest::Unit::TestCase
+ # include MyMinitestPlugin
+ # end
+
+ def before_setup; end
+
+ ##
+ # Runs after every test, before teardown. This hook is meant for
+ # libraries to extend minitest. It is not meant to be used by
+ # test developers.
+ #
+ # See #before_setup for an example.
+
+ def before_teardown; end
+
+ ##
+ # Runs after every test, after teardown. This hook is meant for
+ # libraries to extend minitest. It is not meant to be used by
+ # test developers.
+ #
+ # See #before_setup for an example.
+
+ def after_teardown; end
+ end
+
+ ##
+ # Subclass TestCase to create your own tests. Typically you'll want a
+ # TestCase subclass per implementation class.
+ #
+ # See MiniTest::Assertions
+
+ class TestCase
+ include LifecycleHooks
+ include Guard
+ extend Guard
+
+ attr_reader :__name__ # :nodoc:
+
+ PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException,
+ Interrupt, SystemExit] # :nodoc:
+
+ ##
+ # Runs the tests reporting the status to +runner+
+
+ def run runner
+ trap "INFO" do
+ runner.report.each_with_index do |msg, i|
+ warn "\n%3d) %s" % [i + 1, msg]
+ end
+ warn ''
+ time = runner.start_time ? Time.now - runner.start_time : 0
+ warn "Current Test: %s#%s %.2fs" % [self.class, self.__name__, time]
+ runner.status $stderr
+ end if runner.info_signal
+
+ start_time = Time.now
+
+ result = ""
+ begin
+ @passed = nil
+ self.before_setup
+ self.setup
+ self.after_setup
+ self.run_test self.__name__
+ result = "." unless io?
+ time = Time.now - start_time
+ runner.record self.class, self.__name__, self._assertions, time, nil
+ @passed = true
+ rescue *PASSTHROUGH_EXCEPTIONS
+ raise
+ rescue Exception => e
+ @passed = Skip === e
+ time = Time.now - start_time
+ runner.record self.class, self.__name__, self._assertions, time, e
+ result = runner.puke self.class, self.__name__, e
+ ensure
+ %w{ before_teardown teardown after_teardown }.each do |hook|
+ begin
+ self.send hook
+ rescue *PASSTHROUGH_EXCEPTIONS
+ raise
+ rescue Exception => e
+ @passed = false
+ runner.record self.class, self.__name__, self._assertions, time, e
+ result = runner.puke self.class, self.__name__, e
+ end
+ end
+ trap 'INFO', 'DEFAULT' if runner.info_signal
+ end
+ result
+ end
+
+ alias :run_test :__send__
+
+ def initialize name # :nodoc:
+ @__name__ = name
+ @__io__ = nil
+ @passed = nil
+ @@current = self # FIX: make thread local
+ end
+
+ def self.current # :nodoc:
+ @@current # FIX: make thread local
+ end
+
+ ##
+ # Return the output IO object
+
+ def io
+ @__io__ = true
+ MiniTest::Unit.output
+ end
+
+ ##
+ # Have we hooked up the IO yet?
+
+ def io?
+ @__io__
+ end
+
+ def self.reset # :nodoc:
+ @@test_suites = {}
+ end
+
+ reset
+
+ ##
+ # Call this at the top of your tests when you absolutely
+ # positively need to have ordered tests. In doing so, you're
+ # admitting that you suck and your tests are weak.
+
+ def self.i_suck_and_my_tests_are_order_dependent!
+ class << self
+ undef_method :test_order if method_defined? :test_order
+ define_method :test_order do :alpha end
+ end
+ end
+
+ ##
+ # Make diffs for this TestCase use #pretty_inspect so that diff
+ # in assert_equal can be more details. NOTE: this is much slower
+ # than the regular inspect but much more usable for complex
+ # objects.
+
+ def self.make_my_diffs_pretty!
+ require 'pp'
+
+ define_method :mu_pp do |o|
+ o.pretty_inspect
+ end
+ end
+
+ ##
+ # Call this at the top of your tests when you want to run your
+ # tests in parallel. In doing so, you're admitting that you rule
+ # and your tests are awesome.
+
+ def self.parallelize_me!
+ require "minitest/parallel_each"
+
+ class << self
+ undef_method :test_order if method_defined? :test_order
+ define_method :test_order do :parallel end
+ end
+ end
+
+ def self.inherited klass # :nodoc:
+ @@test_suites[klass] = true
+ super
+ end
+
+ def self.test_order # :nodoc:
+ :random
+ end
+
+ def self.test_suites # :nodoc:
+ @@test_suites.keys.sort_by { |ts| ts.name.to_s }
+ end
+
+ def self.test_methods # :nodoc:
+ methods = public_instance_methods(true).grep(/^test/).map { |m| m.to_s }
+
+ case self.test_order
+ when :parallel
+ max = methods.size
+ ParallelEach.new methods.sort.sort_by { rand max }
+ when :random then
+ max = methods.size
+ methods.sort.sort_by { rand max }
+ when :alpha, :sorted then
+ methods.sort
+ else
+ raise "Unknown test_order: #{self.test_order.inspect}"
+ end
+ end
+
+ ##
+ # Returns true if the test passed.
+
+ def passed?
+ @passed
+ end
+
+ ##
+ # Runs before every test. Use this to set up before each test
+ # run.
+
+ def setup; end
+
+ ##
+ # Runs after every test. Use this to clean up after each test
+ # run.
+
+ def teardown; end
+
+ include MiniTest::Assertions
+ end # class TestCase
+ end # class Unit
+end # module MiniTest
+
+Minitest = MiniTest # :nodoc: because ugh... I typo this all the time
diff --git a/test/lib/test/unit.rb b/test/lib/test/unit.rb
new file mode 100644
index 0000000000..dbdba6edc3
--- /dev/null
+++ b/test/lib/test/unit.rb
@@ -0,0 +1,880 @@
+begin
+ gem 'minitest', '< 5.0.0' if defined? Gem
+rescue Gem::LoadError
+end
+require 'minitest/unit'
+require 'test/unit/assertions'
+require 'test/unit/testcase'
+require 'optparse'
+
+# See Test::Unit
+module Test
+ ##
+ # Test::Unit is an implementation of the xUnit testing framework for Ruby.
+ #
+ # If you are writing new test code, please use MiniTest instead of Test::Unit.
+ #
+ # Test::Unit has been left in the standard library to support legacy test
+ # suites.
+ module Unit
+ TEST_UNIT_IMPLEMENTATION = 'test/unit compatibility layer using minitest' # :nodoc:
+
+ module RunCount # :nodoc: all
+ @@run_count = 0
+
+ def self.have_run?
+ @@run_count.nonzero?
+ end
+
+ def run(*)
+ @@run_count += 1
+ super
+ end
+
+ def run_once
+ return if have_run?
+ return if $! # don't run if there was an exception
+ yield
+ end
+ module_function :run_once
+ end
+
+ module Options # :nodoc: all
+ def initialize(*, &block)
+ @init_hook = block
+ @options = nil
+ super(&nil)
+ end
+
+ def option_parser
+ @option_parser ||= OptionParser.new
+ end
+
+ def process_args(args = [])
+ return @options if @options
+ orig_args = args.dup
+ options = {}
+ opts = option_parser
+ setup_options(opts, options)
+ opts.parse!(args)
+ orig_args -= args
+ args = @init_hook.call(args, options) if @init_hook
+ non_options(args, options)
+ @help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
+ @options = options
+ if @options[:parallel]
+ @files = args
+ @args = orig_args
+ end
+ options
+ end
+
+ private
+ def setup_options(opts, options)
+ opts.separator 'minitest options:'
+ opts.version = MiniTest::Unit::VERSION
+
+ options[:retry] = true
+ options[:job_status] = nil
+
+ opts.on '-h', '--help', 'Display this help.' do
+ puts opts
+ exit
+ end
+
+ opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
+ options[:seed] = m
+ end
+
+ opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
+ options[:verbose] = true
+ self.verbose = options[:verbose]
+ end
+
+ opts.on '-n', '--name PATTERN', "Filter test names on pattern." do |a|
+ options[:filter] = a
+ end
+
+ opts.on '--jobs-status [TYPE]', [:normal, :replace],
+ "Show status of jobs every file; Disabled when --jobs isn't specified." do |type|
+ options[:job_status] = type || :normal
+ end
+
+ opts.on '-j N', '--jobs N', "Allow run tests with N jobs at once" do |a|
+ if /^t/ =~ a
+ options[:testing] = true # For testing
+ options[:parallel] = a[1..-1].to_i
+ else
+ options[:parallel] = a.to_i
+ end
+ end
+
+ opts.on '--separate', "Restart job process after one testcase has done" do
+ options[:parallel] ||= 1
+ options[:separate] = true
+ end
+
+ opts.on '--retry', "Retry running testcase when --jobs specified" do
+ options[:retry] = true
+ end
+
+ opts.on '--no-retry', "Disable --retry" do
+ options[:retry] = false
+ end
+
+ opts.on '--ruby VAL', "Path to ruby; It'll have used at -j option" do |a|
+ options[:ruby] = a.split(/ /).reject(&:empty?)
+ end
+
+ opts.on '-q', '--hide-skip', 'Hide skipped tests' do
+ options[:hide_skip] = true
+ end
+
+ opts.on '--show-skip', 'Show skipped tests' do
+ options[:hide_skip] = false
+ end
+
+ opts.on '--color[=WHEN]',
+ [:always, :never, :auto],
+ "colorize the output. WHEN defaults to 'always'", "or can be 'never' or 'auto'." do |c|
+ options[:color] = c || :always
+ end
+
+ opts.on '--tty[=WHEN]',
+ [:yes, :no],
+ "force to output tty control. WHEN defaults to 'yes'", "or can be 'no'." do |c|
+ @tty = c != :no
+ end
+ end
+
+ def non_options(files, options)
+ begin
+ require "rbconfig"
+ rescue LoadError
+ warn "#{caller(1)[0]}: warning: Parallel running disabled because can't get path to ruby; run specify with --ruby argument"
+ options[:parallel] = nil
+ else
+ options[:ruby] ||= [RbConfig.ruby]
+ end
+
+ true
+ end
+ end
+
+ module GlobOption # :nodoc: all
+ @@testfile_prefix = "test"
+
+ def setup_options(parser, options)
+ super
+ parser.on '-b', '--basedir=DIR', 'Base directory of test suites.' do |dir|
+ options[:base_directory] = dir
+ end
+ parser.on '-x', '--exclude PATTERN', 'Exclude test files on pattern.' do |pattern|
+ (options[:reject] ||= []) << pattern
+ end
+ end
+
+ def non_options(files, options)
+ paths = [options.delete(:base_directory), nil].uniq
+ if reject = options.delete(:reject)
+ reject_pat = Regexp.union(reject.map {|r| /#{r}/ })
+ end
+ files.map! {|f|
+ f = f.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
+ ((paths if /\A\.\.?(?:\z|\/)/ !~ f) || [nil]).any? do |prefix|
+ if prefix
+ path = f.empty? ? prefix : "#{prefix}/#{f}"
+ else
+ next if f.empty?
+ path = f
+ end
+ if !(match = Dir["#{path}/**/#{@@testfile_prefix}_*.rb"]).empty?
+ if reject
+ match.reject! {|n|
+ n[(prefix.length+1)..-1] if prefix
+ reject_pat =~ n
+ }
+ end
+ break match
+ elsif !reject or reject_pat !~ f and File.exist? path
+ break path
+ end
+ end or
+ raise ArgumentError, "file not found: #{f}"
+ }
+ files.flatten!
+ super(files, options)
+ end
+ end
+
+ module LoadPathOption # :nodoc: all
+ def setup_options(parser, options)
+ super
+ parser.on '-Idirectory', 'Add library load path' do |dirs|
+ dirs.split(':').each { |d| $LOAD_PATH.unshift d }
+ end
+ end
+ end
+
+ module GCStressOption # :nodoc: all
+ def setup_options(parser, options)
+ super
+ parser.on '--[no-]gc-stress', 'Set GC.stress as true' do |flag|
+ options[:gc_stress] = flag
+ end
+ end
+
+ def non_options(files, options)
+ if options.delete(:gc_stress)
+ MiniTest::Unit::TestCase.class_eval do
+ oldrun = instance_method(:run)
+ define_method(:run) do |runner|
+ begin
+ gc_stress, GC.stress = GC.stress, true
+ oldrun.bind(self).call(runner)
+ ensure
+ GC.stress = gc_stress
+ end
+ end
+ end
+ end
+ super
+ end
+ end
+
+ module RequireFiles # :nodoc: all
+ def non_options(files, options)
+ return false if !super
+ result = false
+ files.each {|f|
+ d = File.dirname(path = File.realpath(f))
+ unless $:.include? d
+ $: << d
+ end
+ begin
+ require path unless options[:parallel]
+ result = true
+ rescue LoadError
+ puts "#{f}: #{$!}"
+ end
+ }
+ result
+ end
+ end
+
+ class Runner < MiniTest::Unit # :nodoc: all
+ include Test::Unit::Options
+ include Test::Unit::GlobOption
+ include Test::Unit::LoadPathOption
+ include Test::Unit::GCStressOption
+ include Test::Unit::RunCount
+
+ class Worker
+ def self.launch(ruby,args=[])
+ io = IO.popen([*ruby,
+ "#{File.dirname(__FILE__)}/unit/parallel.rb",
+ *args], "rb+")
+ new(io, io.pid, :waiting)
+ end
+
+ attr_reader :quit_called
+
+ def initialize(io, pid, status)
+ @io = io
+ @pid = pid
+ @status = status
+ @file = nil
+ @real_file = nil
+ @loadpath = []
+ @hooks = {}
+ @quit_called = false
+ end
+
+ def puts(*args)
+ @io.puts(*args)
+ end
+
+ def run(task,type)
+ @file = File.basename(task, ".rb")
+ @real_file = task
+ begin
+ puts "loadpath #{[Marshal.dump($:-@loadpath)].pack("m0")}"
+ @loadpath = $:.dup
+ puts "run #{task} #{type}"
+ @status = :prepare
+ rescue Errno::EPIPE
+ died
+ rescue IOError
+ raise unless ["stream closed","closed stream"].include? $!.message
+ died
+ end
+ end
+
+ def hook(id,&block)
+ @hooks[id] ||= []
+ @hooks[id] << block
+ self
+ end
+
+ def read
+ res = (@status == :quit) ? @io.read : @io.gets
+ res && res.chomp
+ end
+
+ def close
+ @io.close unless @io.closed?
+ self
+ rescue IOError
+ end
+
+ def quit
+ return if @io.closed?
+ @quit_called = true
+ @io.puts "quit"
+ @io.close
+ end
+
+ def kill
+ Process.kill(:KILL, @pid)
+ rescue Errno::ESRCH
+ end
+
+ def died(*additional)
+ @status = :quit
+ @io.close
+
+ call_hook(:dead,*additional)
+ end
+
+ def to_s
+ if @file
+ "#{@pid}=#{@file}"
+ else
+ "#{@pid}:#{@status.to_s.ljust(7)}"
+ end
+ end
+
+ attr_reader :io, :pid
+ attr_accessor :status, :file, :real_file, :loadpath
+
+ private
+
+ def call_hook(id,*additional)
+ @hooks[id] ||= []
+ @hooks[id].each{|hook| hook[self,additional] }
+ self
+ end
+
+ end
+
+ class << self; undef autorun; end
+
+ @@stop_auto_run = false
+ def self.autorun
+ at_exit {
+ Test::Unit::RunCount.run_once {
+ exit(Test::Unit::Runner.new.run(ARGV) || true)
+ } unless @@stop_auto_run
+ } unless @@installed_at_exit
+ @@installed_at_exit = true
+ end
+
+ def after_worker_down(worker, e=nil, c=false)
+ return unless @options[:parallel]
+ return if @interrupt
+ warn e if e
+ @need_quit = true
+ warn ""
+ warn "Some worker was crashed. It seems ruby interpreter's bug"
+ warn "or, a bug of test/unit/parallel.rb. try again without -j"
+ warn "option."
+ warn ""
+ STDERR.flush
+ exit c
+ end
+
+ def terminal_width
+ unless @terminal_width ||= nil
+ begin
+ require 'io/console'
+ width = $stdout.winsize[1]
+ rescue LoadError, NoMethodError, Errno::ENOTTY, Errno::EBADF
+ width = ENV["COLUMNS"].to_i.nonzero? || 80
+ end
+ width -= 1 if /mswin|mingw/ =~ RUBY_PLATFORM
+ @terminal_width = width
+ end
+ @terminal_width
+ end
+
+ def del_status_line
+ @status_line_size ||= 0
+ unless @options[:job_status] == :replace
+ $stdout.puts
+ return
+ end
+ print "\r"+" "*@status_line_size+"\r"
+ $stdout.flush
+ @status_line_size = 0
+ end
+
+ def put_status(line)
+ unless @options[:job_status] == :replace
+ print(line)
+ return
+ end
+ @status_line_size ||= 0
+ del_status_line
+ $stdout.flush
+ line = line[0...terminal_width]
+ print line
+ $stdout.flush
+ @status_line_size = line.size
+ end
+
+ def add_status(line)
+ unless @options[:job_status] == :replace
+ print(line)
+ return
+ end
+ @status_line_size ||= 0
+ line = line[0...(terminal_width-@status_line_size)]
+ print line
+ $stdout.flush
+ @status_line_size += line.size
+ end
+
+ def jobs_status
+ return unless @options[:job_status]
+ puts "" unless @options[:verbose] or @options[:job_status] == :replace
+ status_line = @workers.map(&:to_s).join(" ")
+ update_status(status_line) or (puts; nil)
+ end
+
+ def del_jobs_status
+ return unless @options[:job_status] == :replace && @status_line_size.nonzero?
+ del_status_line
+ end
+
+ def after_worker_quit(worker)
+ return unless @options[:parallel]
+ return if @interrupt
+ @workers.delete(worker)
+ @dead_workers << worker
+ @ios = @workers.map(&:io)
+ end
+
+ def launch_worker
+ begin
+ worker = Worker.launch(@options[:ruby],@args)
+ rescue => e
+ abort "ERROR: Failed to launch job process - #{e.class}: #{e.message}"
+ end
+ worker.hook(:dead) do |w,info|
+ after_worker_quit w
+ after_worker_down w, *info if !info.empty? && !worker.quit_called
+ end
+ @workers << worker
+ @ios << worker.io
+ @workers_hash[worker.io] = worker
+ worker
+ end
+
+ def delete_worker(worker)
+ @workers_hash.delete worker.io
+ @workers.delete worker
+ @ios.delete worker.io
+ end
+
+ def quit_workers
+ return if @workers.empty?
+ @workers.reject! do |worker|
+ begin
+ timeout(1) do
+ worker.quit
+ end
+ rescue Errno::EPIPE
+ rescue Timeout::Error
+ end
+ worker.close
+ end
+
+ return if @workers.empty?
+ begin
+ timeout(0.2 * @workers.size) do
+ Process.waitall
+ end
+ rescue Timeout::Error
+ @workers.each do |worker|
+ worker.kill
+ end
+ @worker.clear
+ end
+ end
+
+ def start_watchdog
+ Thread.new do
+ while stat = Process.wait2
+ break if @interrupt # Break when interrupt
+ pid, stat = stat
+ w = (@workers + @dead_workers).find{|x| pid == x.pid }
+ next unless w
+ w = w.dup
+ if w.status != :quit && !w.quit_called?
+ # Worker down
+ w.died(nil, !stat.signaled? && stat.exitstatus)
+ end
+ end
+ end
+ end
+
+ def deal(io, type, result, rep, shutting_down = false)
+ worker = @workers_hash[io]
+ case worker.read
+ when /^okay$/
+ worker.status = :running
+ jobs_status
+ when /^ready(!)?$/
+ bang = $1
+ worker.status = :ready
+
+ return nil unless task = @tasks.shift
+ if @options[:separate] and not bang
+ worker.quit
+ worker = add_worker
+ end
+ worker.run(task, type)
+ @test_count += 1
+
+ jobs_status
+ when /^done (.+?)$/
+ r = Marshal.load($1.unpack("m")[0])
+ result << r[0..1] unless r[0..1] == [nil,nil]
+ rep << {file: worker.real_file, report: r[2], result: r[3], testcase: r[5]}
+ $:.push(*r[4]).uniq!
+ return true
+ when /^p (.+?)$/
+ del_jobs_status
+ print $1.unpack("m")[0]
+ jobs_status if @options[:job_status] == :replace
+ when /^after (.+?)$/
+ @warnings << Marshal.load($1.unpack("m")[0])
+ when /^bye (.+?)$/
+ after_worker_down worker, Marshal.load($1.unpack("m")[0])
+ when /^bye$/, nil
+ if shutting_down || worker.quit_called
+ after_worker_quit worker
+ else
+ after_worker_down worker
+ end
+ end
+ return false
+ end
+
+ def _run_parallel suites, type, result
+ if @options[:parallel] < 1
+ warn "Error: parameter of -j option should be greater than 0."
+ return
+ end
+
+ # Require needed things for parallel running
+ require 'thread'
+ require 'timeout'
+ @tasks = @files.dup # Array of filenames.
+ @need_quit = false
+ @dead_workers = [] # Array of dead workers.
+ @warnings = []
+ @total_tests = @tasks.size.to_s(10)
+ rep = [] # FIXME: more good naming
+
+ @workers = [] # Array of workers.
+ @workers_hash = {} # out-IO => worker
+ @ios = [] # Array of worker IOs
+ begin
+ # Thread: watchdog
+ watchdog = start_watchdog
+
+ @options[:parallel].times {launch_worker}
+
+ while _io = IO.select(@ios)[0]
+ break if _io.any? do |io|
+ @need_quit or
+ (deal(io, type, result, rep).nil? and
+ !@workers.any? {|x| [:running, :prepare].include? x.status})
+ end
+ end
+ rescue Interrupt => ex
+ @interrupt = ex
+ return result
+ ensure
+ watchdog.kill if watchdog
+ if @interrupt
+ @ios.select!{|x| @workers_hash[x].status == :running }
+ while !@ios.empty? && (__io = IO.select(@ios,[],[],10))
+ __io[0].reject! {|io| deal(io, type, result, rep, true)}
+ end
+ end
+
+ quit_workers
+
+ unless @interrupt || !@options[:retry] || @need_quit
+ @options[:parallel] = false
+ suites, rep = rep.partition {|r| r[:testcase] && r[:file] && r[:report].any? {|e| !e[2].is_a?(MiniTest::Skip)}}
+ suites.map {|r| r[:file]}.uniq.each {|file| require file}
+ suites.map! {|r| eval("::"+r[:testcase])}
+ del_status_line or puts
+ unless suites.empty?
+ puts "Retrying..."
+ _run_suites(suites, type)
+ end
+ end
+ unless @options[:retry]
+ del_status_line or puts
+ end
+ unless rep.empty?
+ rep.each do |r|
+ r[:report].each do |f|
+ puke(*f) if f
+ end
+ end
+ if @options[:retry]
+ @errors += rep.map{|x| x[:result][0] }.inject(:+)
+ @failures += rep.map{|x| x[:result][1] }.inject(:+)
+ @skips += rep.map{|x| x[:result][2] }.inject(:+)
+ end
+ end
+ unless @warnings.empty?
+ warn ""
+ @warnings.uniq! {|w| w[1].message}
+ @warnings.each do |w|
+ warn "#{w[0]}: #{w[1].message} (#{w[1].class})"
+ end
+ warn ""
+ end
+ end
+ end
+
+ def _run_suites suites, type
+ _prepare_run(suites, type)
+ @interrupt = nil
+ result = []
+ GC.start
+ if @options[:parallel]
+ _run_parallel suites, type, result
+ else
+ suites.each {|suite|
+ begin
+ result << _run_suite(suite, type)
+ rescue Interrupt => e
+ @interrupt = e
+ break
+ end
+ }
+ end
+ report.reject!{|r| r.start_with? "Skipped:" } if @options[:hide_skip]
+ report.sort_by!{|r| r.start_with?("Skipped:") ? 0 : \
+ (r.start_with?("Failure:") ? 1 : 2) }
+ result
+ end
+
+ alias mini_run_suite _run_suite
+
+ def output
+ (@output ||= nil) || super
+ end
+
+ def _prepare_run(suites, type)
+ options[:job_status] ||= :replace if @tty && !@verbose
+ case options[:color]
+ when :always
+ color = true
+ when :auto, nil
+ color = @options[:job_status] == :replace && /dumb/ !~ ENV["TERM"]
+ else
+ color = false
+ end
+ if color
+ # dircolors-like style
+ colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:]*)/)] : {}
+ @passed_color = "\e[#{colors["pass"] || "32"}m"
+ @failed_color = "\e[#{colors["fail"] || "31"}m"
+ @skipped_color = "\e[#{colors["skip"] || "33"}m"
+ @reset_color = "\e[m"
+ else
+ @passed_color = @failed_color = @skipped_color = @reset_color = ""
+ end
+ if color or @options[:job_status] == :replace
+ @verbose = !options[:parallel]
+ @output = StatusLineOutput.new(self)
+ end
+ if /\A\/(.*)\/\z/ =~ (filter = options[:filter])
+ filter = Regexp.new($1)
+ end
+ type = "#{type}_methods"
+ total = if filter
+ suites.inject(0) {|n, suite| n + suite.send(type).grep(filter).size}
+ else
+ suites.inject(0) {|n, suite| n + suite.send(type).size}
+ end
+ @test_count = 0
+ @total_tests = total.to_s(10)
+ end
+
+ def new_test(s)
+ @test_count += 1
+ update_status(s)
+ end
+
+ def update_status(s)
+ count = @test_count.to_s(10).rjust(@total_tests.size)
+ put_status("#{@passed_color}[#{count}/#{@total_tests}]#{@reset_color} #{s}")
+ end
+
+ def _print(s); $stdout.print(s); end
+ def succeed; del_status_line; end
+
+ def failed(s)
+ sep = "\n"
+ @report_count ||= 0
+ report.each do |msg|
+ if msg.start_with? "Skipped:"
+ if @options[:hide_skip]
+ del_status_line
+ next
+ end
+ color = @skipped_color
+ else
+ color = @failed_color
+ end
+ msg = msg.split(/$/, 2)
+ $stdout.printf("%s%s%3d) %s%s%s\n",
+ sep, color, @report_count += 1,
+ msg[0], @reset_color, msg[1])
+ sep = nil
+ end
+ report.clear
+ end
+
+ # Overriding of MiniTest::Unit#puke
+ def puke klass, meth, e
+ # TODO:
+ # this overriding is for minitest feature that skip messages are
+ # hidden when not verbose (-v), note this is temporally.
+ n = report.size
+ rep = super
+ if MiniTest::Skip === e and /no message given\z/ =~ e.message
+ report.slice!(n..-1)
+ rep = "."
+ end
+ rep
+ end
+
+ def initialize
+ super
+ @tty = $stdout.tty?
+ end
+
+ def status(*args)
+ result = super
+ raise @interrupt if @interrupt
+ result
+ end
+
+ def run(*args)
+ result = super
+ puts "\nruby -v: #{RUBY_DESCRIPTION}"
+ result
+ end
+ end
+
+ class StatusLineOutput < Struct.new(:runner) # :nodoc: all
+ def puts(*a) $stdout.puts(*a) unless a.empty? end
+ def respond_to_missing?(*a) $stdout.respond_to?(*a) end
+ def method_missing(*a, &b) $stdout.__send__(*a, &b) end
+
+ def print(s)
+ case s
+ when /\A(.*\#.*) = \z/
+ runner.new_test($1)
+ when /\A(.* s) = \z/
+ runner.add_status(" = "+$1.chomp)
+ when /\A\.+\z/
+ runner.succeed
+ when /\A[EFS]\z/
+ runner.failed(s)
+ else
+ $stdout.print(s)
+ end
+ end
+ end
+
+ class AutoRunner # :nodoc: all
+ class Runner < Test::Unit::Runner
+ include Test::Unit::RequireFiles
+ end
+
+ attr_accessor :to_run, :options
+
+ def initialize(force_standalone = false, default_dir = nil, argv = ARGV)
+ @force_standalone = force_standalone
+ @runner = Runner.new do |files, options|
+ options[:base_directory] ||= default_dir
+ files << default_dir if files.empty? and default_dir
+ @to_run = files
+ yield self if block_given?
+ files
+ end
+ Runner.runner = @runner
+ @options = @runner.option_parser
+ if @force_standalone
+ @options.banner.sub!(/\[options\]/, '\& tests...')
+ end
+ @argv = argv
+ end
+
+ def process_args(*args)
+ @runner.process_args(*args)
+ !@to_run.empty?
+ end
+
+ def run
+ if @force_standalone and not process_args(@argv)
+ abort @options.banner
+ end
+ @runner.run(@argv) || true
+ end
+
+ def self.run(*args)
+ new(*args).run
+ end
+ end
+
+ class ProxyError < StandardError # :nodoc: all
+ def initialize(ex)
+ @message = ex.message
+ @backtrace = ex.backtrace
+ end
+
+ attr_accessor :message, :backtrace
+ end
+ end
+end
+
+module MiniTest # :nodoc: all
+ class Unit
+ end
+end
+
+class MiniTest::Unit::TestCase # :nodoc: all
+ undef run_test
+ RUN_TEST_TRACE = "#{__FILE__}:#{__LINE__+3}:in `run_test'".freeze
+ def run_test(name)
+ progname, $0 = $0, "#{$0}: #{self.class}##{name}"
+ self.__send__(name)
+ ensure
+ $@.delete(RUN_TEST_TRACE) if $@
+ $0 = progname
+ end
+end
+
+Test::Unit::Runner.autorun
diff --git a/test/lib/test/unit/assertions.rb b/test/lib/test/unit/assertions.rb
new file mode 100644
index 0000000000..869baf505f
--- /dev/null
+++ b/test/lib/test/unit/assertions.rb
@@ -0,0 +1,461 @@
+require 'minitest/unit'
+require 'pp'
+
+module Test
+ module Unit
+ module Assertions
+ include MiniTest::Assertions
+
+ def mu_pp(obj) #:nodoc:
+ obj.pretty_inspect.chomp
+ end
+
+ MINI_DIR = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), "minitest") #:nodoc:
+
+ # :call-seq:
+ # assert(test, [failure_message])
+ #
+ #Tests if +test+ is true.
+ #
+ #+msg+ may be a String or a Proc. If +msg+ is a String, it will be used
+ #as the failure message. Otherwise, the result of calling +msg+ will be
+ #used as the message if the assertion fails.
+ #
+ #If no +msg+ is given, a default message will be used.
+ #
+ # assert(false, "This was expected to be true")
+ def assert(test, *msgs)
+ case msg = msgs.first
+ when String, Proc
+ when nil
+ msgs.shift
+ else
+ bt = caller.reject { |s| s.start_with?(MINI_DIR) }
+ raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt
+ end unless msgs.empty?
+ super
+ end
+
+ # :call-seq:
+ # assert_block( failure_message = nil )
+ #
+ #Tests the result of the given block. If the block does not return true,
+ #the assertion will fail. The optional +failure_message+ argument is the same as in
+ #Assertions#assert.
+ #
+ # assert_block do
+ # [1, 2, 3].any? { |num| num < 1 }
+ # end
+ def assert_block(*msgs)
+ assert yield, *msgs
+ end
+
+ # :call-seq:
+ # assert_raise( *args, &block )
+ #
+ #Tests if the given block raises an exception. Acceptable exception
+ #types may be given as optional arguments. If the last argument is a
+ #String, it will be used as the error message.
+ #
+ # assert_raise do #Fails, no Exceptions are raised
+ # end
+ #
+ # assert_raise NameError do
+ # puts x #Raises NameError, so assertion succeeds
+ # end
+ def assert_raise(*exp, &b)
+ case exp.last
+ when String, Proc
+ msg = exp.pop
+ end
+
+ begin
+ yield
+ rescue MiniTest::Skip => e
+ return e if exp.include? MiniTest::Skip
+ raise e
+ rescue Exception => e
+ expected = exp.any? { |ex|
+ if ex.instance_of? Module then
+ e.kind_of? ex
+ else
+ e.instance_of? ex
+ end
+ }
+
+ assert expected, proc {
+ exception_details(e, message(msg) {"#{mu_pp(exp)} exception expected, not"}.call)
+ }
+
+ return e
+ end
+
+ exp = exp.first if exp.size == 1
+
+ flunk(message(msg) {"#{mu_pp(exp)} expected but nothing was raised"})
+ end
+
+ # :call-seq:
+ # assert_raise_with_message(exception, expected, msg = nil, &block)
+ #
+ #Tests if the given block raises an exception with the expected
+ #message.
+ #
+ # assert_raise_with_message(RuntimeError, "foo") do
+ # nil #Fails, no Exceptions are raised
+ # end
+ #
+ # assert_raise_with_message(RuntimeError, "foo") do
+ # raise ArgumentError, "foo" #Fails, different Exception is raised
+ # end
+ #
+ # assert_raise_with_message(RuntimeError, "foo") do
+ # raise "bar" #Fails, RuntimeError is raised but the message differs
+ # end
+ #
+ # assert_raise_with_message(RuntimeError, "foo") do
+ # raise "foo" #Raises RuntimeError with the message, so assertion succeeds
+ # end
+ def assert_raise_with_message(exception, expected, msg = nil, &block)
+ case expected
+ when String
+ assert = :assert_equal
+ when Regexp
+ assert = :assert_match
+ else
+ raise TypeError, "Expected #{expected.inspect} to be a kind of String or Regexp, not #{expected.class}"
+ end
+
+ ex = assert_raise(exception, *msg) {yield}
+ msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"}
+
+ if assert == :assert_equal
+ assert_equal(expected, ex.message, msg)
+ else
+ msg = message(msg) { "Expected #{mu_pp expected} to match #{mu_pp ex.message}" }
+ assert expected =~ ex.message, msg
+ block.binding.eval("proc{|_|$~=_}").call($~)
+ end
+ ex
+ end
+
+ # :call-seq:
+ # assert_nothing_raised( *args, &block )
+ #
+ #If any exceptions are given as arguments, the assertion will
+ #fail if one of those exceptions are raised. Otherwise, the test fails
+ #if any exceptions are raised.
+ #
+ #The final argument may be a failure message.
+ #
+ # assert_nothing_raised RuntimeError do
+ # raise Exception #Assertion passes, Exception is not a RuntimeError
+ # end
+ #
+ # assert_nothing_raised do
+ # raise Exception #Assertion fails
+ # end
+ def assert_nothing_raised(*args)
+ self._assertions += 1
+ if Module === args.last
+ msg = nil
+ else
+ msg = args.pop
+ end
+ begin
+ line = __LINE__; yield
+ rescue MiniTest::Skip
+ raise
+ rescue Exception => e
+ bt = e.backtrace
+ as = e.instance_of?(MiniTest::Assertion)
+ if as
+ ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o
+ bt.reject! {|ln| ans =~ ln}
+ end
+ if ((args.empty? && !as) ||
+ args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a })
+ msg = message(msg) { "Exception raised:\n<#{mu_pp(e)}>" }
+ raise MiniTest::Assertion, msg.call, bt
+ else
+ raise
+ end
+ end
+ nil
+ end
+
+ # :call-seq:
+ # assert_nothing_thrown( failure_message = nil, &block )
+ #
+ #Fails if the given block uses a call to Kernel#throw, and
+ #returns the result of the block otherwise.
+ #
+ #An optional failure message may be provided as the final argument.
+ #
+ # assert_nothing_thrown "Something was thrown!" do
+ # throw :problem?
+ # end
+ def assert_nothing_thrown(msg=nil)
+ begin
+ ret = yield
+ rescue ArgumentError => error
+ raise error if /\Auncaught throw (.+)\z/m !~ error.message
+ msg = message(msg) { "<#{$1}> was thrown when nothing was expected" }
+ flunk(msg)
+ end
+ assert(true, "Expected nothing to be thrown")
+ ret
+ end
+
+ # :call-seq:
+ # assert_throw( tag, failure_message = nil, &block )
+ #
+ #Fails unless the given block throws +tag+, returns the caught
+ #value otherwise.
+ #
+ #An optional failure message may be provided as the final argument.
+ #
+ # tag = Object.new
+ # assert_throw(tag, "#{tag} was not thrown!") do
+ # throw tag
+ # end
+ def assert_throw(tag, msg = nil)
+ ret = catch(tag) do
+ begin
+ yield(tag)
+ rescue ArgumentError => e
+ raise unless thrown = e.message[/\Auncaught throw (.+)\z/m, 1]
+ end
+ msg = message(msg) {
+ "Expected #{mu_pp(tag)} to have been thrown"\
+ "#{", not #{thrown}" if thrown}"
+ }
+ assert(false, msg)
+ end
+ assert(true)
+ ret
+ end
+
+ # :call-seq:
+ # assert_equal( expected, actual, failure_message = nil )
+ #
+ #Tests if +expected+ is equal to +actual+.
+ #
+ #An optional failure message may be provided as the final argument.
+ def assert_equal(exp, act, msg = nil)
+ msg = message(msg) {
+ exp_str = mu_pp(exp)
+ act_str = mu_pp(act)
+ exp_comment = ''
+ act_comment = ''
+ if exp_str == act_str
+ if (exp.is_a?(String) && act.is_a?(String)) ||
+ (exp.is_a?(Regexp) && act.is_a?(Regexp))
+ exp_comment = " (#{exp.encoding})"
+ act_comment = " (#{act.encoding})"
+ elsif exp.is_a?(Float) && act.is_a?(Float)
+ exp_str = "%\#.#{Float::DIG+2}g" % exp
+ act_str = "%\#.#{Float::DIG+2}g" % act
+ elsif exp.is_a?(Time) && act.is_a?(Time)
+ if exp.subsec * 1000_000_000 == exp.nsec
+ exp_comment = " (#{exp.nsec}[ns])"
+ else
+ exp_comment = " (subsec=#{exp.subsec})"
+ end
+ if act.subsec * 1000_000_000 == act.nsec
+ act_comment = " (#{act.nsec}[ns])"
+ else
+ act_comment = " (subsec=#{act.subsec})"
+ end
+ elsif exp.class != act.class
+ # a subclass of Range, for example.
+ exp_comment = " (#{exp.class})"
+ act_comment = " (#{act.class})"
+ end
+ elsif !Encoding.compatible?(exp_str, act_str)
+ if exp.is_a?(String) && act.is_a?(String)
+ exp_str = exp.dump
+ act_str = act.dump
+ exp_comment = " (#{exp.encoding})"
+ act_comment = " (#{act.encoding})"
+ else
+ exp_str = exp_str.dump
+ act_str = act_str.dump
+ end
+ end
+ "<#{exp_str}>#{exp_comment} expected but was\n<#{act_str}>#{act_comment}"
+ }
+ assert(exp == act, msg)
+ end
+
+ # :call-seq:
+ # assert_not_nil( expression, failure_message = nil )
+ #
+ #Tests if +expression+ is not nil.
+ #
+ #An optional failure message may be provided as the final argument.
+ def assert_not_nil(exp, msg=nil)
+ msg = message(msg) { "<#{mu_pp(exp)}> expected to not be nil" }
+ assert(!exp.nil?, msg)
+ end
+
+ # :call-seq:
+ # assert_not_equal( expected, actual, failure_message = nil )
+ #
+ #Tests if +expected+ is not equal to +actual+.
+ #
+ #An optional failure message may be provided as the final argument.
+ def assert_not_equal(exp, act, msg=nil)
+ msg = message(msg) { "<#{mu_pp(exp)}> expected to be != to\n<#{mu_pp(act)}>" }
+ assert(exp != act, msg)
+ end
+
+ # :call-seq:
+ # assert_no_match( regexp, string, failure_message = nil )
+ #
+ #Tests if the given Regexp does not match a given String.
+ #
+ #An optional failure message may be provided as the final argument.
+ def assert_no_match(regexp, string, msg=nil)
+ assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
+ self._assertions -= 1
+ msg = message(msg) { "<#{mu_pp(regexp)}> expected to not match\n<#{mu_pp(string)}>" }
+ assert(regexp !~ string, msg)
+ end
+
+ # :call-seq:
+ # assert_not_same( expected, actual, failure_message = nil )
+ #
+ #Tests if +expected+ is not the same object as +actual+.
+ #This test uses Object#equal? to test equality.
+ #
+ #An optional failure message may be provided as the final argument.
+ #
+ # assert_not_same("x", "x") #Succeeds
+ def assert_not_same(expected, actual, message="")
+ msg = message(msg) { build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__) }
+<?>
+with id <?> expected to not be equal\\? to
+<?>
+with id <?>.
+EOT
+ assert(!actual.equal?(expected), msg)
+ end
+
+ # :call-seq:
+ # assert_respond_to( object, method, failure_message = nil )
+ #
+ #Tests if the given Object responds to +method+.
+ #
+ #An optional failure message may be provided as the final argument.
+ #
+ # assert_respond_to("hello", :reverse) #Succeeds
+ # assert_respond_to("hello", :does_not_exist) #Fails
+ def assert_respond_to obj, (meth, priv), msg = nil
+ if priv
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}#{" privately" if priv}"
+ }
+ return assert obj.respond_to?(meth, priv), msg
+ end
+ #get rid of overcounting
+ super if !caller[0].rindex(MINI_DIR, 0) || !obj.respond_to?(meth)
+ end
+
+ # :call-seq:
+ # assert_send( +send_array+, failure_message = nil )
+ #
+ # Passes if the method send returns a true value.
+ #
+ # +send_array+ is composed of:
+ # * A receiver
+ # * A method
+ # * Arguments to the method
+ #
+ # Example:
+ # assert_send(["Hello world", :include?, "Hello"]) # -> pass
+ # assert_send(["Hello world", :include?, "Goodbye"]) # -> fail
+ def assert_send send_ary, m = nil
+ recv, msg, *args = send_ary
+ m = message(m) {
+ if args.empty?
+ argsstr = ""
+ else
+ (argsstr = mu_pp(args)).sub!(/\A\[(.*)\]\z/m, '(\1)')
+ end
+ "Expected #{mu_pp(recv)}.#{msg}#{argsstr} to return true"
+ }
+ assert recv.__send__(msg, *args), m
+ end
+
+ # :call-seq:
+ # assert_not_send( +send_array+, failure_message = nil )
+ #
+ # Passes if the method send doesn't return a true value.
+ #
+ # +send_array+ is composed of:
+ # * A receiver
+ # * A method
+ # * Arguments to the method
+ #
+ # Example:
+ # assert_not_send([[1, 2], :member?, 1]) # -> fail
+ # assert_not_send([[1, 2], :member?, 4]) # -> pass
+ def assert_not_send send_ary, m = nil
+ recv, msg, *args = send_ary
+ m = message(m) {
+ if args.empty?
+ argsstr = ""
+ else
+ (argsstr = mu_pp(args)).sub!(/\A\[(.*)\]\z/m, '(\1)')
+ end
+ "Expected #{mu_pp(recv)}.#{msg}#{argsstr} to return false"
+ }
+ assert !recv.__send__(msg, *args), m
+ end
+
+ ms = instance_methods(true).map {|sym| sym.to_s }
+ ms.grep(/\Arefute_/) do |m|
+ mname = ('assert_not_' << m.to_s[/.*?_(.*)/, 1])
+ alias_method(mname, m) unless ms.include? mname
+ end
+ alias assert_include assert_includes
+ alias assert_not_include assert_not_includes
+
+ def assert_all?(obj, m = nil, &blk)
+ failed = []
+ obj.each do |*a, &b|
+ unless blk.call(*a, &b)
+ failed << (a.size > 1 ? a : a[0])
+ end
+ end
+ assert(failed.empty?, message(m) {failed.pretty_inspect})
+ end
+
+ def assert_not_all?(obj, m = nil, &blk)
+ failed = []
+ obj.each do |*a, &b|
+ if blk.call(*a, &b)
+ failed << a.size > 1 ? a : a[0]
+ end
+ end
+ assert(failed.empty?, message(m) {failed.pretty_inspect})
+ end
+
+ def build_message(head, template=nil, *arguments) #:nodoc:
+ template &&= template.chomp
+ template.gsub(/\G((?:[^\\]|\\.)*?)(\\)?\?/) { $1 + ($2 ? "?" : mu_pp(arguments.shift)) }
+ end
+
+ def message(msg = nil, *args, &default) # :nodoc:
+ if Proc === msg
+ super(nil, *args) do
+ [msg.call, (default.call if default)].compact.reject(&:empty?).join(".\n")
+ end
+ else
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/test/lib/test/unit/parallel.rb b/test/lib/test/unit/parallel.rb
new file mode 100644
index 0000000000..d51fe2f7bb
--- /dev/null
+++ b/test/lib/test/unit/parallel.rb
@@ -0,0 +1,190 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../.."
+require 'test/unit'
+
+module Test
+ module Unit
+ class Worker < Runner # :nodoc:
+ class << self
+ undef autorun
+ end
+
+ alias orig_run_suite mini_run_suite
+ undef _run_suite
+ undef _run_suites
+ undef run
+
+ def increment_io(orig) # :nodoc:
+ *rest, io = 32.times.inject([orig.dup]){|ios, | ios << ios.last.dup }
+ rest.each(&:close)
+ io
+ end
+
+ def _run_suites(suites, type) # :nodoc:
+ suites.map do |suite|
+ _run_suite(suite, type)
+ end
+ end
+
+ def _run_suite(suite, type) # :nodoc:
+ @partial_report = []
+ orig_testout = MiniTest::Unit.output
+ i,o = IO.pipe
+
+ MiniTest::Unit.output = o
+ orig_stdin, orig_stdout = $stdin, $stdout
+
+ th = Thread.new do
+ begin
+ while buf = (self.verbose ? i.gets : i.read(5))
+ _report "p", buf
+ end
+ rescue IOError
+ rescue Errno::EPIPE
+ end
+ end
+
+ e, f, s = @errors, @failures, @skips
+
+ begin
+ result = orig_run_suite(suite, type)
+ rescue Interrupt
+ @need_exit = true
+ result = [nil,nil]
+ end
+
+ MiniTest::Unit.output = orig_testout
+ $stdin = orig_stdin
+ $stdout = orig_stdout
+
+ o.close
+ begin
+ th.join
+ rescue IOError
+ raise unless ["stream closed","closed stream"].include? $!.message
+ end
+ i.close
+
+ result << @partial_report
+ @partial_report = nil
+ result << [@errors-e,@failures-f,@skips-s]
+ result << ($: - @old_loadpath)
+ result << suite.name
+
+ begin
+ _report "done", Marshal.dump(result)
+ rescue Errno::EPIPE; end
+ return result
+ ensure
+ MiniTest::Unit.output = orig_stdout
+ $stdin = orig_stdin
+ $stdout = orig_stdout
+ o.close if o && !o.closed?
+ i.close if i && !i.closed?
+ end
+
+ def run(args = []) # :nodoc:
+ process_args args
+ @@stop_auto_run = true
+ @opts = @options.dup
+ @need_exit = false
+
+ @old_loadpath = []
+ begin
+ begin
+ @stdout = increment_io(STDOUT)
+ @stdin = increment_io(STDIN)
+ rescue
+ exit 2
+ end
+ exit 2 unless @stdout && @stdin
+
+ @stdout.sync = true
+ _report "ready!"
+ while buf = @stdin.gets
+ case buf.chomp
+ when /^loadpath (.+?)$/
+ @old_loadpath = $:.dup
+ $:.push(*Marshal.load($1.unpack("m")[0].force_encoding("ASCII-8BIT"))).uniq!
+ when /^run (.+?) (.+?)$/
+ _report "okay"
+
+ @options = @opts.dup
+ suites = MiniTest::Unit::TestCase.test_suites
+
+ begin
+ require $1
+ rescue LoadError
+ _report "after", Marshal.dump([$1, ProxyError.new($!)])
+ _report "ready"
+ next
+ end
+ _run_suites MiniTest::Unit::TestCase.test_suites-suites, $2.to_sym
+
+ if @need_exit
+ begin
+ _report "bye"
+ rescue Errno::EPIPE; end
+ exit
+ else
+ _report "ready"
+ end
+ when /^quit$/
+ begin
+ _report "bye"
+ rescue Errno::EPIPE; end
+ exit
+ end
+ end
+ rescue Errno::EPIPE
+ rescue Exception => e
+ begin
+ trace = e.backtrace
+ err = ["#{trace.shift}: #{e.message} (#{e.class})"] + trace.map{|t| t.prepend("\t") }
+
+ _report "bye", Marshal.dump(err.join("\n"))
+ rescue Errno::EPIPE;end
+ exit
+ ensure
+ @stdin.close if @stdin
+ @stdout.close if @stdout
+ end
+ end
+
+ def _report(res, *args) # :nodoc:
+ res = "#{res} #{args.pack("m0")}" unless args.empty?
+ @stdout.puts(res)
+ end
+
+ def puke(klass, meth, e) # :nodoc:
+ if e.is_a?(MiniTest::Skip)
+ new_e = MiniTest::Skip.new(e.message)
+ new_e.set_backtrace(e.backtrace)
+ e = new_e
+ end
+ @partial_report << [klass.name, meth, e.is_a?(MiniTest::Assertion) ? e : ProxyError.new(e)]
+ super
+ end
+ end
+ end
+end
+
+if $0 == __FILE__
+ module Test
+ module Unit
+ class TestCase < MiniTest::Unit::TestCase # :nodoc: all
+ undef on_parallel_worker?
+ def on_parallel_worker?
+ true
+ end
+ end
+ end
+ end
+ require 'rubygems'
+ module Gem # :nodoc:
+ end
+ class Gem::TestCase < MiniTest::Unit::TestCase # :nodoc:
+ @@project_dir = File.expand_path('../../../..', __FILE__)
+ end
+
+ Test::Unit::Worker.new.run(ARGV)
+end
diff --git a/test/lib/test/unit/test-unit.gemspec b/test/lib/test/unit/test-unit.gemspec
new file mode 100644
index 0000000000..363fd50c02
--- /dev/null
+++ b/test/lib/test/unit/test-unit.gemspec
@@ -0,0 +1,18 @@
+# -*- ruby -*-
+
+Gem::Specification.new do |s|
+ s.name = "test-unit"
+ s.version = "#{RUBY_VERSION}.0"
+ s.homepage = "http://www.ruby-lang.org"
+ s.author = "Shota Fukumori"
+ s.email = "sorah@tubusu.net"
+ s.summary = "test/unit compatible API testing framework"
+ s.description =
+ "This library implements test/unit compatible API on minitest. " +
+ "The test/unit means that test/unit which was bundled with Ruby 1.8."
+ s.executables = ["testrb"]
+
+ # Ruby bundled test/unit is a compatibility layer for minitest,
+ # and it doesn't have support for minitest 5.
+ s.add_runtime_dependency "minitest", '< 5.0.0'
+end
diff --git a/test/lib/test/unit/testcase.rb b/test/lib/test/unit/testcase.rb
new file mode 100644
index 0000000000..984f08dd32
--- /dev/null
+++ b/test/lib/test/unit/testcase.rb
@@ -0,0 +1,34 @@
+require 'test/unit/assertions'
+
+module Test
+ module Unit
+ # remove silly TestCase class
+ remove_const(:TestCase) if defined?(self::TestCase)
+
+ class TestCase < MiniTest::Unit::TestCase # :nodoc: all
+ include Assertions
+
+ def on_parallel_worker?
+ false
+ end
+
+ def run runner
+ @options = runner.options
+ super runner
+ end
+
+ def self.test_order
+ :sorted
+ end
+
+ def self.method_added(name)
+ return unless name.to_s.start_with?("test_")
+ @test_methods ||= {}
+ if @test_methods[name]
+ warn "test/unit warning: method #{ self }##{ name } is redefined"
+ end
+ @test_methods[name] = true
+ end
+ end
+ end
+end
diff --git a/test/runner.rb b/test/runner.rb
index 94beb448cd..9c17743436 100644
--- a/test/runner.rb
+++ b/test/runner.rb
@@ -1,9 +1,11 @@
require 'rbconfig'
-require 'test/unit'
-
src_testdir = File.dirname(File.realpath(__FILE__))
$LOAD_PATH << src_testdir
+$LOAD_PATH.unshift "#{src_testdir}/lib"
+
+require 'test/unit'
+
module Gem
end
class Gem::TestCase < MiniTest::Unit::TestCase
diff --git a/test/testunit/test4test_hideskip.rb b/test/testunit/test4test_hideskip.rb
index 485b1bc1f2..7dba6f57b4 100644
--- a/test/testunit/test4test_hideskip.rb
+++ b/test/testunit/test4test_hideskip.rb
@@ -1,3 +1,5 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+
require 'test/unit'
class TestForTestHideSkip < Test::Unit::TestCase
diff --git a/test/testunit/test4test_redefinition.rb b/test/testunit/test4test_redefinition.rb
index 72c4cb225a..77cfd45015 100644
--- a/test/testunit/test4test_redefinition.rb
+++ b/test/testunit/test4test_redefinition.rb
@@ -1,3 +1,5 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+
require 'test/unit'
class TestForTestRedefinition < Test::Unit::TestCase
diff --git a/test/testunit/test4test_sorting.rb b/test/testunit/test4test_sorting.rb
index 1f7f4a323e..b8bb9430b5 100644
--- a/test/testunit/test4test_sorting.rb
+++ b/test/testunit/test4test_sorting.rb
@@ -1,3 +1,5 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+
require 'test/unit'
class TestForTestHideSkip < Test::Unit::TestCase
diff --git a/test/testunit/test_parallel.rb b/test/testunit/test_parallel.rb
index 6276aac8bd..a36a2729f3 100644
--- a/test/testunit/test_parallel.rb
+++ b/test/testunit/test_parallel.rb
@@ -2,10 +2,9 @@ require 'test/unit'
require 'timeout'
module TestParallel
- PARALLEL_RB = "#{File.dirname(__FILE__)}/../../lib/test/unit/parallel.rb"
+ PARALLEL_RB = "#{File.dirname(__FILE__)}/../lib/test/unit/parallel.rb"
TESTS = "#{File.dirname(__FILE__)}/tests_for_parallel"
-
class TestParallelWorker < Test::Unit::TestCase
def setup
i, @worker_in = IO.pipe
diff --git a/test/testunit/test_rake_integration.rb b/test/testunit/test_rake_integration.rb
index 86a0cf8829..0770404287 100644
--- a/test/testunit/test_rake_integration.rb
+++ b/test/testunit/test_rake_integration.rb
@@ -18,6 +18,7 @@ class RakeIntegration < MiniTest::Unit::TestCase
filename = File.join dir, 'testing.rb'
File.open(filename, 'wb') do |f|
f.write <<-eotest
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
require 'test/unit'
raise 'loaded twice' if defined?(FooTest)
class FooTest; end
diff --git a/test/testunit/tests_for_parallel/runner.rb b/test/testunit/tests_for_parallel/runner.rb
index 261880ad2b..e262e1e6c7 100644
--- a/test/testunit/tests_for_parallel/runner.rb
+++ b/test/testunit/tests_for_parallel/runner.rb
@@ -1,4 +1,7 @@
require 'rbconfig'
+
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib"
+
require 'test/unit'
src_testdir = File.dirname(File.expand_path(__FILE__))