tap-aggregator/src/main.cr

179 lines
4.1 KiB
Crystal

require "tap"
require "option_parser"
require "colorize"
class Storage
def initialize(@root : String = "storage")
end
def store(project, environment, revision, content)
date = Time.utc.to_unix
file_path = "#{@root}/#{project}/#{environment}/#{date}##{revision}.tap"
Dir.mkdir_p File.dirname file_path
File.write file_path, content
end
def projects
Dir.children @root
end
def environments(project)
Dir.children "#{@root}/#{project}"
end
def results(project, environment)
dir_name = "#{@root}/#{project}/#{environment}"
Dir.children(dir_name).compact_map do |file_name|
unless file_name.match /.tap$/
next
end
tap = Tap.parse File.read "#{dir_name}/#{file_name}"
next unless tap
md = file_name.match /^([^#]*)#(.*).tap$/
unless md
next
end
revision = md[2]
timestamp = md[1]
{revision, timestamp, tap}
end
end
end
Colorize.on_tty_only!
args = Array(String).new
show_projects = [] of String
show_environments = [] of String
show_revisions = [] of String
show_as_summary = true
storage_directory = "storage"
OptionParser.parse! do |parser|
parser.banner = "usage: tap-aggregator <command> [options]"
parser.on("-h", "--help", "Show this help") do
puts parser
exit 0
end
parser.on("-p id", "--project id", "Filter results that do not match this project.") do |id|
show_projects << id
end
parser.on("-e id", "--environment id", "Filter results that do not match this environment.") do |id|
show_environments << id
end
parser.on("-r id", "--revision id", "Filter results that do not match this revision id.") do |id|
show_revisions << id
end
parser.on("-s storage", "--storage storage", "Default: storage") do |storage|
storage_directory = storage
end
parser.on("-v", "--verbose", "Prints all data instead of just summaries.") do
show_as_summary = false
end
parser.invalid_option do |flag|
STDERR.puts "ERROR: #{flag} is not a valid option."
STDERR.puts parser
exit 1
end
parser.unknown_args do |x|
args = x
if args.size < 1
puts parser
exit 1
end
end
end
command = args[0]
storage = Storage.new storage_directory
enum Tap::Entry::Status
def to_s
if self == NotOk
"not ok"
elsif self == Ok
"ok"
end
end
end
case command
when "add"
if args.size != 4
STDERR.puts "usage: tap-aggregator add <project> <environment> <revision> [options]"
exit 1
end
project = args[1]
environment = args[2]
revision = args[3]
storage.store project, environment, revision, STDIN.gets_to_end
when "show"
projects = storage.projects.select do |project|
show_projects.size == 0 || show_projects.any? &.==(project)
end
projects.each do |project|
environments = storage.environments(project).select do |environment|
show_environments.size == 0 || show_environments.any? &.==(environment)
end
environments.each do |environment|
# FIXME: This currently parses everything, including entries
# that are not going to be displayed.
results = storage.results(project, environment).select do |revision, timestamp, suite|
show_revisions.size == 0 || show_revisions.any? &.==(revision)
end
results.each do |revision, timestamp, suite|
# FIXME: Only extract most recent revision if show_as_summary.
summary = suite.summary
number_ok = summary.tests_passed.size
number_failed = summary.tests_failed.size
# This prints the header, or summary line of a test suite.
STDOUT << ("%-15s" % project).colorize(:white).bright
STDOUT << " (#{environment}) r=#{revision}, "
STDOUT << "#{number_ok} ok".colorize(:green).bright
STDOUT << ", "
STDOUT << "#{number_failed} not ok".colorize(:red).bright
STDOUT << "\n"
if !show_as_summary
suite.each do |test|
# This prints a single TAP test entry.
STDOUT << " -> "
STDOUT << ("%3s" % test.id).colorize(:cyan)
if test.status.ok?
STDOUT << (" %6s " % test.status.to_s).colorize(:green)
else
STDOUT << (" %6s " % test.status.to_s).colorize(:red)
end
STDOUT << test.title.colorize(:white)
STDOUT << "\n"
STDOUT.flush
end
end
end
end
end
end