Module: SimpleCov::CLI::Affected
- Extended by:
- Affected, CommandHelpers
- Included in:
- Affected
- Defined in:
- lib/simplecov/cli/affected.rb,
lib/simplecov/cli/affected/runner.rb,
lib/simplecov/cli/affected/selection.rb,
lib/simplecov/cli/affected/changed_files.rb
Overview
simplecov affected: select the test files whose recorded tests touch the
code changed since a git base ref, from a coverage.json written under
track_tests. The change is everything between the merge base of --base
and the working tree, plus untracked files.
The set intersection is the easy half; the hard half is knowing when to distrust the map, because a test map is stale the moment something changes that no test mentions by name. Any changed file outside the tracked set fails open to the full suite, out loud: each trigger is named on stderr and stdout stays empty, which a bare runner reads as "run everything". Test files always select themselves, recorded or not.
Defined Under Namespace
Modules: ChangedFiles, Selection, SpawnedRunner, SystemRunner
Constant Summary collapse
- RUNNER =
simplecov:disable branch — fixed by the running engine and OS
(RUBY_ENGINE.eql?("jruby") && Gem.win_platform?) ? SystemRunner : SpawnedRunner
Constants included from CommandHelpers
CommandHelpers::STATS_ROW_FORMAT
Instance Method Summary collapse
- #deliver(selection, opts, stdout, stderr) ⇒ Object
- #emit_json(selection, full, stdout) ⇒ Object
-
#emit_text(selection, full, stdout, stderr) ⇒ Object
Stdout carries the selected files and nothing else, so the list can feed a runner directly, and a full-suite fallback prints nothing, which
rspec $(simplecov affected)reads as "run everything". - #execute(selection, full, opts, stderr) ⇒ Object
- #no_changes(opts, stdout, stderr) ⇒ Object
- #note_untouched(stderr) ⇒ Object
- #parse(args) ⇒ Object
-
#precheck(opts) ⇒ Object
A stray positional looks exactly like a forgotten
--baseref, and silently dropping it would select tests for the wrong change. - #respond(diffed, document, contexts, opts, stdout:, stderr:) ⇒ Object
- #run(args, stdout:, stderr:) ⇒ Object
-
#run_command(command, root, stderr) ⇒ Object
The command is named explicitly in the failure because not every engine's exception message carries it.
-
#split_runner(args) ⇒ Object
Everything after
--runis the runner command, verbatim, so the command's own flags never collide with ours.
Methods included from CommandHelpers
build_parser, command_name, common_options, error, error_nil, on_help, one?, parse_common, quiet_option, recorded_contexts, stats_row
Instance Method Details
#deliver(selection, opts, stdout, stderr) ⇒ Object
91 92 93 94 95 96 97 98 99 |
# File 'lib/simplecov/cli/affected.rb', line 91 def deliver(selection, opts, stdout, stderr) full = !selection.fetch(:triggers).empty? selection.fetch(:triggers).each { |trigger| stderr.puts("simplecov affected: #{trigger}") } stderr.puts("simplecov affected: falling back to the full suite") if full return emit_json(selection, full, stdout) if opts.fetch(:json) return execute(selection, full, opts, stderr) if opts.fetch(:run) emit_text(selection, full, stdout, stderr) end |
#emit_json(selection, full, stdout) ⇒ Object
101 102 103 104 105 106 |
# File 'lib/simplecov/cli/affected.rb', line 101 def emit_json(selection, full, stdout) none = [] #: Array[String] stdout.puts(JSON.pretty_generate("full_suite" => full, "triggers" => selection.fetch(:triggers), "tests" => full ? none : selection.fetch(:tests))) 0 end |
#emit_text(selection, full, stdout, stderr) ⇒ Object
Stdout carries the selected files and nothing else, so the list can feed a
runner directly, and a full-suite fallback prints nothing, which
rspec $(simplecov affected) reads as "run everything".
111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/simplecov/cli/affected.rb', line 111 def emit_text(selection, full, stdout, stderr) return 0 if full tests = selection.fetch(:tests) if tests.empty? note_untouched(stderr) else tests.each { |file| stdout.puts(file) } 0 end end |
#execute(selection, full, opts, stderr) ⇒ Object
128 129 130 131 132 133 134 135 |
# File 'lib/simplecov/cli/affected.rb', line 128 def execute(selection, full, opts, stderr) return run_command(opts.fetch(:run), opts.fetch(:root), stderr) if full return note_untouched(stderr) if selection.fetch(:tests).empty? count = selection.fetch(:tests).size stderr.puts("simplecov affected: running #{count} test file#{"s" unless count.eql?(1)}") run_command(opts.fetch(:run) + selection.fetch(:tests), opts.fetch(:root), stderr) end |
#no_changes(opts, stdout, stderr) ⇒ Object
84 85 86 87 88 89 |
# File 'lib/simplecov/cli/affected.rb', line 84 def no_changes(opts, stdout, stderr) stderr.puts("simplecov affected: no changes against #{opts.fetch(:base)}") empty = {tests: [], triggers: []} #: Hash[Symbol, Array[String]] emit_json(empty, false, stdout) if opts.fetch(:json) 0 end |
#note_untouched(stderr) ⇒ Object
123 124 125 126 |
# File 'lib/simplecov/cli/affected.rb', line 123 def note_untouched(stderr) stderr.puts("simplecov affected: no recorded test touches the changed code") 0 end |
#parse(args) ⇒ Object
46 47 48 49 50 51 52 53 |
# File 'lib/simplecov/cli/affected.rb', line 46 def parse(args) head, runner = split_runner(args) opts, rest = parse_common(head, base: nil, run: runner) do |parser, into| parser.on("--base REF") { |v| into[:base] = v } end opts[:rest] = rest opts end |
#precheck(opts) ⇒ Object
A stray positional looks exactly like a forgotten --base ref, and
silently dropping it would select tests for the wrong change.
66 67 68 69 70 71 72 |
# File 'lib/simplecov/cli/affected.rb', line 66 def precheck(opts) stray = opts.fetch(:rest).first return "unexpected argument #{stray.inspect} (did you mean `--base #{stray}`?)" if stray return "missing command after --run" if opts.fetch(:run) && opts.fetch(:run).empty? "--run and --json cannot be combined" if opts.fetch(:run) && opts.fetch(:json) end |
#respond(diffed, document, contexts, opts, stdout:, stderr:) ⇒ Object
74 75 76 77 78 79 80 81 82 |
# File 'lib/simplecov/cli/affected.rb', line 74 def respond(diffed, document, contexts, opts, stdout:, stderr:) opts[:root] = diffed.fetch(:root) return no_changes(opts, stdout, stderr) if diffed.fetch(:changed).empty? selection = Selection.build(diffed.fetch(:changed), document, contexts, opts, stderr, root: diffed.fetch(:root)) return 1 unless selection deliver(selection, opts, stdout, stderr) end |
#run(args, stdout:, stderr:) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/simplecov/cli/affected.rb', line 29 def run(args, stdout:, stderr:) opts = parse(args) issue = precheck(opts) return error(stderr, issue) if issue document = CoverageFile.load_document(opts.fetch(:input), command: "affected", stderr: stderr) return 1 unless document contexts = recorded_contexts(document, opts, stderr) return 1 unless contexts diffed = ChangedFiles.call(opts[:base] ||= Git.default_base, stderr) return 1 unless diffed respond(diffed, document, contexts, opts, stdout: stdout, stderr: stderr) end |
#run_command(command, root, stderr) ⇒ Object
The command is named explicitly in the failure because not every engine's exception message carries it.
139 140 141 142 143 144 |
# File 'lib/simplecov/cli/affected.rb', line 139 def run_command(command, root, stderr) RUNNER.call(command, root) rescue SystemCallError => e stderr.puts("simplecov affected: cannot run #{command.first.inspect} (#{e})") 127 end |
#split_runner(args) ⇒ Object
Everything after --run is the runner command, verbatim, so the command's
own flags never collide with ours.
57 58 59 60 61 62 |
# File 'lib/simplecov/cli/affected.rb', line 57 def split_runner(args) index = args.index("--run") return [args, nil] unless index [args.take(index), args.drop(index + 1)] end |