Class: SixthSense::Engines::Mutant

Inherits:
MutationEngine show all
Defined in:
lib/sixth_sense/engines/mutant.rb

Constant Summary collapse

SUPPORTED_ENGINE =
"ruby"

Instance Method Summary collapse

Constructor Details

#initialize(config: {}) ⇒ Mutant

Returns a new instance of Mutant.



19
20
21
# File 'lib/sixth_sense/engines/mutant.rb', line 19

def initialize(config: {})
  @config = config
end

Instance Method Details

#availabilityObject



23
24
25
26
27
28
29
30
31
# File 'lib/sixth_sense/engines/mutant.rb', line 23

def availability
  return Availability.new(available: true, reason: "sixth_sense matrix producer is available") if matrix_mode?
  return Availability.new(available: true, reason: "sixth_sense built-in score producer is available") if builtin_score_engine?

  external = external_availability
  return external if external.available? || external_score_engine?

  Availability.new(available: true, reason: "sixth_sense built-in score producer fallback is available")
end

#external_availabilityObject



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/sixth_sense/engines/mutant.rb', line 33

def external_availability
  return Availability.new(available: false, reason: "mutant requires CRuby") unless RUBY_ENGINE == SUPPORTED_ENGINE
  mutant_spec = gem_spec("mutant")
  return Availability.new(available: false, reason: "mutant gem is not installed") unless mutant_spec
  return Availability.new(available: false, reason: "mutant-rspec gem is not installed") unless gem_installed?("mutant-rspec")
  unless supported_mutant_version?(mutant_spec.version)
    return Availability.new(available: false, reason: "mutant #{mutant_spec.version} does not satisfy #{@config.fetch("version_requirement", ">= 0")}")
  end

  Availability.new(available: true, reason: "mutant and mutant-rspec are available")
end

#plan(sut_units:, test_files:) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/sixth_sense/engines/mutant.rb', line 45

def plan(sut_units:, test_files:)
  configured = test_files.flat_map { |test_file| Array(test_file.&.fetch(:mutation_subjects, [])) }
  subjects = (configured + sut_units.flat_map { |unit| expressions_for(unit) }).uniq
  MutationPlan.new(
    engine: "mutant",
    mode: @config.fetch("mode", "score"),
    subjects: subjects,
    test_files: test_files.map(&:path),
    source_units: sut_units.map(&:to_h)
  )
end

#run(plan, cache:) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/sixth_sense/engines/mutant.rb', line 57

def run(plan, cache:)
  return failed_result(plan, ["sixth_sense", "mutation-run"], "mutation plan has no subjects") if plan.subjects.empty?
  return run_matrix(plan, cache: cache) if matrix_mode?(plan.mode)
  return run_builtin_score(plan, cache: cache) if builtin_score_engine? || requires_builtin_score?(plan)

  available = external_availability
  unless available.available?
    return failed_result(plan, command_for(plan, cache: cache), "mutation engine unavailable: #{available.reason}")
  end
  begin
    neutral_run!(plan)
  rescue Error => error
    return failed_result(plan, command_for(plan, cache: cache), error.message)
  end
  command = command_for(plan, cache: cache)
  stdout, stderr, status = Open3.capture3(*command)
  MutationScoreCacheWriter.new(cache: cache).write(plan, stdout: stdout, stderr: stderr) if status.success? && cache
  MutationRunResult.new(
    plan: plan,
    command: command,
    success: status.success?,
    stdout: stdout,
    stderr: stderr,
    status: status.exitstatus
  )
end