class FlakeySpecCatcher::RerunManager

RerunManager class

Pairs file changes with user-specified re-run commands. Those pairs are defined via environment variables.

Attributes

rerun_capsules[R]

Public Class Methods

new(git_controller: FlakeySpecCatcher::GitController.new, user_config: FlakeySpecCatcher::UserConfig.new) click to toggle source
# File lib/flakey_spec_catcher/rerun_manager.rb, line 13
def initialize(git_controller: FlakeySpecCatcher::GitController.new,
  user_config: FlakeySpecCatcher::UserConfig.new)

  @git_controller = git_controller
  @user_config = user_config
  @rerun_capsules = []
  determine_rerun_usage
  split!
end

Public Instance Methods

all_non_example_groups() click to toggle source

Get all change contexts that can invoke expectations/assertions

# File lib/flakey_spec_catcher/rerun_manager.rb, line 50
def all_non_example_groups
  @git_controller.capsule_manager.sorted_change_contexts.reject(&:example_group?)
                 .map(&:rerun_info)
end
condense_reruns() click to toggle source
# File lib/flakey_spec_catcher/rerun_manager.rb, line 45
def condense_reruns
  @git_controller.capsule_manager.condense_reruns
end
determine_rerun_usage() click to toggle source
# File lib/flakey_spec_catcher/rerun_manager.rb, line 23
def determine_rerun_usage
  if @user_config.manual_rerun_patterns.nil?
    pair_reruns_with_usages
  else
    @rerun_capsules = []
    inject_manual_reruns(@user_config.manual_rerun_patterns,
                         @user_config.manual_rerun_usage)
  end
end
identify_tag_excluded_reruns() click to toggle source

Identifies all contexts that have a tag present

# File lib/flakey_spec_catcher/rerun_manager.rb, line 56
def identify_tag_excluded_reruns
  @git_controller.capsule_manager.find_reruns_by_tags(@user_config.excluded_tags)
end
inject_manual_reruns(patterns, usage) click to toggle source

rubocop:disable Metrics/AbcSize, Metrics/MethodLength

# File lib/flakey_spec_catcher/rerun_manager.rb, line 81
def inject_manual_reruns(patterns, usage)
  tests = []
  patterns.tr(' ', '').split(',').each do |pattern|
    # Check if file exists first and handle if user supplies testcase:line_number
    file_name = pattern.split(':')[0]
    line_number_present = pattern.split(':').count > 1
    matching_files = Dir.glob(file_name)
    # If no file matches are run, don't queue up re-runs
    if matching_files.count.zero?
      puts "Specified pattern #{pattern} did not match an existing file"
      raise ArgumentError
    end
    # It won't make sense to have multiple files to run with one specific line number
    if line_number_present
      if matching_files.count > 1
        puts "Specified pattern #{pattern} matched multiple files but a line number was given"
        raise ArgumentError
      else
        tests.push pattern
      end
    # No line numbers, queue up all matching files
    else
      matching_files.each do |file|
        tests.push file
      end
    end
  end
  add_rerun_capsule(testcase: tests, usage: usage)
end
pair_reruns_with_usages() click to toggle source
# File lib/flakey_spec_catcher/rerun_manager.rb, line 60
def pair_reruns_with_usages
  reruns = tests_for_rerun
  configured_usage_patterns = @user_config.rspec_usage_patterns
  if configured_usage_patterns.count.zero?
    add_capsules_with_default_usage(reruns)
    return
  end

  configured_usage_patterns.each do |usage_pattern_pair|
    tests = []
    pattern, usage = usage_pattern_pair
    reruns.each do |rerun|
      tests.push rerun if rerun =~ /#{pattern}/
    end
    reruns -= tests
    add_rerun_capsule(testcase: tests, usage: usage)
  end
  add_rerun_capsule(testcase: reruns) if reruns.any?
end
tests_for_rerun() click to toggle source
# File lib/flakey_spec_catcher/rerun_manager.rb, line 33
def tests_for_rerun
  tests = if @user_config.rerun_file_only
    @git_controller.capsule_manager.changed_files
    # If no tags are specified, condense re-runs else expand them
  elsif @user_config.excluded_tags.empty?
    condense_reruns - identify_tag_excluded_reruns
  else
    all_non_example_groups - identify_tag_excluded_reruns
  end
  filter_reruns_by_ignore_files(tests)
end

Private Instance Methods

add_capsules_with_default_usage(reruns) click to toggle source
# File lib/flakey_spec_catcher/rerun_manager.rb, line 160
def add_capsules_with_default_usage(reruns)
  @rerun_capsules.push(FlakeySpecCatcher::RerunCapsule.new(testcase: reruns)) unless reruns.empty?
end
add_rerun_capsule(testcase: [], usage: nil) click to toggle source
# File lib/flakey_spec_catcher/rerun_manager.rb, line 164
def add_rerun_capsule(testcase: [], usage: nil)
  return if testcase.empty?

  capsule = @rerun_capsules.find { |cap| cap.usage == usage }
  if capsule
    capsule.testcase.push(testcase)
  else
    @rerun_capsules.push(FlakeySpecCatcher::RerunCapsule.new(testcase: testcase, usage: usage))
  end
end
filter_reruns_by_ignore_files(reruns) click to toggle source

rubocop:enable Metrics/AbcSize

# File lib/flakey_spec_catcher/rerun_manager.rb, line 145
def filter_reruns_by_ignore_files(reruns)
  return reruns if @user_config.ignore_files.count.zero?

  filtered_reruns = []
  ignore_files = "(#{@user_config.ignore_files.join('|')})"
  ignore_files_regex = /#{ignore_files}/

  reruns.each do |file|
    next if file =~ ignore_files_regex

    filtered_reruns.push(file)
  end
  filtered_reruns
end
split!() click to toggle source

rubocop:disable Metrics/AbcSize

# File lib/flakey_spec_catcher/rerun_manager.rb, line 115
def split!
  return unless !@user_config.split_nodes.nil? && !@user_config.split_index.nil?

  # we create this restriction for simplicity. if we have multiple capsules then
  # we have to split while being aware of splitting across capsules.
  # if we absolutely need to split with different usage types, then we can implement that later..
  raise ArgumentError('can only split on one usage type') if @rerun_capsules.size != 1

  # ensure everything is sorted consistently before splitting
  capsule = @rerun_capsules[0]
  capsule.testcase.sort!

  # compute the total tests that are changing
  test_count = capsule.testcase.size

  # the last node is going to be different from the previous nodes.
  # for instance, if there are 13 test to run and you want 3 nodes:
  # node1 -> 5
  # node2 -> 5
  # node3 -> 3
  # it may be more efficient to split differently, but for now this will work.
  node_test_count = (test_count / @user_config.split_nodes.to_f).ceil

  skipping = @user_config.split_index * node_test_count
  skipping_until = (@user_config.split_index + 1) * node_test_count
  split_tests = capsule.testcase[skipping...skipping_until]
  @rerun_capsules = [FlakeySpecCatcher::RerunCapsule.new(usage: capsule.usage, testcase: split_tests)]
end