require 'reap/task' #require 'test/unit' require 'test/unit/ui/testrunnermediator' require 'facet/string/tabto' Test::Unit.run = true # don't run auto tests # # Test Task # # The Reap test task runs each test in # it's own process, making for purer # test facility. # # NOTE: This works well enough but it is a bit of # hack. It actually marshals test results across # a stdout->stdin shell pipe. One consequence of # this is that you can't send debug info to stdout # (including #p and #puts). This, hopefully can be # remedied in the future. # class Reap::Test < Reap::Task def task_desc "Run unit-tests (each in a separate process)." end attr_accessor :libs, :files, :fixture, :against_installed def init @libs ||= ['lib'] @files ||= [ 'test/*/**/*.rb', 'test/**/tc*.rb', 'test/**/test*.rb', 'test/**/*test.rb' ] @fixture ||= [] @against_installed ||= false # interal use @results = TestResults.new @errors = [] @failures = [] end def run # get test files test_libs = @libs.join(':') test_files = FileList.new test_files.include(*@files) if test_files.empty? puts "No test files found." return end print "Reap is shelling out work to Ruby's Test Suite..." test_files.each { |f| $stdout << '.'; $stdout.flush dotest( f ) #ruby %{-r"test/unit" "#{f}"} } puts "done." # Don't know why empty arrays get in them yet, but... @failures.reject! { |e| e == [] } @errors.reject! { |e| e == [] } # Display Failures puts puts %{FAILURES:#{@failures.empty? ? ' []' : ''}} @failures.reverse.each { |fails| fails.reverse.each { |failure| puts puts %{ - test : #{failure.test_name}} puts %{ location : #{failure.location}} if failure.message.index("\n") puts %{ message : >} puts failure.message.tabto(6) else puts %{ message : #{failure.message}} end } } # Display Errors puts puts %{ERRORS:#{@errors.empty? ? ' []' : ''}} @errors.reverse.each { |errs| errs.reverse.each { |err| puts puts %{ - test : #{err.test_name}} puts %{ message : #{err.exception.message}} puts %{ backtrace :} err.exception.backtrace[0...-1].each { |bt| puts %Q{ - #{bt}} } } } # Display Final Results puts puts @results puts # old way (don't work right) #puts %Q{ ruby %{-I#{test_libs} -e0 -r"test/unit" \\\n#{test_reqs}#{test_opts}} } if $DEBUG #ruby %{-I#{test_libs} -e0 -r"test/unit" \\\n#{test_reqs}#{test_opts}} #ruby %{-e0 -r"test/unit" \\\n#{test_reqs}#{test_opts}} end def dotest( test_file ) if ! File.file?( test_file ) r = nil else r = fork_test( test_file ) end unless r.passed? @errors << r.instance_variable_get('@errors') @failures << r.instance_variable_get('@failures') end @results << r end # Currently send program output to null device. # Could send to a logger in future version. def fork_test( testfile ) src = '' src << %{$:.unshift('lib')} unless against_installed src << %{ #require 'test/unit' require 'test/unit/collector' require 'test/unit/collector/objectspace' require 'test/unit/ui/testrunnermediator' } fixture.each do |fix| src << %Q{ require '#{fix}' } end src << %{ output = STDOUT.dup STDOUT.reopen( PLATFORM =~ /mswin/ ? "NUL" : "/dev/null" ) load('#{testfile}') tests = Test::Unit::Collector::ObjectSpace.new.collect runner = Test::Unit::UI::TestRunnerMediator.new( tests ) result = runner.run_suite output << Marshal.dump(result) STDOUT.reopen(output) output.close } result = IO.popen("ruby","w+") do |ruby| ruby.puts src ruby.close_write ruby.read end p testfile if $VERBOSE return Marshal.load(result) end # # Support class for collecting test results # class TestResults attr_reader :assertion_count, :run_count, :failure_count, :error_count def initialize @assertion_count = 0 @run_count = 0 @failure_count = 0 @error_count = 0 end def <<( result ) @assertion_count += result.assertion_count @run_count += result.run_count @failure_count += result.failure_count @error_count += result.error_count end def to_s s = %{SUMMARY:\n\n} s << %{ tests : #{@run_count}\n} s << %{ assertions : #{@assertion_count}\n} s << %{ failures : #{@failure_count}\n} s << %{ errors : #{@error_count}\n} s end end end