From 81eb635f8cf9e2c25703cb9735dce012daa70ccc Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 29 Nov 2012 03:23:11 +0000 Subject: Imported minitest 4.3.2 (r8026) git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37967 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 5 + lib/minitest/README.txt | 120 +++++++++---- lib/minitest/hell.rb | 16 ++ lib/minitest/mock.rb | 43 ++++- lib/minitest/parallel_each.rb | 36 ++++ lib/minitest/pride.rb | 2 +- lib/minitest/spec.rb | 2 +- lib/minitest/unit.rb | 277 +++++++++++++++++++++-------- test/minitest/metametameta.rb | 22 ++- test/minitest/test_minitest_benchmark.rb | 1 - test/minitest/test_minitest_mock.rb | 116 ++++++++++-- test/minitest/test_minitest_spec.rb | 68 ++++--- test/minitest/test_minitest_unit.rb | 292 +++++++++++++++++++++++-------- 13 files changed, 760 insertions(+), 240 deletions(-) create mode 100644 lib/minitest/hell.rb create mode 100644 lib/minitest/parallel_each.rb diff --git a/ChangeLog b/ChangeLog index 4b6c949274..f05da7cec5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Thu Nov 29 12:13:54 2012 Ryan Davis + + * lib/minitest/*: Imported minitest 4.3.2 (r8026) + * test/minitest/*: ditto + Thu Nov 29 11:06:06 2012 KOSAKI Motohiro * thread.c (thread_start_func_2): remove unused code. When diff --git a/lib/minitest/README.txt b/lib/minitest/README.txt index d0ebaa324d..6430c1b442 100644 --- a/lib/minitest/README.txt +++ b/lib/minitest/README.txt @@ -49,6 +49,17 @@ discovery. -- 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. @@ -93,6 +104,10 @@ Given that you'd like to test the following class: 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 @@ -260,43 +275,74 @@ fixture loading: == Known Extensions: -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-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-growl :: Test notifier for minitest via growl. -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-predicates :: Adds support for .predicate? methods -minitest-pry :: A minitest plugin to drop into pry on assertion failure. -minitest-rails :: MiniTest integration for Rails 3.1. -minitest-reporters :: Create customizable MiniTest output formats -minitest-rg :: redgreen minitest -minitest-spec-magic :: Minitest::Spec extensions for Rails and beyond -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. +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-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-growl :: Test notifier for minitest via growl. +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-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-rg :: redgreen minitest +minitest-shouldify :: Adding all manner of shoulds to MiniTest (bad idea) +minitest-spec-magic :: Minitest::Spec extensions for Rails and beyond +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 +* capybara_minitest_spec +* detroit-minitest +* em-minitest-spec +* flexmock-minitest +* guard-minitest +* guard-minitest-decisiv +* minitest-activemodel +* minitest-ar-assertions +* minitest-around +* minitest-capybara-unit +* minitest-colorer +* minitest-deluxe +* minitest-extra-assertions +* minitest-nc +* minitest-rails-shoulda +* minitest-spec +* minitest-spec-context +* minitest-spec-rails +* minitest-spec-should +* minitest-sugar +* minitest_should +* mongoid-minitest +* spork-minitest == REQUIREMENTS: diff --git a/lib/minitest/hell.rb b/lib/minitest/hell.rb new file mode 100644 index 0000000000..b1c602c875 --- /dev/null +++ b/lib/minitest/hell.rb @@ -0,0 +1,16 @@ +# 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 Minitest::Unit::TestCase + class << self + alias :old_test_order :test_order + + def test_order # :nodoc: + :parallel + end + end +end diff --git a/lib/minitest/mock.rb b/lib/minitest/mock.rb index b741b8e742..4c1b604425 100644 --- a/lib/minitest/mock.rb +++ b/lib/minitest/mock.rb @@ -31,8 +31,8 @@ module MiniTest end ## - # Expect that method +name+ is called, optionally with +args+, and returns - # +retval+. + # 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 @@ -40,6 +40,10 @@ module MiniTest # @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. # @@ -51,9 +55,14 @@ module MiniTest # @mock.uses_one_string("bar") # => true # @mock.verify # => raises MockExpectationError - def expect(name, retval, args=[]) - raise ArgumentError, "args must be an array" unless Array === args - @expected_calls[name] << { :retval => retval, :args => args } + 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 @@ -103,7 +112,17 @@ module MiniTest [sym, args] end - expected_args, retval = expected_call[:args], expected_call[:retval] + 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" % @@ -127,9 +146,9 @@ module MiniTest retval end - def respond_to?(sym) # :nodoc: + def respond_to?(sym, include_private = false) # :nodoc: return true if @expected_calls.has_key?(sym.to_sym) - return __respond_to?(sym) + return __respond_to?(sym, include_private) end end end @@ -155,7 +174,15 @@ class Object # :nodoc: 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) diff --git a/lib/minitest/parallel_each.rb b/lib/minitest/parallel_each.rb new file mode 100644 index 0000000000..d501aa34ef --- /dev/null +++ b/lib/minitest/parallel_each.rb @@ -0,0 +1,36 @@ +# 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 ParallelEach + require 'thread' + include Enumerable + + N = (ENV['N'] || 2).to_i + + 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 + self.class.new super + 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 +end diff --git a/lib/minitest/pride.rb b/lib/minitest/pride.rb index 9de4e37a98..40c35394fa 100644 --- a/lib/minitest/pride.rb +++ b/lib/minitest/pride.rb @@ -47,7 +47,7 @@ class PrideIO def puts(*o) # :nodoc: o.map! { |s| - s.sub(/Finished tests/) { + s.to_s.sub(/Finished tests/) { @index = 0 'Fabulous tests'.split(//).map { |c| pride(c) diff --git a/lib/minitest/spec.rb b/lib/minitest/spec.rb index 1d85cd9f32..7588982038 100644 --- a/lib/minitest/spec.rb +++ b/lib/minitest/spec.rb @@ -537,5 +537,5 @@ module MiniTest::Expectations end class Object # :nodoc: - include MiniTest::Expectations + include MiniTest::Expectations unless ENV["MT_NO_EXPECTATIONS"] end diff --git a/lib/minitest/unit.rb b/lib/minitest/unit.rb index ab16d1891f..a221dda4b4 100644 --- a/lib/minitest/unit.rb +++ b/lib/minitest/unit.rb @@ -7,6 +7,8 @@ require 'optparse' require 'rbconfig' +require 'thread' # required for 1.8 +require 'minitest/parallel_each' ## # Minimal (mostly drop-in) replacement for test-unit. @@ -37,24 +39,36 @@ module MiniTest class Skip < Assertion; end - def self.filter_backtrace bt # :nodoc: - return ["No backtrace"] unless bt + class << self + attr_accessor :backtrace_filter + end - new_bt = [] + class BacktraceFilter # :nodoc: + def filter bt + return ["No backtrace"] unless bt - unless $DEBUG then - bt.each do |line| - break if line =~ /lib\/minitest/ - new_bt << line + 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 = bt.reject { |line| line =~ /lib\/minitest/ } if new_bt.empty? - new_bt = bt.dup if new_bt.empty? - else - new_bt = bt.dup + new_bt end + end - new_bt + self.backtrace_filter = BacktraceFilter.new + + def self.filter_backtrace bt # :nodoc: + backtrace_filter.filter bt end ## @@ -68,14 +82,12 @@ module MiniTest "UNDEFINED" # again with the rdoc bugs... :( end - WINDOZE = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ # :nodoc: - ## # Returns the diff command to use in #diff. Tries to intelligently # figure out what diff to use. def self.diff - @diff = if WINDOZE + @diff = if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ then "diff.exe -u" else if system("gdiff", __FILE__, __FILE__) @@ -136,10 +148,11 @@ module MiniTest if result.empty? then klass = exp.class result = [ - "No visible difference.", - "You should look at your implementation of #{klass}#==.", - expect - ].join "\n" + "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 @@ -314,6 +327,8 @@ module MiniTest # "" 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 @@ -351,30 +366,30 @@ module MiniTest 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 + return e if exp.include? MiniTest::Skip + raise e 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)) + 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." if - should_raise + + flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised." end ## @@ -431,6 +446,8 @@ module MiniTest 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 @@ -446,24 +463,76 @@ module MiniTest # 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' - orig_stdout, orig_stderr = $stdout, $stderr captured_stdout, captured_stderr = StringIO.new, StringIO.new - $stdout, $stderr = captured_stdout, captured_stderr - yield + 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 - ensure - $stdout = orig_stdout - $stderr = orig_stderr + 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 ## @@ -656,10 +725,19 @@ module MiniTest msg ||= "Skipped, no message given" raise MiniTest::Skip, msg, bt 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 = "3.4.0" # :nodoc: + VERSION = "4.3.2" # :nodoc: attr_accessor :report, :failures, :errors, :skips # :nodoc: attr_accessor :test_count, :assertion_count # :nodoc: @@ -667,7 +745,6 @@ module MiniTest attr_accessor :help # :nodoc: attr_accessor :verbose # :nodoc: attr_writer :options # :nodoc: - attr_accessor :last_error # :nodoc: ## # Lazy accessor for options. @@ -695,7 +772,8 @@ module MiniTest def self.autorun at_exit { - next if $! # don't run if there was an exception + # 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 @@ -810,10 +888,15 @@ module MiniTest end ## - # Runs all the +suites+ for a given +type+. + # 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 - suites.map { |suite| _run_suite suite, 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 ## @@ -832,14 +915,10 @@ module MiniTest print "#{suite}##{method} = " if @verbose - @start_time = Time.now - self.last_error = nil + start_time = Time.now if @verbose result = inst.run self - time = Time.now - @start_time - record suite, method, inst._assertions, time, last_error - - print "%.2f s = " % time if @verbose + print "%.2f s = " % (Time.now - start_time) if @verbose print result puts if @verbose @@ -878,7 +957,6 @@ module MiniTest # exception +e+ def puke klass, meth, e - self.last_error = e e = case e when MiniTest::Skip then @skips += 1 @@ -900,7 +978,11 @@ module MiniTest @report = [] @errors = @failures = @skips = 0 @verbose = false - self.last_error = nil + @mutex = Mutex.new + end + + def synchronize # :nodoc: + @mutex.synchronize { yield } end def process_args args = [] # :nodoc: @@ -1103,10 +1185,33 @@ module MiniTest module Deprecated # :nodoc: - ## - # This entire module is deprecated and slated for removal on 2013-01-01. + ## + # This entire module is deprecated and slated for removal on 2013-01-01. module Hooks + def run_setup_hooks # :nodoc: + _run_hooks self.class.setup_hooks + end + + def _run_hooks hooks # :nodoc: + hooks.each do |hook| + if hook.respond_to?(:arity) && hook.arity == 1 + hook.call(self) + else + hook.call + end + end + end + + def run_teardown_hooks # :nodoc: + _run_hooks self.class.teardown_hooks.reverse + end + end + + ## + # This entire module is deprecated and slated for removal on 2013-01-01. + + module HooksCM ## # Adds a block of code that will be executed before every # TestCase is run. @@ -1114,13 +1219,13 @@ module MiniTest # NOTE: This method is deprecated, use before/after_setup. It # will be removed on 2013-01-01. - def self.add_setup_hook arg=nil, &block + def add_setup_hook arg=nil, &block warn "NOTE: MiniTest::Unit::TestCase.add_setup_hook is deprecated, use before/after_setup via a module (and call super!). It will be removed on 2013-01-01. Called from #{caller.first}" hook = arg || block @setup_hooks << hook end - def self.setup_hooks # :nodoc: + def setup_hooks # :nodoc: if superclass.respond_to? :setup_hooks then superclass.setup_hooks else @@ -1128,20 +1233,6 @@ module MiniTest end + @setup_hooks end - def run_setup_hooks # :nodoc: - _run_hooks self.class.setup_hooks - end - - def _run_hooks hooks # :nodoc: - 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. @@ -1149,23 +1240,19 @@ module MiniTest # NOTE: This method is deprecated, use before/after_teardown. It # will be removed on 2013-01-01. - def self.add_teardown_hook arg=nil, &block + def add_teardown_hook arg=nil, &block warn "NOTE: MiniTest::Unit::TestCase#add_teardown_hook is deprecated, use before/after_teardown. It will be removed on 2013-01-01. Called from #{caller.first}" hook = arg || block @teardown_hooks << hook end - def self.teardown_hooks # :nodoc: + def teardown_hooks # :nodoc: if superclass.respond_to? :teardown_hooks then superclass.teardown_hooks else [] end + @teardown_hooks end - - def run_teardown_hooks # :nodoc: - _run_hooks self.class.teardown_hooks.reverse - end end end @@ -1178,6 +1265,7 @@ module MiniTest class TestCase include LifecycleHooks include Deprecated::Hooks + extend Deprecated::HooksCM # UGH... I can't wait 'til 2013! include Guard extend Guard @@ -1202,6 +1290,8 @@ module MiniTest runner.status $stderr end if SUPPORTS_INFO_SIGNAL + start_time = Time.now + result = "" begin @passed = nil @@ -1210,11 +1300,15 @@ module MiniTest 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 = false + 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| @@ -1278,6 +1372,32 @@ module MiniTest 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! + 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 klass.reset_setup_teardown_hooks @@ -1296,6 +1416,9 @@ module MiniTest 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 } diff --git a/test/minitest/metametameta.rb b/test/minitest/metametameta.rb index a4f7dfa1e6..65eece07ca 100644 --- a/test/minitest/metametameta.rb +++ b/test/minitest/metametameta.rb @@ -24,7 +24,9 @@ class MetaMetaMetaTestCase < MiniTest::Unit::TestCase EOM - @tu.run flags + with_output do + @tu.run flags + end output = @output.string.dup output.sub!(/Finished tests in .*/, "Finished tests in 0.00") @@ -49,14 +51,24 @@ class MetaMetaMetaTestCase < MiniTest::Unit::TestCase srand 42 MiniTest::Unit::TestCase.reset @tu = MiniTest::Unit.new - @output = StringIO.new("") + MiniTest::Unit.runner = nil # protect the outer runner from the inner tests - MiniTest::Unit.output = @output end def teardown super - MiniTest::Unit.output = $stdout - Object.send :remove_const, :ATestCase if defined? ATestCase + end + + def with_output + synchronize do + begin + @output = StringIO.new("") + MiniTest::Unit.output = @output + + yield + ensure + MiniTest::Unit.output = STDOUT + end + end end end diff --git a/test/minitest/test_minitest_benchmark.rb b/test/minitest/test_minitest_benchmark.rb index fc1301e63b..708589b3fa 100644 --- a/test/minitest/test_minitest_benchmark.rb +++ b/test/minitest/test_minitest_benchmark.rb @@ -117,4 +117,3 @@ class TestMiniTestBenchmark < MiniTest::Unit::TestCase assert_in_delta exp_b, b end end - diff --git a/test/minitest/test_minitest_mock.rb b/test/minitest/test_minitest_mock.rb index cd235d60da..cfd97e4002 100644 --- a/test/minitest/test_minitest_mock.rb +++ b/test/minitest/test_minitest_mock.rb @@ -11,6 +11,8 @@ require 'minitest/unit' MiniTest::Unit.autorun class TestMiniTestMock < MiniTest::Unit::TestCase + parallelize_me! if ENV["PARALLEL"] + def setup @mock = MiniTest::Mock.new.expect(:foo, nil) @mock.expect(:meaning_of_life, 42) @@ -103,6 +105,7 @@ class TestMiniTestMock < MiniTest::Unit::TestCase def test_respond_appropriately assert @mock.respond_to?(:foo) + assert @mock.respond_to?(:foo, true) assert @mock.respond_to?('foo') assert !@mock.respond_to?(:bar) end @@ -202,6 +205,68 @@ class TestMiniTestMock < MiniTest::Unit::TestCase assert_equal exp, e.message end + def test_verify_passes_when_mock_block_returns_true + mock = MiniTest::Mock.new + mock.expect :foo, nil do + true + end + + mock.foo + + assert mock.verify + end + + def test_mock_block_is_passed_function_params + arg1, arg2, arg3 = :bar, [1,2,3], {:a => 'a'} + mock = MiniTest::Mock.new + mock.expect :foo, nil do |a1, a2, a3| + a1 == arg1 && + a2 == arg2 && + a3 == arg3 + end + + mock.foo arg1, arg2, arg3 + + assert mock.verify + end + + def test_verify_fails_when_mock_block_returns_false + mock = MiniTest::Mock.new + mock.expect :foo, nil do + false + end + + e = assert_raises(MockExpectationError) { mock.foo } + exp = "mocked method :foo failed block w/ []" + + assert_equal exp, e.message + end + + def test_mock_block_throws_if_args_passed + mock = MiniTest::Mock.new + + e = assert_raises(ArgumentError) do + mock.expect :foo, nil, [:a, :b, :c] do + true + end + end + + exp = "args ignored when block given" + + assert_equal exp, e.message + end + + def test_mock_returns_retval_when_called_with_block + mock = MiniTest::Mock.new + mock.expect(:foo, 32) do + true + end + + rs = mock.foo + + assert_equal rs, 32 + end + def util_verify_bad exp e = assert_raises MockExpectationError do @mock.verify @@ -214,6 +279,8 @@ end require "minitest/metametameta" class TestMiniTestStub < MiniTest::Unit::TestCase + parallelize_me! if ENV["PARALLEL"] + def setup super MiniTest::Unit::TestCase.reset @@ -230,13 +297,15 @@ class TestMiniTestStub < MiniTest::Unit::TestCase def assert_stub val_or_callable @assertion_count += 1 - t = Time.now.to_i + synchronize do + t = Time.now.to_i - Time.stub :now, val_or_callable do - @tc.assert_equal 42, Time.now - end + Time.stub :now, val_or_callable do + @tc.assert_equal 42, Time.now + end - @tc.assert_operator Time.now.to_i, :>=, t + @tc.assert_operator Time.now.to_i, :>=, t + end end def test_stub_value @@ -250,13 +319,15 @@ class TestMiniTestStub < MiniTest::Unit::TestCase def test_stub_block_args @assertion_count += 1 - t = Time.now.to_i + synchronize do + t = Time.now.to_i - Time.stub :now, lambda { |n| n * 2 } do - @tc.assert_equal 42, Time.now(21) - end + Time.stub :now, lambda { |n| n * 2 } do + @tc.assert_equal 42, Time.now(21) + end - @tc.assert_operator Time.now.to_i, :>=, t + @tc.assert_operator Time.now.to_i, :>=, t + end end def test_stub_callable @@ -278,4 +349,29 @@ class TestMiniTestStub < MiniTest::Unit::TestCase @tc.assert_equal "bar", val end + + def test_dynamic_method + @assertion_count = 2 + + dynamic = Class.new do + def self.respond_to?(meth) + meth == :found + end + + def self.method_missing(meth, *args, &block) + if meth == :found + false + else + super + end + end + end + + val = dynamic.stub(:found, true) do |s| + s.found + end + + @tc.assert_equal true, val + @tc.assert_equal false, dynamic.found + end end diff --git a/test/minitest/test_minitest_spec.rb b/test/minitest/test_minitest_spec.rb index 56aa78d620..85cb95f42b 100644 --- a/test/minitest/test_minitest_spec.rb +++ b/test/minitest/test_minitest_spec.rb @@ -15,6 +15,8 @@ class ExampleA; end class ExampleB < ExampleA; end describe MiniTest::Spec do + # do not parallelize this suite... it just can't handle it. + def assert_triggered expected = "blah", klass = MiniTest::Assertion @assertion_count += 2 @@ -567,11 +569,28 @@ describe MiniTest::Spec, :subject do end end -class TestMeta < MiniTest::Unit::TestCase - def test_setup - srand 42 - MiniTest::Unit::TestCase.reset +class TestMetaStatic < MiniTest::Unit::TestCase + def test_children + MiniTest::Spec.children.clear # prevents parallel run + + x = y = z = nil + x = describe "top-level thingy" do + y = describe "first thingy" do end + + it "top-level-it" do end + + z = describe "second thingy" do end + end + + assert_equal [x], MiniTest::Spec.children + assert_equal [y, z], x.children + assert_equal [], y.children + assert_equal [], z.children end +end + +class TestMeta < MiniTest::Unit::TestCase + parallelize_me! if ENV["PARALLEL"] def util_structure x = y = z = nil @@ -659,35 +678,15 @@ class TestMeta < MiniTest::Unit::TestCase _, _, z, before_list, after_list = util_structure @tu = MiniTest::Unit.new - @output = StringIO.new("") MiniTest::Unit.runner = nil # protect the outer runner from the inner tests - MiniTest::Unit.output = @output - tc = z.new :test_0002_anonymous - tc.run @tu + with_output do + tc = z.new :test_0002_anonymous + tc.run @tu + end assert_equal [1, 2, 3], before_list assert_equal [3, 2, 1], after_list - ensure - MiniTest::Unit.output = $stdout - end - - def test_children - MiniTest::Spec.children.clear - - x = y = z = nil - x = describe "top-level thingy" do - y = describe "first thingy" do end - - it "top-level-it" do end - - z = describe "second thingy" do end - end - - assert_equal [x], MiniTest::Spec.children - assert_equal [y, z], x.children - assert_equal [], y.children - assert_equal [], z.children end def test_describe_first_structure @@ -723,4 +722,17 @@ class TestMeta < MiniTest::Unit::TestCase assert_respond_to y.new(nil), "xyz" assert_respond_to z.new(nil), "xyz" end + + def with_output # REFACTOR: dupe from metametameta + synchronize do + begin + @output = StringIO.new("") + MiniTest::Unit.output = @output + + yield + ensure + MiniTest::Unit.output = STDOUT + end + end + end end diff --git a/test/minitest/test_minitest_unit.rb b/test/minitest/test_minitest_unit.rb index eadf59ee70..45bd96fcd4 100644 --- a/test/minitest/test_minitest_unit.rb +++ b/test/minitest/test_minitest_unit.rb @@ -13,6 +13,8 @@ class AnError < StandardError; include MyModule; end class ImmutableString < String; def inspect; super.freeze; end; end class TestMiniTestUnit < MetaMetaMetaTestCase + parallelize_me! if ENV["PARALLEL"] + pwd = Pathname.new File.expand_path Dir.pwd basedir = Pathname.new(File.expand_path "lib/minitest") + 'mini' basedir = basedir.relative_path_from(pwd).to_s @@ -22,6 +24,23 @@ class TestMiniTestUnit < MetaMetaMetaTestCase "#{MINITEST_BASE_DIR}/test.rb:139:in `run'", "#{MINITEST_BASE_DIR}/test.rb:106:in `run'"] + def test_wtf + $hook_value = nil + + capture_io do # don't care about deprecation + MiniTest::Unit::TestCase.add_setup_hook do + $hook_value = 42 + end + end + + run_setup_hooks + + assert_equal 42, $hook_value + assert_equal [Proc], MiniTest::Unit::TestCase.setup_hooks.map(&:class) + MiniTest::Unit::TestCase.reset_setup_teardown_hooks + assert_equal [], MiniTest::Unit::TestCase.setup_hooks.map(&:class) + end + def test_class_puke_with_assertion_failed exception = MiniTest::Assertion.new "Oh no!" exception.set_backtrace ["unhappy"] @@ -156,6 +175,85 @@ class TestMiniTestUnit < MetaMetaMetaTestCase assert_equal ex, fu end + def test_default_runner_is_minitest_unit + assert_instance_of MiniTest::Unit, MiniTest::Unit.runner + end + + def with_overridden_include + Class.class_eval do + def inherited_with_hacks klass + throw :inherited_hook + end + + alias inherited_without_hacks inherited + alias inherited inherited_with_hacks + alias IGNORE_ME! inherited # 1.8 bug. god I love venture bros + end + + yield + ensure + Class.class_eval do + alias inherited inherited_without_hacks + + undef_method :inherited_with_hacks + undef_method :inherited_without_hacks + end + + refute_respond_to Class, :inherited_with_hacks + refute_respond_to Class, :inherited_without_hacks + end + + def test_inherited_hook_plays_nice_with_others + with_overridden_include do + assert_throws :inherited_hook do + Class.new MiniTest::Unit::TestCase + end + end + end + + def test_passed_eh_teardown_good + test_class = Class.new MiniTest::Unit::TestCase do + def teardown; assert true; end + def test_omg; assert true; end + end + + test = test_class.new :test_omg + test.run @tu + assert test.passed? + end + + def test_passed_eh_teardown_flunked + test_class = Class.new MiniTest::Unit::TestCase do + def teardown; flunk; end + def test_omg; assert true; end + end + + test = test_class.new :test_omg + test.run @tu + refute test.passed? + end + + def util_expand_bt bt + if RUBY_VERSION >= '1.9.0' then + bt.map { |f| (f =~ /^\./) ? File.expand_path(f) : f } + else + bt + end + end +end + +class TestMiniTestRunner < MetaMetaMetaTestCase + # do not parallelize this suite... it just can't handle it. + + def test_class_test_suites + @assertion_count = 0 + + tc = Class.new(MiniTest::Unit::TestCase) + + assert_equal 1, MiniTest::Unit::TestCase.test_suites.size + assert_equal [tc], MiniTest::Unit::TestCase.test_suites + end + def test_run_test Class.new MiniTest::Unit::TestCase do attr_reader :foo @@ -352,10 +450,6 @@ class TestMiniTestUnit < MetaMetaMetaTestCase assert_report expected, %w[--seed 42 --verbose] end - def test_default_runner_is_minitest_unit - assert_instance_of MiniTest::Unit, MiniTest::Unit.runner - end - def test_run_with_other_runner MiniTest::Unit.runner = Class.new MiniTest::Unit do def _run_suite suite, type @@ -393,37 +487,74 @@ class TestMiniTestUnit < MetaMetaMetaTestCase assert_report expected end - def with_overridden_include - Class.class_eval do - def inherited_with_hacks klass - throw :inherited_hook - end + require 'monitor' - alias inherited_without_hacks inherited - alias inherited inherited_with_hacks - alias IGNORE_ME! inherited # 1.8 bug. god I love venture bros + class Latch + def initialize count = 1 + @count = count + @lock = Monitor.new + @cv = @lock.new_cond end - yield - ensure - Class.class_eval do - alias inherited inherited_without_hacks - - undef_method :inherited_with_hacks - undef_method :inherited_without_hacks + def release + @lock.synchronize do + @count -= 1 if @count > 0 + @cv.broadcast if @count == 0 + end end - refute_respond_to Class, :inherited_with_hacks - refute_respond_to Class, :inherited_without_hacks + def await + @lock.synchronize { @cv.wait_while { @count > 0 } } + end end - def test_inherited_hook_plays_nice_with_others - with_overridden_include do - assert_throws :inherited_hook do - Class.new MiniTest::Unit::TestCase + def test_run_parallel + test_count = 2 + test_latch = Latch.new test_count + main_latch = Latch.new + + thread = Thread.new { + Thread.current.abort_on_exception = true + + # This latch waits until both test latches have been released. Both + # latches can't be released unless done in separate threads because + # `main_latch` keeps the test method from finishing. + test_latch.await + main_latch.release + } + + Class.new MiniTest::Unit::TestCase do + parallelize_me! + + test_count.times do |i| + define_method :"test_wait_on_main_thread_#{i}" do + test_latch.release + + # This latch blocks until the "main thread" releases it. The main + # thread can't release this latch until both test latches have + # been released. This forces the latches to be released in separate + # threads. + main_latch.await + assert true + end end end + + expected = clean <<-EOM + .. + + Finished tests in 0.00 + + 2 tests, 2 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_report expected + assert thread.join end +end + +class TestMiniTestUnitOrder < MetaMetaMetaTestCase + # do not parallelize this suite... it just can't handle it. def test_before_setup call_order = [] @@ -440,34 +571,14 @@ class TestMiniTestUnit < MetaMetaMetaTestCase def test_omg; assert true; end end - @tu.run %w[--seed 42] + with_output do + @tu.run %w[--seed 42] + end expected = [:before_setup, :setup] assert_equal expected, call_order end - def test_passed_eh_teardown_good - test_class = Class.new MiniTest::Unit::TestCase do - def teardown; assert true; end - def test_omg; assert true; end - end - - test = test_class.new :test_omg - test.run @tu - assert test.passed? - end - - def test_passed_eh_teardown_flunked - test_class = Class.new MiniTest::Unit::TestCase do - def teardown; flunk; end - def test_omg; assert true; end - end - - test = test_class.new :test_omg - test.run @tu - refute test.passed? - end - def test_after_teardown call_order = [] Class.new MiniTest::Unit::TestCase do @@ -483,7 +594,9 @@ class TestMiniTestUnit < MetaMetaMetaTestCase def test_omg; assert true; end end - @tu.run %w[--seed 42] + with_output do + @tu.run %w[--seed 42] + end expected = [:teardown, :after_teardown] assert_equal expected, call_order @@ -513,7 +626,9 @@ class TestMiniTestUnit < MetaMetaMetaTestCase def test_omg; assert true; end end - @tu.run %w[--seed 42] + with_output do + @tu.run %w[--seed 42] + end expected = [:before_teardown, :teardown, :after_teardown] assert_equal expected, call_order @@ -538,24 +653,20 @@ class TestMiniTestUnit < MetaMetaMetaTestCase _ = Class.new parent - @tu.run %w[--seed 42] + with_output do + @tu.run %w[--seed 42] + end # Once for the parent class, once for the child expected = [:setup_method, :test, :teardown_method] * 2 assert_equal expected, call_order end - - def util_expand_bt bt - if RUBY_VERSION >= '1.9.0' then - bt.map { |f| (f =~ /^\./) ? File.expand_path(f) : f } - else - bt - end - end end class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase + parallelize_me! if ENV["PARALLEL"] + RUBY18 = ! defined? Encoding def setup @@ -633,6 +744,30 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase @tc.assert_equal 1, 1 end + def test_assert_equal_different_collection_array_hex_invisible + object1 = Object.new + object2 = Object.new + msg = "No visible difference in the Array#inspect output. + You should look at the implementation of #== on Array or its members. + [#]".gsub(/^ +/, "") + util_assert_triggered msg do + @tc.assert_equal [object1], [object2] + end + end + + def test_assert_equal_different_collection_hash_hex_invisible + h1, h2 = {}, {} + h1[1] = Object.new + h2[1] = Object.new + msg = "No visible difference in the Hash#inspect output. + You should look at the implementation of #== on Hash or its members. + {1=>#}".gsub(/^ +/, "") + + util_assert_triggered msg do + @tc.assert_equal h1, h2 + end + end + def test_assert_equal_different_diff_deactivated without_diff do util_assert_triggered util_msg("haha" * 10, "blah" * 10) do @@ -667,8 +802,8 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase o1 = Object.new o2 = Object.new - msg = "No visible difference. - You should look at your implementation of Object#==. + msg = "No visible difference in the Object#inspect output. + You should look at the implementation of #== on Object or its members. #".gsub(/^ +/, "") util_assert_triggered msg do @@ -693,8 +828,8 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase end def test_assert_equal_different_long_invisible - msg = "No visible difference. - You should look at your implementation of String#==. + msg = "No visible difference in the String#inspect output. + You should look at the implementation of #== on String or its members. \"blahblahblahblahblahblahblahblahblahblah\"".gsub(/^ +/, "") util_assert_triggered msg do @@ -1181,6 +1316,7 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase orig_verbose = $VERBOSE $VERBOSE = false + out, err = capture_io do puts 'hi' warn 'bye!' @@ -1192,6 +1328,24 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase $VERBOSE = orig_verbose end + def test_capture_subprocess_io + @assertion_count = 0 + skip "Dunno why but the parallel run of this fails" + + orig_verbose = $VERBOSE + $VERBOSE = false + + out, err = capture_subprocess_io do + system("echo 'hi'") + system("echo 'bye!' 1>&2") + end + + assert_equal "hi\n", out + assert_equal "bye!\n", err + ensure + $VERBOSE = orig_verbose + end + def test_class_asserts_match_refutes @assertion_count = 0 @@ -1215,15 +1369,6 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase assert_empty asserts.map { |n| n.sub(/^assert/, 'refute') } - refutes end - def test_class_test_suites - @assertion_count = 0 - - tc = Class.new(MiniTest::Unit::TestCase) - - assert_equal 1, MiniTest::Unit::TestCase.test_suites.size - assert_equal [tc], MiniTest::Unit::TestCase.test_suites - end - def test_expectation @assertion_count = 2 @@ -1458,6 +1603,7 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase @assertion_count = 0 sample_test_case = Class.new MiniTest::Unit::TestCase do + def self.test_order; :random; end def test_test1; assert "does not matter" end def test_test2; assert "does not matter" end def test_test3; assert "does not matter" end @@ -1532,6 +1678,8 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase end class TestMiniTestGuard < MiniTest::Unit::TestCase + parallelize_me! if ENV["PARALLEL"] + def test_mri_eh assert self.class.mri? "ruby blah" assert self.mri? "ruby blah" -- cgit v1.2.3