You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
5.8 KiB
231 lines
5.8 KiB
#!/usr/bin/env ruby
|
|
# Purpose: run Debian package checks using lintian and report in JUnit format
|
|
################################################################################
|
|
# Notes:
|
|
# * for JUnit spec details see http://windyroad.org/dl/Open%20Source/JUnit.xsd
|
|
#
|
|
# This is loosely based on PHPUnit 4.x JUnit output an XSD for it can be found
|
|
# at https://github.com/jenkinsci/xunit-plugin
|
|
#
|
|
# Ideas:
|
|
# * integrate within Jenkins plugin (using jruby)
|
|
# * integrate in Violations plugin (for further reporting options)
|
|
# https://github.com/jenkinsci/violations-plugin.git
|
|
################################################################################
|
|
|
|
require 'shellwords'
|
|
|
|
### cmdline parsing {{{
|
|
require 'optparse'
|
|
options = {}
|
|
lintian_options = []
|
|
|
|
# default
|
|
lintian_file = "lintian.txt"
|
|
|
|
o = OptionParser.new do|opts|
|
|
opts.banner = "Usage: #{$0} [<options>] <debian_package_file(s)>"
|
|
|
|
options[:warnings] = false
|
|
opts.on( '-w', '--warnings', 'Output lintian errors *AND* warnings' ) do
|
|
options[:warnings] = true
|
|
end
|
|
|
|
options[:disablenotes] = false
|
|
opts.on('--disable-notes', 'Disable verbose lintian output' ) do
|
|
options[:disablenotes] = true
|
|
end
|
|
|
|
opts.on("--filename <filename>", String, "Write lintian output to <filename> (defaults to lintian.txt)") do |f|
|
|
lintian_file = f
|
|
end
|
|
|
|
options[:skiplintian] = false
|
|
opts.on("--skip-lintian", String, "filename file will be processed") do
|
|
options[:skiplintian] = true
|
|
end
|
|
|
|
options[:disableplaintext] = false
|
|
opts.on('--disable-plaintext', 'Disable recording lintian output in lintian.txt' ) do
|
|
options[:disableplaintext] = true
|
|
end
|
|
|
|
opts.on('--lintian-opt=OPTION', 'Pass OPTION to lintian. Can be given multiple times.') do |lo|
|
|
lintian_options << lo
|
|
end
|
|
|
|
opts.on('--mark-warnings-skipped',
|
|
'Mark warnings as skipped test cases.') do
|
|
options[:markwarningsskipped] = true
|
|
end
|
|
|
|
opts.on('--mark-as-skipped=tag1,tag2,...', Array,
|
|
'Mark selected tags as skipped.') do |l|
|
|
options[:markasskipped] = l
|
|
end
|
|
|
|
opts.on( '-h', '--help', 'Display this screen' ) do
|
|
puts opts
|
|
exit
|
|
end
|
|
end
|
|
|
|
begin o.parse! ARGV
|
|
rescue OptionParser::InvalidOption => e
|
|
puts e
|
|
puts o
|
|
exit(1)
|
|
end
|
|
|
|
# brrr!
|
|
def usage
|
|
$stderr.puts "Usage: #{$0} [<options>] <debian_package_file(s)>"
|
|
exit(1)
|
|
end
|
|
|
|
files = ARGV
|
|
usage if files.empty?
|
|
### }}}
|
|
|
|
if ! options[:skiplintian] then
|
|
### make sure lintian is available {{{
|
|
if not system("which lintian >/dev/null 2>&1") then
|
|
$stderr.puts "Error: lintian not available."
|
|
exit(1)
|
|
end
|
|
# }}}
|
|
|
|
### run lintian {{{
|
|
start = Time.now.to_f
|
|
|
|
lintian_options << "--info" unless options[:disablenotes]
|
|
|
|
# Ruby 1.8's IO.popen expects a string instead of an array :(
|
|
lintian_cmd = (['lintian'] + lintian_options + files).collect { |v| Shellwords.escape(v) }.join(" ")
|
|
$output = IO.popen(lintian_cmd) do |io|
|
|
io.read
|
|
end
|
|
|
|
$duration = Time.now.to_f - start
|
|
|
|
if ! options[:disablenotes] then
|
|
File.open(lintian_file, 'w') {|f| f.write($output) }
|
|
end
|
|
### }}}
|
|
else
|
|
$duration = 0
|
|
$output = File.open(lintian_file, 'r').read
|
|
end
|
|
|
|
class JUnitOutput
|
|
require 'rexml/formatters/transitive'
|
|
require 'rexml/document'
|
|
|
|
def initialize(duration)
|
|
@duration = duration
|
|
@document = REXML::Document::new
|
|
@document << REXML::XMLDecl::new
|
|
@failures = 0
|
|
@skipped = 0
|
|
@suite = @document.add_element 'testsuite', {'time' => duration, 'name' => 'lintian'}
|
|
end
|
|
|
|
def add_case(package, tag)
|
|
tc = @suite.add_element 'testcase'
|
|
tc.attributes['name'] = "#{tag}"
|
|
tc.attributes['classname'] = "lintian.#{package}"
|
|
tc.attributes['assertions'] = 0
|
|
@last_tag = tag
|
|
@last_package = package
|
|
end
|
|
|
|
def mark(kind, message)
|
|
tc = @suite[-1]
|
|
e = tc.add_element kind
|
|
e.attributes['type'] = @last_tag
|
|
e.attributes['message'] = message
|
|
end
|
|
|
|
def mark_skipped(message = '')
|
|
@skipped += 1
|
|
mark('skipped', message)
|
|
end
|
|
|
|
def mark_failure(message = '')
|
|
@failures += 1
|
|
mark('failure', message)
|
|
end
|
|
|
|
def append_stdout(s)
|
|
stdout = @suite[-1].get_elements('system-out')[0]
|
|
stdout ||= @suite[-1].add_element 'system-out'
|
|
stdout.add_text(s)
|
|
end
|
|
|
|
def add_success
|
|
self.add_case('<all>', 'lintian-checks')
|
|
end
|
|
|
|
def finish
|
|
self.add_success unless @suite.has_elements?
|
|
tc_time = @duration / @suite.size
|
|
@suite.each { |tc| tc.attributes['time'] = tc_time }
|
|
@suite.attributes['tests'] = @suite.size
|
|
@suite.attributes['failures'] = @failures
|
|
@suite.attributes['skipped'] = @skipped
|
|
@suite.attributes['errors'] = 0
|
|
@suite.attributes['assertions'] = 0
|
|
end
|
|
|
|
def write
|
|
self.finish
|
|
formatter = REXML::Formatters::Transitive::new
|
|
formatter.write(@document, $stdout)
|
|
end
|
|
end
|
|
|
|
junit_output = JUnitOutput::new($duration)
|
|
|
|
infos = Hash.new { |hash, key| hash[key] = "" }
|
|
last_tag = nil
|
|
tc_open = false
|
|
|
|
$output.each_line do |line|
|
|
case line
|
|
when /^([EW]):\s([^:]*):\s(.*)/
|
|
kind, package, note = $~.captures
|
|
|
|
if tc_open then
|
|
junit_output.append_stdout(infos[last_tag])
|
|
tc_open = false
|
|
end
|
|
|
|
tag = note.match('^[^\s]*')[0]
|
|
|
|
if kind == 'E' || options[:warnings] then
|
|
junit_output.add_case(package, note)
|
|
if (kind == 'W' && options[:markwarningsskipped]) ||
|
|
(options[:markasskipped] && options[:markasskipped].include?(tag))
|
|
junit_output.mark_skipped(line.rstrip)
|
|
else
|
|
junit_output.mark_failure(line.rstrip)
|
|
end
|
|
tc_open = true
|
|
end
|
|
|
|
last_tag = tag
|
|
|
|
# Tag descriptions are prefixed with four spaces, which differentiates them
|
|
# from debug messages
|
|
when /^N:\s{4}/
|
|
infos[last_tag] << line if last_tag
|
|
end
|
|
end
|
|
|
|
junit_output.append_stdout(infos[last_tag]) if tc_open
|
|
junit_output.write
|
|
### }}}
|
|
|
|
## END OF FILE #################################################################
|
|
# vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2 ft=ruby
|