rspec_trunk_flaky_tests 0.12.9.pre.beta.0
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 +7 -0
- data/lib/rspec_trunk_flaky_tests.rb +34 -0
- data/lib/trunk_spec_helper.rb +260 -0
- metadata +73 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '00089c490aca3f0766ade829dd6e96620cc900a7173fef7851d6844a8ebb7375'
|
|
4
|
+
data.tar.gz: 9a62065dccb28f61dbb2d440d6880a65b6e2174d4ae86634158fff88ab3cd5cb
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 561359db55239e235de3929888c2aa779d26afc05862477723c764e4821c9760d19dd44195e87bfa2f771fbb242392224b297944d861f5ccfd814d4a4ad22269
|
|
7
|
+
data.tar.gz: bf4fca81d43e248167cc4c3b4bc8c8495c87083f2c9c43201e9333caa0e48017f8948b2a6a01c02174f2a022b62f2f4372cdf792f8c417e877072e00b60f13ad
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
|
|
5
|
+
require_relative "rspec_trunk_flaky_tests/#{ruby_version}/rspec_trunk_flaky_tests"
|
|
6
|
+
rescue LoadError
|
|
7
|
+
begin
|
|
8
|
+
require_relative 'rspec_trunk_flaky_tests/rspec_trunk_flaky_tests'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
raise LoadError, <<~MSG
|
|
11
|
+
Could not load the native extension for rspec_trunk_flaky_tests.
|
|
12
|
+
|
|
13
|
+
You are using the pure ruby variant of this gem, which is a placeholder
|
|
14
|
+
that exists so that Gemfile.lock files with `PLATFORMS ruby` can resolve
|
|
15
|
+
this dependency. It does not include a precompiled native extension.
|
|
16
|
+
|
|
17
|
+
Precompiled native gems are available for:
|
|
18
|
+
x86_64-linux, aarch64-linux, arm64-darwin, x86_64-darwin
|
|
19
|
+
|
|
20
|
+
If you are on a supported platform and seeing this error, make sure
|
|
21
|
+
bundler is selecting the native variant for your platform:
|
|
22
|
+
|
|
23
|
+
bundle lock --add-platform #{RUBY_PLATFORM}
|
|
24
|
+
bundle install
|
|
25
|
+
|
|
26
|
+
If you are on an unsupported platform, this gem cannot be used in your
|
|
27
|
+
environment. Please open an issue at:
|
|
28
|
+
https://github.com/trunk-io/analytics-cli/issues
|
|
29
|
+
|
|
30
|
+
Current platform: #{RUBY_PLATFORM}
|
|
31
|
+
Current Ruby version: #{RUBY_VERSION}
|
|
32
|
+
MSG
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Trunk RSpec Helper
|
|
4
|
+
#
|
|
5
|
+
# This helper integrates Trunk Flaky Tests with RSpec to automatically
|
|
6
|
+
# quarantine flaky tests and upload test results.
|
|
7
|
+
#
|
|
8
|
+
# Required environment variables:
|
|
9
|
+
# TRUNK_ORG_URL_SLUG - Your organization's URL slug
|
|
10
|
+
# TRUNK_API_TOKEN - Your API token for authentication
|
|
11
|
+
#
|
|
12
|
+
# Optional environment variables for repository metadata:
|
|
13
|
+
# TRUNK_REPO_ROOT - Path to repository root
|
|
14
|
+
# TRUNK_REPO_URL - Repository URL (e.g., https://github.com/org/repo.git)
|
|
15
|
+
# TRUNK_REPO_HEAD_SHA - HEAD commit SHA
|
|
16
|
+
# TRUNK_REPO_HEAD_BRANCH - HEAD branch name
|
|
17
|
+
# TRUNK_REPO_HEAD_COMMIT_EPOCH - HEAD commit timestamp (seconds since epoch)
|
|
18
|
+
# TRUNK_REPO_HEAD_AUTHOR_NAME - HEAD commit author name
|
|
19
|
+
# TRUNK_PR_NUMBER - PR number, if uploading from a PR (normally inferred from CI environment variables)
|
|
20
|
+
#
|
|
21
|
+
# Optional environment variables for configuration:
|
|
22
|
+
# TRUNK_CODEOWNERS_PATH - Path to CODEOWNERS file
|
|
23
|
+
# TRUNK_VARIANT - Variant name for test results (e.g., 'linux', 'pr-123')
|
|
24
|
+
# TRUNK_DISABLE_QUARANTINING - Set to 'true' to disable quarantining
|
|
25
|
+
# TRUNK_ALLOW_EMPTY_TEST_RESULTS - Set to 'true' to allow empty results
|
|
26
|
+
# TRUNK_DRY_RUN - Set to 'true' to save bundle locally instead of uploading
|
|
27
|
+
# TRUNK_USE_UNCLONED_REPO - Set to 'true' for uncloned repo mode
|
|
28
|
+
# TRUNK_LOCAL_UPLOAD_DIR - Directory to save test results locally (disables upload)
|
|
29
|
+
# TRUNK_QUARANTINED_TESTS_DISK_CACHE_TTL_SECS - Time to cache quarantined tests on disk (in seconds)
|
|
30
|
+
# DISABLE_RSPEC_TRUNK_FLAKY_TESTS - Set to 'true' to completely disable Trunk
|
|
31
|
+
#
|
|
32
|
+
require 'rspec/core'
|
|
33
|
+
require 'time'
|
|
34
|
+
require 'rspec_trunk_flaky_tests'
|
|
35
|
+
|
|
36
|
+
# String is an override to the main String class that is used to colorize the output
|
|
37
|
+
# it is used to make the output more readable
|
|
38
|
+
class String
|
|
39
|
+
def colorize(color_code)
|
|
40
|
+
"\e[#{color_code}m#{self}\e[0m"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def red
|
|
44
|
+
colorize(31)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def green
|
|
48
|
+
colorize(32)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def yellow
|
|
52
|
+
colorize(33)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def escape(str)
|
|
57
|
+
str.dump[1..-2]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Knapsack example detector instantiates all test cases in order to determine how to shard them
|
|
61
|
+
# These instantiations should not generate test bundles, so we
|
|
62
|
+
# disable the gem when running under knapsack_pro:rspec_test_example_detector
|
|
63
|
+
def knapsack_detector_mode?
|
|
64
|
+
knapsack_detector_command?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def knapsack_detector_command?
|
|
68
|
+
command_line = "#{$PROGRAM_NAME} #{ARGV.join(' ')}".strip
|
|
69
|
+
command_line.include?('knapsack_pro:rspec_test_example_detector') ||
|
|
70
|
+
command_line.include?('knapsack_pro:queue:rspec:initialize')
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def trunk_disabled
|
|
74
|
+
knapsack_detector_mode? || ENV['DISABLE_RSPEC_TRUNK_FLAKY_TESTS'] == 'true' ||
|
|
75
|
+
ENV['TRUNK_ORG_URL_SLUG'].nil? || ENV['TRUNK_API_TOKEN'].nil?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# we want to cache the test report in memory so we can add to it as we go and reduce the number of API calls
|
|
79
|
+
$test_report = TestReport.new('rspec', "#{$PROGRAM_NAME} #{ARGV.join(' ')}", nil)
|
|
80
|
+
$failure_encountered_and_quarantining_disabled = false
|
|
81
|
+
|
|
82
|
+
module RSpec
|
|
83
|
+
module Core
|
|
84
|
+
# Example is the class that represents a test case
|
|
85
|
+
class Example
|
|
86
|
+
# keep the original method around so we can call it
|
|
87
|
+
alias set_exception_core set_exception
|
|
88
|
+
alias assign_generated_description_core assign_generated_description
|
|
89
|
+
# RSpec uses the existance of an exception to determine if the test failed
|
|
90
|
+
# We need to override this to allow us to capture the exception and then
|
|
91
|
+
# decide if we want to fail the test or not
|
|
92
|
+
# trunk-ignore(rubocop/Naming/AccessorMethodName)
|
|
93
|
+
def set_exception(exception)
|
|
94
|
+
return set_exception_core(exception) if metadata[:pending]
|
|
95
|
+
return set_exception_core(exception) if trunk_disabled
|
|
96
|
+
return set_exception_core(exception) if metadata[:retry_attempts]&.positive?
|
|
97
|
+
|
|
98
|
+
handle_quarantine_check(exception)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# trunk-ignore(rubocop/Metrics/AbcSize,rubocop/Metrics/MethodLength)
|
|
102
|
+
def handle_quarantine_check(exception)
|
|
103
|
+
id = generate_trunk_id
|
|
104
|
+
name = full_description
|
|
105
|
+
parent_name = example_group.metadata[:description]
|
|
106
|
+
parent_name = parent_name.empty? ? 'rspec' : parent_name
|
|
107
|
+
file = escape(metadata[:file_path])
|
|
108
|
+
classname = file.sub(%r{\.[^/.]+\Z}, '').gsub('/', '.').gsub(/\A\.+|\.+\Z/, '')
|
|
109
|
+
unless $failure_encountered_and_quarantining_disabled
|
|
110
|
+
puts "Test failed, checking if it can be quarantined: `#{location}`".yellow
|
|
111
|
+
end
|
|
112
|
+
is_quarantined_result = $test_report.is_quarantined(id, name, parent_name, classname, file)
|
|
113
|
+
if is_quarantined_result.quarantining_disabled_for_repo
|
|
114
|
+
unless $failure_encountered_and_quarantining_disabled
|
|
115
|
+
puts 'Quarantining is disabled for this repo, no failures will be quarantined'.yellow
|
|
116
|
+
$failure_encountered_and_quarantining_disabled = true
|
|
117
|
+
end
|
|
118
|
+
set_exception_core(exception)
|
|
119
|
+
elsif is_quarantined_result.test_is_quarantined
|
|
120
|
+
# monitor the override in the metadata
|
|
121
|
+
metadata[:quarantined_exception] = exception
|
|
122
|
+
puts "Test is quarantined, overriding exception: #{exception}".green
|
|
123
|
+
nil
|
|
124
|
+
else
|
|
125
|
+
puts 'Test is not quarantined, continuing'.red
|
|
126
|
+
set_exception_core(exception)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def assign_generated_description
|
|
131
|
+
metadata[:is_description_generated] = description_generated?
|
|
132
|
+
assign_generated_description_core
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def description_generated?
|
|
136
|
+
return metadata[:is_description_generated] unless metadata[:is_description_generated].nil?
|
|
137
|
+
|
|
138
|
+
description == location_description
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def generate_trunk_id
|
|
142
|
+
return "trunk:#{id}-#{location}" if description_generated?
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Procsy is a class that is used to wrap execution of the Example class
|
|
146
|
+
class Procsy
|
|
147
|
+
def run_with_trunk
|
|
148
|
+
RSpec::Trunk.new(self).run
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
module RSpec
|
|
156
|
+
# Trunk is a class that is used to monitor the execution of the Example class
|
|
157
|
+
class Trunk
|
|
158
|
+
def self.setup
|
|
159
|
+
RSpec.configure do |config|
|
|
160
|
+
if trunk_disabled
|
|
161
|
+
config.around(:each, &:run)
|
|
162
|
+
else
|
|
163
|
+
config.around(:each, &:run_with_trunk)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def initialize(example)
|
|
169
|
+
@example = example
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def current_example
|
|
173
|
+
@current_example ||= RSpec.current_example
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def run
|
|
177
|
+
# run the test
|
|
178
|
+
@example.run
|
|
179
|
+
# monitor attempts in the metadata
|
|
180
|
+
if @example.metadata[:attempt_number]
|
|
181
|
+
@example.metadata[:attempt_number] += 1
|
|
182
|
+
else
|
|
183
|
+
@example.metadata[:attempt_number] = 0
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# TrunkAnalyticsListener is a class that is used to listen to the execution of the Example class
|
|
190
|
+
# it generates and submits the final test reports
|
|
191
|
+
class TrunkAnalyticsListener
|
|
192
|
+
def initialize
|
|
193
|
+
@testreport = $test_report
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def example_finished(notification)
|
|
197
|
+
add_test_case(notification.example)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# trunk-ignore(rubocop/Metrics/MethodLength)
|
|
201
|
+
def close(_notification)
|
|
202
|
+
if $failure_encountered_and_quarantining_disabled
|
|
203
|
+
puts 'Note: Quarantining is disabled for this repo. Test failures were not quarantined.'.yellow
|
|
204
|
+
end
|
|
205
|
+
if ENV['TRUNK_LOCAL_UPLOAD_DIR']
|
|
206
|
+
saved = @testreport.try_save(ENV['TRUNK_LOCAL_UPLOAD_DIR'])
|
|
207
|
+
if saved
|
|
208
|
+
puts 'Local Flaky tests report generated'.green
|
|
209
|
+
else
|
|
210
|
+
puts 'Failed to generate local flaky tests report'.red
|
|
211
|
+
end
|
|
212
|
+
else
|
|
213
|
+
published = @testreport.publish
|
|
214
|
+
if published
|
|
215
|
+
puts 'Flaky tests report upload complete'.green
|
|
216
|
+
else
|
|
217
|
+
puts 'Failed to publish flaky tests report'.red
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# trunk-ignore(rubocop/Metrics/CyclomaticComplexity,rubocop/Metrics/AbcSize,rubocop/Metrics/MethodLength)
|
|
223
|
+
def add_test_case(example)
|
|
224
|
+
failure_message = example.exception.to_s if example.exception
|
|
225
|
+
failure_message = example.metadata[:quarantined_exception].to_s if example.metadata[:quarantined_exception]
|
|
226
|
+
# TODO: should we use concatenated string or alias when auto-generated description?
|
|
227
|
+
name = example.full_description
|
|
228
|
+
file = escape(example.metadata[:file_path])
|
|
229
|
+
classname = file.sub(%r{\.[^/.]+\Z}, '').gsub('/', '.').gsub(/\A\.+|\.+\Z/, '')
|
|
230
|
+
line = example.metadata[:line_number]
|
|
231
|
+
started_at = example.execution_result.started_at.to_i
|
|
232
|
+
finished_at = example.execution_result.finished_at.to_i
|
|
233
|
+
id = example.generate_trunk_id
|
|
234
|
+
|
|
235
|
+
attempt_number = example.metadata[:retry_attempts] || example.metadata[:attempt_number] || 0
|
|
236
|
+
status = example.execution_result.status.to_s
|
|
237
|
+
# set the status to failure, but mark it as quarantined
|
|
238
|
+
is_quarantined = example.metadata[:quarantined_exception] ? true : false
|
|
239
|
+
case example.execution_result.status
|
|
240
|
+
when :passed
|
|
241
|
+
status = is_quarantined ? Status.new('failure') : Status.new('success')
|
|
242
|
+
when :failed
|
|
243
|
+
status = Status.new('failure')
|
|
244
|
+
when :pending
|
|
245
|
+
status = Status.new('skipped')
|
|
246
|
+
end
|
|
247
|
+
parent_name = example.example_group.metadata[:description]
|
|
248
|
+
parent_name = parent_name.empty? ? 'rspec' : parent_name
|
|
249
|
+
@testreport.add_test(id, name, classname, file, parent_name, line, status, attempt_number,
|
|
250
|
+
started_at, finished_at, failure_message || '', is_quarantined)
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
RSpec.configure do |c|
|
|
255
|
+
next if trunk_disabled
|
|
256
|
+
|
|
257
|
+
c.reporter.register_listener TrunkAnalyticsListener.new, :example_finished, :close
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
RSpec::Trunk.setup
|
metadata
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rspec_trunk_flaky_tests
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.12.9.pre.beta.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Trunk Technologies, Inc.
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rspec-core
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.3'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3.3'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rspec
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
description: Integrates RSpec with Trunk Flaky Tests to automatically upload test
|
|
41
|
+
results from your CI jobs. Enables accurate flaky test detection, quarantining,
|
|
42
|
+
and analytics.
|
|
43
|
+
email: support@trunk.io
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- lib/rspec_trunk_flaky_tests.rb
|
|
49
|
+
- lib/trunk_spec_helper.rb
|
|
50
|
+
homepage: https://docs.trunk.io/flaky-tests/get-started/frameworks/rspec
|
|
51
|
+
licenses:
|
|
52
|
+
- MIT
|
|
53
|
+
metadata:
|
|
54
|
+
source_code_uri: https://github.com/trunk-io/analytics-cli/tree/main/rspec-trunk-flaky-tests
|
|
55
|
+
rdoc_options: []
|
|
56
|
+
require_paths:
|
|
57
|
+
- lib
|
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '3.0'
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
requirements: []
|
|
69
|
+
rubygems_version: 4.0.6
|
|
70
|
+
specification_version: 4
|
|
71
|
+
summary: RSpec plugin for Trunk Flaky Tests - automatically uploads test results to
|
|
72
|
+
detect and quarantine flaky tests
|
|
73
|
+
test_files: []
|