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
|
# File 'lib/profiler/test_helpers/reporter.rb', line 8
def self.print
return unless Profiler.enabled?
profiles = Profiler.storage.list(limit: 1000).select { |p| p.profile_type == "test" }
return if profiles.empty?
passed = profiles.count { |p| test_status(p) == "passed" }
failed = profiles.count { |p| test_status(p) == "failed" }
pending = profiles.count { |p| test_status(p) == "pending" }
total_queries = profiles.sum { |p| (p.collector_data("database") || {})["total_queries"].to_i }
n1_profiles = profiles.select { |p| has_n1?(p) }
total_ms = profiles.sum(&:duration).round(0).to_i
lines = []
lines << ""
lines << cyan("┌─ Profiler Test Report " + "─" * (WIDTH - 23) + "┐")
lines << cyan("│") + " #{profiles.size} tests · #{passed} passed · #{failed} failed · #{pending} pending" +
pad_to(WIDTH - 1, "#{profiles.size} tests · #{passed} passed · #{failed} failed · #{pending} pending") +
cyan("│")
lines << cyan("│") + " Total: #{total_ms}ms · #{total_queries} queries · #{n1_profiles.size} N+1 detected" +
pad_to(WIDTH - 1, "Total: #{total_ms}ms · #{total_queries} queries · #{n1_profiles.size} N+1 detected") +
cyan("│")
slowest = profiles.sort_by { |p| -p.duration }.first(5)
lines << cyan("├─ Slowest tests " + "─" * (WIDTH - 17) + "┤")
slowest.each_with_index do |p, i|
test_data = p.collector_data("test") || {}
db_data = p.collector_data("database") || {}
name = truncate(test_data["test_name"] || p.path, 44)
queries = db_data["total_queries"].to_i
n1_flag = has_n1?(p) ? " #{red("⚠ N+1")}" : ""
stats_raw = "#{p.duration.round(0).to_i}ms #{queries}q"
pad = [0, WIDTH - 6 - name.length - stats_raw.length].max
lines << cyan("│") + " #{i + 1}. #{name}#{" " * pad}#{stats_raw}#{n1_flag}"
end
if n1_profiles.any?
lines << cyan("├─ N+1 patterns " + "─" * (WIDTH - 16) + "┤")
n1_profiles.first(3).each do |p|
test_data = p.collector_data("test") || {}
db_data = p.collector_data("database") || {}
queries = db_data["queries"] || []
pattern = top_n1_pattern(queries)
name = truncate(test_data["test_name"] || p.path, WIDTH - 4)
lines << cyan("│") + " #{yellow("▸")} #{yellow(truncate(pattern.to_s, WIDTH - 6))}"
lines << cyan("│") + " → #{name}"
end
end
failed_profiles = profiles.select { |p| test_status(p) == "failed" }
if failed_profiles.any?
lines << cyan("├─ Failed tests " + "─" * (WIDTH - 15) + "┤")
failed_profiles.first(5).each do |p|
test_data = p.collector_data("test") || {}
name = test_data["test_name"] || p.path
exception = test_data["exception_message"]
lines << cyan("│") + " #{red("✗")} #{truncate(name, WIDTH - 5)}"
lines << cyan("│") + " #{truncate(exception.to_s, WIDTH - 6)}" if exception
end
end
lines << cyan("└" + "─" * WIDTH + "┘")
lines << ""
$stdout.puts lines.join("\n")
rescue => e
warn "Profiler Reporter: failed to generate report: #{e.message}"
end
|