Skip to content
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Tests

on: [push]
on: [push, pull_request]

jobs:
ruby-tests:
Expand Down
9 changes: 8 additions & 1 deletion ruby/lib/ci/queue/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ def from_env(env)

def load_flaky_tests(path)
return [] unless path
::File.readlines(path).map(&:chomp).to_set
if ::File.extname(path) == ".xml"
require 'rexml/document'
REXML::Document.new(::File.read(path)).elements.to_a("//testcase").map do |element|
"#{element.attributes['classname']}##{element.attributes['name']}"
end.to_set
else
::File.readlines(path).map(&:chomp).to_set
end
rescue SystemCallError
[]
end
Expand Down
92 changes: 90 additions & 2 deletions ruby/lib/minitest/queue/build_status_reporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,88 @@ module Queue
class BuildStatusReporter < Minitest::Reporters::BaseReporter
include ::CI::Queue::OutputHelpers

class JUnitReporter
def initialize(file, error_reports)
@file = file
@error_reports = error_reports
end

def write
File.open(@file, 'w+') do |file|
format_document(generate_document(@error_reports), file)
end
end

private

def generate_document(error_reports)
suites = error_reports.group_by { |error_report| error_report.test_suite }

doc = REXML::Document.new(nil, {
:prologue_quote => :quote,
:attribute_quote => :quote,
})
doc << REXML::XMLDecl.new('1.1', 'utf-8')

testsuites = doc.add_element('testsuites')
suites.each do |suite, error_reports|
add_tests_to(testsuites, suite, error_reports)
end

doc
end

def format_document(doc, io)
formatter = REXML::Formatters::Pretty.new
formatter.write(doc, io)
io << "\n"
end

def add_tests_to(testsuites, suite, error_reports)
testsuite = testsuites.add_element(
'testsuite',
'name' => suite,
'filepath' => Minitest::Queue.relative_path(error_reports.first.test_file),
'tests' => error_reports.count,
)

error_reports.each do |error_report|
attributes = {
'name' => error_report.test_name,
'classname' => error_report.test_suite,
}
attributes['lineno'] = error_report.test_line

testcase = testsuite.add_element('testcase', attributes)
add_xml_message_for(testcase, error_report)
rescue REXML::ParseException, RuntimeError => error
puts error
end
end

def add_xml_message_for(testcase, error_report)
failure = testcase.add_element('failure', 'type' => error_report.error_class, 'message' => truncate_message(error_report.to_s))
failure.add_text(REXML::CData.new(message_for(error_report)))
end

def truncate_message(message)
message.lines.first.chomp.gsub(/\e\[[^m]+m/, '')
end

def project_root_path_matcher
@project_root_path_matcher ||= %r{(?<=\s)#{Regexp.escape(Minitest::Queue.project_root)}/}
end

def message_for(error_report)
suite = error_report.test_suite
name = error_report.test_name
error = error_report.to_s

message_with_relative_paths = error.gsub(project_root_path_matcher, '')
"\nFailure:\n#{name}(#{suite}) [#{Minitest::Queue.relative_path(error_report.test_file)}]:\n#{message_with_relative_paths}\n"
end
end

def initialize(supervisor:, **options)
@supervisor = supervisor
@build = supervisor.build
Expand Down Expand Up @@ -107,11 +189,17 @@ def progress
end

def write_failure_file(file)
File.write(file, error_reports.map(&:to_h).to_json)
File.open(file, 'w') do |f|
JSON.dump(error_reports.map(&:to_h), f)
end
xml_file = File.join(File.dirname(file), "#{File.basename(file, File.extname(file))}.xml")
JUnitReporter.new(xml_file, error_reports).write
end

def write_flaky_tests_file(file)
File.write(file, flaky_reports.to_json)
File.open(file, 'w') do |f|
JSON.dump(flaky_reports, f)
end
end

private
Expand Down
4 changes: 4 additions & 0 deletions ruby/lib/minitest/queue/error_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ def test_name
@data[:test_name]
end

def error_class
@data[:error_class]
end

def test_and_module_name
@data[:test_and_module_name]
end
Expand Down
4 changes: 3 additions & 1 deletion ruby/lib/minitest/queue/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,9 @@ def display_warnings(build)
warnings = build.pop_warnings.map do |type, attributes|
attributes.merge(type: type)
end.compact
File.write(queue_config.warnings_file, warnings.to_json)
File.open(queue_config.warnings_file, 'w') do |f|
JSON.dump(warnings, f)
end
end

def run_tests_in_fork(queue)
Expand Down
15 changes: 15 additions & 0 deletions ruby/test/ci/queue/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,21 @@ def test_parses_file_correctly

flaky_tests = Configuration.load_flaky_tests('/tmp/does-not-exist')
assert_empty flaky_tests

Tempfile.open(['flaky_test_file', '.junit.xml']) do |file|
file.write(<<~XML)
<testsuite name="ATest">
<testcase name="test_foo" classname="ATest" />
<testcase name="test_bar" classname="ATest" />
</testsuite>
XML
file.close

flaky_tests = Configuration.load_flaky_tests(file.path)
assert_equal 2, flaky_tests.size
assert_includes flaky_tests, "ATest#test_foo"
assert_includes flaky_tests, "ATest#test_bar"
end
end

def test_queue_init_timeout_unset
Expand Down
8 changes: 8 additions & 0 deletions ruby/test/integration/minitest_redis_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,14 @@ def test_redis_reporter_failure_file
.sort_by { |failure_report| failure_report[:test_line] }
.first

xml_file = File.join(File.dirname(failure_file), "#{File.basename(failure_file, File.extname(failure_file))}.xml")
xml_content = File.read(xml_file)
xml = REXML::Document.new(xml_content)
testcase = xml.elements['testsuites/testsuite/testcase[@name="test_bar"]']
assert_equal "ATest", testcase.attributes['classname']
assert_equal "test_bar", testcase.attributes['name']
assert_equal "test/dummy_test.rb", testcase.parent.attributes['filepath']
assert_equal "ATest", testcase.parent.attributes['name']

## output and test_file
expected = {
Expand Down
Loading