[前][次][番号順一覧][スレッド一覧]

ruby-changes:20988

From: ryan <ko1@a...>
Date: Wed, 24 Aug 2011 06:56:18 +0900 (JST)
Subject: [ruby-changes:20988] ryan:r33037 (ruby_1_9_3): backport of 33036

ryan	2011-08-24 06:55:38 +0900 (Wed, 24 Aug 2011)

  New Revision: 33037

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=33037

  Log:
    backport of 33036

  Added files:
    branches/ruby_1_9_3/lib/minitest/README.txt
  Modified files:
    branches/ruby_1_9_3/ChangeLog
    branches/ruby_1_9_3/lib/minitest/mock.rb
    branches/ruby_1_9_3/lib/minitest/pride.rb
    branches/ruby_1_9_3/lib/minitest/spec.rb
    branches/ruby_1_9_3/lib/minitest/unit.rb
    branches/ruby_1_9_3/test/minitest/test_minitest_mock.rb
    branches/ruby_1_9_3/test/minitest/test_minitest_spec.rb
    branches/ruby_1_9_3/test/minitest/test_minitest_unit.rb

Index: ruby_1_9_3/ChangeLog
===================================================================
--- ruby_1_9_3/ChangeLog	(revision 33036)
+++ ruby_1_9_3/ChangeLog	(revision 33037)
@@ -1,3 +1,10 @@
+Wed Aug 24 06:45:20 2011  Ryan Davis  <ryand-ruby@z...>
+
+	* backport r33036 from trunk.
+
+	* lib/minitest/*: Imported minitest 2.5.0 (r6557)
+	* test/minitest/*: ditto
+
 Tue Aug 23 15:23:56 2011  Eric Hodel  <drbrain@s...>
 
 	* backport r33027 from trunk.
Index: ruby_1_9_3/lib/minitest/unit.rb
===================================================================
--- ruby_1_9_3/lib/minitest/unit.rb	(revision 33036)
+++ ruby_1_9_3/lib/minitest/unit.rb	(revision 33037)
@@ -152,7 +152,7 @@
 
     def mu_pp obj
       s = obj.inspect
-      s = s.force_encoding Encoding.default_external if defined? Encoding
+      s = s.encode Encoding.default_external if defined? Encoding
       s
     end
 
@@ -491,7 +491,7 @@
     # Fails if +obj+ is empty.
 
     def refute_empty obj, msg = nil
-      msg = message(msg) { "Expected #{obj.inspect} to not be empty" }
+      msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" }
       assert_respond_to obj, :empty?
       refute obj.empty?, msg
     end
@@ -577,7 +577,7 @@
     end
 
     ##
-    # Fails if +o1+ is not +op+ +o2+ nil. eg:
+    # Fails if +o1+ is not +op+ +o2+. Eg:
     #
     #   refute_operator 1, :>, 2 #=> pass
     #   refute_operator 1, :<, 2 #=> fail
@@ -620,7 +620,7 @@
   end
 
   class Unit
-    VERSION = "2.2.2" # :nodoc:
+    VERSION = "2.5.0" # :nodoc:
 
     attr_accessor :report, :failures, :errors, :skips # :nodoc:
     attr_accessor :test_count, :assertion_count       # :nodoc:
@@ -945,6 +945,7 @@
         begin
           @passed = nil
           self.setup
+          self.run_setup_hooks
           self.__send__ self.__name__
           result = "." unless io?
           @passed = true
@@ -955,6 +956,7 @@
           result = runner.puke self.class, self.__name__, e
         ensure
           begin
+            self.run_teardown_hooks
             self.teardown
           rescue *PASSTHROUGH_EXCEPTIONS
             raise
@@ -987,16 +989,24 @@
 
       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
 
-      ##
-      # Defines test order and is subclassable. Defaults to :random
-      # but can be overridden to return :alpha if your tests are order
-      # dependent (read: weak).
-
-      def self.test_order
+      def self.test_order # :nodoc:
         :random
       end
 
@@ -1035,6 +1045,111 @@
 
       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
Index: ruby_1_9_3/lib/minitest/pride.rb
===================================================================
--- ruby_1_9_3/lib/minitest/pride.rb	(revision 33036)
+++ ruby_1_9_3/lib/minitest/pride.rb	(revision 33037)
@@ -10,32 +10,90 @@
 # Show your testing pride!
 
 class PrideIO
+  ESC = "\e["
+  NND = "#{ESC}0m"
+
   attr_reader :io
 
-  # stolen from /System/Library/Perl/5.10.0/Term/ANSIColor.pm
-  COLORS = (31..36).to_a
-  CHARS = ["*"]
-
   def initialize io
     @io = io
-    @colors = COLORS.cycle
-    @chars  = CHARS.cycle
+    # 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 "\e[#{@colors.next}m#{@chars.next}\e[0m"
+      io.print pride o
     when "E", "F" then
-      io.print "\e[41m\e[37m#{o}\e[0m"
+      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
 
-MiniTest::Unit.output = PrideIO.new(MiniTest::Unit.output)
+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)
Index: ruby_1_9_3/lib/minitest/README.txt
===================================================================
--- ruby_1_9_3/lib/minitest/README.txt	(revision 0)
+++ ruby_1_9_3/lib/minitest/README.txt	(revision 33037)
@@ -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.
Index: ruby_1_9_3/lib/minitest/mock.rb
===================================================================
--- ruby_1_9_3/lib/minitest/mock.rb	(revision 33036)
+++ ruby_1_9_3/lib/minitest/mock.rb	(revision 33037)
@@ -15,20 +15,39 @@
   # 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+.
+    # 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 }
@@ -43,25 +62,45 @@
     def verify
       @expected_calls.each_key do |name|
         expected = @expected_calls[name]
-        msg = "expected #{name}, #{expected.inspect}"
-        raise MockExpectationError, msg unless
+        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:
-      raise NoMethodError unless @expected_calls.has_key?(sym)
-      raise ArgumentError unless @expected_calls[sym][:args].size == args.size
-      retval = @expected_calls[sym][:retval]
-      @actual_calls[sym] << { :retval => retval, :args => args }
+      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
 
-    alias :original_respond_to? :respond_to?
     def respond_to?(sym) # :nodoc:
       return true if @expected_calls.has_key?(sym)
-      return original_respond_to?(sym)
+      return __respond_to?(sym)
     end
   end
 end
Index: ruby_1_9_3/lib/minitest/spec.rb
===================================================================
--- ruby_1_9_3/lib/minitest/spec.rb	(revision 33036)
+++ ruby_1_9_3/lib/minitest/spec.rb	(revision 33037)
@@ -8,7 +8,7 @@
 
 require 'minitest/unit'
  (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]