File: controller.rb

package info (click to toggle)
mkvtoolnix 97.0-1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 60,284 kB
  • sloc: cpp: 217,034; ruby: 11,453; xml: 8,125; ansic: 6,885; sh: 5,274; python: 1,041; perl: 191; makefile: 113; awk: 16; javascript: 4
file content (228 lines) | stat: -rw-r--r-- 7,703 bytes parent folder | download
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