mirror of
https://github.com/we-promise/sure.git
synced 2026-05-29 23:39:03 +00:00
162 lines
4.6 KiB
Ruby
162 lines
4.6 KiB
Ruby
require "json"
|
|
require "fileutils"
|
|
require "minitest"
|
|
require "pathname"
|
|
require "time"
|
|
|
|
module CiSystemTestTimingPlugin
|
|
OUTPUT_DIR = File.expand_path("../../tmp/ci", __dir__)
|
|
|
|
THEME_ALIASES = {
|
|
"account" => "accounts",
|
|
"drag" => "imports",
|
|
"import" => "imports",
|
|
"transaction" => "transactions",
|
|
"setting" => "settings"
|
|
}.freeze
|
|
|
|
class Reporter < Minitest::StatisticsReporter
|
|
attr_reader :entries
|
|
|
|
def start
|
|
super
|
|
@entries = []
|
|
end
|
|
|
|
def record(result)
|
|
super
|
|
|
|
source_location = Array(result.source_location)
|
|
file = source_location.first
|
|
line = source_location.last
|
|
relative_file = file && Pathname.new(file).relative_path_from(Pathname.pwd).to_s
|
|
|
|
@entries << {
|
|
class_name: result.class_name,
|
|
name: result.name,
|
|
location: line && relative_file ? "#{relative_file}:#{line}" : relative_file,
|
|
file: relative_file,
|
|
theme: theme_for(relative_file),
|
|
time: result.time.to_f,
|
|
assertions: result.assertions,
|
|
failures: result.failures.size,
|
|
skipped: result.skipped?,
|
|
error: result.error?
|
|
}
|
|
end
|
|
|
|
def report
|
|
write_reports
|
|
super
|
|
end
|
|
|
|
private
|
|
def write_reports
|
|
return if entries.empty?
|
|
|
|
FileUtils.mkdir_p(OUTPUT_DIR)
|
|
|
|
payload = {
|
|
generated_at: Time.now.utc.iso8601,
|
|
total_time: total_time,
|
|
test_count: entries.size,
|
|
groups: grouped_summary,
|
|
slowest: slowest_entries,
|
|
tests: entries.sort_by { |entry| -entry[:time] }
|
|
}
|
|
|
|
json_path = File.join(OUTPUT_DIR, "system_test_timing.json")
|
|
markdown_path = File.join(OUTPUT_DIR, "system_test_timing.md")
|
|
|
|
File.write(json_path, JSON.pretty_generate(payload))
|
|
|
|
markdown = build_markdown(payload)
|
|
File.write(markdown_path, markdown)
|
|
|
|
puts
|
|
puts markdown
|
|
|
|
append_step_summary(markdown)
|
|
end
|
|
|
|
def grouped_summary
|
|
entries
|
|
.group_by { |entry| entry[:theme] }
|
|
.map do |theme, theme_entries|
|
|
{
|
|
theme: theme,
|
|
total_time: theme_entries.sum { |entry| entry[:time] },
|
|
test_count: theme_entries.size,
|
|
slowest_test: theme_entries.max_by { |entry| entry[:time] }
|
|
}
|
|
end
|
|
.sort_by { |group| -group[:total_time] }
|
|
end
|
|
|
|
def slowest_entries(limit = 15)
|
|
entries.sort_by { |entry| -entry[:time] }.first(limit)
|
|
end
|
|
|
|
def build_markdown(payload)
|
|
lines = []
|
|
lines << "## System test timing summary"
|
|
lines <<
|
|
"Measured #{payload[:test_count]} tests in #{format_seconds(payload[:total_time])}. " \
|
|
"Grouped by likely split theme for future workflow sharding."
|
|
lines << ""
|
|
lines << "### Slowest themes"
|
|
lines << "| Theme | Total | Tests | Slowest test |"
|
|
lines << "| --- | ---: | ---: | --- |"
|
|
|
|
payload[:groups].each do |group|
|
|
slowest_test = group[:slowest_test]
|
|
lines << "| #{group[:theme]} | #{format_seconds(group[:total_time])} | #{group[:test_count]} | #{slowest_test[:class_name]}##{slowest_test[:name]} (#{format_seconds(slowest_test[:time])}) |"
|
|
end
|
|
|
|
lines << ""
|
|
lines << "### Slowest individual tests"
|
|
lines << "| Test | Theme | Time | Location |"
|
|
lines << "| --- | --- | ---: | --- |"
|
|
|
|
payload[:slowest].each do |entry|
|
|
lines << "| #{entry[:class_name]}##{entry[:name]} | #{entry[:theme]} | #{format_seconds(entry[:time])} | `#{entry[:location]}` |"
|
|
end
|
|
|
|
lines << ""
|
|
lines << "Artifacts: `tmp/ci/system_test_timing.json`, `tmp/ci/system_test_timing.md`"
|
|
lines.join("\n")
|
|
end
|
|
|
|
def append_step_summary(markdown)
|
|
summary_path = ENV["GITHUB_STEP_SUMMARY"]
|
|
return if summary_path.to_s.empty?
|
|
|
|
File.open(summary_path, "a") do |file|
|
|
file.puts(markdown)
|
|
file.puts
|
|
end
|
|
end
|
|
|
|
def format_seconds(seconds)
|
|
format("%.2fs", seconds)
|
|
end
|
|
|
|
def theme_for(relative_file)
|
|
return "unknown" if relative_file.to_s.empty?
|
|
|
|
test_path = relative_file.sub(%r{\Atest/system/}, "")
|
|
first_segment = test_path.split("/").first.to_s.sub(/_test\.rb\z/, "")
|
|
stem = first_segment.split("_").first
|
|
|
|
stem = nil if stem.nil? || stem.empty?
|
|
THEME_ALIASES.fetch(stem, stem || "unknown")
|
|
end
|
|
end
|
|
|
|
def self.minitest_plugin_init(_options)
|
|
Minitest.reporter << Reporter.new($stdout, {})
|
|
end
|
|
end
|
|
|
|
Minitest.register_plugin(CiSystemTestTimingPlugin)
|