#!/usr/local/bin/ruby -w require 'rubygems' require 'minitest/autorun' require 'stringio' require 'autotest' # NOT TESTED: # class_run # add_sigint_handler # all_good # get_to_green # reset # ruby # run # run_tests class Autotest attr_reader :exception_list def self.clear_hooks HOOKS.clear end def self.reset_options @@options = {} end end class TestAutotest < Minitest::Test alias :deny :refute RUBY = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) def setup @test_class = 'TestBlah' @test = 'test/test_blah.rb' @other_test = 'test/test_blah_other.rb' @impl = 'lib/blah.rb' @inner_test = 'test/outer/test_inner.rb' @outer_test = 'test/test_outer.rb' @inner_test_class = "TestOuter::TestInner" klassname = self.class.name.sub(/^Test/, '') klassname.sub!(/^(\w+)(Autotest)$/, '\2::\1') unless klassname == "Autotest" @a = klassname.split(/::/).inject(Object) { |k,n| k.const_get(n) }.new @a.output = StringIO.new @a.last_mtime = Time.at(2) @files = {} @files[@impl] = Time.at(1) @files[@test] = Time.at(2) @a.find_order = @files.keys.sort end def test_add_exception current = util_exceptions @a.add_exception 'blah' actual = util_exceptions expect = current + ["blah"] assert_equal expect, actual end def test_add_mapping current = util_mappings @a.add_mapping(/blah/) do 42 end actual = util_mappings expect = current + [/blah/] assert_equal expect, actual end def test_add_mapping_front current = util_mappings @a.add_mapping(/blah/, :front) do 42 end actual = util_mappings expect = [/blah/] + current assert_equal expect, actual end def test_clear_exceptions test_add_exception @a.clear_exceptions actual = util_exceptions expect = [] assert_equal expect, actual end def test_clear_mapping @a.clear_mappings actual = util_mappings expect = [] assert_equal expect, actual end def test_consolidate_failures_experiment @files.clear @files[@impl] = Time.at(1) @files[@test] = Time.at(2) @a.find_order = @files.keys.sort input = [['test_fail1', @test_class], ['test_fail2', @test_class], ['test_error1', @test_class], ['test_error2', @test_class]] result = @a.consolidate_failures input expected = { @test => %w( test_fail1 test_fail2 test_error1 test_error2 ) } assert_equal expected, result end def test_consolidate_failures_green result = @a.consolidate_failures([]) expected = {} assert_equal expected, result end def test_consolidate_failures_multiple_possibilities @files[@other_test] = Time.at(42) result = @a.consolidate_failures([['test_unmatched', @test_class]]) expected = { @test => ['test_unmatched']} assert_equal expected, result expected = "" assert_equal expected, @a.output.string end def test_consolidate_failures_nested_classes @files.clear @files['lib/outer.rb'] = Time.at(5) @files['lib/outer/inner.rb'] = Time.at(5) @files[@inner_test] = Time.at(5) @files[@outer_test] = Time.at(5) @a.find_order = @files.keys.sort result = @a.consolidate_failures([['test_blah1', @inner_test_class]]) expected = { @inner_test => ['test_blah1'] } assert_equal expected, result expected = "" assert_equal expected, @a.output.string end def test_consolidate_failures_no_match result = @a.consolidate_failures([['test_blah1', @test_class], ['test_blah2', @test_class], ['test_blah1', 'TestUnknown']]) expected = {@test => ['test_blah1', 'test_blah2']} assert_equal expected, result expected = "Unable to map class TestUnknown to a file\n" assert_equal expected, @a.output.string end def test_consolidate_failures_red result = @a.consolidate_failures([['test_blah1', @test_class], ['test_blah2', @test_class]]) expected = {@test => ['test_blah1', 'test_blah2']} assert_equal expected, result end def test_exceptions @a.clear_exceptions test_add_exception assert_equal(/blah/, @a.exceptions) end def test_exceptions_nil @a.clear_exceptions assert_nil @a.exceptions end # TODO: lots of filename edgecases for find_files_to_test def test_find_files_to_test @a.last_mtime = Time.at(0) assert @a.find_files_to_test(@files) @a.last_mtime = @files.values.sort.last + 1 deny @a.find_files_to_test(@files) end def test_find_files_to_test_dunno empty = {} files = { "fooby.rb" => Time.at(42) } @a.options[:verbose] = true capture_io do assert @a.find_files_to_test(files) # we find fooby, assert_equal empty, @a.files_to_test # but it isn't something to test end assert_equal "No tests matched fooby.rb\n", @a.output.string end def test_find_files_to_test_lib # ensure we add test_blah.rb when blah.rb updates util_find_files_to_test(@impl, @test => []) end def test_find_files_to_test_no_change empty = {} # ensure world is virginal assert_equal empty, @a.files_to_test # ensure we do nothing when nothing changes... files = { @impl => @files[@impl] } # same time deny @a.find_files_to_test(files) assert_equal empty, @a.files_to_test assert_equal "", @a.output.string files = { @impl => @files[@impl] } # same time assert(! @a.find_files_to_test(files)) assert_equal empty, @a.files_to_test assert_equal "", @a.output.string end def test_find_files_to_test_test # ensure we add test_blah.rb when test_blah.rb itself updates util_find_files_to_test(@test, @test => []) end def test_reorder_alpha @a.order = :alpha expected = @files.sort assert_equal expected, @a.reorder(@files) end def test_reorder_reverse @a.order = :reverse expected = @files.sort.reverse assert_equal expected, @a.reorder(@files) end def test_reorder_random @a.order = :random srand 42 expected, size = @files.dup, @files.size expected = expected.sort_by { rand(size) } srand 42 result = @a.reorder(@files.dup) assert_equal expected, result end def test_reorder_natural srand 42 @files['lib/untested_blah.rb'] = Time.at(2) @a.find_order = @files.keys.sort_by { rand } @a.order = :natural expected = @a.find_order.map { |f| [f, @files[f]] } assert_equal expected, @a.reorder(@files) end def test_handle_results skip "this is minitest 4 output... needs to work in both" @a.files_to_test.clear @files.clear @files[@impl] = Time.at(1) @files[@test] = Time.at(2) @a.find_order = @files.keys.sort empty = {} assert_equal empty, @a.files_to_test, "must start empty" s1 = "Loaded suite -e Started ............ Finished in 0.001655 seconds. 12 tests, 18 assertions, 0 failures, 0 errors " @a.handle_results(s1) assert_equal empty, @a.files_to_test, "must stay empty" exp = { "tests" => 12, "assertions" => 18, "failures" => 0, "errors" => 0 } assert_equal exp, @a.latest_results # when colours are in the error message color_error = " 1) \e[31mFailure:\e[0m test_fail1(#{@test_class}) [#{@test}:59]: 2) \e[31mFailure:\e[0m test_fail2(#{@test_class}) [#{@test}:60]: 3) \e[31mError:\e[0m test_error1(#{@test_class}): 3) \e[31mError:\e[0m test_error2(#{@test_class}): 12 tests, 18 assertions, 2 failures, 2 errors " @a.handle_results(color_error) assert @a.tainted s2 = " 1) Failure: test_fail1(#{@test_class}) [#{@test}:59]: 2) Failure: test_fail2(#{@test_class}) [#{@test}:60]: 3) Error: test_error1(#{@test_class}): 3) Error: test_error2(#{@test_class}): 12 tests, 18 assertions, 2 failures, 2 errors " @a.handle_results(s2) expected = { @test => %w( test_fail1 test_fail2 test_error1 test_error2 ) } assert_equal expected, @a.files_to_test assert @a.tainted exp = { "tests" => 12, "assertions" => 18, "failures" => 2, "errors" => 2 } assert_equal exp, @a.latest_results @a.handle_results(s1) assert_equal empty, @a.files_to_test s3 = ' /opt/bin/ruby -I.:lib:test -rubygems -e "%w[test/unit #{@test}].each { |f| require f }" | unit_diff -u -e:1:in `require\': ./#{@test}:23: parse error, unexpected tIDENTIFIER, expecting \'}\' (SyntaxError) settings_fields.each {|e| assert_equal e, version.send e.intern} ^ from -e:1 from -e:1:in `each\' from -e:1 ' @a.files_to_test[@test] = Time.at(42) @files[@test] = [] expected = { @test => Time.at(42) } assert_equal expected, @a.files_to_test @a.handle_results(s3) assert_equal expected, @a.files_to_test assert @a.tainted @a.tainted = false @a.handle_results(s1) assert_equal empty, @a.files_to_test deny @a.tainted end def test_handle_results_minitest5 @a.files_to_test.clear @files.clear @files[@impl] = Time.at(1) @files[@test] = Time.at(2) @a.find_order = @files.keys.sort empty = {} assert_equal empty, @a.files_to_test, "must start empty" s2 = " 1) Error: #{@test_class}#test_error1: 1) Failure: #{@test_class}#test_fail1 [./test/test_autotest.rb:375]: 12 tests, 18 assertions, 1 failures, 1 errors " @a.handle_results(s2) expected = { @test => %w( test_error1 test_fail1 ) } assert_equal expected, @a.files_to_test assert @a.tainted exp = { "tests" => 12, "assertions" => 18, "failures" => 1, "errors" => 1 } assert_equal exp, @a.latest_results end def test_hook_overlap_returning_false util_reset_hooks_returning false @a.hook :blah assert @a.instance_variable_get(:@blah1), "Hook1 should work on blah" assert @a.instance_variable_get(:@blah2), "Hook2 should work on blah" assert @a.instance_variable_get(:@blah3), "Hook3 should work on blah" end def test_hook_overlap_returning_true util_reset_hooks_returning true @a.hook :blah assert @a.instance_variable_get(:@blah1), "Hook1 should work on blah" deny @a.instance_variable_get(:@blah2), "Hook2 should NOT work on blah" deny @a.instance_variable_get(:@blah3), "Hook3 should NOT work on blah" end def test_hook_response Autotest.clear_hooks deny @a.hook(:blah) Autotest.add_hook(:blah) { false } deny @a.hook(:blah) Autotest.add_hook(:blah) { false } deny @a.hook(:blah) Autotest.add_hook(:blah) { true } assert @a.hook(:blah) end def test_make_test_cmd f = { @test => [], 'test/test_fooby.rb' => [ 'test_something1', 'test_something2' ] } pre = "#{RUBY} -I.:lib:test -rubygems" req = ".each { |f| require f }\"" expected = [ "#{pre} -e \"gem 'minitest'", "%w[minitest/autorun #{@test}]#{req}", "#{pre} test/test_fooby.rb -n \"/^(test_something1|test_something2)$/\"" ].join("; ") result = @a.make_test_cmd f assert_equal expected.gsub(/ /, "\n"), result.gsub(/ /, "\n") end def test_make_test_cmd_unit_diff @a.unit_diff = "unit_diff -u" f = { @test => [], 'test/test_fooby.rb' => [ 'test_something1', 'test_something2' ] } pre = "#{RUBY} -I.:lib:test -rubygems" req = ".each { |f| require f }\"" post = "| unit_diff -u" expected = [ "#{pre} -e \"gem 'minitest'", "%w[minitest/autorun #{@test}]#{req} #{post}", "#{pre} test/test_fooby.rb -n \"/^(test_something1|test_something2)$/\" #{post}" ].join("; ") result = @a.make_test_cmd f assert_equal expected, result end def test_path_to_classname # non-rails util_path_to_classname 'TestBlah', 'test/test_blah.rb' util_path_to_classname 'TestOuter::TestInner', 'test/outer/test_inner.rb' util_path_to_classname 'TestRuby2Ruby', 'test/test_ruby2ruby.rb' end def test_remove_exception test_add_exception current = util_exceptions @a.remove_exception 'blah' actual = util_exceptions expect = current - ["blah"] assert_equal expect, actual end def test_remove_mapping current = util_mappings @a.remove_mapping(/^lib\/.*\.rb$/) actual = util_mappings expect = current - [/^lib\/.*\.rb$/] assert_equal expect, actual end def test_test_files_for assert_equal [@test], @a.test_files_for(@impl) assert_equal [@test], @a.test_files_for(@test) assert_equal [], @a.test_files_for('test/test_unknown.rb') assert_equal [], @a.test_files_for('lib/unknown.rb') assert_equal [], @a.test_files_for('unknown.rb') assert_equal [], @a.test_files_for('test_unknown.rb') end def test_testlib assert_equal "minitest/autorun", @a.testlib @a.testlib = "MONKEY" assert_equal "MONKEY", @a.testlib f = { @test => [], "test/test_fooby.rb" => %w(first second) } assert_match @a.testlib, @a.make_test_cmd(f) end def test_runner_accepts_rc_options exp = {:rc=>["autotest_rc"], :args=>["--rc", "autotest_rc"]} assert_equal exp, Autotest.parse_options(['--rc', 'autotest_rc']) assert_kind_of Autotest, Autotest.new ensure Autotest.reset_options end def util_exceptions @a.exception_list.sort_by { |r| r.to_s } end def util_find_files_to_test(f, expected) t = @a.last_mtime files = { f => t + 1 } capture_io do assert @a.find_files_to_test(files) assert_equal expected, @a.files_to_test end assert_equal t, @a.last_mtime assert_equal "", @a.output.string end def util_mappings @a.test_mappings.map { |k,v| k } end def util_path_to_classname(e,i) assert_equal e, @a.path_to_classname(i) end def util_reset_hooks_returning val Autotest.clear_hooks @a.instance_variable_set :@blah1, false @a.instance_variable_set :@blah2, false @a.instance_variable_set :@blah3, false Autotest.add_hook(:blah) do |at| at.instance_variable_set :@blah1, true val end Autotest.add_hook(:blah) do |at| at.instance_variable_set :@blah2, true val end Autotest.add_hook(:blah) do |at| at.instance_variable_set :@blah3, true val end end end