1# frozen_string_literal: true 2 3module Sometimes 4 def run_with_retries(example_to_run, retries) 5 example = RSpec.current_example 6 example.metadata[:retries] ||= retries 7 8 retries.times do |t| 9 example.metadata[:retried] = t + 1 10 example.instance_variable_set(:@exception, nil) 11 example_to_run.run 12 break unless example.exception 13 end 14 15 if e = example.exception 16 new_exception = e.exception(e.message + "[Retried #{retries} times]") 17 new_exception.set_backtrace e.backtrace 18 example.instance_variable_set(:@exception, new_exception) 19 end 20 end 21end 22 23RSpec.configure do |config| 24 config.include Sometimes 25 config.alias_example_to :sometimes, :sometimes => true 26 config.add_setting :sometimes_retry_count, :default => 5 27 28 config.around(:each, :sometimes => true) do |example| 29 retries = example.metadata[:retries] || RSpec.configuration.sometimes_retry_count 30 run_with_retries(example, retries) 31 end 32 33 config.after(:suite) do 34 message = proc do |color, text| 35 colored = RSpec::Core::Formatters::ConsoleCodes.wrap(text, color) 36 notification = RSpec::Core::Notifications::MessageNotification.new(colored) 37 formatter = RSpec.configuration.formatters.first 38 formatter.message(notification) if formatter.respond_to?(:message) 39 end 40 41 retried_examples = RSpec.world.example_groups.map do |g| 42 g.descendants.map do |d| 43 d.filtered_examples.select do |e| 44 e.metadata[:sometimes] && e.metadata.fetch(:retried, 1) > 1 45 end 46 end 47 end.flatten 48 49 message.call(retried_examples.empty? ? :green : :yellow, "\n\nRetried examples: #{retried_examples.count}") 50 51 retried_examples.each do |e| 52 message.call(:cyan, " #{e.full_description}") 53 path = RSpec::Core::Metadata.relative_path(e.location) 54 message.call(:cyan, " [#{e.metadata[:retried]}/#{e.metadata[:retries]}] " + path) 55 end 56 end 57end 58