forked from capability-boosters-dev/canvas-lms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrspec-queue
executable file
·159 lines (136 loc) · 4.96 KB
/
rspec-queue
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
#!/usr/bin/env ruby
$stdout.sync = $stderr.sync = true
require 'test_queue'
require 'test_queue/runner/rspec'
class CanvasSpecRunner < TestQueue::Runner::RSpec
def initialize(*)
detect_instance_termination if ENV["TEST_QUEUE_DETECT_INSTANCE_TERMINATION"]
# make sure we're applying our config 'n stuff right away
require ::RSpec::Core::RubyProject.root + "/spec/spec_helper"
super
end
def detect_instance_termination
File.delete("/tmp/instance_terminating") if File.exist?("/tmp/instance_terminating")
exit if instance_terminating?
Thread.new do
loop do
break if instance_terminating?
sleep 5
end
puts "Received spot instance termination warning, shutting down relay"
end
end
def instance_terminating?
system("curl -f -s -o /tmp/instance_terminating http://169.254.169.254/latest/meta-data/spot/termination-time")
end
def after_fork(num)
# for the runtime logger
ENV["TEST_ENV_NUMBER"] = num > 1 ? num.to_s : ""
ENV["SELINIMUM_BATCH_NAME"] = "#{ENV["SERVER_NUMBER"]}.#{ENV["TEST_ENV_NUMBER"]}"
Canvas::Reloader.reload!
TestDatabaseUtils.reconnect!
TestDatabaseUtils.reset_database!
Canvas.reconnect_redis
end
# test-queue does `exit!` in a couple places; override so at_exit hooks run (like simplecov)
# also preserve rspec exit codes
def summarize
output_profile_data if ::RSpec.configuration.profile_examples?
error_statuses = @completed.map { |worker| worker.status.exitstatus } - [0]
# a percentage of workers can abort/fail prior to running any specs
# (e.g. due to selenium/headless/firefox woes, etc)
allowed_sadness = 0.2
sadness_exit_status = 98
max_sadnesses = allowed_sadness * @completed.size
num_sadnesses = error_statuses.count(sadness_exit_status)
if num_sadnesses > 0 && num_sadnesses <= max_sadnesses
puts "Warning: #{num_sadnesses} workers failed prior to running specs, still below threshold"
error_statuses -= [sadness_exit_status]
end
error_statuses << 1 if @timed_out
error_statuses.uniq!
if error_statuses.empty?
# yay
exit 0
elsif error_statuses == [::RSpec.configuration.failure_exit_code]
# :'( but we can retry
exit ::RSpec.configuration.failure_exit_code
elsif error_statuses.include?(nil)
# We're seeing this with Chromedriver crashes from test-queue, we can't do much about these for now
puts "Error statuses: "
p @completed.reduce([]){|memo, c| c.pid == 0 ? memo : memo + [c.pid, c.status.exitstatus]}
exit 98
else
puts "Error statuses: "
p @completed.map{|c| [c.pid, c.status.exitstatus]}
# this shouldn't happen, crap is broken
exit 1
end
end
def run_worker(iterator)
@run_worker_ret = super
end
def cleanup_worker
Kernel.exit @run_worker_ret || 0
end
def output_profile_data
e = "(?:\e\\[\\d+m)?"
examples = raw_profile_output.scan(/^( .*\n #{e}([\d.]+)#{e} #{e}seconds#{e} \..*\n)/)
.sort_by { |e| e.last.to_f }
.map(&:first)
.reverse
.first(::RSpec.configuration.profile_examples)
puts "\nTop #{examples.count} slowest examples:"
puts examples.join
groups = raw_profile_output.scan(/^( .*\n #{e}([\d.]+)#{e} #{e}seconds#{e} average .*\n)/)
.sort_by { |e| e.last.to_f }
.map(&:first)
.reverse
.first(::RSpec.configuration.profile_examples)
puts "\nTop #{groups.count} slowest example groups:"
puts groups.join
puts
end
def raw_profile_output
@raw_profile_output ||= ""
end
def worker_completed(worker)
return if @aborting
if ::RSpec.configuration.profile_examples? && profile_output = worker.output.match(/^Top \d+ slowest.*\n(?=Finished in )/m)
profile_output = profile_output[0]
raw_profile_output << profile_output
# take it out of the output, if we're displaying it
worker.output.sub!(profile_output, "") if ENV['TEST_QUEUE_VERBOSE'] || worker.status.exitstatus != 0
end
super
end
end
TestQueue::Iterator.prepend(Module.new {
def query(*)
super
ensure
if !@done && instance_terminating?
puts "Received spot instance termination warning, shutting down worker"
@done = true
end
end
def instance_terminating?
File.exist?("/tmp/instance_terminating")
end
})
TestQueue::Runner::RSpec::LazyGroups::BackgroundLoaderProxy.singleton_class.prepend(Module.new {
def order_files(files)
files = super
puts "Running files according to descending cost (slowest spec + hooks)"
puts "Any file with a cost of Infinity either 1. is brand new or 2. has no specs and could maybe be removed"
files.each do |file|
base_cost = (file_group_map[file] || [])
.map { |group| stats[group] }
.compact
.max || Float::INFINITY
puts "#{file} cost: #{base_cost}, file size: #{File.size(file)}"
end
files
end
}) if defined?(TestQueue::Runner::RSpec::LazyGroups) && ENV["TEST_QUEUE_VERBOSE"]
CanvasSpecRunner.new.execute