fast_ci 0.1.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +26 -0
- data/.rubocop.yml +3 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +154 -43
- data/LICENSE.txt +1 -1
- data/README.md +5 -1
- data/bin/fastci_brakeman +9 -0
- data/bin/fastci_bundle_audit +55 -0
- data/bin/fastci_rubycritic +10 -0
- data/fast_ci.gemspec +8 -5
- data/lib/fast_ci/brakeman/commandline.rb +12 -0
- data/lib/fast_ci/brakeman.rb +30 -0
- data/lib/fast_ci/configuration.rb +20 -5
- data/lib/fast_ci/dryrun_runner_prepend.rb +28 -0
- data/lib/fast_ci/extract_definitions.rb +44 -0
- data/lib/fast_ci/rspec_dryrun_formatter.rb +117 -0
- data/lib/fast_ci/rspec_formatter.rb +274 -0
- data/lib/fast_ci/rspec_run_formatter.rb +247 -0
- data/lib/fast_ci/ruby_critic/cli/application.rb +51 -0
- data/lib/fast_ci/runner_prepend.rb +107 -0
- data/lib/fast_ci/simple_cov/reporting.rb +39 -0
- data/lib/fast_ci/simple_cov.rb +68 -0
- data/lib/fast_ci/version.rb +1 -1
- data/lib/fast_ci.rb +112 -26
- data/lib/minitest/reporters/fastci_reporter.rb +287 -0
- data/lib/minitest/rubyci_plugin.rb +9 -0
- metadata +86 -9
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FastCI
|
4
|
+
class ExtractDescriptions
|
5
|
+
def call(example_group, count: false)
|
6
|
+
data = {}
|
7
|
+
|
8
|
+
data[scoped_id(example_group)] = {
|
9
|
+
description: description(example_group),
|
10
|
+
line_number: line_number(example_group),
|
11
|
+
}
|
12
|
+
|
13
|
+
if count
|
14
|
+
data[:test_count] ||= 0
|
15
|
+
data[:test_count] += RSpec.world.example_count([example_group])
|
16
|
+
end
|
17
|
+
|
18
|
+
example_group.examples.each do |ex|
|
19
|
+
data[scoped_id(example_group)][scoped_id(ex)] = {
|
20
|
+
line_number: line_number(ex),
|
21
|
+
description: description(ex),
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
example_group.children.each do |child|
|
26
|
+
data[scoped_id(example_group)].merge! call(child)
|
27
|
+
end
|
28
|
+
|
29
|
+
data
|
30
|
+
end
|
31
|
+
|
32
|
+
def scoped_id(example_group)
|
33
|
+
example_group.metadata[:scoped_id].split(":").last
|
34
|
+
end
|
35
|
+
|
36
|
+
def line_number(example_group)
|
37
|
+
example_group.metadata[:line_number]
|
38
|
+
end
|
39
|
+
|
40
|
+
def description(example_group)
|
41
|
+
example_group.metadata[:description]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FastCI
|
4
|
+
class RspecDryrunFormatter
|
5
|
+
RSpec::Core::Formatters.register self,
|
6
|
+
:start,
|
7
|
+
:message,
|
8
|
+
:example_passed,
|
9
|
+
:example_failed,
|
10
|
+
:example_pending,
|
11
|
+
:example_group_finished,
|
12
|
+
:example_group_started,
|
13
|
+
:dump_summary,
|
14
|
+
:close
|
15
|
+
|
16
|
+
def initialize(output)
|
17
|
+
@output = output
|
18
|
+
@current_group_path = []
|
19
|
+
@current_file = nil
|
20
|
+
@current_file_count = 0
|
21
|
+
@events = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(message_notification)
|
25
|
+
if message_notification.message.include?('An error occurred')
|
26
|
+
msg(:message, Base64.strict_encode64("\nAn error occurred#{message_notification.message}"))
|
27
|
+
send_events
|
28
|
+
end
|
29
|
+
@output.print message_notification.message
|
30
|
+
@output.print "\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def start(example_count)
|
34
|
+
msg(:start, { "example_count" => example_count.count, "timestamp" => time_now })
|
35
|
+
end
|
36
|
+
|
37
|
+
def close(*args)
|
38
|
+
msg(:close, { "timestamp" => time_now })
|
39
|
+
send_events
|
40
|
+
end
|
41
|
+
|
42
|
+
def dump_summary(summary_notification)
|
43
|
+
end
|
44
|
+
|
45
|
+
def time_now
|
46
|
+
time_frozen? ? Timecop.return { Time.now } : Time.now
|
47
|
+
end
|
48
|
+
|
49
|
+
def time_frozen?
|
50
|
+
return unless defined?(Timecop)
|
51
|
+
Timecop.frozen?
|
52
|
+
end
|
53
|
+
|
54
|
+
def example_passed(example_notification)
|
55
|
+
example_finished(example_notification)
|
56
|
+
@output.print RSpec::Core::Formatters::ConsoleCodes.wrap('.', :success)
|
57
|
+
end
|
58
|
+
|
59
|
+
def example_failed(example_notification)
|
60
|
+
example_finished(example_notification)
|
61
|
+
@output.print RSpec::Core::Formatters::ConsoleCodes.wrap('F', :failure)
|
62
|
+
end
|
63
|
+
|
64
|
+
def example_pending(example_notification)
|
65
|
+
example_finished(example_notification)
|
66
|
+
@output.print RSpec::Core::Formatters::ConsoleCodes.wrap('*', :pending)
|
67
|
+
end
|
68
|
+
|
69
|
+
def start_dump(_notification)
|
70
|
+
@output.print "\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
def example_group_finished(_group_notification)
|
74
|
+
@current_group_path.pop
|
75
|
+
if @current_group_path.empty? # its a file
|
76
|
+
msg(:file_examples_count, [@current_file, @current_file_count])
|
77
|
+
@current_file_count = 0
|
78
|
+
@current_file = nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def example_group_started(group_notification)
|
83
|
+
if @current_group_path.size == 0
|
84
|
+
@current_file = group_notification.group.metadata[:file_path].gsub("./".freeze, "".freeze)
|
85
|
+
@current_file_count = 0
|
86
|
+
end
|
87
|
+
@current_group_path << 1
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def example_finished(example_notification)
|
93
|
+
@current_file_count += 1
|
94
|
+
end
|
95
|
+
|
96
|
+
def send_events
|
97
|
+
return unless @events.length > 0
|
98
|
+
|
99
|
+
json_events = {
|
100
|
+
build_id: FastCI.configuration.orig_build_id,
|
101
|
+
compressed_data: Base64.strict_encode64(Zlib::Deflate.deflate(JSON.fast_generate(@events), 9)),
|
102
|
+
}
|
103
|
+
|
104
|
+
FastCI.send_events(json_events)
|
105
|
+
|
106
|
+
@events = []
|
107
|
+
end
|
108
|
+
|
109
|
+
def msg(event, data)
|
110
|
+
@events << ["rspec_dryrun_#{event}".upcase, ['0', data]]
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_scope_id(metadata)
|
114
|
+
metadata[:scoped_id].split(":").last || raise("No scoped id")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FastCI
|
4
|
+
class RspecFormatter
|
5
|
+
attr_reader :current_test_key
|
6
|
+
|
7
|
+
def initialize(output)
|
8
|
+
@output = output
|
9
|
+
@event_output = {}
|
10
|
+
@is_failed = false
|
11
|
+
@current_path = []
|
12
|
+
@current_path_started_at = []
|
13
|
+
@max_heap_live_num = 0
|
14
|
+
@dup_stdout = STDOUT.clone
|
15
|
+
@events = []
|
16
|
+
|
17
|
+
$stdout = StringIO.new()
|
18
|
+
|
19
|
+
@log_thread = Thread.new do
|
20
|
+
loop do
|
21
|
+
sleep 10
|
22
|
+
check_heap_live_num
|
23
|
+
@should_send_events = true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def time_now
|
29
|
+
time_frozen? ? Timecop.return { Time.now } : Time.now
|
30
|
+
end
|
31
|
+
|
32
|
+
def time_frozen?
|
33
|
+
return unless defined?(Timecop)
|
34
|
+
Timecop.frozen?
|
35
|
+
end
|
36
|
+
|
37
|
+
def rspec_runner_index
|
38
|
+
ENV["TEST_ENV_NUMBER"]
|
39
|
+
end
|
40
|
+
|
41
|
+
def send_events
|
42
|
+
@should_send_events = false
|
43
|
+
|
44
|
+
if @events.length > 0
|
45
|
+
json_events = {
|
46
|
+
build_id: FastCI.configuration.orig_build_id,
|
47
|
+
compressed_data: Base64.strict_encode64(Zlib::Deflate.deflate(JSON.fast_generate(@events), 9)),
|
48
|
+
}
|
49
|
+
|
50
|
+
FastCI.send_events(json_events)
|
51
|
+
|
52
|
+
@events = []
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def check_heap_live_num
|
57
|
+
@max_heap_live_num = [@max_heap_live_num, GC.stat[:heap_live_slots] || GC.stat[:heap_live_num]].max
|
58
|
+
end
|
59
|
+
|
60
|
+
def passed?
|
61
|
+
!@is_failed
|
62
|
+
end
|
63
|
+
|
64
|
+
def current_test_key=(value)
|
65
|
+
@current_test_key = value
|
66
|
+
end
|
67
|
+
|
68
|
+
def start(start_notification)
|
69
|
+
# $stderr = $stdout
|
70
|
+
|
71
|
+
data = {
|
72
|
+
load_time: start_notification.load_time,
|
73
|
+
example_count: start_notification.count,
|
74
|
+
started_at: time_now.to_s
|
75
|
+
}
|
76
|
+
|
77
|
+
return if running_only_failed? ||
|
78
|
+
running_gem_or_engine? ||
|
79
|
+
ENV["EXTRA_SLOWER_RUN"]
|
80
|
+
|
81
|
+
msg(:start, data)
|
82
|
+
end
|
83
|
+
|
84
|
+
def close(null_notification)
|
85
|
+
# check_heap_live_num
|
86
|
+
msg(:gc_stat, GC.stat.merge(max_heap_live_num: @max_heap_live_num))
|
87
|
+
unless running_only_failed? || ENV["EXTRA_SLOWER_RUN"] || running_gem_or_engine?
|
88
|
+
msg(:close, {final_output: get_output})
|
89
|
+
end
|
90
|
+
send_events
|
91
|
+
$stdout = @dup_stdout
|
92
|
+
end
|
93
|
+
|
94
|
+
def example_group_started(group_notification)
|
95
|
+
metadata = group_notification.group.metadata
|
96
|
+
@current_path_started_at << time_now
|
97
|
+
|
98
|
+
if @current_path.size == 0
|
99
|
+
@example_failed_index = 0
|
100
|
+
file_path = metadata[:file_path].gsub("./".freeze, "".freeze)
|
101
|
+
file_path = [ENV["DIR_PREFIX"], file_path].join("/") if ENV["DIR_PREFIX"]
|
102
|
+
@current_path << file_path
|
103
|
+
end
|
104
|
+
|
105
|
+
@current_path << id(metadata)
|
106
|
+
|
107
|
+
msg(:group_started, [
|
108
|
+
path_with_file(group_notification.group),
|
109
|
+
{
|
110
|
+
line_number: metadata[:line_number],
|
111
|
+
description: metadata[:description],
|
112
|
+
}
|
113
|
+
])
|
114
|
+
end
|
115
|
+
|
116
|
+
def example_started(example_notification)
|
117
|
+
@output_before = get_output
|
118
|
+
end
|
119
|
+
|
120
|
+
def example_passed(example_notification)
|
121
|
+
metadata = example_notification.example.metadata
|
122
|
+
broadcast_example_finished(serialize_example(metadata, "passed".freeze), example_notification.example)
|
123
|
+
end
|
124
|
+
|
125
|
+
def example_failed(example_notification)
|
126
|
+
@example_failed_index += 1
|
127
|
+
metadata = example_notification.example.metadata
|
128
|
+
fully_formatted = example_notification.fully_formatted(@example_failed_index, ::RSpec::Core::Formatters::ConsoleCodes)
|
129
|
+
|
130
|
+
broadcast_example_finished(
|
131
|
+
serialize_example(metadata, "failed".freeze, fully_formatted),
|
132
|
+
example_notification.example
|
133
|
+
)
|
134
|
+
end
|
135
|
+
|
136
|
+
def example_pending(example_notification)
|
137
|
+
metadata = example_notification.example.metadata
|
138
|
+
broadcast_example_finished(
|
139
|
+
serialize_example(metadata, "pending".freeze),
|
140
|
+
example_notification.example
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
def example_group_finished(group_notification)
|
145
|
+
run_time = time_now - @current_path_started_at.pop
|
146
|
+
if (run_time < 0) || (run_time > 2400)
|
147
|
+
run_time = 0.525
|
148
|
+
end
|
149
|
+
msg(:group_finished, [path_with_file(group_notification.group), {run_time: run_time}])
|
150
|
+
# msg(:group_finished, [@current_path.map(&:to_s), {run_time: run_time}])
|
151
|
+
@current_path.pop
|
152
|
+
@current_path.pop if @current_path.size == 1 # Remove the file_path at the beggining
|
153
|
+
end
|
154
|
+
|
155
|
+
def example_finished(notification)
|
156
|
+
example = notification.example
|
157
|
+
metadata = example.metadata
|
158
|
+
|
159
|
+
*example_group_ids, example_id = metadata[:scoped_id].split(":")
|
160
|
+
|
161
|
+
file_output = @event_output[current_test_key] ||= {}
|
162
|
+
|
163
|
+
example_group = example_group_ids.reduce(file_output) do |output, scope_id|
|
164
|
+
output[scope_id] ||= {}
|
165
|
+
output[scope_id]
|
166
|
+
end
|
167
|
+
|
168
|
+
example_group[example_id] = {
|
169
|
+
run_time: example.execution_result.run_time,
|
170
|
+
status: example.execution_result.status
|
171
|
+
}
|
172
|
+
|
173
|
+
if example.execution_result.status == :failed
|
174
|
+
@is_failed = true
|
175
|
+
example_group[example_id][:fully_formatted] =
|
176
|
+
notification.fully_formatted(0, ::RSpec::Core::Formatters::ConsoleCodes)
|
177
|
+
elsif metadata[:retry_attempts] && metadata[:retry_attempts] > 0
|
178
|
+
example_group[example_id][:retry_attempts] = metadata[:retry_attempts]
|
179
|
+
example_group[example_id][:fully_formatted] =
|
180
|
+
example.set_exception metadata[:retry_exceptions].first.to_s
|
181
|
+
end
|
182
|
+
|
183
|
+
example_group[example_id]
|
184
|
+
end
|
185
|
+
|
186
|
+
def dump_and_reset
|
187
|
+
event_output = @event_output
|
188
|
+
@event_output = {}
|
189
|
+
event_output
|
190
|
+
end
|
191
|
+
|
192
|
+
def path_with_file(group)
|
193
|
+
file_path = group.parent_groups.last.file_path.gsub("./".freeze, "".freeze)
|
194
|
+
|
195
|
+
group.metadata[:scoped_id].split(":").unshift(file_path)
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def running_gem_or_engine?
|
201
|
+
!!ENV["DIR_PREFIX"]
|
202
|
+
end
|
203
|
+
|
204
|
+
def running_only_failed?
|
205
|
+
!!ENV["RERUN_FAILED_FILES"]
|
206
|
+
end
|
207
|
+
|
208
|
+
def serialize_example(metadata, status, fully_formatted = nil)
|
209
|
+
run_time = metadata[:execution_result].run_time
|
210
|
+
if (run_time < 0) || (run_time > 2400)
|
211
|
+
run_time = 0.525
|
212
|
+
end
|
213
|
+
|
214
|
+
result = {
|
215
|
+
id: id(metadata),
|
216
|
+
status: status,
|
217
|
+
line_number: metadata[:line_number].to_s,
|
218
|
+
description: metadata[:description],
|
219
|
+
run_time: run_time,
|
220
|
+
fully_formatted: fully_formatted,
|
221
|
+
scoped_id: metadata[:scoped_id],
|
222
|
+
}.compact
|
223
|
+
|
224
|
+
result[:gem_or_engine] = true if running_gem_or_engine?
|
225
|
+
|
226
|
+
if running_only_failed?
|
227
|
+
result[:reruned] = true
|
228
|
+
elsif status == "failed" && !running_gem_or_engine?
|
229
|
+
File.write('tmp/rspec_failures', "#{@current_path.first}[#{metadata[:scoped_id]}]", mode: 'a+')
|
230
|
+
end
|
231
|
+
|
232
|
+
if status == "failed"
|
233
|
+
img_path = metadata.dig(:screenshot, :image) ||
|
234
|
+
fully_formatted&.scan(/\[Screenshot Image\]: (.*)$/).flatten.first&.strip&.chomp ||
|
235
|
+
fully_formatted&.scan(/\[Screenshot\]: (.*)$/).flatten.first&.strip&.chomp
|
236
|
+
|
237
|
+
if img_path && File.exist?(img_path)
|
238
|
+
STDOUT.puts "SCREENSHOT!"
|
239
|
+
result[:screenshots_base64] ||= []
|
240
|
+
result[:screenshots_base64] << Base64.strict_encode64(File.read(img_path))
|
241
|
+
end
|
242
|
+
end
|
243
|
+
@last_example_finished_at = time_now
|
244
|
+
# TODO annalyze this: run_time: metadata[:execution_result].run_time,
|
245
|
+
result
|
246
|
+
end
|
247
|
+
|
248
|
+
def msg(event, data)
|
249
|
+
@events << ["rspec_#{event}".upcase, [rspec_runner_index, data]]
|
250
|
+
end
|
251
|
+
|
252
|
+
def id(metadata)
|
253
|
+
metadata[:scoped_id].split(":").last || raise("No scoped id")
|
254
|
+
end
|
255
|
+
|
256
|
+
def broadcast_example_finished(data, example)
|
257
|
+
msg(:example_finished, [
|
258
|
+
path_with_file(example.example_group),
|
259
|
+
data.merge(output_inside: get_output, output_before: @output_before)
|
260
|
+
])
|
261
|
+
|
262
|
+
send_events if @should_send_events
|
263
|
+
end
|
264
|
+
|
265
|
+
def get_output
|
266
|
+
return if $stdout.pos == 0
|
267
|
+
$stdout.rewind
|
268
|
+
res = $stdout.read
|
269
|
+
$stdout.flush
|
270
|
+
$stdout.rewind
|
271
|
+
res
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "stringio"
|
3
|
+
|
4
|
+
module FastCI
|
5
|
+
class RspecRunFormatter
|
6
|
+
RSpec::Core::Formatters.register self,
|
7
|
+
:start,
|
8
|
+
:example_group_started,
|
9
|
+
:example_started,
|
10
|
+
:example_passed,
|
11
|
+
:example_failed,
|
12
|
+
:example_pending,
|
13
|
+
:example_group_finished,
|
14
|
+
:close
|
15
|
+
|
16
|
+
def initialize(output)
|
17
|
+
@output = output
|
18
|
+
@event_output = {}
|
19
|
+
@is_failed = false
|
20
|
+
@current_path = []
|
21
|
+
@current_path_started_at = []
|
22
|
+
@max_heap_live_num = 0
|
23
|
+
@dup_stdout = STDOUT.clone
|
24
|
+
@events = []
|
25
|
+
|
26
|
+
$stdout = StringIO.new()
|
27
|
+
|
28
|
+
@log_thread = Thread.new do
|
29
|
+
loop do
|
30
|
+
sleep 10
|
31
|
+
check_heap_live_num
|
32
|
+
@should_send_events = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def time_now
|
38
|
+
time_frozen? ? Timecop.return { Time.now } : Time.now
|
39
|
+
end
|
40
|
+
|
41
|
+
def time_frozen?
|
42
|
+
return unless defined?(Timecop)
|
43
|
+
Timecop.frozen?
|
44
|
+
end
|
45
|
+
|
46
|
+
def rspec_runner_index
|
47
|
+
ENV["TEST_ENV_NUMBER"]
|
48
|
+
end
|
49
|
+
|
50
|
+
def send_events
|
51
|
+
@should_send_events = false
|
52
|
+
|
53
|
+
if @events.length > 0
|
54
|
+
json_events = {
|
55
|
+
build_id: FastCI.configuration.orig_build_id,
|
56
|
+
compressed_data: Base64.strict_encode64(Zlib::Deflate.deflate(JSON.fast_generate(@events), 9)),
|
57
|
+
}
|
58
|
+
|
59
|
+
FastCI.send_events(json_events)
|
60
|
+
|
61
|
+
@events = []
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def check_heap_live_num
|
66
|
+
@max_heap_live_num = [@max_heap_live_num, GC.stat[:heap_live_slots] || GC.stat[:heap_live_num]].max
|
67
|
+
end
|
68
|
+
|
69
|
+
def passed?
|
70
|
+
!@is_failed
|
71
|
+
end
|
72
|
+
|
73
|
+
def start(start_notification)
|
74
|
+
@output.print "Starting rspec run"
|
75
|
+
# $stderr = $stdout
|
76
|
+
|
77
|
+
data = {
|
78
|
+
load_time: start_notification.load_time,
|
79
|
+
example_count: start_notification.count,
|
80
|
+
started_at: time_now.to_s
|
81
|
+
}
|
82
|
+
|
83
|
+
return if running_only_failed? ||
|
84
|
+
running_gem_or_engine? ||
|
85
|
+
ENV["EXTRA_SLOWER_RUN"]
|
86
|
+
|
87
|
+
msg(:start, data)
|
88
|
+
end
|
89
|
+
|
90
|
+
def close(null_notification)
|
91
|
+
@output.print "Finished rspec run"
|
92
|
+
# check_heap_live_num
|
93
|
+
msg(:gc_stat, GC.stat.merge(max_heap_live_num: @max_heap_live_num))
|
94
|
+
unless running_only_failed? || ENV["EXTRA_SLOWER_RUN"] || running_gem_or_engine?
|
95
|
+
msg(:close, {final_output: get_output})
|
96
|
+
end
|
97
|
+
send_events
|
98
|
+
$stdout = @dup_stdout
|
99
|
+
end
|
100
|
+
|
101
|
+
def example_group_started(group_notification)
|
102
|
+
metadata = group_notification.group.metadata
|
103
|
+
@current_path_started_at << time_now
|
104
|
+
|
105
|
+
if @current_path.size == 0
|
106
|
+
@example_failed_index = 0
|
107
|
+
file_path = metadata[:file_path].gsub("./".freeze, "".freeze)
|
108
|
+
file_path = [ENV["DIR_PREFIX"], file_path].join("/") if ENV["DIR_PREFIX"]
|
109
|
+
@current_path << file_path
|
110
|
+
end
|
111
|
+
|
112
|
+
@current_path << id(metadata)
|
113
|
+
|
114
|
+
msg(:group_started, [
|
115
|
+
path_with_file(group_notification.group),
|
116
|
+
{
|
117
|
+
line_number: metadata[:line_number],
|
118
|
+
description: metadata[:description],
|
119
|
+
}
|
120
|
+
])
|
121
|
+
end
|
122
|
+
|
123
|
+
def example_started(example_notification)
|
124
|
+
@output_before = get_output
|
125
|
+
end
|
126
|
+
|
127
|
+
def example_passed(example_notification)
|
128
|
+
metadata = example_notification.example.metadata
|
129
|
+
broadcast_example_finished(serialize_example(metadata, "passed".freeze), example_notification.example)
|
130
|
+
@output.print RSpec::Core::Formatters::ConsoleCodes.wrap('.', :success)
|
131
|
+
end
|
132
|
+
|
133
|
+
def example_failed(example_notification)
|
134
|
+
@example_failed_index += 1
|
135
|
+
metadata = example_notification.example.metadata
|
136
|
+
fully_formatted = example_notification.fully_formatted(@example_failed_index, ::RSpec::Core::Formatters::ConsoleCodes)
|
137
|
+
|
138
|
+
broadcast_example_finished(
|
139
|
+
serialize_example(metadata, "failed".freeze, fully_formatted),
|
140
|
+
example_notification.example
|
141
|
+
)
|
142
|
+
@output.print RSpec::Core::Formatters::ConsoleCodes.wrap('F', :failure)
|
143
|
+
end
|
144
|
+
|
145
|
+
def example_pending(example_notification)
|
146
|
+
metadata = example_notification.example.metadata
|
147
|
+
broadcast_example_finished(
|
148
|
+
serialize_example(metadata, "pending".freeze),
|
149
|
+
example_notification.example
|
150
|
+
)
|
151
|
+
@output.print RSpec::Core::Formatters::ConsoleCodes.wrap('*', :pending)
|
152
|
+
end
|
153
|
+
|
154
|
+
def example_group_finished(group_notification)
|
155
|
+
run_time = time_now - @current_path_started_at.pop
|
156
|
+
if (run_time < 0) || (run_time > 2400)
|
157
|
+
run_time = 0.525
|
158
|
+
end
|
159
|
+
msg(:group_finished, [path_with_file(group_notification.group), {run_time: run_time}])
|
160
|
+
# msg(:group_finished, [@current_path.map(&:to_s), {run_time: run_time}])
|
161
|
+
@current_path.pop
|
162
|
+
@current_path.pop if @current_path.size == 1 # Remove the file_path at the beggining
|
163
|
+
end
|
164
|
+
|
165
|
+
def path_with_file(group)
|
166
|
+
file_path = group.parent_groups.last.file_path.gsub("./".freeze, "".freeze)
|
167
|
+
|
168
|
+
group.metadata[:scoped_id].split(":").unshift(file_path)
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def running_gem_or_engine?
|
174
|
+
!!ENV["DIR_PREFIX"]
|
175
|
+
end
|
176
|
+
|
177
|
+
def running_only_failed?
|
178
|
+
!!ENV["RERUN_FAILED_FILES"]
|
179
|
+
end
|
180
|
+
|
181
|
+
def serialize_example(metadata, status, fully_formatted = nil)
|
182
|
+
run_time = metadata[:execution_result].run_time
|
183
|
+
if (run_time < 0) || (run_time > 2400)
|
184
|
+
run_time = 0.525
|
185
|
+
end
|
186
|
+
|
187
|
+
result = {
|
188
|
+
id: id(metadata),
|
189
|
+
status: status,
|
190
|
+
line_number: metadata[:line_number].to_s,
|
191
|
+
description: metadata[:description],
|
192
|
+
run_time: run_time,
|
193
|
+
fully_formatted: fully_formatted,
|
194
|
+
scoped_id: metadata[:scoped_id],
|
195
|
+
}.compact
|
196
|
+
|
197
|
+
result[:gem_or_engine] = true if running_gem_or_engine?
|
198
|
+
|
199
|
+
if running_only_failed?
|
200
|
+
result[:reruned] = true
|
201
|
+
elsif status == "failed" && !running_gem_or_engine?
|
202
|
+
File.write('tmp/rspec_failures', "#{@current_path.first}[#{metadata[:scoped_id]}]", mode: 'a+')
|
203
|
+
end
|
204
|
+
|
205
|
+
if status == "failed"
|
206
|
+
img_path = metadata.dig(:screenshot, :image) ||
|
207
|
+
fully_formatted&.scan(/\[Screenshot Image\]: (.*)$/).flatten.first&.strip&.chomp ||
|
208
|
+
fully_formatted&.scan(/\[Screenshot\]: (.*)$/).flatten.first&.strip&.chomp
|
209
|
+
|
210
|
+
if img_path && File.exist?(img_path)
|
211
|
+
STDOUT.puts "SCREENSHOT!"
|
212
|
+
result[:screenshots_base64] ||= []
|
213
|
+
result[:screenshots_base64] << Base64.strict_encode64(File.read(img_path))
|
214
|
+
end
|
215
|
+
end
|
216
|
+
@last_example_finished_at = time_now
|
217
|
+
# TODO annalyze this: run_time: metadata[:execution_result].run_time,
|
218
|
+
result
|
219
|
+
end
|
220
|
+
|
221
|
+
def msg(event, data)
|
222
|
+
@events << ["rspec_#{event}".upcase, [rspec_runner_index, data]]
|
223
|
+
end
|
224
|
+
|
225
|
+
def id(metadata)
|
226
|
+
metadata[:scoped_id].split(":").last || raise("No scoped id")
|
227
|
+
end
|
228
|
+
|
229
|
+
def broadcast_example_finished(data, example)
|
230
|
+
msg(:example_finished, [
|
231
|
+
path_with_file(example.example_group),
|
232
|
+
data.merge(output_inside: get_output, output_before: @output_before)
|
233
|
+
])
|
234
|
+
|
235
|
+
send_events if @should_send_events
|
236
|
+
end
|
237
|
+
|
238
|
+
def get_output
|
239
|
+
return if $stdout.pos == 0
|
240
|
+
$stdout.rewind
|
241
|
+
res = $stdout.read
|
242
|
+
$stdout.flush
|
243
|
+
$stdout.rewind
|
244
|
+
res
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|