mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 11:54:01 +00:00
4442cd5673
The old code was pretty convoluted and hard to reason about. On top of that it didn't buffer PES packets properly before trying to parse the PES headers. This lead to accesses beyond the end of the buffer.
355 lines
10 KiB
Ruby
355 lines
10 KiB
Ruby
class SimpleTest
|
|
@@json_schema_identification = nil
|
|
|
|
EXIT_CODE_ALIASES = {
|
|
:success => 0,
|
|
:warning => 1,
|
|
:error => 2,
|
|
}
|
|
|
|
def self.instantiate class_name
|
|
file_name = class_name_to_file_name class_name
|
|
content = IO.readlines(file_name).join("")
|
|
|
|
if ! %r{class\s+.*?\s+<\s+Test}.match(content)
|
|
content = %Q!
|
|
class ::#{class_name} < SimpleTest
|
|
def initialize
|
|
super
|
|
|
|
#{content}
|
|
end
|
|
end
|
|
!
|
|
else
|
|
content.gsub!(/class\s+/, 'class ::')
|
|
end
|
|
|
|
eval content, nil, file_name, 5
|
|
|
|
constantize(class_name).new
|
|
end
|
|
|
|
def initialize
|
|
@commands = []
|
|
@tmp_num = 0
|
|
@tmp_num_mutex = Mutex.new
|
|
@blocks = {
|
|
:setup => [],
|
|
:tests => [],
|
|
:cleanup => [],
|
|
}
|
|
end
|
|
|
|
def commands
|
|
@commands
|
|
end
|
|
|
|
def describe description
|
|
@description = description
|
|
end
|
|
|
|
def setup &block
|
|
@blocks[:setup] << block
|
|
end
|
|
|
|
def cleanup &block
|
|
@blocks[:cleanup] << block
|
|
end
|
|
|
|
def tmp_name_prefix
|
|
[ "/tmp/mkvtoolnix-auto-test-#{self.class.name}", $$.to_s, Thread.current[:number] ].join("-") + "-"
|
|
end
|
|
|
|
def tmp_name
|
|
@tmp_num_mutex.lock
|
|
@tmp_num ||= 0
|
|
@tmp_num += 1
|
|
result = self.tmp_name_prefix + @tmp_num.to_s
|
|
@tmp_num_mutex.unlock
|
|
|
|
result
|
|
end
|
|
|
|
def tmp
|
|
@tmp ||= tmp_name
|
|
end
|
|
|
|
def clean_tmp
|
|
File.unlink(@tmp) if File.exists?(@tmp) && (ENV["KEEP_TMPFILES"] != "1")
|
|
@tmp = nil
|
|
end
|
|
|
|
def hash_file name
|
|
md5 name
|
|
end
|
|
|
|
def hash_tmp erase = true
|
|
output = hash_file @tmp
|
|
|
|
clean_tmp if erase
|
|
|
|
output
|
|
end
|
|
|
|
def unlink_tmp_files
|
|
return if ENV["KEEP_TMPFILES"] == "1"
|
|
re = %r{^#{self.tmp_name_prefix}}
|
|
Dir.entries("/tmp").each do |entry|
|
|
file = "/tmp/#{entry}"
|
|
File.unlink(file) if re.match(file) and File.exists?(file)
|
|
end
|
|
end
|
|
|
|
def test name, &block
|
|
@blocks[:tests] << { :name => name, :block => block }
|
|
end
|
|
|
|
def exit_code_string exit_code
|
|
return exit_code == 0 ? 'ok' \
|
|
: exit_code == 1 ? 'warning' \
|
|
: exit_code == 2 ? 'error' \
|
|
: "unknown(#{exit_code})"
|
|
end
|
|
|
|
def test_merge file, *args
|
|
options = args.extract_options!
|
|
full_command_line = [ options[:args], file ].flatten.join(' ')
|
|
options[:name] ||= full_command_line
|
|
options[:result_type] ||= :hash
|
|
@blocks[:tests] << {
|
|
:name => full_command_line,
|
|
:block => lambda {
|
|
output = options[:output] || tmp
|
|
_, exit_code = *merge(full_command_line, :exit_code => options[:exit_code], :output => output)
|
|
result = options[:exit_code] == :error ? 'error' \
|
|
: options[:result_type] == :hash ? hash_file(output) \
|
|
: exit_code_string(exit_code)
|
|
|
|
clean_tmp unless options[:keep_tmp]
|
|
|
|
result
|
|
},
|
|
}
|
|
end
|
|
|
|
def test_identify file, *args
|
|
options = args.extract_options!
|
|
options[:verbose] = true if options[:verbose].nil?
|
|
full_command_line = [ options[:verbose] ? "--identify-verbose" : "--identify", options[:args], file ].flatten.join(' ')
|
|
options[:name] ||= full_command_line
|
|
@blocks[:tests] << {
|
|
:name => full_command_line,
|
|
:block => lambda {
|
|
sys "../src/mkvmerge #{full_command_line} > #{tmp}", :exit_code => options[:exit_code]
|
|
if options[:filter]
|
|
text = options[:filter].call(IO.readlines(tmp).join(''))
|
|
File.open(tmp, 'w') { |tmp_file| tmp_file.puts text }
|
|
end
|
|
options[:keep_tmp] ? hash_file(tmp) : hash_tmp
|
|
},
|
|
}
|
|
end
|
|
|
|
def test_info file, *args
|
|
options = args.extract_options!
|
|
full_command_line = [ options[:args], file ].flatten.join(' ')
|
|
options[:name] ||= full_command_line
|
|
@blocks[:tests] << {
|
|
:name => full_command_line,
|
|
:block => lambda {
|
|
output = options[:output] || tmp
|
|
info full_command_line, :exit_code => options[:exit_code], :output => output
|
|
options[:keep_tmp] ? hash_file(output) : hash_tmp
|
|
},
|
|
}
|
|
end
|
|
|
|
def test_merge_unsupported file, *args
|
|
options = args.extract_options!
|
|
full_command_line = [ options[:args], file ].flatten.join(' ')
|
|
options[:name] ||= full_command_line
|
|
@blocks[:tests] << {
|
|
:name => full_command_line,
|
|
:block => lambda {
|
|
sys "../src/mkvmerge --identify-verbose #{full_command_line} > #{tmp}", :exit_code => 3
|
|
%r{unsupported container}.match(IO.readlines(tmp).first || '') ? :ok : :bad
|
|
},
|
|
}
|
|
end
|
|
|
|
def test_ui_locale locale, *args
|
|
describe "mkvmerge / UI locale: #{locale}"
|
|
|
|
@blocks[:tests] << {
|
|
:name => "mkvmerge UI locale #{locale}",
|
|
:block => lambda {
|
|
sys "../src/mkvmerge -o /dev/null --ui-language #{locale} data/avi/v.avi | head -n 2 | tail -n 1 > #{tmp}-#{locale}"
|
|
result = hash_file "#{tmp}-#{locale}"
|
|
self.error 'Locale #{locale} not supported by MKVToolNix' if result == 'f54ee70a6ad9bfc5f61de5ba5ca5a3c8'
|
|
result
|
|
},
|
|
}
|
|
@blocks[:tests] << {
|
|
:name => "mkvinfo UI locale #{locale}",
|
|
:block => lambda {
|
|
sys "../src/mkvinfo --ui-language #{locale} data/mkv/complex.mkv | head -n 2 > #{tmp}-#{locale}"
|
|
result = hash_file "#{tmp}-#{locale}"
|
|
self.error 'Locale #{locale} not supported by MKVToolNix' if result == 'f54ee70a6ad9bfc5f61de5ba5ca5a3c8'
|
|
result
|
|
},
|
|
}
|
|
end
|
|
|
|
def description
|
|
@description || fail("Class #{self.class.name} misses its description")
|
|
end
|
|
|
|
def run_test
|
|
@blocks[:setup].each(&:call)
|
|
|
|
results = @blocks[:tests].collect do |test|
|
|
result = nil
|
|
begin
|
|
result = test[:block].call
|
|
rescue RuntimeError => ex
|
|
show_message "Test case '#{self.class.name}', sub-test '#{test[:name]}': #{ex}"
|
|
end
|
|
result
|
|
end
|
|
|
|
@blocks[:cleanup].each(&:call)
|
|
|
|
unlink_tmp_files
|
|
|
|
results.join '-'
|
|
end
|
|
|
|
def merge *args
|
|
options = args.extract_options!
|
|
fail ArgumentError if args.empty?
|
|
|
|
output = options[:output] || self.tmp
|
|
command = "../src/mkvmerge --engage no_variable_data -o #{output} #{args.first}"
|
|
self.sys command, :exit_code => options[:exit_code]
|
|
end
|
|
|
|
def identify *args
|
|
options = args.extract_options!
|
|
fail ArgumentError if args.empty?
|
|
|
|
verbose = !options[:verbose].nil? ? options[:verbose] : true
|
|
format = options[:format] ? options[:format].to_s.downcase.gsub(/_/, '-') : verbose ? 'verbose-text' : 'text'
|
|
|
|
command = "../src/mkvmerge --identify --identification-format #{format} --engage no_variable_data #{args.first}"
|
|
|
|
self.sys command, :exit_code => options[:exit_code]
|
|
end
|
|
|
|
def identify_json *args
|
|
options = args.extract_options!
|
|
fail ArgumentError if args.empty?
|
|
|
|
command = "../src/mkvmerge --identify --identification-format json --engage no_variable_data #{args.first}"
|
|
|
|
output, _ = self.sys(command, :exit_code => options[:exit_code])
|
|
|
|
return JSON.load(output.join(''))
|
|
end
|
|
|
|
def info *args
|
|
options = args.extract_options!
|
|
fail ArgumentError if args.empty?
|
|
|
|
output = options[:output] || self.tmp
|
|
output = "> #{output}" unless %r{^[>\|]}.match(output)
|
|
output = '' if options[:output] == :return
|
|
command = "../src/mkvinfo --engage no_variable_data --ui-language en_US #{args.first} #{output}"
|
|
self.sys command, :exit_code => options[:exit_code]
|
|
end
|
|
|
|
def extract *args
|
|
options = args.extract_options!
|
|
fail ArgumentError if args.empty?
|
|
|
|
mode = options[:mode] || :tracks
|
|
command = "../src/mkvextract --engage no_variable_data #{mode} #{args.first} " + options.keys.select { |key| key.is_a?(Numeric) }.sort.collect { |key| "#{key}:#{options[key]}" }.join(' ')
|
|
|
|
self.sys command, :exit_code => options[:exit_code]
|
|
end
|
|
|
|
def propedit file_name, *args
|
|
options = args.extract_options!
|
|
fail ArgumentError if args.empty?
|
|
|
|
command = "../src/mkvpropedit --engage no_variable_data #{file_name} #{args.first}"
|
|
*result = self.sys command, :exit_code => options[:exit_code]
|
|
|
|
self.sys "../src/tools/ebml_validator -M #{file_name}", dont_record_command: true if FileTest.exists?("../src/tools/ebml_validator")
|
|
|
|
return *result
|
|
end
|
|
|
|
def sys *args
|
|
options = args.extract_options!
|
|
options[:exit_code] = EXIT_CODE_ALIASES[ options[:exit_code] ] || options[:exit_code] || 0
|
|
fail ArgumentError if args.empty?
|
|
|
|
command = args.shift
|
|
@commands << command unless options[:dont_record_command]
|
|
|
|
if !%r{>}.match command
|
|
temp_file = Tempfile.new('mkvtoolnix-test-output')
|
|
temp_file.close
|
|
command << " >#{temp_file.path} 2>&1 "
|
|
end
|
|
|
|
puts "COMMAND #{command}" if ENV['DEBUG']
|
|
|
|
exit_code = 0
|
|
if !system(command)
|
|
exit_code = $? >> 8
|
|
self.error "system command failed: #{command} (#{exit_code})" if options[:exit_code] != exit_code
|
|
end
|
|
|
|
return IO.readlines(temp_file.path), exit_code if temp_file
|
|
return exit_code
|
|
end
|
|
|
|
def error reason
|
|
show_message " Failed. Reason: #{reason}"
|
|
raise "test failed"
|
|
end
|
|
|
|
def json_schema_identification
|
|
return @@json_schema_identification if @@json_schema_identification
|
|
|
|
require "json_schema"
|
|
|
|
json_store = JsonSchema::DocumentStore.new
|
|
parser = JsonSchema::Parser.new
|
|
expander = JsonSchema::ReferenceExpander.new
|
|
schema = parser.parse JSON.load(File.read("../doc/json-schema/mkvmerge-identification-output-schema-v4.json"))
|
|
|
|
expander.expand(schema, store: json_store)
|
|
json_store.add_schema schema
|
|
|
|
@@json_schema_identification = schema
|
|
end
|
|
|
|
def json_summarize_tracks json
|
|
basics = %w{id type codec}
|
|
properties = %w{language number audio_channels audio_sampling_frequency pixel_dimensions}
|
|
|
|
json["tracks"].
|
|
map do |t|
|
|
(basics.map { |b| t[b] } +
|
|
properties.map { |p| t["properties"][p] }).
|
|
reject(&:nil?).
|
|
join("+").
|
|
gsub(%r{[:/+-]+}, "_")
|
|
end.
|
|
join("/")
|
|
end
|
|
end
|