class RSpecTracer::Runner

Constants

EXAMPLE_RUN_REASON

Attributes

cache[R]
reporter[R]

Public Class Methods

new() click to toggle source
# File lib/rspec_tracer/runner.rb, line 19
def initialize
  @cache = RSpecTracer::Cache.new
  @reporter = RSpecTracer::Reporter.new
  @filtered_examples = {}

  return if @cache.run_id.nil?

  @cache.load_cache_for_run
  filter_examples_to_run
end

Public Instance Methods

generate_missed_coverage() click to toggle source

rubocop:disable Metrics/AbcSize

# File lib/rspec_tracer/runner.rb, line 67
def generate_missed_coverage
  missed_coverage = Hash.new do |files_coverage, file_path|
    files_coverage[file_path] = Hash.new do |strength, line_number|
      strength[line_number] = 0
    end
  end

  @cache.cached_examples_coverage.each_pair do |example_id, example_coverage|
    example_coverage.each_pair do |file_path, line_coverage|
      next unless @reporter.example_skipped?(example_id)

      file_name = RSpecTracer::SourceFile.file_name(file_path)

      next if @reporter.file_deleted?(file_name)

      line_coverage.each_pair do |line_number, strength|
        missed_coverage[file_path][line_number] += strength
      end
    end
  end

  missed_coverage
end
generate_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 130
def generate_report
  @reporter.generate_last_run_report
  generate_examples_status_report

  %i[all_files all_examples dependency examples_coverage reverse_dependency].each do |report_type|
    starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)

    send("generate_#{report_type}_report")

    ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)

    puts "RSpec tracer generated #{report_type.to_s.tr('_', ' ')} report (took #{elpased})" if RSpecTracer.verbose?
  end

  @reporter.write_reports
end
on_example_failed(example_id, execution_result) click to toggle source
# File lib/rspec_tracer/runner.rb, line 54
def on_example_failed(example_id, execution_result)
  @reporter.on_example_failed(example_id, execution_result)
end
on_example_passed(example_id, execution_result) click to toggle source
# File lib/rspec_tracer/runner.rb, line 50
def on_example_passed(example_id, execution_result)
  @reporter.on_example_passed(example_id, execution_result)
end
on_example_pending(example_id, execution_result) click to toggle source
# File lib/rspec_tracer/runner.rb, line 58
def on_example_pending(example_id, execution_result)
  @reporter.on_example_pending(example_id, execution_result)
end
on_example_skipped(example_id) click to toggle source
# File lib/rspec_tracer/runner.rb, line 46
def on_example_skipped(example_id)
  @reporter.on_example_skipped(example_id)
end
register_deleted_examples() click to toggle source
# File lib/rspec_tracer/runner.rb, line 62
def register_deleted_examples
  @reporter.register_deleted_examples(@cache.all_examples)
end
register_dependency(examples_coverage) click to toggle source

rubocop:enable Metrics/AbcSize

# File lib/rspec_tracer/runner.rb, line 92
def register_dependency(examples_coverage)
  filtered_files = Set.new

  examples_coverage.each_pair do |example_id, example_coverage|
    register_example_files_dependency(example_id)

    example_coverage.each_key do |file_path|
      next if filtered_files.include?(file_path)

      filtered_files << file_path unless register_file_dependency(example_id, file_path)
    end
  end

  @reporter.pending_examples.each do |example_id|
    register_example_files_dependency(example_id)
  end
end
register_example(example) click to toggle source
# File lib/rspec_tracer/runner.rb, line 42
def register_example(example)
  @reporter.register_example(example)
end
register_examples_coverage(examples_coverage) click to toggle source
# File lib/rspec_tracer/runner.rb, line 126
def register_examples_coverage(examples_coverage)
  @reporter.register_examples_coverage(examples_coverage)
end
register_untraced_dependency(trace_point_files) click to toggle source
# File lib/rspec_tracer/runner.rb, line 110
def register_untraced_dependency(trace_point_files)
  untraced_files = generate_untraced_files(trace_point_files)

  untraced_files.each do |file_path|
    source_file = RSpecTracer::SourceFile.from_path(file_path)

    next if RSpecTracer.filters.any? { |filter| filter.match?(source_file) }

    @reporter.register_source_file(source_file)

    @reporter.all_examples.each_key do |example_id|
      @reporter.register_dependency(example_id, source_file[:file_name])
    end
  end
end
run_example?(example_id) click to toggle source
# File lib/rspec_tracer/runner.rb, line 30
def run_example?(example_id)
  return true if explicit_run?

  !@cache.all_examples.key?(example_id) || @filtered_examples.key?(example_id)
end
run_example_reason(example_id) click to toggle source
# File lib/rspec_tracer/runner.rb, line 36
def run_example_reason(example_id)
  return EXAMPLE_RUN_REASON[:explicit_run] if explicit_run?

  @filtered_examples[example_id] || EXAMPLE_RUN_REASON[:no_cache]
end

Private Instance Methods

add_previously_failed_examples() click to toggle source
# File lib/rspec_tracer/runner.rb, line 192
def add_previously_failed_examples
  @cache.failed_examples.each do |example_id|
    next if @filtered_examples.key?(example_id)

    @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:failed_example]

    next unless (@changed_files & @cache.dependency[example_id]).empty?

    @reporter.register_possibly_flaky_example(example_id)
  end
end
add_previously_flaky_examples() click to toggle source
# File lib/rspec_tracer/runner.rb, line 182
def add_previously_flaky_examples
  @cache.flaky_examples.each do |example_id|
    @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:flaky_example]

    next unless (@changed_files & @cache.dependency[example_id]).empty?

    @reporter.register_possibly_flaky_example(example_id)
  end
end
add_previously_pending_examples() click to toggle source
# File lib/rspec_tracer/runner.rb, line 204
def add_previously_pending_examples
  @cache.pending_examples.each do |example_id|
    @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:pending_example]
  end
end
explicit_run?() click to toggle source
# File lib/rspec_tracer/runner.rb, line 150
def explicit_run?
  ENV.fetch('RSPEC_TRACER_NO_SKIP', 'false') == 'true'
end
fetch_changed_files() click to toggle source
# File lib/rspec_tracer/runner.rb, line 210
def fetch_changed_files
  @cache.all_files.each_value do |cached_file|
    file_name = cached_file[:file_name]
    source_file = RSpecTracer::SourceFile.from_name(file_name)

    if source_file.nil?
      @reporter.on_file_deleted(file_name)
    elsif cached_file[:digest] != source_file[:digest]
      @reporter.on_file_modified(file_name)
    end
  end

  @reporter.modified_files | @reporter.deleted_files
end
fetch_rspec_required_files() click to toggle source
# File lib/rspec_tracer/runner.rb, line 233
def fetch_rspec_required_files
  rspec_root = RSpec::Core::RubyProject.root
  rspec_path = RSpec.configuration.default_path

  RSpec.configuration.requires.each_with_object([]) do |file_name, required_files|
    file_name = "#{file_name}.rb" if File.extname(file_name).empty?
    file_path = File.join(rspec_root, rspec_path, file_name)

    required_files << file_path if File.file?(file_path)
  end
end
filter_by_example_status() click to toggle source
# File lib/rspec_tracer/runner.rb, line 167
def filter_by_example_status
  add_previously_flaky_examples
  add_previously_failed_examples
  add_previously_pending_examples
end
filter_by_files_changed() click to toggle source
# File lib/rspec_tracer/runner.rb, line 173
def filter_by_files_changed
  @cache.dependency.each_pair do |example_id, files|
    next if @filtered_examples.key?(example_id)
    next if (@changed_files & files).empty?

    @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:files_changed]
  end
end
filter_examples_to_run() click to toggle source
# File lib/rspec_tracer/runner.rb, line 154
def filter_examples_to_run
  starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  @changed_files = fetch_changed_files

  filter_by_example_status
  filter_by_files_changed

  ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)

  puts "RSpec tracer processed cache (took #{elpased})" if RSpecTracer.verbose?
end
generate_all_examples_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 295
def generate_all_examples_report
  @cache.all_examples.each_pair do |example_id, data|
    next if @reporter.all_examples.key?(example_id) ||
      @reporter.example_deleted?(example_id)

    @reporter.all_examples[example_id] = data
  end
end
generate_all_files_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 286
def generate_all_files_report
  @cache.all_files.each_pair do |file_name, data|
    next if @reporter.all_files.key?(file_name) ||
      @reporter.file_deleted?(file_name)

    @reporter.all_files[file_name] = data
  end
end
generate_dependency_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 332
def generate_dependency_report
  @cache.dependency.each_pair do |example_id, data|
    next if @reporter.dependency.key?(example_id) ||
      @reporter.example_deleted?(example_id)

    @reporter.dependency[example_id] = data.reject do |file_name|
      @reporter.file_deleted?(file_name)
    end
  end

  @reporter.dependency.transform_values!(&:to_a)
end
generate_examples_coverage_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 345
def generate_examples_coverage_report
  @cache.cached_examples_coverage.each_pair do |example_id, data|
    next if @reporter.examples_coverage.key?(example_id) ||
      @reporter.example_deleted?(example_id)

    @reporter.examples_coverage[example_id] = data.reject do |file_name|
      @reporter.file_deleted?(file_name)
    end
  end
end
generate_examples_status_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 273
def generate_examples_status_report
  starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)

  generate_flaky_examples_report
  generate_failed_examples_report
  generate_pending_examples_report

  ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)

  puts "RSpec tracer generated flaky, failed, and pending examples report (took #{elpased})" if RSpecTracer.verbose?
end
generate_failed_examples_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 314
def generate_failed_examples_report
  @cache.failed_examples.each do |example_id|
    next if @reporter.example_deleted?(example_id) ||
      @reporter.all_examples.key?(example_id)

    @reporter.register_failed_example(example_id)
  end
end
generate_flaky_examples_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 304
def generate_flaky_examples_report
  @reporter.possibly_flaky_examples.each do |example_id|
    next if @reporter.example_deleted?(example_id)
    next unless @cache.flaky_examples.include?(example_id) ||
      @reporter.example_passed?(example_id)

    @reporter.register_flaky_example(example_id)
  end
end
generate_pending_examples_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 323
def generate_pending_examples_report
  @cache.pending_examples.each do |example_id|
    next if @reporter.example_deleted?(example_id) ||
      @reporter.all_examples.key?(example_id)

    @reporter.register_pending_example(example_id)
  end
end
generate_reverse_dependency_report() click to toggle source
# File lib/rspec_tracer/runner.rb, line 356
def generate_reverse_dependency_report
  @reporter.generate_reverse_dependency_report
end
generate_untraced_files(trace_point_files) click to toggle source
# File lib/rspec_tracer/runner.rb, line 225
def generate_untraced_files(trace_point_files)
  all_files = @reporter.all_files
    .each_value
    .with_object([]) { |source_file, files| files << source_file[:file_path] }

  (trace_point_files | fetch_rspec_required_files) - all_files
end
register_example_file_dependency(example_id, file_name) click to toggle source
# File lib/rspec_tracer/runner.rb, line 255
def register_example_file_dependency(example_id, file_name)
  source_file = RSpecTracer::SourceFile.from_name(file_name)

  @reporter.register_source_file(source_file)
  @reporter.register_dependency(example_id, file_name)
end
register_example_files_dependency(example_id) click to toggle source
# File lib/rspec_tracer/runner.rb, line 245
def register_example_files_dependency(example_id)
  example = @reporter.all_examples[example_id]

  register_example_file_dependency(example_id, example[:file_name])

  return if example[:rerun_file_name] == example[:file_name]

  register_example_file_dependency(example_id, example[:rerun_file_name])
end
register_file_dependency(example_id, file_path) click to toggle source
# File lib/rspec_tracer/runner.rb, line 262
def register_file_dependency(example_id, file_path)
  source_file = RSpecTracer::SourceFile.from_path(file_path)

  return false if RSpecTracer.filters.any? { |filter| filter.match?(source_file) }

  @reporter.register_source_file(source_file)
  @reporter.register_dependency(example_id, source_file[:file_name])

  true
end