require 'test/unit' require 'test/unit/testresult' module Hydra #:nodoc: # Hydra class responsible for running test files. # # The Runner is never run directly by a user. Runners are created by a # Worker to run test files. # # The general convention is to have one Runner for each logical processor # of a machine. class Runner include Hydra::Messages::Runner traceable('RUNNER') # Boot up a runner. It takes an IO object (generally a pipe from its # parent) to send it messages on which files to execute. def initialize(opts = {}) @io = opts.fetch(:io) { raise "No IO Object" } @verbose = opts.fetch(:verbose) { false } Test::Unit.run = true $stdout.sync = true trace 'Booted. Sending Request for file' @io.write RequestFile.new begin process_messages rescue => ex trace ex.to_s raise ex end end # Run a test file and report the results def run_file(file) trace "Running file: #{file}" require file output = [] @result = Test::Unit::TestResult.new @result.add_listener(Test::Unit::TestResult::FAULT) do |value| output << value end klasses = Runner.find_classes_in_file(file) begin klasses.each{|klass| klass.suite.run(@result){|status, name| ;}} rescue => ex output << ex.to_s end output << '.' if output.empty? @io.write Results.new(:output => output.join("\n"), :file => file) end # Stop running def stop @running = false end private # The runner will continually read messages and handle them. def process_messages trace "Processing Messages" @running = true while @running begin message = @io.gets if message and !message.class.to_s.index("Worker").nil? trace "Received message from worker" trace "\t#{message.inspect}" message.handle(self) else @io.write Ping.new end rescue IOError => ex trace "Runner lost Worker" @running = false end end end def self.find_classes_in_file(f) code = "" File.open(f) {|buffer| code = buffer.read} matches = code.scan(/class\s+([\S]+)/) klasses = matches.collect do |c| begin if c.first.respond_to? :constantize c.first.constantize else eval(c.first) end rescue NameError # means we could not load [c.first], but thats ok, its just not # one of the classes we want to test nil rescue SyntaxError # see above nil end end return klasses.select{|k| k.respond_to? 'suite'} end end end