# encoding: UTF-8 require "pathname" require "minitest/metametameta" if defined? Encoding then e = Encoding.default_external if e != Encoding::UTF_8 then warn "" warn "" warn "NOTE: External encoding #{e} is not UTF-8. Tests WILL fail." warn " Run tests with `RUBYOPT=-Eutf-8 rake` to avoid errors." warn "" warn "" end end class Minitest::Runnable def whatever # faked for testing assert true end end class TestMinitestUnit < MetaMetaMetaTestCase parallelize_me! 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 MINITEST_BASE_DIR = basedir[/\A\./] ? basedir : "./#{basedir}" BT_MIDDLE = ["#{MINITEST_BASE_DIR}/test.rb:161:in `each'", "#{MINITEST_BASE_DIR}/test.rb:158:in `each'", "#{MINITEST_BASE_DIR}/test.rb:139:in `run'", "#{MINITEST_BASE_DIR}/test.rb:106:in `run'"] def test_filter_backtrace # this is a semi-lame mix of relative paths. # I cheated by making the autotest parts not have ./ bt = (["lib/autotest.rb:571:in `add_exception'", "test/test_autotest.rb:62:in `test_add_exception'", "#{MINITEST_BASE_DIR}/test.rb:165:in `__send__'"] + BT_MIDDLE + ["#{MINITEST_BASE_DIR}/test.rb:29", "test/test_autotest.rb:422"]) bt = util_expand_bt bt ex = ["lib/autotest.rb:571:in `add_exception'", "test/test_autotest.rb:62:in `test_add_exception'"] ex = util_expand_bt ex Minitest::Test.io_lock.synchronize do # try not to trounce in parallel fu = Minitest.filter_backtrace(bt) assert_equal ex, fu end end def test_filter_backtrace_all_unit bt = (["#{MINITEST_BASE_DIR}/test.rb:165:in `__send__'"] + BT_MIDDLE + ["#{MINITEST_BASE_DIR}/test.rb:29"]) ex = bt.clone fu = Minitest.filter_backtrace(bt) assert_equal ex, fu end def test_filter_backtrace_unit_starts bt = (["#{MINITEST_BASE_DIR}/test.rb:165:in `__send__'"] + BT_MIDDLE + ["#{MINITEST_BASE_DIR}/mini/test.rb:29", "-e:1"]) bt = util_expand_bt bt ex = ["-e:1"] Minitest::Test.io_lock.synchronize do # try not to trounce in parallel fu = Minitest.filter_backtrace bt assert_equal ex, fu end end def test_filter_backtrace__empty with_empty_backtrace_filter do bt = %w[first second third] fu = Minitest.filter_backtrace bt.dup assert_equal bt, fu end end def test_infectious_binary_encoding @tu = Class.new FakeNamedTest do def test_this_is_not_ascii_assertion assert_equal "ЁЁЁ", "ёёё" end def test_this_is_non_ascii_failure_message fail 'ЁЁЁ'.force_encoding('ASCII-8BIT') end end expected = clean <<-EOM FE Finished in 0.00 1) Failure: FakeNamedTestXX#test_this_is_not_ascii_assertion [FILE:LINE]: Expected: \"ЁЁЁ\" Actual: \"ёёё\" 2) Error: FakeNamedTestXX#test_this_is_non_ascii_failure_message: RuntimeError: ЁЁЁ FILE:LINE:in `test_this_is_non_ascii_failure_message' 2 runs, 1 assertions, 1 failures, 1 errors, 0 skips EOM Minitest::Test.io_lock.synchronize do # try not to trounce in parallel assert_report expected end end def test_passed_eh_teardown_good test_class = Class.new FakeNamedTest do def teardown; assert true; end def test_omg; assert true; end end test = test_class.new :test_omg test.run refute_predicate test, :error? assert_predicate test, :passed? refute_predicate test, :skipped? end def test_passed_eh_teardown_skipped test_class = Class.new FakeNamedTest do def teardown; assert true; end def test_omg; skip "bork"; end end test = test_class.new :test_omg test.run refute_predicate test, :error? refute_predicate test, :passed? assert_predicate test, :skipped? end def test_passed_eh_teardown_flunked test_class = Class.new FakeNamedTest do def teardown; flunk; end def test_omg; assert true; end end test = test_class.new :test_omg test.run refute_predicate test, :error? refute_predicate test, :passed? refute_predicate test, :skipped? end def util_expand_bt bt bt.map { |f| (f =~ /^\./) ? File.expand_path(f) : f } end end class TestMinitestUnitInherited < MetaMetaMetaTestCase 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 FakeNamedTest end end end end class TestMinitestRunner < MetaMetaMetaTestCase # do not parallelize this suite... it just can't handle it. def test_class_runnables @assertion_count = 0 tc = Class.new(Minitest::Test) assert_equal 1, Minitest::Test.runnables.size assert_equal [tc], Minitest::Test.runnables end def test_run_test @tu = Class.new FakeNamedTest do attr_reader :foo def run @foo = "hi mom!" r = super @foo = "okay" r end def test_something assert_equal "hi mom!", foo end end expected = clean <<-EOM . Finished in 0.00 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips EOM assert_report expected end def test_run_error @tu = Class.new FakeNamedTest do def test_something assert true end def test_error raise "unhandled exception" end end expected = clean <<-EOM .E Finished in 0.00 1) Error: FakeNamedTestXX#test_error: RuntimeError: unhandled exception FILE:LINE:in \`test_error\' 2 runs, 1 assertions, 0 failures, 1 errors, 0 skips EOM assert_report expected end def test_run_error_teardown @tu = Class.new FakeNamedTest do def test_something assert true end def teardown raise "unhandled exception" end end expected = clean <<-EOM E Finished in 0.00 1) Error: FakeNamedTestXX#test_something: RuntimeError: unhandled exception FILE:LINE:in \`teardown\' 1 runs, 1 assertions, 0 failures, 1 errors, 0 skips EOM assert_report expected end def test_run_failing setup_basic_tu expected = clean <<-EOM .F Finished in 0.00 1) Failure: FakeNamedTestXX#test_failure [FILE:LINE]: Expected false to be truthy. 2 runs, 2 assertions, 1 failures, 0 errors, 0 skips EOM assert_report expected end def setup_basic_tu @tu = Class.new FakeNamedTest do def test_something assert true end def test_failure assert false end end end def test_seed # this is set for THIS run, so I'm not testing it's actual value assert_instance_of Integer, Minitest.seed end def test_run_failing_filtered setup_basic_tu expected = clean <<-EOM . Finished in 0.00 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips EOM assert_report expected, %w[--name /some|thing/ --seed 42] end def assert_filtering filter, name, expected, a = false args = %W[--#{filter} #{name} --seed 42] alpha = Class.new FakeNamedTest do define_method :test_something do assert a end end Object.const_set(:Alpha, alpha) beta = Class.new FakeNamedTest do define_method :test_something do assert true end end Object.const_set(:Beta, beta) @tus = [alpha, beta] assert_report expected, args ensure Object.send :remove_const, :Alpha Object.send :remove_const, :Beta end def test_run_filtered_including_suite_name expected = clean <<-EOM . Finished in 0.00 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips EOM assert_filtering "name", "/Beta#test_something/", expected end def test_run_filtered_including_suite_name_string expected = clean <<-EOM . Finished in 0.00 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips EOM assert_filtering "name", "Beta#test_something", expected end def test_run_filtered_string_method_only expected = clean <<-EOM .. Finished in 0.00 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips EOM assert_filtering "name", "test_something", expected, :pass end def test_run_failing_excluded setup_basic_tu expected = clean <<-EOM . Finished in 0.00 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips EOM assert_report expected, %w[--exclude /failure/ --seed 42] end def test_run_filtered_excluding_suite_name expected = clean <<-EOM . Finished in 0.00 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips EOM assert_filtering "exclude", "/Alpha#test_something/", expected end def test_run_filtered_excluding_suite_name_string expected = clean <<-EOM . Finished in 0.00 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips EOM assert_filtering "exclude", "Alpha#test_something", expected end def test_run_filtered_excluding_string_method_only expected = clean <<-EOM Finished in 0.00 0 runs, 0 assertions, 0 failures, 0 errors, 0 skips EOM assert_filtering "exclude", "test_something", expected, :pass end def test_run_passing @tu = Class.new FakeNamedTest do def test_something assert true end end expected = clean <<-EOM . Finished in 0.00 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips EOM assert_report expected end def test_run_skip @tu = Class.new FakeNamedTest do def test_something assert true end def test_skip skip "not yet" end end expected = clean <<-EOM .S Finished in 0.00 2 runs, 1 assertions, 0 failures, 0 errors, 1 skips You have skipped tests. Run with --verbose for details. EOM restore_env do assert_report expected end end def test_run_skip_verbose @tu = Class.new FakeNamedTest do def test_something assert true end def test_skip skip "not yet" end end expected = clean <<-EOM FakeNamedTestXX#test_something = 0.00 s = . FakeNamedTestXX#test_skip = 0.00 s = S Finished in 0.00 1) Skipped: FakeNamedTestXX#test_skip [FILE:LINE]: not yet 2 runs, 1 assertions, 0 failures, 0 errors, 1 skips EOM assert_report expected, %w[--seed 42 --verbose] end def test_run_skip_show_skips @tu = Class.new FakeNamedTest do def test_something assert true end def test_skip skip "not yet" end end expected = clean <<-EOM .S Finished in 0.00 1) Skipped: FakeNamedTestXX#test_skip [FILE:LINE]: not yet 2 runs, 1 assertions, 0 failures, 0 errors, 1 skips EOM assert_report expected, %w[--seed 42 --show-skips] end def test_run_with_other_runner @tu = Class.new FakeNamedTest do def self.run reporter, options = {} @reporter = reporter before_my_suite super end def self.name; "wacky!" end def self.before_my_suite @reporter.io.puts "Running #{self.name} tests" @@foo = 1 end def test_something assert_equal 1, @@foo end def test_something_else assert_equal 1, @@foo end end expected = clean <<-EOM Running wacky! tests .. Finished in 0.00 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips EOM assert_report expected end require "monitor" class Latch def initialize count = 1 @count = count @lock = Monitor.new @cv = @lock.new_cond end def release @lock.synchronize do @count -= 1 if @count > 0 @cv.broadcast if @count == 0 end end def await @lock.synchronize { @cv.wait_while { @count > 0 } } end end def test_run_parallel test_count = 2 test_latch = Latch.new test_count wait_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 } @tu = Class.new FakeNamedTest 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 in 0.00 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips EOM skip if Minitest.parallel_executor.size < 2 # locks up test runner if 1 CPU assert_report(expected) do |reporter| reporter.extend(Module.new { define_method("record") do |result| super(result) wait_latch.release end define_method("report") do wait_latch.await super() end }) end 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 = [] @tu = Class.new FakeNamedTest do define_method :setup do super() call_order << :setup end define_method :before_setup do call_order << :before_setup end def test_omg; assert true; end end run_tu_with_fresh_reporter expected = [:before_setup, :setup] assert_equal expected, call_order end def test_after_teardown call_order = [] @tu = Class.new FakeNamedTest do define_method :teardown do super() call_order << :teardown end define_method :after_teardown do call_order << :after_teardown end def test_omg; assert true; end end run_tu_with_fresh_reporter expected = [:teardown, :after_teardown] assert_equal expected, call_order end def test_all_teardowns_are_guaranteed_to_run call_order = [] @tu = Class.new FakeNamedTest do define_method :after_teardown do super() call_order << :after_teardown raise end define_method :teardown do super() call_order << :teardown raise end define_method :before_teardown do super() call_order << :before_teardown raise end def test_omg; assert true; end end run_tu_with_fresh_reporter expected = [:before_teardown, :teardown, :after_teardown] assert_equal expected, call_order end def test_setup_and_teardown_survive_inheritance call_order = [] @tu = Class.new FakeNamedTest do define_method :setup do call_order << :setup_method end define_method :teardown do call_order << :teardown_method end define_method :test_something do call_order << :test end end run_tu_with_fresh_reporter @tu = Class.new @tu run_tu_with_fresh_reporter # Once for the parent class, once for the child expected = [:setup_method, :test, :teardown_method] * 2 assert_equal expected, call_order end end class BetterError < RuntimeError # like better_error w/o infecting RuntimeError def set_backtrace bt super @bad_ivar = binding end end class TestMinitestRunnable < Minitest::Test def setup_marshal klass tc = klass.new "whatever" tc.assertions = 42 tc.failures << "a failure" yield tc if block_given? def tc.setup @blah = "blah" end tc.setup @tc = Minitest::Result.from tc end def assert_marshal expected_ivars new_tc = Marshal.load Marshal.dump @tc ivars = new_tc.instance_variables.map(&:to_s).sort assert_equal expected_ivars, ivars assert_equal "whatever", new_tc.name assert_equal 42, new_tc.assertions assert_equal ["a failure"], new_tc.failures yield new_tc if block_given? end def test_marshal setup_marshal Minitest::Runnable assert_marshal %w[@NAME @assertions @failures @klass @source_location @time] end def test_spec_marshal klass = describe("whatever") { it("passes") { assert true } } rm = klass.runnable_methods.first # Run the test @tc = klass.new(rm).run assert_kind_of Minitest::Result, @tc # Pass it over the wire over_the_wire = Marshal.load Marshal.dump @tc assert_equal @tc.time, over_the_wire.time assert_equal @tc.name, over_the_wire.name assert_equal @tc.assertions, over_the_wire.assertions assert_equal @tc.failures, over_the_wire.failures assert_equal @tc.klass, over_the_wire.klass end def test_spec_marshal_with_exception klass = describe("whatever") { it("raises, badly") { raise Class.new(StandardError), "this is bad!" } } rm = klass.runnable_methods.first # Run the test @tc = klass.new(rm).run assert_kind_of Minitest::Result, @tc assert_instance_of Minitest::UnexpectedError, @tc.failure msg = @tc.failure.error.message assert_includes msg, "Neutered Exception #