A text report of the time it took to run each file
Create a new Master
Options:
:files
An array of test files to be run. These should be relative paths from the root of the project, since they may be run on different machines which may have different paths.
:workers
An array of hashes. Each hash should be the configuration options for a worker.
:listeners
An array of Hydra::Listener objects. See Hydra::Listener::MinimalOutput for an example listener
:verbose
Set to true to see lots of Hydra output (for debugging)
:autosort
Set to false to disable automatic sorting by historical run-time per file
# File lib/hydra/master.rb, line 36 def initialize(opts = { }) opts.stringify_keys! config_file = opts.delete('config') { nil } if config_file begin config_erb = ERB.new(IO.read(config_file)).result(binding) rescue Exception => e raise(YmlLoadError,"config file was found, but could not be parsed with ERB.\n#{$!.inspect}") end begin config_yml = YAML::load(config_erb) rescue StandardError => e raise(YmlLoadError,"config file was found, but could not be parsed.\n#{$!.inspect}") end opts.merge!(config_yml.stringify_keys!) end @files = Array(opts.fetch('files') { nil }) raise "No files, nothing to do" if @files.empty? @incomplete_files = @files.dup @failed_files = [] @workers = [] @listeners = [] @event_listeners = Array(opts.fetch('listeners') { nil } ) @event_listeners.select{|l| l.is_a? String}.each do |l| @event_listeners.delete_at(@event_listeners.index(l)) listener = eval(l) @event_listeners << listener if listener.is_a?(Hydra::Listener::Abstract) end @string_runner_event_listeners = Array( opts.fetch( 'runner_listeners' ) { nil } ) @runner_log_file = opts.fetch('runner_log_file') { nil } @verbose = opts.fetch('verbose') { false } @autosort = opts.fetch('autosort') { true } @sync = opts.fetch('sync') { nil } @environment = opts.fetch('environment') { 'test' } @options = opts.fetch('options') { '' } if @autosort sort_files_from_report @event_listeners << Hydra::Listener::ReportGenerator.new(File.new(heuristic_file, 'w')) end # default is one worker that is configured to use a pipe with one runner worker_cfg = opts.fetch('workers') { [ { 'type' => 'local', 'runners' => 1} ] } trace "Initialized" trace " Files: (#{@files.inspect})" trace " Workers: (#{worker_cfg.inspect})" trace " Verbose: (#{@verbose.inspect})" @event_listeners.each{|l| l.testing_begin(@files) } boot_workers worker_cfg process_messages end
Process the results coming back from the worker.
# File lib/hydra/master.rb, line 114 def process_results(worker, message) if message.output =~ %rActiveRecord::StatementInvalid(.*)[Dd]eadlock/ or message.output =~ %rPGError: ERROR(.*)[Dd]eadlock/ or message.output =~ %rMysql::Error: SAVEPOINT(.*)does not exist: ROLLBACK/ or message.output =~ %rMysql::Error: Deadlock found/ trace "Deadlock detected running [#{message.file}]. Will retry at the end" @files.push(message.file) send_file(worker) else @incomplete_files.delete_at(@incomplete_files.index(message.file)) trace "#{@incomplete_files.size} Files Remaining" @event_listeners.each{|l| l.file_end(message.file, message.output) } unless message.output == '.' @failed_files << message.file end if @incomplete_files.empty? @workers.each do |worker| @event_listeners.each{|l| l.worker_end(worker) } end shutdown_all_workers else send_file(worker) end end end
Send a file down to a worker.
# File lib/hydra/master.rb, line 102 def send_file(worker) f = @files.shift if f trace "Sending #{f.inspect}" @event_listeners.each{|l| l.file_begin(f) } worker[:io].write(RunFile.new(:file => f)) else trace "No more files to send" end end
Message handling
# File lib/hydra/master.rb, line 97 def worker_begin(worker) @event_listeners.each {|l| l.worker_begin(worker) } end