1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
class Controller
attr_accessor :test_failed, :test_new, :test_date_after, :teset_date_before, :update_failed, :num_failed, :record_duration, :show_duration
attr_reader :num_threads, :results
def initialize
@results = Results.new(self)
@test_failed = false
@test_new = false
@test_date_after = nil
@test_date_before = nil
@update_failed = false
@num_threads = self.get_num_processors
@record_duration = false
@show_duration = false
@for_mkvgoodbad = %r{^(1|true)$}i.match(ENV['MKVGOODBAD_OUTPUT'] || '')
@tests = Array.new
@exclusions = Array.new
@dir_entries = Dir.entries(".")
end
def get_num_processors
np = case RUBY_PLATFORM
when $is_macos then `/usr/sbin/sysctl -n hw.availcpu`.to_i
else `nproc`.to_i
end
[ np, 0 ].max + 1
end
def num_threads=(num)
error_and_exit "Invalid number of threads: must be > 0" if 0 >= num
@num_threads = num
end
def add_test_case(num)
@tests += @dir_entries.select { |entry| /^test-#{num}/.match(entry) }
end
def exclude_test_case(num)
@exclusions += @dir_entries.select { |entry| /^test-#{num}/.match(entry) }
end
def get_tests_to_run
test_all = !@test_failed && !@test_new
tests = @tests.empty? ? @dir_entries : @tests
tests -= @exclusions
return tests.collect do |entry|
if (FileTest.file?(entry) && (entry =~ /^test-.*\.rb$/))
class_name = file_name_to_class_name entry
test_this = test_all
test_this ||= (@results.exist?(class_name) && ((@test_failed && (@results.status?(class_name) == :failed)) || (@test_new && (@results.status?(class_name) == :new))) ||
(@test_date_after && (@results.date_added?(class_name) < @test_date_after)) ||
(@test_date_before && (@results.date_added?(class_name) > @test_date_before)))
test_this ||= !@results.exist?(class_name) && (@test_new || @test_failed)
test_this ? class_name : nil
else
nil
end
end.compact.sort_by { |class_name| @results.duration? class_name }
end
def go
@num_failed = 0
@tests_to_run = self.get_tests_to_run
num_tests = @tests_to_run.size
@tests_mutex = Mutex.new
@results_mutex = Mutex.new
start = Time.now
self.run_threads
self.join_threads
test_end = Time.now
duration = test_end - start
show_message "#{@num_failed}/#{num_tests} failed (" + (num_tests > 0 ? (@num_failed * 100 / num_tests).to_s : "0") + "%). " +
"Tests took #{duration}s (#{start.strftime("%Y-%m-%d %H:%M:%S")} – #{test_end.strftime("%Y-%m-%d %H:%M:%S")})"
end
def run_threads
@threads = Array.new
(1..@num_threads).each do |number|
@threads << Thread.new(number) do |thread_number|
Thread.current[:number] = thread_number
while true
@tests_mutex.lock
class_name = @tests_to_run.shift
@tests_mutex.unlock
break unless class_name
self.run_test class_name
end
end
end
end
def join_threads
@threads.each &:join
end
def run_test(class_name)
begin
current_test = SimpleTest.instantiate class_name
rescue Exception => ex
self.add_result class_name, :failed, :message => " Failed to load or create an instance of class '#{class_name}'. #{ex}"
return
end
if (current_test.description == "INSERT DESCRIPTION")
show_message "Skipping '#{class_name}': Not implemented yet"
return
end
if current_test.methods.include?(:skip?) and current_test.skip?
show_message "Skipping '#{class_name}': disabled"
return
end
show_message "Running '#{class_name}': #{current_test.description}"
expected_results = @results.exist?(class_name) ? @results.hash?(class_name).split(/-/) : nil
start = Time.now
result = current_test.run_test expected_results
duration = Time.now - start
puts "Finished '#{class_name}' after #{sprintf('%0.3f', duration)}s" if self.show_duration
if (result)
if result.include?("failed")
self.add_result class_name, :failed, :message => " #{class_name} FAILED: test failed with an exception"
elsif (!@results.exist? class_name)
self.add_result class_name, :passed, :message => " NEW test. Storing result '#{result}'.", :checksum => result, :duration => duration
elsif (@results.hash?(class_name) == result)
self.add_result class_name, :passed, :duration => duration
else
for_mkvgoodbad = @for_mkvgoodbad ? " for mkvgoodbad" : ""
msg = " #{class_name} FAILED: checksum is different. Commands#{ for_mkvgoodbad}:\n"
actual_results = result.split(/-/)
idx = 0
if (@for_mkvgoodbad)
current_test.commands.each do |command|
command = { :command => command } unless command.is_a?(Hash)
if !command[:no_result] && (expected_results[idx] != actual_results[idx])
mkvgoodbad_cmd = command[:command].gsub(%r{^\.\./src/| -o [a-z0-9._/-]+| *>.*}i, '').gsub(%r{ +}, ' ')
if %r{^mkvmerge}.match(mkvgoodbad_cmd)
mkvgoodbad_cmd.
gsub!(%r{^mkvmerge}, 'mkvgoodbad').
gsub!(%r{ *--engage no_variable_data *}, ' ').
gsub!(%r{ +}, ' ')
end
if %r{ -i | -J |--identify}.match(mkvgoodbad_cmd)
mkvgoodbad_cmd.gsub!(%r{^mkvgoodbad}, 'mkvgoodbadidentify')
end
msg += "#{mkvgoodbad_cmd}\n"
end
idx += 1 unless command[:no_result]
end
else
current_test.commands.each do |command|
command = { :command => command } unless command.is_a?(Hash)
prefix = !command[:no_result] && (expected_results[idx] != actual_results[idx]) ? "(*)" : " "
msg += " #{prefix} #{command[:command]}\n"
idx += 1 unless command[:no_result]
end
end
if (update_failed && actual_results.include?("failed"))
self.add_result class_name, :failed, :message => msg + " FATAL: cannot update result as test results include 'failed'"
elsif (update_failed)
self.add_result class_name, :passed, :message => msg + " UPDATING result\n", :checksum => result, :duration => duration
else
self.add_result class_name, :failed, :message => msg
end
end
else
self.add_result class_name, :failed, :message => " #{class_name} FAILED: no result from test"
end
end
def add_result(class_name, result, opts = {})
@results_mutex.lock
show_message opts[:message] if opts[:message]
@num_failed += 1 if result == :failed
if !@results.exist? class_name
@results.add class_name, opts[:checksum]
else
@results.set class_name, result
@results.set_hash class_name, opts[:checksum] if opts[:checksum]
end
@results.set_duration class_name, opts[:duration] if opts[:duration] && (result == :passed)
@results_mutex.unlock
end
def list_failed_ids
entries = @dir_entries.collect do |entry|
if (FileTest.file?(entry) && (entry =~ /^test-(\d+).*\.rb$/))
class_name = file_name_to_class_name entry
@results.status?(class_name) == :failed ? $1 : nil
else
nil
end
end
puts entries.reject(&:nil?).sort_by(&:to_i).join(" ")
end
end
|