#-- # _____ _ # |_ _|__ __| |_ # | |/ -_|_-< _| # |_|\___/__/\__| # #++ #require 'test/unit' require 'test/unit/ui/testrunnermediator' require 'facet/string/tabto' require 'facet/progressbar' ::Test::Unit.run = true # don't run auto tests module Reap # == Test # # The Reap test class runs each test in it's own # proccess, making for a more pure test facility. # Reap runs each test in a separate proccess to aviod # potential conflicts between scripts. # # files Test files (eg. test/tc_**/*.rb) # Defaults to typcial selection. # # libs List of lookup directories to include in # load path. './lib' is always included. # # live Flag to quickly deactive use of local libs. # Test against installed files instead. # # requires List of any files to pre-require. # # NOTE This works well enough but it is a tad delicate. # It actually marshals test results across stdout->stdin # shell pipe. One consequence of this is that you can't # send debug info to stdout in your tests (including #p and #puts). class Test include TaskUtils attr_accessor :files, :requires, :live, :libs def initialize( tst ) @files = [ 'test/*/**/*.rb', 'test/**/tc*.rb', 'test/**/test*.rb', 'test/**/*test.rb' ] @requires = [] @live = false @libs = [] #['./lib'] super end # Start testing. def test run_tests end alias_method :call, :test private # Run each test. def run_tests # interal use @results = TestResults.new @errors = [] @failures = [] # Get test files test_libs = @libs.join(':') test_files = FileList.new test_files.include(*@files) test_files.uniq! if test_files.empty? tell "No test files found." return end #tell "Reap is passing off work to Ruby's Test Suite..." pbar = Console::ProgressBar.new( 'Testing', test_files.size ) # Run tests # (why arn't these unique to begin with?) test_files.each do |test_file| pbar.inc #$stdout << '.'; $stdout.flush 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 #ruby %{-r"test/unit" "#{f}"} end pbar.finish # 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 private # Runs a test. # # Currently send program output to null device. # Could send to a logger in future version. def fork_test( testfile ) src = '' unless @live l = File.join( Dir.pwd, 'lib' ) if File.directory?( l ) src << %{$:.unshift('#{l}')\n} end @libs.each { |r| src << %{$:.unshift('#{r}')\n} } end src << %{ #require 'test/unit' require 'test/unit/collector' require 'test/unit/collector/objectspace' require 'test/unit/ui/testrunnermediator' } @requires.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 begin marshalled = Marshal.dump(result) rescue TypeError => e $stderr << "MARSHAL ERROR\n" $stderr << "TEST: #{testfile}\n" $stderr << "DATA:" << result.inspect exit -1 end output << marshalled 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 begin marsh = Marshal.load(result) rescue ArgumentError $stderr << "\nCannot load marshalled test data.\n" $stderr << result << "\n" exit -1 end return marsh 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 end #module Reap