Module: SimpleCov::CLI::Affected::Selection
Constant Summary collapse
- TEST_FILE_NAME =
/\A(?:test_.*|.*_(?:test|spec))\.rb\z/
Instance Method Summary collapse
- #build(changed, document, contexts, opts, stderr, root:) ⇒ Object
-
#classify(path, state) ⇒ Object
A recorded test file selects itself, a file the report covers selects the files of the tests touching it, a test-looking file the map has never seen always runs, and anything else is a trigger.
- #covering_table(path, entry, state) ⇒ Object
- #entry_for(path, state) ⇒ Object
- #initial_state(changed, contexts, coverage, opts, stderr, root:) ⇒ Object
- #note_untracked(path, state) ⇒ Object
-
#on_disk?(path, state) ⇒ Boolean
Existence judged at the repository root the paths are relative to, not the cwd, so a subdirectory run answers the same.
-
#select_context(id, path, state) ⇒ Object
Selected via its file, or a staleness trigger when it has no file to select: a locationless id, or a file that exists neither on disk nor in the change.
- #select_covering(path, entry, state) ⇒ Object
-
#select_test_file(path, state, note_deleted: true) ⇒ Object
A changed test file runs, unless the change is its deletion: its tests are gone, which removes them from the answer without distrusting the map.
-
#test_file_of(id) ⇒ Object
"spec/a_spec.rb:12" and the :file granularity's bare "spec/a_spec.rb" both name a file, while a locationless fallback id names none.
Instance Method Details
#build(changed, document, contexts, opts, stderr, root:) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/simplecov/cli/affected/selection.rb', line 11 def build(changed, document, contexts, opts, stderr, root:) none = {} #: Hash[String, untyped] coverage = document.fetch("coverage", none) unless coverage.instance_of?(Hash) return CoverageFile.report_invalid(stderr, "affected", opts.fetch(:input), '"coverage" must be an object') end state = initial_state(changed, contexts, coverage, opts, stderr, root: root) return nil unless changed.all? { |path| classify(path, state) } {tests: state.fetch(:tests).uniq.sort, triggers: state.fetch(:triggers)} end |
#classify(path, state) ⇒ Object
A recorded test file selects itself, a file the report covers selects the files of the tests touching it, a test-looking file the map has never seen always runs, and anything else is a trigger. Changed paths are exact root-relative names, so they resolve against the report exactly: a lookalike entry elsewhere must not stand in for a file it doesn't carry.
37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/simplecov/cli/affected/selection.rb', line 37 def classify(path, state) entry = entry_for(path, state) if state.fetch(:known).include?(path) select_test_file(path, state) elsif entry select_covering(path, entry, state) elsif TEST_FILE_NAME.match?(File.basename(path)) select_test_file(path, state, note_deleted: false) else note_untracked(path, state) end end |
#covering_table(path, entry, state) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/simplecov/cli/affected/selection.rb', line 80 def covering_table(path, entry, state) unless entry.instance_of?(Hash) return CoverageFile.report_invalid(state.fetch(:stderr), "affected", state.fetch(:opts).fetch(:input), "entry for #{path} must be an object") end raw = entry["contexts"] || {} table = raw.instance_of?(Hash) && Tests.decode_table(raw, state.fetch(:contexts).size) return table if table CoverageFile.report_invalid(state.fetch(:stderr), "affected", state.fetch(:opts).fetch(:input), "entry for #{path} carries a malformed \"contexts\" table") end |
#entry_for(path, state) ⇒ Object
71 72 73 |
# File 'lib/simplecov/cli/affected/selection.rb', line 71 def entry_for(path, state) state.fetch(:index)[File.(path, state.fetch(:root))] || state.fetch(:index)[path] end |
#initial_state(changed, contexts, coverage, opts, stderr, root:) ⇒ Object
24 25 26 27 28 29 30 |
# File 'lib/simplecov/cli/affected/selection.rb', line 24 def initial_state(changed, contexts, coverage, opts, stderr, root:) { tests: [], triggers: [], changed: changed, contexts: contexts, known: contexts.each_with_object(Set.new) { |id, set| set << test_file_of(id) }, index: CoverageFile.exact_index(coverage), root: root, opts: opts, stderr: stderr } #: Hash[Symbol, untyped] end |
#note_untracked(path, state) ⇒ Object
50 51 52 |
# File 'lib/simplecov/cli/affected/selection.rb', line 50 def note_untracked(path, state) state.fetch(:triggers) << "#{path} changed but #{state.fetch(:opts).fetch(:input)} has no data for it" end |
#on_disk?(path, state) ⇒ Boolean
Existence judged at the repository root the paths are relative to, not the cwd, so a subdirectory run answers the same.
67 68 69 |
# File 'lib/simplecov/cli/affected/selection.rb', line 67 def on_disk?(path, state) File.exist?(File.(path, state.fetch(:root))) end |
#select_context(id, path, state) ⇒ Object
Selected via its file, or a staleness trigger when it has no file to select: a locationless id, or a file that exists neither on disk nor in the change.
97 98 99 100 101 102 103 104 105 106 |
# File 'lib/simplecov/cli/affected/selection.rb', line 97 def select_context(id, path, state) file = test_file_of(id) if file.nil? state.fetch(:triggers) << "recorded test #{id} touches #{path} but has no file location" elsif on_disk?(file, state) state.fetch(:tests) << file elsif !state.fetch(:changed).include?(file) state.fetch(:triggers) << "recorded test file #{file} no longer exists" end end |
#select_covering(path, entry, state) ⇒ Object
75 76 77 78 |
# File 'lib/simplecov/cli/affected/selection.rb', line 75 def select_covering(path, entry, state) table = covering_table(path, entry, state) table&.each_key { |index| select_context(state.fetch(:contexts).fetch(index), path, state) } end |
#select_test_file(path, state, note_deleted: true) ⇒ Object
A changed test file runs, unless the change is its deletion: its tests are gone, which removes them from the answer without distrusting the map.
56 57 58 59 60 61 62 63 |
# File 'lib/simplecov/cli/affected/selection.rb', line 56 def select_test_file(path, state, note_deleted: true) if on_disk?(path, state) state.fetch(:tests) << path elsif note_deleted state.fetch(:stderr).puts("simplecov affected: skipping deleted test file #{path}") end path end |
#test_file_of(id) ⇒ Object
"spec/a_spec.rb:12" and the :file granularity's bare "spec/a_spec.rb" both name a file, while a locationless fallback id names none.
110 111 112 113 |
# File 'lib/simplecov/cli/affected/selection.rb', line 110 def test_file_of(id) base = id.sub(/:\d+\z/, "") base if base.include?("/") || base.end_with?(".rb") end |