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
|
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'optparse'
require 'fileutils'
require 'uri'
require 'json'
require 'set' # -- Ruby 3.1 and earlier needs this. Drop this line after Ruby 3.2+ is only supported.
class FailedTests
DEFAULT_OPTIONS = {
previous_tests_report_path: 'test_results/previous/test_reports.json',
output_directory: 'tmp/previous_failed_tests/',
format: :oneline,
rspec_pg_regex: /rspec .+ pg14( .+)?/,
rspec_ee_pg_regex: /rspec-ee .+ pg14( .+)?/
}.freeze
def initialize(options)
@filename = options.delete(:previous_tests_report_path)
@output_directory = options.delete(:output_directory)
@format = options.delete(:format).to_sym
@rspec_pg_regex = options.delete(:rspec_pg_regex)
@rspec_ee_pg_regex = options.delete(:rspec_ee_pg_regex)
end
def output_failed_tests
create_output_dir
failed_cases_for_suite_collection.each do |suite_name, suite_tests|
puts "[FailedTests] Detected #{suite_tests.size} failed tests in suite #{suite_name}..."
suite_tests =
case format
when :oneline
suite_tests.map { |test| test['file'] }.join(' ') # rubocop:disable Rails/Pluck
when :json
JSON.pretty_generate(suite_tests.to_a)
end
output_file = File.join(output_directory, "#{suite_name}_failed_tests.#{output_file_format}")
File.open(output_file, 'w') do |file|
file.write(suite_tests)
end
end
end
def failed_cases_for_suite_collection
suite_map.each_with_object(Hash.new { |h, k| h[k] = Set.new }) do |(suite_name, suite_collection_regex), hash|
failed_suites.each do |suite|
hash[suite_name].merge(failed_cases(suite)) if suite_collection_regex.match?(suite['name'])
end
end
end
def suite_map
@suite_map ||= {
rspec: rspec_pg_regex,
rspec_ee: rspec_ee_pg_regex,
jest: /jest/
}
end
private
attr_reader :filename, :output_directory, :format, :rspec_pg_regex, :rspec_ee_pg_regex
def file_contents
@file_contents ||= begin
File.read(filename)
rescue Errno::ENOENT
'{}'
end
end
def file_contents_as_json
@file_contents_as_json ||= begin
JSON.parse(file_contents)
rescue JSON::ParserError
{}
end
end
def output_file_format
case format
when :oneline
'txt'
when :json
'json'
else
raise "[FailedTests] Unsupported format `#{format}` (allowed formats: `oneline` and `json`)!"
end
end
def failed_suites
return [] unless file_contents_as_json['suites']
file_contents_as_json['suites'].select { |suite| suite['failed_count'] > 0 }
end
def failed_cases(suite)
return [] unless suite
suite['test_cases'].filter_map do |failure_hash|
next if failure_hash['status'] != 'failed'
failure_hash['job_url'] = suite['job_url']
failure_hash['file'] = failure_hash['file'].delete_prefix('./')
failure_hash
end
end
def create_output_dir
return if File.directory?(output_directory)
puts '[FailedTests] Creating output directory...'
FileUtils.mkdir_p(output_directory)
end
end
if $PROGRAM_NAME == __FILE__
options = FailedTests::DEFAULT_OPTIONS.dup
OptionParser.new do |opts|
opts.on("-p", "--previous-tests-report-path PREVIOUS_TESTS_REPORT_PATH", String,
"Path of the file listing previous test failures (defaults to " \
"`#{FailedTests::DEFAULT_OPTIONS[:previous_tests_report_path]}`)") do |value|
options[:previous_tests_report_path] = value
end
opts.on("-o", "--output-directory OUTPUT_DIRECTORY", String,
"Output directory for failed test files (defaults to " \
"`#{FailedTests::DEFAULT_OPTIONS[:output_directory]}`)") do |value|
options[:output_directory] = value
end
opts.on("-f", "--format [oneline|json]", String,
"Format of the output files: oneline (with test filenames) or JSON (defaults to " \
"`#{FailedTests::DEFAULT_OPTIONS[:format]}`)") do |value|
options[:format] = value
end
opts.on("--rspec-pg-regex RSPEC_PG_REGEX", Regexp,
"Regex to use when finding matching RSpec jobs (defaults to " \
"`#{FailedTests::DEFAULT_OPTIONS[:rspec_pg_regex]}`)") do |value|
options[:rspec_pg_regex] = value
end
opts.on("--rspec-ee-pg-regex RSPEC_EE_PG_REGEX", Regexp,
"Regex to use when finding matching RSpec EE jobs (defaults to " \
"`#{FailedTests::DEFAULT_OPTIONS[:rspec_ee_pg_regex]}`)") do |value|
options[:rspec_ee_pg_regex] = value
end
opts.on("-h", "--help", "Prints this help") do
puts opts
exit
end
end.parse!
FailedTests.new(options).output_failed_tests
end
|