diff options
Diffstat (limited to 'ruby_1_9_3/lib/minitest')
-rw-r--r-- | ruby_1_9_3/lib/minitest/README.txt | 269 | ||||
-rw-r--r-- | ruby_1_9_3/lib/minitest/autorun.rb | 18 | ||||
-rw-r--r-- | ruby_1_9_3/lib/minitest/benchmark.rb | 372 | ||||
-rw-r--r-- | ruby_1_9_3/lib/minitest/mock.rb | 106 | ||||
-rw-r--r-- | ruby_1_9_3/lib/minitest/pride.rb | 99 | ||||
-rw-r--r-- | ruby_1_9_3/lib/minitest/spec.rb | 519 | ||||
-rw-r--r-- | ruby_1_9_3/lib/minitest/unit.rb | 1169 |
7 files changed, 2552 insertions, 0 deletions
diff --git a/ruby_1_9_3/lib/minitest/README.txt b/ruby_1_9_3/lib/minitest/README.txt new file mode 100644 index 0000000000..7b1e6e681f --- /dev/null +++ b/ruby_1_9_3/lib/minitest/README.txt @@ -0,0 +1,269 @@ += minitest/* + +* http://rubyforge.org/projects/bfts + +== DESCRIPTION: + +minitest provides a complete suite of testing facilities supporting +TDD, BDD, mocking, and benchmarking. + +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 object +framework. + +minitest/pride shows pride in testing and adds coloring to your test +output. + +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. + +== 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 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 + 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 + +=== 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 + n.times do + @obj.my_algorithm + end + 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 + +=== 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 + +== 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 ... + +== 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/ruby_1_9_3/lib/minitest/autorun.rb b/ruby_1_9_3/lib/minitest/autorun.rb new file mode 100644 index 0000000000..443f2f61d4 --- /dev/null +++ b/ruby_1_9_3/lib/minitest/autorun.rb @@ -0,0 +1,18 @@ +###################################################################### +# 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/ruby_1_9_3/lib/minitest/benchmark.rb b/ruby_1_9_3/lib/minitest/benchmark.rb new file mode 100644 index 0000000000..77c0afafb7 --- /dev/null +++ b/ruby_1_9_3/lib/minitest/benchmark.rb @@ -0,0 +1,372 @@ +###################################################################### +# 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 + attr_accessor :runner + + 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 |x| + # @obj.algorithm + # 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 |x| + # @obj.algorithm + # 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 |x| + # @obj.algorithm + # 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 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 |x| + # @obj.algorithm + # 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 + + ## + # 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 + + 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 + # @obj.fast_algorithm + # 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 + # @obj.zoom_algorithm! + # 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 + # @obj.algorithm + # 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/ruby_1_9_3/lib/minitest/mock.rb b/ruby_1_9_3/lib/minitest/mock.rb new file mode 100644 index 0000000000..c342c04995 --- /dev/null +++ b/ruby_1_9_3/lib/minitest/mock.rb @@ -0,0 +1,106 @@ +###################################################################### +# 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 + +## +# A simple and clean mock object framework. + +module MiniTest + + ## + # 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 = {} + @actual_calls = Hash.new {|h,k| h[k] = [] } + end + + ## + # Expect that method +name+ is called, optionally with +args+, 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 + # + # +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=[]) + @expected_calls[name] = { :retval => retval, :args => args } + self + 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_key do |name| + expected = @expected_calls[name] + msg1 = "expected #{name}, #{expected.inspect}" + msg2 = "#{msg1}, got #{@actual_calls[name].inspect}" + + 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 + true + end + + def method_missing(sym, *args) # :nodoc: + expected = @expected_calls[sym] + + unless expected then + raise NoMethodError, "unmocked method %p, expected one of %p" % + [sym, @expected_calls.keys.sort_by(&:to_s)] + end + + expected_args, retval = expected[:args], expected[:retval] + + unless expected_args.size == args.size + raise ArgumentError, "mocked method %p expects %d arguments, got %d" % + [sym, expected[:args].size, args.size] + end + + @actual_calls[sym] << { + :retval => retval, + :args => expected_args.zip(args).map { |mod, a| mod if mod === a } + } + + retval + end + + def respond_to?(sym) # :nodoc: + return true if @expected_calls.has_key?(sym) + return __respond_to?(sym) + end + end +end diff --git a/ruby_1_9_3/lib/minitest/pride.rb b/ruby_1_9_3/lib/minitest/pride.rb new file mode 100644 index 0000000000..ac7745695c --- /dev/null +++ b/ruby_1_9_3/lib/minitest/pride.rb @@ -0,0 +1,99 @@ +###################################################################### +# 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 + ESC = "\e[" + NND = "#{ESC}0m" + + attr_reader :io + + def initialize io + @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 + + 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) + o.map! { |s| + s.sub(/Finished tests/) { + @index = 0 + 'Fabulous tests'.split(//).map { |c| + pride(c) + }.join + } + } + + super + end + + def pride string + string = "*" if string == "." + c = @colors[@index % @size] + @index += 1 + "#{ESC}#{c}m#{string}#{NND}" + end + + def method_missing msg, *args + io.send(msg, *args) + end +end + +class PrideLOL < PrideIO # inspired by lolcat, but massively cleaned up + PI_3 = Math::PI / 3 + + def initialize io + # 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 + + 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/ruby_1_9_3/lib/minitest/spec.rb b/ruby_1_9_3/lib/minitest/spec.rb new file mode 100644 index 0000000000..a70bbdd405 --- /dev/null +++ b/ruby_1_9_3/lib/minitest/spec.rb @@ -0,0 +1,519 @@ +###################################################################### +# 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, &block + return MiniTest::Spec.current.#{meth}(*args, &self) if + Proc === self + return MiniTest::Spec.current.#{meth}(args.first, self) if + args.size == 1 unless #{!!dont_flip} + return MiniTest::Spec.current.#{meth}(self, *args) + 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 && self < MiniTest::Spec 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 + ## + # 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 self.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 self.spec_type desc + TYPES.find { |matcher, klass| + if matcher.respond_to? :call then + matcher.call desc + else + matcher === desc.to_s + end + }.last + end + + @@describe_stack = [] + def self.describe_stack # :nodoc: + @@describe_stack + end + + def self.current # :nodoc: + @@current_spec + end + + ## + # Returns the children of this spec. + + def self.children + @children ||= [] + end + + def initialize name # :nodoc: + super + @@current_spec = self + end + + def self.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 self.before type = :each, &block + raise "unsupported before type: #{type}" unless type == :each + + add_setup_hook {|tc| tc.instance_eval(&block) } + 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 self.after type = :each, &block + raise "unsupported after type: #{type}" unless type == :each + + add_teardown_hook {|tc| tc.instance_eval(&block) } + 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 inheritence, so this goes way out of + # its way to make sure that expectations aren't inherited. + # + # Hint: If you _do_ want inheritence, use minitest/unit. You can mix + # and match between assertions and expectations as much as you want. + + def self.it desc, &block + block ||= proc { skip "(no tests defined)" } + + @specs ||= 0 + @specs += 1 + + name = "test_%04d_%s" % [ @specs, desc.gsub(/\W+/, '_').downcase ] + + define_method name, &block + + self.children.each do |mod| + mod.send :undef_method, name if mod.public_method_defined? name + end + end + + def self.let name, &block + define_method name do + @_memoized ||= {} + @_memoized.fetch(name) { |k| @_memoized[k] = instance_eval(&block) } + end + end + + def self.subject &block + let :subject, &block + end + + def self.create name, desc # :nodoc: + cls = Class.new(self) do + @name = name + @desc = desc + + nuke_test_methods! + end + + children << cls + + cls + end + + def self.to_s # :nodoc: + defined?(@name) ? @name : super + end + + # :stopdoc: + class << self + attr_reader :name, :desc + end + # :startdoc: +end + +module MiniTest::Expectations + ## + # See MiniTest::Assertions#assert_empty. + # + # collection.must_be_empty + # + # :method: must_be_empty + + infect_an_assertion :assert_empty, :must_be_empty + + ## + # 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_within_delta + + infect_an_assertion :assert_in_delta, :must_be_close_to + + alias :must_be_within_delta :must_be_close_to + + ## + # 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 + + ## + # See MiniTest::Assertions#assert_operator + # + # n.must_be :<=, 42 + # + # :method: must_be + + infect_an_assertion :assert_operator, :must_be + + ## + # 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 + + ## + # 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_within_delta + + infect_an_assertion :refute_in_delta, :wont_be_within_delta + + alias :wont_be_close_to :wont_be_within_delta + # FIX: reverse aliases + + ## + # 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 + + ## + # See MiniTest::Assertions#refute_operator + # + # n.wont_be :<=, 42 + # + # :method: wont_be + + infect_an_assertion :refute_operator, :wont_be + + ## + # 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 + include MiniTest::Expectations +end diff --git a/ruby_1_9_3/lib/minitest/unit.rb b/ruby_1_9_3/lib/minitest/unit.rb new file mode 100644 index 0000000000..922ef70183 --- /dev/null +++ b/ruby_1_9_3/lib/minitest/unit.rb @@ -0,0 +1,1169 @@ +###################################################################### +# 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 + + ## + # Assertion base class + + class Assertion < Exception; end + + ## + # Assertion raised when skipping a test + + class Skip < Assertion; end + + file = if RUBY_VERSION =~ /^1\.9/ then # bt's expanded, but __FILE__ isn't :( + File.expand_path __FILE__ + elsif __FILE__ =~ /^[^\.]/ then # assume both relative + require 'pathname' + pwd = Pathname.new Dir.pwd + pn = Pathname.new File.expand_path(__FILE__) + relpath = pn.relative_path_from(pwd) rescue pn + pn = File.join ".", relpath unless pn.relative? + pn.to_s + else # assume both are expanded + __FILE__ + end + + # './lib' in project dir, or '/usr/local/blahblah' if installed + MINI_DIR = File.dirname(File.dirname(file)) # :nodoc: + + def self.filter_backtrace bt # :nodoc: + return ["No backtrace"] unless bt + + new_bt = [] + + unless $DEBUG then + bt.each do |line| + break if line.rindex MINI_DIR, 0 + new_bt << line + end + + new_bt = bt.reject { |line| line.rindex MINI_DIR, 0 } if new_bt.empty? + new_bt = bt.dup if new_bt.empty? + else + new_bt = bt.dup + end + + new_bt + end + + ## + # MiniTest Assertions. All assertion methods accept a +msg+ which is + # printed if the assertion fails. + + module Assertions + + WINDOZE = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + + ## + # Returns the diff command to use in #diff. Tries to intelligently + # figure out what diff to use. + + def self.diff + @diff = if WINDOZE + "diff.exe -u" + else + if system("gdiff", __FILE__, __FILE__) + "gdiff -u" # solaris and kin suck + elsif system("diff", __FILE__, __FILE__) + "diff -u" + else + nil + end + 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.", + "You should look at your implementation of #{klass}#==.", + expect + ].join "\n" + 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 # TODO: possibly rename + mu_pp(obj).gsub(/\\n/, "\n").gsub(/0x[a-f0-9]+/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 the block returns a true value. + + def assert_block msg = nil + msg = message(msg) { "Expected block to return true value" } + assert yield, msg + 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, b].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 instace 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 +exp+ is <tt>=~</tt> +act+. + + def assert_match exp, act, msg = nil + msg = message(msg) { "Expected #{mu_pp(exp)} to match #{mu_pp(act)}" } + assert_respond_to act, :"=~" + exp = Regexp.new Regexp.escape exp if String === exp and String === act + assert exp =~ act, 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 equality operators and so-forth. + # + # assert_operator 5, :<=, 4 + + def assert_operator o1, op, o2, msg = nil + 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. + # + # See also: #assert_silent + + def assert_output stdout = nil, stderr = nil + out, err = capture_io do + yield + end + + x = assert_equal stdout, out, "In stdout" if stdout + y = assert_equal stderr, err, "In stderr" if stderr + + (!stdout || x) && (!stderr || y) + end + + ## + # Fails unless the block raises one of +exp+ + + def assert_raises *exp + msg = "#{exp.pop}\n" if String === exp.last + + should_raise = false + begin + yield + should_raise = true + rescue MiniTest::Skip => e + details = "#{msg}#{mu_pp(exp)} exception expected, not" + + if exp.include? MiniTest::Skip then + return e + else + raise e + end + rescue Exception => e + details = "#{msg}#{mu_pp(exp)} exception expected, not" + assert(exp.any? { |ex| + ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class + }, exception_details(e, details)) + + return e + end + + exp = exp.first if exp.size == 1 + flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised." if + should_raise + 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 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 + # warn "You did a bad thing" + # end + # + # assert_match %r%bad%, err + + def capture_io + require 'stringio' + + orig_stdout, orig_stderr = $stdout, $stderr + captured_stdout, captured_stderr = StringIO.new, StringIO.new + $stdout, $stderr = captured_stdout, captured_stderr + + yield + + return captured_stdout.string, captured_stderr.string + ensure + $stdout = orig_stdout + $stderr = orig_stderr + 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 { + 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 +exp+ <tt>=~</tt> +act+ + + def refute_match exp, act, msg = nil + msg = message(msg) { "Expected #{mu_pp(exp)} to not match #{mu_pp(act)}" } + assert_respond_to act, :"=~" + exp = (/#{Regexp.escape exp}/) if String === exp and String === act + refute exp =~ act, 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, msg = nil + msg = message(msg) { + "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}" + } + refute o1.__send__(op, o2), 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" + raise MiniTest::Skip, msg, bt + end + end + + class Unit + VERSION = "2.5.1" # :nodoc: + + attr_accessor :report, :failures, :errors, :skips # :nodoc: + attr_accessor :test_count, :assertion_count # :nodoc: + attr_accessor :start_time # :nodoc: + attr_accessor :help # :nodoc: + attr_accessor :verbose # :nodoc: + attr_writer :options # :nodoc: + + def options + @options ||= {} + end + + @@installed_at_exit ||= false + @@out = $stdout + + ## + # A simple hook allowing you to run a block of code after the + # tests are done. Eg: + # + # MiniTest::Unit.after_tests { p $debugging_info } + + def self.after_tests + at_exit { at_exit { yield } } + end + + ## + # Registers MiniTest::Unit to run tests at process exit + + def self.autorun + at_exit { + next if $! # don't run if there was an exception + + # 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 { 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 + + ## + # Returns the stream to use for output. + # + # DEPRECATED: use ::output instead. + + def self.out + warn "::out deprecated, use ::output instead." if $VERBOSE + output + 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 + + def output + self.class.output + end + + def puts *a # :nodoc: + output.puts(*a) + end + + def print *a # :nodoc: + output.print(*a) + end + + 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 + + def _run_suites suites, type + suites.map { |suite| _run_suite suite, type } + end + + 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 =~ /\/(.*)\// + + assertions = suite.send("#{type}_methods").grep(filter).map { |method| + inst = suite.new method + inst._assertions = 0 + + print "#{suite}##{method} = " if @verbose + + @start_time = Time.now + result = inst.run self + time = Time.now - @start_time + + print "%.2f s = " % time if @verbose + print result + puts if @verbose + + inst._assertions + } + + return assertions.size, assertions.inject(0) { |sum, n| sum + n } + 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#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n" + when MiniTest::Assertion then + @failures += 1 + "Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n" + else + @errors += 1 + bt = MiniTest::filter_backtrace(e.backtrace).join "\n " + "Error:\n#{meth}(#{klass}):\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 + end + + def process_args args = [] + 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." 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 = [] + self.options = process_args args + + puts "Run options: #{help}" + + self.class.plugins.each do |plugin| + send plugin + break unless report.empty? + end + + return failures + errors if @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 + + ## + # Subclass TestCase to create your own tests. Typically you'll want a + # TestCase subclass per implementation class. + # + # See MiniTest::Assertions + + class TestCase + attr_reader :__name__ # :nodoc: + + PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, + Interrupt, SystemExit] # :nodoc: + + SUPPORTS_INFO_SIGNAL = Signal.list['INFO'] # :nodoc: + + ## + # Runs the tests reporting the status to +runner+ + + def run runner + trap "INFO" do + time = runner.start_time ? Time.now - runner.start_time : 0 + warn "%s#%s %.2fs" % [self.class, self.__name__, time] + runner.status $stderr + end if SUPPORTS_INFO_SIGNAL + + result = "" + begin + @passed = nil + self.setup + self.run_setup_hooks + self.__send__ self.__name__ + result = "." unless io? + @passed = true + rescue *PASSTHROUGH_EXCEPTIONS + raise + rescue Exception => e + @passed = false + result = runner.puke self.class, self.__name__, e + ensure + begin + self.run_teardown_hooks + self.teardown + rescue *PASSTHROUGH_EXCEPTIONS + raise + rescue Exception => e + result = runner.puke self.class, self.__name__, e + end + trap 'INFO', 'DEFAULT' if SUPPORTS_INFO_SIGNAL + end + result + end + + def initialize name # :nodoc: + @__name__ = name + @__io__ = nil + @passed = nil + end + + def io + @__io__ = true + MiniTest::Unit.output + end + + 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 + define_method :test_order do :alpha end + end + end + + def self.inherited klass # :nodoc: + @@test_suites[klass] = true + klass.reset_setup_teardown_hooks + 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 :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 refactor test initialization. + + def setup; end + + ## + # Runs after every test. Use this to refactor test cleanup. + + def teardown; end + + def self.reset_setup_teardown_hooks # :nodoc: + @setup_hooks = [] + @teardown_hooks = [] + end + + reset_setup_teardown_hooks + + ## + # Adds a block of code that will be executed before every TestCase is + # run. Equivalent to +setup+, but usable multiple times and without + # re-opening any classes. + # + # All of the setup hooks will run in order after the +setup+ method, if + # one is defined. + # + # The argument can be any object that responds to #call or a block. + # That means that this call, + # + # MiniTest::TestCase.add_setup_hook { puts "foo" } + # + # ... is equivalent to: + # + # module MyTestSetup + # def call + # puts "foo" + # end + # end + # + # MiniTest::TestCase.add_setup_hook MyTestSetup + # + # The blocks passed to +add_setup_hook+ take an optional parameter that + # will be the TestCase instance that is executing the block. + + def self.add_setup_hook arg=nil, &block + hook = arg || block + @setup_hooks << hook + end + + def self.setup_hooks # :nodoc: + if superclass.respond_to? :setup_hooks then + superclass.setup_hooks + else + [] + end + @setup_hooks + end + + def run_setup_hooks # :nodoc: + self.class.setup_hooks.each do |hook| + if hook.respond_to?(:arity) && hook.arity == 1 + hook.call(self) + else + hook.call + end + end + end + + ## + # Adds a block of code that will be executed after every TestCase is + # run. Equivalent to +teardown+, but usable multiple times and without + # re-opening any classes. + # + # All of the teardown hooks will run in reverse order after the + # +teardown+ method, if one is defined. + # + # The argument can be any object that responds to #call or a block. + # That means that this call, + # + # MiniTest::TestCase.add_teardown_hook { puts "foo" } + # + # ... is equivalent to: + # + # module MyTestTeardown + # def call + # puts "foo" + # end + # end + # + # MiniTest::TestCase.add_teardown_hook MyTestTeardown + # + # The blocks passed to +add_teardown_hook+ take an optional parameter + # that will be the TestCase instance that is executing the block. + + def self.add_teardown_hook arg=nil, &block + hook = arg || block + @teardown_hooks << hook + end + + def self.teardown_hooks # :nodoc: + if superclass.respond_to? :teardown_hooks then + superclass.teardown_hooks + else + [] + end + @teardown_hooks + end + + def run_teardown_hooks # :nodoc: + self.class.teardown_hooks.reverse.each do |hook| + if hook.respond_to?(:arity) && hook.arity == 1 + hook.call(self) + else + hook.call + end + end + end + + include MiniTest::Assertions + end # class TestCase + end # class Unit +end # module MiniTest + +if $DEBUG then + module Test # :nodoc: + module Unit # :nodoc: + class TestCase # :nodoc: + def self.inherited x # :nodoc: + # this helps me ferret out porting issues + raise "Using minitest and test/unit in the same process: #{x}" + end + end + end + end +end |