Class: Mutant::ContextMap Private

Inherits:
Object
  • Object
show all
Includes:
Unparser::Adamantium
Defined in:
lib/mutant/context_map.rb,
lib/mutant/context_map/loader.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Per test coverage, read from the artifact a coverage tool wrote.

Mutant does not depend on the tool that produces the recording. It reads the contexts sections of simplecov's coverage.json, which appear once track_tests is enabled: an interned list of test ids, and per source file a bitmap of the lines each of those tests executed.

The recording answers the question the expression selector can only guess at, which tests actually executed the lines a subject is made of.

Defined Under Namespace

Classes: Loader

Constant Summary collapse

ARTIFACT_BASENAME =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Basename simplecov writes its report under

'coverage.json'
SUPPORTED_SCHEMA =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Report schema versions this reader understands

/\A1\./

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.normalize(location, root) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Absolute path:line form of a location

Test ids are recorded relative to the project root, while the integrations report locations the way their framework spells them. Both sides pass through here so that they compare as strings.

Parameters:

  • location (String)
  • root (String)

Returns:

  • (String)


32
33
34
35
36
37
38
# File 'lib/mutant/context_map.rb', line 32

def self.normalize(location, root)
  path, separator, line = location.rpartition(':')

  return location if separator.empty?

  "#{File.expand_path(path, root)}:#{line}"
end

.popcount(value) ⇒ Integer

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Number of set bits

Ruby has no population count on Integer, and a binary string is both the idiom and fast enough at the sizes a line bitmap reaches.

Parameters:

  • value (Integer)

Returns:

  • (Integer)


92
# File 'lib/mutant/context_map.rb', line 92

def self.popcount(value) = value.to_s(2).count('1')

Instance Method Details

#context_idsSet<String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Every test id the recording knows about

Returns:

  • (Set<String>)


97
# File 'lib/mutant/context_map.rb', line 97

def context_ids = tables.each_value.flat_map(&:keys).to_set

#footprint(context) ⇒ Integer

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Lines a test executed across the whole project

The share of that footprint falling inside a subject is how focused the test is on it. A unit test spends most of its lines there, while a feature spec touching thousands of lines spends almost none.

Parameters:

  • context (String)

Returns:

  • (Integer)


82
# File 'lib/mutant/context_map.rb', line 82

def footprint(context) = footprints.fetch(context, 0)

#normalize(location) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Absolute path:line form of a location, against this recording's root

Parameters:

  • location (String)

Returns:

  • (String)


45
# File 'lib/mutant/context_map.rb', line 45

def normalize(location) = self.class.normalize(location, root)

#reach(path:, lines:) ⇒ Hash{String => Integer}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

How many of lines in path each test executed, for the tests that executed any of them

The count is what ranks the tests: one that ran the whole method body is likelier to reach a mutated expression than one that grazed a guard.

Parameters:

  • path (Pathname)
  • lines (Range<Integer>)

Returns:

  • (Hash{String => Integer})


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/mutant/context_map.rb', line 57

def reach(path:, lines:)
  table = tables.fetch(File.expand_path(path), nil)

  return EMPTY_HASH unless table

  # Bit N is line N+1, so the subject's lines are the bits from
  # `lines.begin - 1` up to and including `lines.end - 1`.
  mask = (1 << lines.end) - (1 << (lines.begin - 1))

  table.each_with_object({}) do |(context, bitmap), reached|
    covered = self.class.popcount(bitmap & mask)

    reached[context] = covered unless covered.zero?
  end
end