Class: Henitai::SourceFileSelection

Inherits:
Object
  • Object
show all
Defined in:
lib/henitai/source_file_selection.rb,
sig/henitai.rbs

Overview

Resolves which source files a run mutates: everything under includes:, minus anything matched by excludes:, optionally narrowed to what changed since a git ref.

Excludes are absolute, not a tiebreak: an excluded file (a standalone entry point that cannot be mutation-tested in-process, say) stays excluded even when it is the only thing that changed. Both stages are filters over the same list, so they commute — excludes run first only to keep the per-test-coverage lookups in filter_changed off files that are already out of scope.

Instance Method Summary collapse

Constructor Details

#initialize(config:, since:, git_diff_analyzer:, per_test_coverage:) ⇒ SourceFileSelection

Returns a new instance of SourceFileSelection.



15
16
17
18
19
20
# File 'lib/henitai/source_file_selection.rb', line 15

def initialize(config:, since:, git_diff_analyzer:, per_test_coverage:)
  @config = config
  @since = since
  @git_diff_analyzer = git_diff_analyzer
  @per_test_coverage = per_test_coverage
end

Instance Method Details

#callArray[String]

Returns:

  • (Array[String])


22
23
24
# File 'lib/henitai/source_file_selection.rb', line 22

def call
  filter_changed(reject_excluded(included_source_files))
end

#changed_paths_sinceArray[String]

Committed changes since the ref plus the current working tree (tracked dirty and untracked files): the working tree is what gets tested, so it is always part of "changed since REF".

Returns:

  • (Array[String])


60
61
62
63
# File 'lib/henitai/source_file_selection.rb', line 60

def changed_paths_since
  @git_diff_analyzer.changed_files(from: @since, to: "HEAD") +
    @git_diff_analyzer.working_tree_changed_files
end

#covered_sources_for_changed_tests(changed_paths) ⇒ Array[String]

Changed test files select the source files they cover, so an edited test re-tests the subjects it can kill. Uses the per-test map from the previous run — Gate 1 runs before this run's bootstrap finishes.

Parameters:

  • (Array[String])

Returns:

  • (Array[String])


68
69
70
71
72
# File 'lib/henitai/source_file_selection.rb', line 68

def covered_sources_for_changed_tests(changed_paths)
  changed_paths
    .flat_map { |path| @per_test_coverage.source_files_covered_by(path) }
    .map { |path| normalize_path(path) }
end

#excluded_source_filesArray[String]

Returns:

  • (Array[String])


51
52
53
54
55
# File 'lib/henitai/source_file_selection.rb', line 51

def excluded_source_files
  Array(@config.excludes)
    .flat_map { |pattern| Dir.glob(pattern) }
    .map { |path| normalize_path(path) }
end

#filter_changed(files) ⇒ Array[String]

Parameters:

  • (Array[String])

Returns:

  • (Array[String])


41
42
43
44
45
46
47
# File 'lib/henitai/source_file_selection.rb', line 41

def filter_changed(files)
  return files unless @since

  changed = changed_paths_since.map { |path| normalize_path(path) }
  changed += covered_sources_for_changed_tests(changed)
  files.select { |path| changed.include?(normalize_path(path)) }
end

#included_source_filesArray[String]

Returns:

  • (Array[String])


26
27
28
29
30
# File 'lib/henitai/source_file_selection.rb', line 26

def included_source_files
  Array(@config.includes).flat_map do |include_path|
    Dir.glob(File.join(include_path, "**", "*.rb"))
  end.uniq
end

#normalize_path(path) ⇒ String

Parameters:

  • (String)

Returns:

  • (String)


74
# File 'lib/henitai/source_file_selection.rb', line 74

def normalize_path(path) = File.expand_path(path)

#reject_excluded(files) ⇒ Array[String]

Drops files matched by any excludes: glob. Compared as expanded paths so a relative glob and an absolute candidate still match.

Parameters:

  • (Array[String])

Returns:

  • (Array[String])


34
35
36
37
38
39
# File 'lib/henitai/source_file_selection.rb', line 34

def reject_excluded(files)
  excluded = excluded_source_files
  return files if excluded.empty?

  files.reject { |path| excluded.include?(normalize_path(path)) }
end