flakey_spec_catcher 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71dd2fe6a58fc01c2647d06f6aa20f9c904f6ced4d1a54278d96344c40534713
4
- data.tar.gz: cc060a637c1e14e04e7fd914b84c0b6c854912512a649fcf95696f57069444e1
3
+ metadata.gz: 1c2c74dc190189f31023ee816f41a7856aceee56ccca2de3f385b3559932cfde
4
+ data.tar.gz: 15b1cf40884ea015014a2d0b4b96ea223f46f9381751282d370e21641a0456da
5
5
  SHA512:
6
- metadata.gz: 28114d12131611ae6405bd0c3d066b44c16c3efece45633e46b1c8f5a263eec7189c45b46efbf112897e5e7cbeecf110f493ee499c79d76647f64e2da4a99abe
7
- data.tar.gz: be57cbc9ab2052f770507effd1b2e985124607bc5bfa11b0c659279726969aea9390df6b352501bdd31a1e179cb3c554fd448dc24c705f4974087772f502bd00
6
+ metadata.gz: f3eea41fa5d50fd29d292810a5b1fbf0c25452e57c85c81999fb257063a02f823657e15bd2cb9e23c63fe90923367d89b9098e5aef56142dcf51f9638412d86e
7
+ data.tar.gz: 7fbc79e679dfc5c4f38c995c377ab119eae0d4e890d1d5b7b97ee9516215f60e094439f246dfd7e97c2b4fa3f5c2dd5293979d230d26da7fc19174d1192ce327
data/README.md CHANGED
@@ -75,6 +75,20 @@ FSC_USAGE_PATTERNS = '{ spec/ui => bundle exec rspec }, { spec/api => parallel_r
75
75
 
76
76
  ```
77
77
 
78
+ ### Manually re-running a test:
79
+
80
+ Once the gem has been installed, you may manually specify a test to run along with a custom usage
81
+ (if none is provided, the specified test will run with RSpec::Core::Runner) and a repeat_factor.
82
+ If no repeat_factor is provided, FSC will check ENV['FSC_REPEAT_FACTOR'] to see if a value
83
+ has been configured. If none was provided, it will fall back on the default of 20 re-runs
84
+
85
+ Usage Examples:
86
+ ```
87
+ flakey_spec_catcher --test='api/spec/user_spec.rb:3' --repeat='15'
88
+ flakey_spec_catcher --test='api/spec/*_spec.rb' --usage='bundle exec parallel_rspec'
89
+ flakey_spec_catcher --test='api/spec/admin_spec.rb' --repeat='10' --usage='rspec'
90
+ ```
91
+
78
92
  ### How FSC works:
79
93
 
80
94
  1. GitController runs a git diff and creates a ChangeSummary object to
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+
5
+ module FlakeySpecCatcher
6
+ # CliOverride class
7
+ #
8
+ # Captures command line arguments for manual re-runs
9
+ class CliOverride
10
+ attr_reader :rerun_pattern, :rerun_usage, :repeat_factor
11
+
12
+ def initialize
13
+ parse_command_line_args
14
+ end
15
+
16
+ private
17
+
18
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
19
+ def parse_command_line_args
20
+ OptionParser.new do |opts|
21
+ opts.banner = 'Usage: flakey_spec_catcher [OPTIONS]'
22
+
23
+ opts.on('-t', '--test=TEST_NAME',
24
+ 'Specify a spec as `path/to/file:line_number` or `file/path` to run') do |test|
25
+ @rerun_pattern = remove_formatter_quotes(test)
26
+ end
27
+
28
+ opts.on('-u', '--usage=USAGE', 'Specify a re-run usage for the manual re-run') do |usage|
29
+ raise ArgumentError if @rerun_pattern.nil?
30
+
31
+ @rerun_usage = remove_formatter_quotes(usage)
32
+ end
33
+
34
+ opts.on('-r', '--repeat=REPEAT_FACTOR',
35
+ 'Specify a repeat factor for the manual re-run(s)') do |repeat|
36
+ raise ArgumentError if @rerun_pattern.nil?
37
+
38
+ parsed_repeat_factor = remove_formatter_quotes(repeat).to_i
39
+ @repeat_factor = parsed_repeat_factor if parsed_repeat_factor.positive?
40
+ end
41
+
42
+ opts.on('-v', '--version', 'Prints current flakey_spec_catcher_version') do
43
+ puts "flakey_spec_catcher Version: #{FlakeySpecCatcher::VERSION}"
44
+ end
45
+
46
+ opts.on('-h', '--help', 'Displays available flakey_spec_catcher cli overrides') do
47
+ puts opts
48
+ end
49
+ end.parse!
50
+ end
51
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
52
+
53
+ def remove_formatter_quotes(env_var)
54
+ env_var.tr('\'\"', '')
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec'
4
+
5
+ module FlakeySpecCatcher
6
+ # EventListener class
7
+ #
8
+ # Listens for events from RSpec and processes data from those events.
9
+ #
10
+ # This event listener will receive example_failed and example_passed events
11
+ # from RSpec and will log those results into the provided result manager
12
+ class EventListener
13
+ def initialize(result_manager)
14
+ @result_manager = result_manager
15
+ end
16
+
17
+ def example_failed(notification)
18
+ description = notification.example.full_description.to_s.strip
19
+ @result_manager.add_result(description, format_message(notification))
20
+ end
21
+
22
+ def example_passed(notification)
23
+ description = notification.example.full_description.to_s.strip
24
+ @result_manager.add_result(description)
25
+ end
26
+
27
+ private
28
+
29
+ def format_message(notification)
30
+ exception = notification.example.execution_result.exception
31
+ message = if exception.is_a?(RSpec::Core::MultipleExceptionError)
32
+ exception.all_exceptions.join("\n").strip
33
+ else
34
+ exception.to_s.strip
35
+ end
36
+
37
+ message
38
+ end
39
+ end
40
+ end
@@ -16,7 +16,17 @@ module FlakeySpecCatcher
16
16
  @git_controller = git_controller
17
17
  @user_config = user_config
18
18
  @rerun_capsules = []
19
- pair_reruns_with_usages
19
+ determine_rerun_usage
20
+ end
21
+
22
+ def determine_rerun_usage
23
+ if @user_config.manual_rerun_pattern.nil?
24
+ pair_reruns_with_usages
25
+ else
26
+ @rerun_capsules.clear
27
+ inject_manual_reruns(@user_config.manual_rerun_pattern,
28
+ @user_config.manual_rerun_usage)
29
+ end
20
30
  end
21
31
 
22
32
  def tests_for_rerun
@@ -52,6 +62,37 @@ module FlakeySpecCatcher
52
62
  end
53
63
  end
54
64
 
65
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
66
+ def inject_manual_reruns(pattern, usage)
67
+ # Check if file exists first and handle if user supplies testcase:line_number
68
+ file_name = pattern.split(':')[0]
69
+ line_number_present = pattern.split(':').count > 1
70
+ matching_files = Dir.glob(file_name)
71
+
72
+ # If no file matches are run, don't queue up re-runs
73
+ if matching_files.count.zero?
74
+ puts "Specified pattern #{pattern} did not match an existing file"
75
+ raise ArgumentError
76
+ end
77
+
78
+ # It won't make sense to have multiple files to run with one specific line number
79
+ if line_number_present
80
+ if matching_files.count > 1
81
+ puts "Specified pattern #{pattern} matched multiple files but a line number was specified"
82
+ raise ArgumentError
83
+ else
84
+ add_rerun_capsule(testcase: pattern, usage: usage)
85
+ end
86
+
87
+ # No line numbers, queue up all matching files
88
+ else
89
+ matching_files.each do |file|
90
+ add_rerun_capsule(testcase: file, usage: usage)
91
+ end
92
+ end
93
+ end
94
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
95
+
55
96
  private
56
97
 
57
98
  def filter_reruns_by_ignore_files(reruns)
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../helpers/colorize'
4
+ require_relative '../helpers/indent_string'
5
+
3
6
  module FlakeySpecCatcher
4
7
  # RspecResult class
5
8
  #
@@ -9,116 +12,50 @@ module FlakeySpecCatcher
9
12
  # re-run of that spec, the results will be pushed to the RspecResult object.
10
13
  # This class will then organize and output the results accordingly.
11
14
  class RspecResult
12
- attr_reader :file_name, :total_examples_run, :total_failures, :failure_summary
15
+ attr_accessor :description, :total_times_run, :total_failures
13
16
 
14
- def initialize(file_name)
15
- @file_name = file_name
16
- @total_examples_run = 0
17
- @total_failures = 0
18
- @failure_summary = []
17
+ def initialize(description, exception_message = nil)
18
+ @description = description
19
+ @total_times_run = 1
20
+ @total_failures = exception_message ? 1 : 0
21
+ @failures = []
22
+ add_failure(exception_message) if exception_message
19
23
  end
20
24
 
21
- def info
22
- ratio = "#{@total_examples_run - @total_failures}/#{@total_examples_run}"
23
- puts "\nFile Name: #{@file_name} passed #{ratio} examples"
25
+ def add_run(exception_message = nil)
26
+ @total_times_run += 1
24
27
 
25
- return unless @total_failures.positive?
28
+ return unless exception_message
26
29
 
27
- puts ' Failure Summary:'
28
- @failure_summary.each do |example, _occurrences|
29
- example.info
30
- end
30
+ @total_failures += 1
31
+ add_failure(exception_message)
31
32
  end
32
33
 
33
- def add_run(results)
34
- parse_totals(results)
35
-
36
- # Parse the failed examples and add them into the failure_summary
37
- if @failure_summary.empty?
38
- # Add the initial runs
39
- @failure_summary = parse_failed_examples(results)
40
- else
41
- populate_failure_summary(parse_failed_examples(results))
42
- end
34
+ def add_failure(exception_message)
35
+ failure = @failures.find { |f| f.exception_message == exception_message }
36
+ failure ? failure.add_failure : @failures.push(RSpecFailure.new(exception_message))
43
37
  end
44
38
 
45
- private
46
-
47
- def parse_failed_examples(results)
48
- failures = []
49
- # Currently we're only going to return the name of the example that failed
50
- # Example:
51
- # 1) Flakey spec Passes some of the time
52
- # Failure/Error: expect(1).to eq(rand(3))
53
- #
54
- # expected: 0
55
- # got: 1
56
- #
57
- # (compared using ==)
58
- # ./spec/flakey_example_spec.rb:6:in `block (2 levels) in <top (required)>'
59
-
60
- # Would return 'Flakey spec Passes some of the time'
61
-
62
- # Get the line that contains the failed example - Denoted by '1)' in the examples
63
- # and strip the example ennumerators
64
- example_lines = results.scan(/^\s*\d\).*$/)
65
- failed_examples = example_lines.map { |example| example.strip.sub!(/\d\)\s*/, '') }
66
-
67
- # Use the first line of the failed_examples to get the actual failures
68
- failed_examples.each do |example|
69
- # Go from the spec example description until '#)' or 'Finished in'
70
- result = results[/#{Regexp.escape(example)}(.*?)(Finished in| \d\))/m, 1]
71
- failures << RSpecFailure.new(example, result.strip)
39
+ def print_results
40
+ puts "\n#{@description.yellow}\nFAILED #{total_failures} / #{total_times_run} times"
41
+ @failures.each do |f|
42
+ puts "#{f.count.to_s.indent(2)} times with exception message:"
43
+ puts f.exception_message.indent(4).red.to_s
72
44
  end
73
-
74
- # Return the array of RSpecFailures
75
- failures
76
45
  end
77
46
 
78
- def parse_totals(results)
79
- # Get summary line to determine number of run results.
80
- # Will appear as "3 examples, 0 failures"
81
- summary = results.split("\n").find { |line| line =~ /example.*failure/ }
82
- pass_count, fail_count = summary.split(' ').select { |x| x[/\d/] }
83
- @total_examples_run += pass_count.to_i
84
- @total_failures += fail_count.to_i
85
- end
47
+ # Simple class to contain failed example data
48
+ class RSpecFailure
49
+ attr_reader :exception_message, :count
86
50
 
87
- def populate_failure_summary(failures)
88
- # For each failure, if that example is in @failure_summary, add the results to it
89
- failures.each do |failure|
90
- match_found = false
91
- @failure_summary.each do |example|
92
- # If example is already in the summary, add the expect/actual to the example's failures
93
- if example.example_name == failure.example_name
94
- example.add_case(failure.failure_details.join(''))
95
- match_found = true
96
- end
97
- end
98
- # If example is not in the summary, add the whole object to the array of examples
99
- @failure_summary << failure unless match_found
51
+ def initialize(exception_message)
52
+ @exception_message = exception_message
53
+ @count = 1
100
54
  end
101
- end
102
- end
103
55
 
104
- # Simple class to contain failed example data
105
- class RSpecFailure
106
- attr_reader :example_name, :failure_details
107
-
108
- def initialize(example_name, results)
109
- @example_name = example_name
110
- @failure_count = 1
111
- @failure_details = [results]
112
- end
113
-
114
- def add_case(results)
115
- @failure_count += 1
116
- @failure_details << results
117
- end
118
-
119
- def info
120
- puts "\n '#{@example_name}' failed #{@failure_count} time(s)"
121
- @failure_details.each { |failure| puts "\n #{failure}" }
56
+ def add_failure
57
+ @count += 1
58
+ end
122
59
  end
123
60
  end
124
61
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helpers/colorize'
4
+ require_relative './rspec_result'
5
+
6
+ module FlakeySpecCatcher
7
+ # RspecResultManager class
8
+ #
9
+ # Holds a collection of RSpec results and provides helper methods
10
+ #
11
+ # An RspecResultManager will hold a collection of results, one for each
12
+ # distinct example. It also provides helpers for adding new results,
13
+ # displaying aggregate results, and checking the state of the collection.
14
+ class RspecResultManager
15
+ def initialize(rspec_result_class)
16
+ @result_class = rspec_result_class
17
+ @results = []
18
+ end
19
+
20
+ def add_result(description, message = nil)
21
+ result = @results.find { |r| r.description == description }
22
+ result ? result.add_run(message) : @results.push(@result_class.new(description, message))
23
+ end
24
+
25
+ def print_results
26
+ puts "\n********** SUMMARY **********"
27
+ print_successes_count(successes)
28
+ failures.each(&:print_results) if failures.any?
29
+ end
30
+
31
+ def failures
32
+ @results.select { |r| r.total_failures.positive? }
33
+ end
34
+
35
+ def successes
36
+ @results.select { |r| r.total_failures.zero? }
37
+ end
38
+
39
+ def empty?
40
+ @results.empty?
41
+ end
42
+
43
+ private
44
+
45
+ def print_successes_count(successes)
46
+ puts "#{successes.count} example(s) ran without any failures".green if successes.any?
47
+ end
48
+ end
49
+ end
@@ -1,27 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rspec'
4
- require_relative './rspec_result'
5
4
  require_relative './git_controller'
6
5
  require_relative './capsule_manager'
7
6
  require_relative './user_config'
8
7
  require_relative './rerun_manager'
8
+ require_relative './rspec_result_manager'
9
+ require_relative './event_listener.rb'
9
10
 
10
11
  module FlakeySpecCatcher
11
12
  class Runner
12
13
  attr_reader :user_config, :rerun_manager, :git_controller
13
- attr_reader :rspec_results
14
14
 
15
15
  def initialize(test_mode: false,
16
16
  git_controller: FlakeySpecCatcher::GitController.new(test_mode: test_mode),
17
17
  user_config: FlakeySpecCatcher::UserConfig.new,
18
+ result_manager: FlakeySpecCatcher::RspecResultManager.new(FlakeySpecCatcher::RspecResult),
18
19
  rerun_manager: FlakeySpecCatcher::RerunManager.new(git_controller: git_controller,
19
20
  user_config: user_config))
20
21
 
21
- @rspec_results = []
22
22
  @git_controller = git_controller
23
23
  @user_config = user_config
24
24
  @rerun_manager = rerun_manager
25
+ @rspec_result_manager = result_manager
25
26
  end
26
27
 
27
28
  # Debug Methods
@@ -51,47 +52,61 @@ module FlakeySpecCatcher
51
52
  status = 0
52
53
  @user_config.repeat_factor.times do
53
54
  @rerun_manager.rerun_capsules.sort.each do |capsule|
54
- iteration_status = 0
55
- if capsule.default_usage?
56
- iteration_status = invoke_rspec_runner(capsule.testcase)
57
- else
58
- `#{capsule.usage} #{capsule.testcase}`
59
- iteration_status = $?.exitstatus # rubocop:disable Style/SpecialGlobalVars
60
- end
55
+ iteration_status = handle_capsule_rerun(capsule)
61
56
  status = [status, iteration_status].max
62
57
  end
63
58
  end
64
59
 
60
+ display_results(status)
61
+
65
62
  # Always return 0 if silent_mode is enabled
66
- if @user_config.silent_mode
67
- 0
68
- else
69
- status
70
- end
63
+ @user_config.silent_mode ? 0 : status
71
64
  end
72
65
 
73
66
  private
74
67
 
68
+ def display_results(status)
69
+ @rspec_result_manager.print_results unless @rspec_result_manager.empty?
70
+ status.zero? ? print_no_flakey_specs_detected_message : print_flakey_specs_detected_message
71
+ end
72
+
75
73
  def invoke_rspec_runner(test)
76
- return_status = RSpec::Core::Runner.run([test])
74
+ configure_listener
75
+ # Pass in CLI options to suppress normal output, and only run the specified test
76
+ return_status = RSpec::Core::Runner.run(['--out', '/dev/null', test])
77
77
  RSpec.clear_examples
78
78
  return_status
79
79
  end
80
80
 
81
- def print_rspec_results
82
- @rspec_results.map(&:get_info)
81
+ def invoke_custom_rspec_runner(usage, testcase)
82
+ `#{usage} #{testcase}`
83
+ $?.exitstatus # rubocop:disable Style/SpecialGlobalVars
84
+ end
83
85
 
84
- # Determine the exit code based on the parsed results
85
- if @rspec_results.any? { |r| r.total_failures.positive? }
86
- puts "\n********************************************"
87
- puts 'Flakiness Detected! Exiting with status code 1'
88
- puts "********************************************\n"
89
- return 1
86
+ def handle_capsule_rerun(capsule)
87
+ if capsule.default_usage?
88
+ invoke_rspec_runner(capsule.testcase)
90
89
  else
91
- puts "\n***********************************************"
92
- puts 'No Flakiness Detected! Exiting with status code 0'
93
- puts "***********************************************\n"
94
- return 0
90
+ invoke_custom_rspec_runner(capsule.usage, capsule.testcase)
91
+ end
92
+ end
93
+
94
+ def print_flakey_specs_detected_message
95
+ puts "\n**********************************************".magenta
96
+ puts ' Flakiness Detected!'.magenta
97
+ puts "**********************************************\n".magenta
98
+ end
99
+
100
+ def print_no_flakey_specs_detected_message
101
+ puts "\n*************************************************".green
102
+ puts ' No Flakiness Detected!'.green
103
+ puts "*************************************************\n".green
104
+ end
105
+
106
+ def configure_listener
107
+ RSpec.configure do |c|
108
+ c.reporter.register_listener EventListener.new(@rspec_result_manager),
109
+ :example_failed, :example_passed
95
110
  end
96
111
  end
97
112
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './cli_override'
3
4
  module FlakeySpecCatcher
4
5
  # UserConfig class
5
6
  #
@@ -7,21 +8,40 @@ module FlakeySpecCatcher
7
8
  class UserConfig
8
9
  attr_reader :repeat_factor, :ignore_files, :ignore_branches, :silent_mode
9
10
  attr_reader :rerun_file_only, :rspec_usage_patterns
11
+ attr_reader :manual_rerun_pattern, :manual_rerun_usage
10
12
  USER_CONFIG_ENV_VARS = %w[FSC_REPEAT_FACTOR FSC_IGNORE_FILES FSC_IGNORE_BRANCHES
11
13
  FSC_SILENT_MODE FSC_RERUN_FILE_ONLY FSC_USAGE_PATTERNS].freeze
12
14
 
13
- def initialize
15
+ def initialize(cli_override: CliOverride.new)
14
16
  @repeat_factor = initialize_repeat_factor(ENV['FSC_REPEAT_FACTOR'])
15
17
  @ignore_files = env_var_string_to_array(ENV['FSC_IGNORE_FILES'])
16
18
  @ignore_branches = env_var_string_to_array(ENV['FSC_IGNORE_BRANCHES'])
17
19
  @silent_mode = env_var_string_to_bool(ENV['FSC_SILENT_MODE'])
18
20
  @rerun_file_only = env_var_string_to_bool(ENV['FSC_RERUN_FILE_ONLY'])
19
21
  @rspec_usage_patterns = env_var_string_to_pairs(ENV['FSC_USAGE_PATTERNS'])
20
- parse_commit_message
22
+ @cli_override = cli_override
23
+ override_settings
21
24
  end
22
25
 
23
26
  private
24
27
 
28
+ def override_settings
29
+ apply_cli_override
30
+ return unless @manual_rerun_pattern.nil?
31
+
32
+ parse_commit_message
33
+ end
34
+
35
+ def apply_cli_override
36
+ @manual_rerun_pattern = @cli_override.rerun_pattern
37
+ @manual_rerun_usage = @cli_override.rerun_usage
38
+ @repeat_factor = @cli_override.repeat_factor if @cli_override.repeat_factor.to_i.positive?
39
+ end
40
+
41
+ def remove_formatter_quotes(env_var)
42
+ env_var.tr('\'\"', '')
43
+ end
44
+
25
45
  def initialize_repeat_factor(env_var)
26
46
  env_var.to_i.positive? ? env_var.to_i : 20
27
47
  end
@@ -60,7 +80,7 @@ module FlakeySpecCatcher
60
80
 
61
81
  # For each array of matches, flatten the individual match group sub-array
62
82
  # then remove the formatter quotes we require the values to be wrapped in
63
- matches.map { |m| m.join('').tr("'", '') }
83
+ matches.map { |m| remove_formatter_quotes(m.join('')) }
64
84
  end
65
85
 
66
86
  def parse_commit_message
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FlakeySpecCatcher
4
- VERSION = '0.3.1'
4
+ VERSION = '0.5.0'
5
5
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This is an extension of the Ruby String class.
4
+ # Example usage: puts "This message is red".red
5
+ # From http://stackoverflow.com/a/11482430/3038677
6
+ class String
7
+ module Colors
8
+ RED = 31
9
+ GREEN = 32
10
+ YELLOW = 33
11
+ MAGENTA = 35
12
+ end
13
+
14
+ def red
15
+ colorize(Colors::RED)
16
+ end
17
+
18
+ def green
19
+ colorize(Colors::GREEN)
20
+ end
21
+
22
+ def yellow
23
+ colorize(Colors::YELLOW)
24
+ end
25
+
26
+ def magenta
27
+ colorize(Colors::MAGENTA)
28
+ end
29
+
30
+ private
31
+
32
+ def colorize(color_code)
33
+ "\e[#{color_code}m#{self}\e[0m"
34
+ end
35
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: false
2
+
3
+ String.class_eval do
4
+ def indent(count, char = ' ')
5
+ gsub(/([^\n]*)(\n|$)/) do |_match|
6
+ last_iteration = (Regexp.last_match(1) == '' && Regexp.last_match(2) == '')
7
+ line = ''
8
+ line << (char * count) unless last_iteration
9
+ line << Regexp.last_match(1)
10
+ line << Regexp.last_match(2)
11
+ line
12
+ end
13
+ end
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flakey_spec_catcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Watson
@@ -90,13 +90,18 @@ files:
90
90
  - lib/flakey_spec_catcher/change_capsule.rb
91
91
  - lib/flakey_spec_catcher/change_context.rb
92
92
  - lib/flakey_spec_catcher/change_summary.rb
93
+ - lib/flakey_spec_catcher/cli_override.rb
94
+ - lib/flakey_spec_catcher/event_listener.rb
93
95
  - lib/flakey_spec_catcher/git_controller.rb
94
96
  - lib/flakey_spec_catcher/rerun_capsule.rb
95
97
  - lib/flakey_spec_catcher/rerun_manager.rb
96
98
  - lib/flakey_spec_catcher/rspec_result.rb
99
+ - lib/flakey_spec_catcher/rspec_result_manager.rb
97
100
  - lib/flakey_spec_catcher/runner.rb
98
101
  - lib/flakey_spec_catcher/user_config.rb
99
102
  - lib/flakey_spec_catcher/version.rb
103
+ - lib/helpers/colorize.rb
104
+ - lib/helpers/indent_string.rb
100
105
  homepage:
101
106
  licenses:
102
107
  - MIT
@@ -117,7 +122,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
122
  - !ruby/object:Gem::Version
118
123
  version: '0'
119
124
  requirements: []
120
- rubygems_version: 3.0.1
125
+ rubyforge_project:
126
+ rubygems_version: 2.7.6
121
127
  signing_key:
122
128
  specification_version: 4
123
129
  summary: Run new or changed specs many times to prevent unreliable specs