rspec-fortify 1.0.0.pre

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.
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core'
4
+ require 'rspec/fortify/version'
5
+ require 'rspec_ext/rspec_ext'
6
+
7
+ module RSpec
8
+ class Fortify
9
+ def self.setup # rubocop:disable Metrics/AbcSize
10
+ RSpec.configure do |config|
11
+ config.add_setting :clear_lets_on_failure, default: true
12
+ config.add_setting :default_retry_count, default: 1
13
+ config.add_setting :default_sleep_interval, default: 0
14
+ config.add_setting :display_try_failure_messages, default: true
15
+ config.add_setting :exponential_backoff, default: false
16
+ config.add_setting :retry_on_failure, default: main? || pr?
17
+ config.add_setting :retry_on_failure_count, default: 2
18
+ config.add_setting :retry_on_success, default: pr? && changed_specs.size < 30
19
+ config.add_setting :retry_on_success_count, default: 10
20
+ config.add_setting :verbose_retry, default: true
21
+
22
+ # retry based on example metadata
23
+ config.add_setting :retry_count_condition, default: ->(_) {}
24
+
25
+ # If a list of exceptions is provided and 'retry' > 1, we only retry if
26
+ # the exception that was raised by the example is NOT in that list. Otherwise
27
+ # we ignore the 'retry' value and fail immediately.
28
+ #
29
+ # If no list of exceptions is provided and 'retry' > 1, we always retry.
30
+ config.add_setting :exceptions_to_hard_fail, default: []
31
+
32
+ # If a list of exceptions is provided and 'retry' > 1, we only retry if
33
+ # the exception that was raised by the example is in that list. Otherwise
34
+ # we ignore the 'retry' value and fail immediately.
35
+ #
36
+ # If no list of exceptions is provided and 'retry' > 1, we always retry.
37
+ config.add_setting :exceptions_to_retry, default: []
38
+
39
+ # Callback between retries
40
+ config.add_setting :retry_callback, default: nil
41
+
42
+ config.around :each, &:run_with_retry
43
+ end
44
+ end
45
+
46
+ attr_reader :context, :ex
47
+
48
+ def initialize(example, opts = {})
49
+ @ex = example
50
+ @ex.metadata.merge!(opts)
51
+ current_example.attempts ||= 0
52
+ end
53
+
54
+ def current_example
55
+ @current_example ||= RSpec.current_example
56
+ end
57
+
58
+ def retry_count # rubocop:disable Metrics/AbcSize
59
+ if retry_on_success?
60
+ RSpec.configuration.retry_on_success_count
61
+ elsif retry_on_failure?
62
+ RSpec.configuration.retry_on_failure_count
63
+ else
64
+ [
65
+ (
66
+ ENV['RSPEC_FORTIFY_RETRY_COUNT'] ||
67
+ ex.metadata[:retry] ||
68
+ RSpec.configuration.retry_count_condition.call(ex) ||
69
+ RSpec.configuration.default_retry_count
70
+ ).to_i,
71
+ 1,
72
+ ].max
73
+ end
74
+ end
75
+
76
+ def attempts
77
+ current_example.attempts ||= 0
78
+ end
79
+
80
+ def attempts=(val)
81
+ current_example.attempts = val
82
+ end
83
+
84
+ def clear_lets
85
+ if ex.metadata[:clear_lets_on_failure].nil?
86
+ RSpec.configuration.clear_lets_on_failure
87
+ else
88
+ ex.metadata[:clear_lets_on_failure]
89
+ end
90
+ end
91
+
92
+ def sleep_interval
93
+ if ex.metadata[:exponential_backoff]
94
+ (2**(current_example.attempts - 1)) * ex.metadata[:retry_wait]
95
+ else
96
+ ex.metadata[:retry_wait] ||
97
+ RSpec.configuration.default_sleep_interval
98
+ end
99
+ end
100
+
101
+ def exceptions_to_hard_fail
102
+ ex.metadata[:exceptions_to_hard_fail] ||
103
+ RSpec.configuration.exceptions_to_hard_fail
104
+ end
105
+
106
+ def exceptions_to_retry
107
+ ex.metadata[:exceptions_to_retry] ||
108
+ RSpec.configuration.exceptions_to_retry
109
+ end
110
+
111
+ def verbose_retry?
112
+ RSpec.configuration.verbose_retry?
113
+ end
114
+
115
+ def display_try_failure_messages?
116
+ RSpec.configuration.display_try_failure_messages?
117
+ end
118
+
119
+ def run # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/CyclomaticComplexity
120
+ loop do
121
+ RSpec.configuration.formatters.each { |f| f.retry(ex) if f.respond_to? :retry } if attempts.positive?
122
+
123
+ if verbose_retry? && (log_first_attempt? || attempts.positive?)
124
+ message = "RSpec::Fortify: #{ordinalize(attempts + 1)} try #{ex.location}"
125
+ message = "\n#{message}" if attempts == 1
126
+ RSpec.configuration.reporter.message(message)
127
+ end
128
+
129
+ ex.metadata[:retry_attempts] = attempts
130
+ ex.metadata[:retry_exceptions] ||= []
131
+
132
+ current_example.clear_exception
133
+ ex.run
134
+
135
+ self.attempts += 1
136
+
137
+ break unless should_retry?
138
+
139
+ ex.metadata[:retry_exceptions] << ex.exception if ex.exception
140
+
141
+ break if attempts >= retry_count
142
+
143
+ if exceptions_to_hard_fail.any? && ex.exception && exception_exists_in?(exceptions_to_hard_fail, ex.exception)
144
+ break
145
+ end
146
+
147
+ break if exceptions_to_retry.any? && ex.exception && !exception_exists_in?(exceptions_to_retry, ex.exception)
148
+
149
+ if verbose_retry? && display_try_failure_messages? && retry_on_failure? && (attempts != retry_count)
150
+ exception_strings =
151
+ if ex.exception.is_a?(::RSpec::Core::MultipleExceptionError::InterfaceTag)
152
+ ex.exception.all_exceptions.map(&:to_s)
153
+ else
154
+ [ex.exception.to_s]
155
+ end
156
+
157
+ try_message = "\n#{ordinalize(attempts)} Try error in #{ex.location}:\n#{exception_strings.join "\n"}\n"
158
+ RSpec.configuration.reporter.message(try_message)
159
+ end
160
+
161
+ ex.example_group_instance.clear_lets if clear_lets
162
+
163
+ if RSpec.configuration.retry_callback
164
+ ex.ex_group_instance.instance_exec(ex, &RSpec.configuration.retry_callback)
165
+ end
166
+
167
+ sleep sleep_interval if sleep_interval.to_f.positive?
168
+ end
169
+ end
170
+
171
+ private
172
+
173
+ # borrowed from ActiveSupport::Inflector
174
+ def ordinalize(number)
175
+ if (11..13).cover?(number.to_i % 100)
176
+ "#{number}th"
177
+ else
178
+ case number.to_i % 10
179
+ when 1 then "#{number}st"
180
+ when 2 then "#{number}nd"
181
+ when 3 then "#{number}rd"
182
+ else "#{number}th"
183
+ end
184
+ end
185
+ end
186
+
187
+ def exception_exists_in?(list, exception)
188
+ list.any? do |exception_klass|
189
+ exception.is_a?(exception_klass) || exception_klass === exception # rubocop:disable Style/CaseEquality
190
+ end
191
+ end
192
+
193
+ def log_first_attempt?
194
+ cast_to_boolean(ENV.fetch('RSPEC_FORTIFY_LOG_FIRST_ATTEMPT', 'false'))
195
+ end
196
+
197
+ def retry_on_failure?
198
+ RSpec.configuration.retry_on_failure
199
+ end
200
+
201
+ def retry_on_success?
202
+ RSpec.configuration.retry_on_success && current_example_changed?
203
+ end
204
+
205
+ def current_example_changed?
206
+ # Added or modified specs can be passed in via this
207
+ # CHANGED_SPECS env var using some version of git diff like:
208
+ # git diff origin/main...HEAD --name-only --relative --diff-filter=AM | grep '_spec.rb$' | tr '\n' ',' | sed 's/,$//'
209
+ changed_specs = ENV.fetch('CHANGED_SPECS', nil)&.split(',') || []
210
+ changed_specs.include?(ex.file_path.sub(%r{^\./}, ''))
211
+ end
212
+
213
+ def current_attempt_failed?
214
+ !ex.exception.nil?
215
+ end
216
+
217
+ def current_attempt_succeeded?
218
+ ex.exception.nil?
219
+ end
220
+
221
+ def should_retry?
222
+ if retry_on_success?
223
+ current_attempt_succeeded?
224
+ elsif retry_on_failure?
225
+ current_attempt_failed?
226
+ end
227
+ end
228
+ end
229
+ end
230
+
231
+ def cast_to_boolean(value)
232
+ if value.nil? || %w(false f 0 no n).include?(value.to_s.downcase) # rubocop:disable Style/IfWithBooleanLiteralBranches
233
+ false
234
+ else
235
+ true
236
+ end
237
+ end
238
+
239
+ def ci?
240
+ cast_to_boolean(ENV.fetch('CI', 'false'))
241
+ end
242
+
243
+ def pr?
244
+ ci? && cast_to_boolean(ENV.fetch('CIRCLE_PULL_REQUEST', 'false'))
245
+ end
246
+
247
+ def main?
248
+ ci? && !pr?
249
+ end
250
+
251
+ def changed_specs
252
+ ENV.fetch('CHANGED_SPECS', nil)&.split(',') || []
253
+ end
254
+
255
+ RSpec::Fortify.setup
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module Core
5
+ class Example
6
+ attr_accessor :attempts
7
+
8
+ def clear_exception
9
+ @exception = nil
10
+ end
11
+
12
+ class Procsy
13
+ def run_with_retry(opts = {})
14
+ RSpec::Fortify.new(self, opts).run
15
+ end
16
+
17
+ def attempts
18
+ @example.attempts
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ module RSpec
26
+ module Core
27
+ class ExampleGroup
28
+ def clear_memoized
29
+ if respond_to? :__init_memoized, true
30
+ __init_memoized
31
+ else
32
+ @__memoized = nil
33
+ end
34
+ end
35
+
36
+ def clear_lets
37
+ clear_memoized
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require File.expand_path('lib/rspec/fortify/version', __dir__)
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.authors = ['Yusuke Mito', 'Michael Glass', 'Devin Burnette']
8
+ spec.email = ['devin@betterment.com']
9
+ spec.description = 'retry intermittently failing rspec examples'
10
+ spec.summary = 'retry intermittently failing rspec examples'
11
+ spec.homepage = 'https://github.com/Betterment/rspec-fortify'
12
+ spec.license = 'MIT'
13
+ spec.metadata['rubygems_mfa_required'] = 'true'
14
+
15
+ spec.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
16
+ spec.executables = []
17
+ spec.name = 'rspec-fortify'
18
+ spec.require_paths = ['lib']
19
+ spec.version = RSpec::Fortify::VERSION
20
+ spec.required_ruby_version = '>= 3.0'
21
+ spec.add_runtime_dependency 'rspec-core', '>3.9'
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'Bad Test Example' do # rubocop:disable RSpec/FilePath
6
+ let(:foo) { false }
7
+
8
+ it 'is a bad test' do
9
+ expect(foo).to eq true
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'Flaky Test Example' do # rubocop:disable RSpec/FilePath
6
+ before(:all) do # rubocop:disable RSpec/BeforeAfterAll
7
+ $try_counter = 0 # rubocop:disable Style/GlobalVars
8
+ end
9
+
10
+ it 'fails on the third try' do
11
+ $try_counter += 1 # rubocop:disable Style/GlobalVars
12
+
13
+ expect($try_counter).not_to eq(3), 'Intentionally failing on the third try' # rubocop:disable Style/GlobalVars
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'Good Test Example' do # rubocop:disable RSpec/FilePath
6
+ let(:foo) { true }
7
+
8
+ it 'is a good test' do
9
+ expect(foo).to eq true
10
+ end
11
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'tty-command'
5
+
6
+ describe RSpec::Fortify do
7
+ let(:cmd) { TTY::Command.new printer: :null }
8
+
9
+ describe '#run' do
10
+ context 'when main' do
11
+ context 'when example changed' do
12
+ let(:env) do
13
+ {
14
+ 'CHANGED_SPECS' => 'spec/fixtures/flaky_test.rb,spec/fixtures/good_test.rb,spec/fixtures/bad_test.rb',
15
+ 'CI' => 'true',
16
+ 'RSPEC_FORTIFY_LOG_FIRST_ATTEMPT' => 'true',
17
+ }
18
+ end
19
+
20
+ it 'retries bad test examples the configured failed retry amount' do
21
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/bad_test.rb', env: env)
22
+ expect(out).to include('2nd try')
23
+ expect(out).not_to include('10th try')
24
+ end
25
+
26
+ it 'runs good test examples once' do
27
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/good_test.rb', env: env)
28
+ expect(out).to include('1st try')
29
+ expect(out).not_to include('2nd try')
30
+ end
31
+
32
+ it 'runs flaky test examples once' do
33
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/flaky_test.rb', env: env)
34
+ expect(out).to include('1st try')
35
+ expect(out).not_to include('2nd try')
36
+ end
37
+ end
38
+
39
+ context 'when example did not change' do
40
+ let(:env) do
41
+ {
42
+ 'CI' => 'true',
43
+ 'RSPEC_FORTIFY_LOG_FIRST_ATTEMPT' => 'true',
44
+ }
45
+ end
46
+
47
+ it 'retries bad test examples the configured failed retry amount' do
48
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/bad_test.rb', env: env)
49
+ expect(out).to include('2nd try')
50
+ expect(out).not_to include('10th try')
51
+ end
52
+
53
+ it 'runs good test examples once' do
54
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/good_test.rb', env: env)
55
+ expect(out).to include('1st try')
56
+ expect(out).not_to include('2nd try')
57
+ end
58
+
59
+ it 'runs flaky test examples once' do
60
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/flaky_test.rb', env: env)
61
+ expect(out).to include('1st try')
62
+ expect(out).not_to include('2nd try')
63
+ end
64
+ end
65
+ end
66
+
67
+ context 'when pr' do
68
+ context 'when example changed' do
69
+ context 'when too many changed specs' do
70
+ let(:env) do
71
+ {
72
+ 'CHANGED_SPECS' => 'spec/fixtures/flaky_test.rb,spec/fixtures/good_test.rb,spec/fixtures/bad_test.rb,spec/fixtures/fake_test1.rb,spec/fixtures/fake_test2.rb,spec/fixtures/fake_test3.rb,spec/fixtures/fake_test4.rb,spec/fixtures/fake_test5.rb,spec/fixtures/fake_test6.rb,spec/fixtures/fake_test7.rb,spec/fixtures/fake_test8.rb,spec/fixtures/fake_test9.rb,spec/fixtures/fake_test10.rb,spec/fixtures/fake_test11.rb,spec/fixtures/fake_test12.rb,spec/fixtures/fake_test13.rb,spec/fixtures/fake_test14.rb,spec/fixtures/fake_test15.rb,spec/fixtures/fake_test16.rb,spec/fixtures/fake_test17.rb,spec/fixtures/fake_test18.rb,spec/fixtures/fake_test19.rb,spec/fixtures/fake_test20.rb,spec/fixtures/fake_test21.rb,spec/fixtures/fake_test22.rb,spec/fixtures/fake_test23.rb,spec/fixtures/fake_test24.rb,spec/fixtures/fake_test25.rb,spec/fixtures/fake_test26.rb,spec/fixtures/fake_test27.rb,spec/fixtures/fake_test28.rb,spec/fixtures/fake_test29.rb,spec/fixtures/fake_test30.rb', # rubocop:disable Layout/LineLength
73
+ 'CI' => 'true',
74
+ 'CIRCLE_PULL_REQUEST' => 'https://github.com/foo/bar/pull/123',
75
+ 'RSPEC_FORTIFY_LOG_FIRST_ATTEMPT' => 'true',
76
+ }
77
+ end
78
+
79
+ it 'retries bad test examples the configured failed retry amount' do
80
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/bad_test.rb', env: env)
81
+ expect(out).to include('2nd try')
82
+ expect(out).not_to include('10th try')
83
+ end
84
+
85
+ it 'runs good test examples once' do
86
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/good_test.rb', env: env)
87
+ expect(out).to include('1st try')
88
+ expect(out).not_to include('2nd try')
89
+ end
90
+
91
+ it 'runs flaky test examples once' do
92
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/flaky_test.rb', env: env)
93
+ expect(out).to include('1st try')
94
+ expect(out).not_to include('2nd try')
95
+ end
96
+ end
97
+
98
+ context 'when not too many changed specs' do
99
+ let(:env) do
100
+ {
101
+ 'CHANGED_SPECS' => 'spec/fixtures/flaky_test.rb,spec/fixtures/good_test.rb,spec/fixtures/bad_test.rb',
102
+ 'CI' => 'true',
103
+ 'CIRCLE_PULL_REQUEST' => 'https://github.com/foo/bar/pull/123',
104
+ 'RSPEC_FORTIFY_LOG_FIRST_ATTEMPT' => 'true',
105
+ }
106
+ end
107
+
108
+ it 'retries bad test examples the configured failed retry amount' do
109
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/bad_test.rb', env: env)
110
+ expect(out).to include('1st try')
111
+ expect(out).not_to include('2nd try')
112
+ end
113
+
114
+ it 'retries good test examples the configured success retry amount' do
115
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/good_test.rb', env: env)
116
+ expect(out).to include('10th try')
117
+ end
118
+
119
+ it 'retries flaky test examples until they flake' do
120
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/flaky_test.rb', env: env)
121
+ expect(out).to include('2nd try')
122
+ end
123
+ end
124
+ end
125
+
126
+ context 'when example did not change' do
127
+ let(:env) do
128
+ {
129
+ 'CI' => 'true',
130
+ 'CIRCLE_PULL_REQUEST' => 'https://github.com/foo/bar/pull/123',
131
+ 'RSPEC_FORTIFY_LOG_FIRST_ATTEMPT' => 'true',
132
+ }
133
+ end
134
+
135
+ it 'retries bad test examples the configured failed retry amount' do
136
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/bad_test.rb', env: env)
137
+ expect(out).to include('2nd try')
138
+ expect(out).not_to include('10th try')
139
+ end
140
+
141
+ it 'runs good test examples once' do
142
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/good_test.rb', env: env)
143
+ expect(out).to include('1st try')
144
+ expect(out).not_to include('2nd try')
145
+ end
146
+
147
+ it 'runs flaky test examples once' do
148
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/flaky_test.rb', env: env)
149
+ expect(out).to include('1st try')
150
+ expect(out).not_to include('2nd try')
151
+ end
152
+ end
153
+ end
154
+
155
+ context 'when local dev' do
156
+ context 'when example changed' do
157
+ let(:env) do
158
+ {
159
+ 'CHANGED_SPECS' => 'spec/fixtures/flaky_test.rb,spec/fixtures/good_test.rb,spec/fixtures/bad_test.rb',
160
+ 'CI' => 'false',
161
+ 'RSPEC_FORTIFY_LOG_FIRST_ATTEMPT' => 'true',
162
+ }
163
+ end
164
+
165
+ it 'runs bad test examples once' do
166
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/bad_test.rb', env: env)
167
+ expect(out).to include('1st try')
168
+ expect(out).not_to include('2nd try')
169
+ end
170
+
171
+ it 'runs good test examples once' do
172
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/good_test.rb', env: env)
173
+ expect(out).to include('1st try')
174
+ expect(out).not_to include('2nd try')
175
+ end
176
+
177
+ it 'runs flaky test examples once' do
178
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/flaky_test.rb', env: env)
179
+ expect(out).to include('1st try')
180
+ expect(out).not_to include('2nd try')
181
+ end
182
+ end
183
+
184
+ context 'when example did not change' do
185
+ let(:env) do
186
+ {
187
+ 'CI' => 'false',
188
+ 'RSPEC_FORTIFY_LOG_FIRST_ATTEMPT' => 'true',
189
+ }
190
+ end
191
+
192
+ it 'runs bad test examples once' do
193
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/bad_test.rb', env: env)
194
+ expect(out).to include('1st try')
195
+ expect(out).not_to include('2nd try')
196
+ end
197
+
198
+ it 'runs good test examples once' do
199
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/good_test.rb', env: env)
200
+ expect(out).to include('1st try')
201
+ expect(out).not_to include('2nd try')
202
+ end
203
+
204
+ it 'runs flaky test examples once' do
205
+ out, _err = cmd.run!('bundle exec rspec spec/fixtures/flaky_test.rb', env: env)
206
+ expect(out).to include('1st try')
207
+ expect(out).not_to include('2nd try')
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/fortify'
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-fortify
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Yusuke Mito
8
+ - Michael Glass
9
+ - Devin Burnette
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2025-06-12 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec-core
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">"
20
+ - !ruby/object:Gem::Version
21
+ version: '3.9'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">"
27
+ - !ruby/object:Gem::Version
28
+ version: '3.9'
29
+ description: retry intermittently failing rspec examples
30
+ email:
31
+ - devin@betterment.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".github/workflows/ci.yml"
37
+ - ".github/workflows/linter.yml"
38
+ - ".gitignore"
39
+ - ".rubocop.yml"
40
+ - ".ruby-version"
41
+ - ".travis.yml"
42
+ - Appraisals
43
+ - Gemfile
44
+ - Guardfile
45
+ - LICENSE
46
+ - README.md
47
+ - Rakefile
48
+ - changelog.md
49
+ - gemfiles/rspec_3.10.gemfile
50
+ - gemfiles/rspec_3.10.gemfile.lock
51
+ - gemfiles/rspec_3.11.gemfile
52
+ - gemfiles/rspec_3.11.gemfile.lock
53
+ - gemfiles/rspec_3.12.gemfile
54
+ - gemfiles/rspec_3.12.gemfile.lock
55
+ - gemfiles/rspec_3.13.gemfile
56
+ - gemfiles/rspec_3.13.gemfile.lock
57
+ - gemfiles/rspec_3.9.gemfile
58
+ - gemfiles/rspec_3.9.gemfile.lock
59
+ - lib/rspec/fortify.rb
60
+ - lib/rspec/fortify/formatter.rb
61
+ - lib/rspec/fortify/version.rb
62
+ - lib/rspec_ext/rspec_ext.rb
63
+ - rspec-fortify.gemspec
64
+ - spec/fixtures/bad_test.rb
65
+ - spec/fixtures/flaky_test.rb
66
+ - spec/fixtures/good_test.rb
67
+ - spec/lib/rspec/fortify_spec.rb
68
+ - spec/spec_helper.rb
69
+ homepage: https://github.com/Betterment/rspec-fortify
70
+ licenses:
71
+ - MIT
72
+ metadata:
73
+ rubygems_mfa_required: 'true'
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">"
86
+ - !ruby/object:Gem::Version
87
+ version: 1.3.1
88
+ requirements: []
89
+ rubygems_version: 3.3.26
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: retry intermittently failing rspec examples
93
+ test_files: []