Class: DeepCover::Coverage

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/deep_cover/coverage.rb,
lib/deep_cover/coverage/analysis.rb

Overview

A collection of CoveredCode

Defined Under Namespace

Classes: Analysis

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCoverage

Returns a new instance of Coverage.



12
13
14
# File 'lib/deep_cover/coverage.rb', line 12

def initialize
  @covered_code_per_path = {}
end

Class Method Details

.load(cache_directory = DeepCover.config.cache_directory) ⇒ Object



110
111
112
113
114
115
116
117
118
119
# File 'lib/deep_cover/coverage.rb', line 110

def self.load(cache_directory = DeepCover.config.cache_directory)
  tracker_hits_per_path = Persistence.new(cache_directory).load_trackers
  coverage = Coverage.new

  tracker_hits_per_path.each do |path, tracker_hits|
    coverage.covered_code(path, tracker_hits: tracker_hits)
  end

  coverage
end

Instance Method Details

#add_missing_covered_codesObject

If a file wasn’t required, it won’t be in the trackers. This adds those mossing files



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/deep_cover/coverage.rb', line 60

def add_missing_covered_codes
  top_level_path = DeepCover.config.paths.detect do |path|
    next unless path.is_a?(String)
    path = File.expand_path(path)
    File.dirname(path) == path
  end
  if top_level_path
    # One of the paths is a root path.
    # Either a mistake, or the user wanted to check everything that gets loaded. Either way,
    # we will probably hang from searching the files to load. (and then loading them, as there
    # can be lots of ruby files) We prefer to warn the user and not do anything.
    suggestion = "#{top_level_path}/**/*.rb"
    warn ["Because the `paths` configured in DeepCover include #{top_level_path.inspect}, which is a root of your fs, ",
          'DeepCover will not attempt to find Ruby files that were not required. Your coverage report will not include ',
          'files that were not instrumented. This is to avoid extremely long wait times. If you actually want this to ',
          "happen, then replace the specified `paths` with #{suggestion.inspect}.",
         ].join
    return
  end

  DeepCover.all_tracked_file_paths.each do |path|
    covered_code(path, tracker_hits: :zeroes)
  end
  nil
end

#analysis(**options) ⇒ Object



131
132
133
# File 'lib/deep_cover/coverage.rb', line 131

def analysis(**options)
  Analysis.new(covered_codes, **options)
end

#covered_code(path, **options) ⇒ Object



28
29
30
31
32
# File 'lib/deep_cover/coverage.rb', line 28

def covered_code(path, **options)
  raise 'path must be an absolute path' unless Pathname.new(path).absolute?
  @covered_code_per_path[path] ||= CoveredCode.new(path: path,
                                                   **options)
end

#covered_code?(path) ⇒ Boolean

Returns:

  • (Boolean)


24
25
26
# File 'lib/deep_cover/coverage.rb', line 24

def covered_code?(path)
  @covered_code_per_path.include?(path)
end

#covered_code_or_warn(path, **options) ⇒ Object



34
35
36
37
38
39
40
41
42
43
# File 'lib/deep_cover/coverage.rb', line 34

def covered_code_or_warn(path, **options)
  covered_code(path, **options)
rescue Parser::SyntaxError => e
  if e.message =~ /contains escape sequences incompatible with UTF-8/
    warn "Can't cover #{path} because of incompatible encoding (see issue #9)"
  else
    warn "The file #{path} can't be instrumented"
  end
  nil
end

#covered_codesObject



16
17
18
# File 'lib/deep_cover/coverage.rb', line 16

def covered_codes
  each.to_a
end

#eachObject



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/deep_cover/coverage.rb', line 46

def each
  return to_enum unless block_given?
  @covered_code_per_path.each_key do |path|
    begin
      cov_code = covered_code(path)
    rescue Parser::SyntaxError
      next
    end
    yield cov_code
  end
  self
end

#line_coverage(filename, **options) ⇒ Object



20
21
22
# File 'lib/deep_cover/coverage.rb', line 20

def line_coverage(filename, **options)
  covered_code(filename).line_coverage(**options)
end

#report(reporter: , **options) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/deep_cover/coverage.rb', line 86

def report(reporter: DEFAULTS[:reporter], **options)
  add_missing_covered_codes
  case reporter.to_sym
  when :html
    msg = if (output = options.fetch(:output, DEFAULTS[:output]))
            Reporter::HTML.report(self, **options)
            "HTML generated: open #{output}/index.html"
          else
            'No HTML generated'
          end
    Reporter::Text.report(self, **options) + "\n\n" + msg
  when :istanbul
    if Reporter::Istanbul.available?
      Reporter::Istanbul.report(self, **options)
    else
      warn 'nyc not available. Please install `nyc` using `yarn global add nyc` or `npm i nyc -g`'
    end
  when :text
    Reporter::Text.report(self, **options)
  else
    raise ArgumentError, "Unknown reporter: #{reporter}"
  end
end

#save_trackersObject



121
122
123
124
125
126
127
128
129
# File 'lib/deep_cover/coverage.rb', line 121

def save_trackers
  tracker_hits_per_path = @covered_code_per_path.map do |path, covered_code|
    [path, covered_code.tracker_hits]
  end
  tracker_hits_per_path = tracker_hits_per_path.to_h

  DeepCover.persistence.save_trackers(tracker_hits_per_path)
  self
end