Module: DeepCover::Base

Included in:
DeepCover
Defined in:
lib/deep_cover/base.rb

Instance Method Summary collapse

Instance Method Details

#all_tracked_file_pathsObject



157
158
159
160
161
162
163
164
165
# File 'lib/deep_cover/base.rb', line 157

def all_tracked_file_paths
  return @all_tracked_file_paths.dup if @all_tracked_file_paths
  paths_found = Dir[*lookup_globs]
  paths_found.select! { |path| path.end_with?('.rb') }
  paths_found.select! { |path| File.file?(path) }
  paths_found.uniq!
  @all_tracked_file_paths = paths_found
  @all_tracked_file_paths.dup
end

#auto_detected_pathsObject

Auto detects path that we want to cover. This is used when :auto_detect is in the config.paths. If the results aren’t what you expect, then specify the paths yourself. We want this to work for most project’s struture:

  • Single gems: just a gem directly in the top-level

  • Multi gems: contains multiple gems, each in a dir of the top-level dir (the rails gem does that)

  • Hybrid gems: a gem in the top-level dir and one in sub-dirs (the deep-cover gem does that)

  • Rails application

For gems and Rails application, normally, everything to check coverage for is in lib/, and app/. For other projects, we go for every directories except test/ spec/ bin/ exe/.

If the current dir has a .gemspec file, we consider it a “root”. In addition, if any sub-dir of the current dir (not recursive) has a .gemspec file, we also consider them as “roots” If the current dir looks like a Rails application, add it as a “root” For each “roots”, the “tracked dirs” will be “#Top Level Namespace/app” and “#Top Level Namespace/lib”

If no “tracked dirs” exist, fallback to everything in current directory except each of test/ spec/ bin/ exe/.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/deep_cover/base.rb', line 126

def auto_detected_paths
  require_relative 'tools/looks_like_rails_project'

  gemspec_paths = Dir['./*.gemspec'] + Dir['./*/*.gemspec']
  root_paths = gemspec_paths.map!(&File.method(:dirname))
  root_paths.uniq!
  root_paths << '.' if !root_paths.include?('.') && Tools::LooksLikeRailsProject.looks_like_rails_project?('.')

  tracked_paths = root_paths.flat_map { |p| [File.join(p, 'app'), File.join(p, 'lib')] }
  tracked_paths.select! { |p| File.exist?(p) }

  if tracked_paths.empty?
    # So track every sub-dirs except a couple
    # The final '/' in Dir[] makes it return directories only, but they will also end with a '/'
    # So need to include that last '/' in the substracted paths.
    # TODO: We probably want a cleaner way of filtering out some directories. But for now, that's what we got.
    tracked_paths = Dir['./*/'] - %w(./autotest/ ./features/ ./spec/ ./test/ ./bin/ ./exe/)
    # And track every ruby files in the top-level
    tracked_paths << './*.rb'
  end
  tracked_paths
  # path expansion is done in #lookup_globs
end

#autoload_trackerObject



171
172
173
# File 'lib/deep_cover/base.rb', line 171

def autoload_tracker
  @autoload_tracker ||= AutoloadTracker.new
end

#config_changed(what) ⇒ Object



68
69
70
71
72
73
74
75
76
77
# File 'lib/deep_cover/base.rb', line 68

def config_changed(what)
  case what
  when :paths
    warn "Changing DeepCover's paths after starting coverage is highly discouraged" if running?
    @lookup_globs = @all_tracked_file_paths = nil
  when :tracker_global
    raise NotImplementedError, "Changing DeepCover's tracker global after starting coverage is not supported" if running?
    @coverage = nil
  end
end

#cover(paths: nil) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/deep_cover/base.rb', line 56

def cover(paths: nil)
  if paths
    prev = config.paths
    config.paths(paths)
  end
  start
  yield
ensure
  stop
  config.paths(prev) if paths
end

#coverageObject



86
87
88
# File 'lib/deep_cover/base.rb', line 86

def coverage
  @coverage ||= Coverage.new
end

#covered_code(filename) ⇒ Object



52
53
54
# File 'lib/deep_cover/base.rb', line 52

def covered_code(filename)
  coverage.covered_code(handle_relative_filename(filename))
end

#custom_requirerObject



167
168
169
# File 'lib/deep_cover/base.rb', line 167

def custom_requirer
  @custom_requirer ||= CustomRequirer.new
end

#delete_trackersObject



44
45
46
# File 'lib/deep_cover/base.rb', line 44

def delete_trackers
  persistence.delete_trackers
end

#line_coverage(filename) ⇒ Object



48
49
50
# File 'lib/deep_cover/base.rb', line 48

def line_coverage(filename)
  coverage.line_coverage(handle_relative_filename(filename), **config.to_h)
end

#lookup_globsObject



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

def lookup_globs
  return @lookup_globs if @lookup_globs
  paths = Array(config.paths || :auto_detect).dup
  paths.concat(auto_detected_paths) if paths.delete(:auto_detect)

  paths = paths.map { |p| File.expand_path(p) }
  paths = ['/'] if paths.include?('/')
  globs = paths.map! do |path|
    if File.directory?(path)
      # File.join is needed to avoid //**/*.rb
      File.join(path, '**/*.rb')
    else
      # Either a single file's path, a glob, or a path that doesn't exists
      path
    end
  end
  @lookup_globs = globs
end

#persistenceObject



175
176
177
# File 'lib/deep_cover/base.rb', line 175

def persistence
  @persistence ||= Persistence.new(config.cache_directory)
end

#resetObject



79
80
81
82
83
84
# File 'lib/deep_cover/base.rb', line 79

def reset
  stop if running?
  @coverage = @custom_requirer = @autoload_tracker = @lookup_globs = @all_tracked_file_paths = nil
  config.reset
  self
end

#running?Boolean

Returns:

  • (Boolean)


5
6
7
# File 'lib/deep_cover/base.rb', line 5

def running?
  @started ||= false # rubocop:disable Naming/MemoizedInstanceVariableName [#5648]
end

#startObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/deep_cover/base.rb', line 9

def start
  return if running?
  if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
    # Autoload is not supported in JRuby. We currently need to use binding_of_caller
    # and that is not available in JRuby. An extension may be able to replace this requirement.
    # require_relative 'core_ext/autoload_overrides'
    # AutoloadOverride.active = true
    require_relative 'core_ext/load_overrides'
    require_relative 'core_ext/require_overrides'
    LoadOverride.active = RequireOverride.active = true
  elsif RUBY_VERSION >= '2.3.0'
    require_relative 'core_ext/instruction_sequence_load_iseq'
  else
    require_relative 'core_ext/autoload_overrides'
    require_relative 'core_ext/load_overrides'
    require_relative 'core_ext/require_overrides'
    AutoloadOverride.active = LoadOverride.active = RequireOverride.active = true
    autoload_tracker.initialize_autoloaded_paths { |mod, name, path| mod.autoload_without_deep_cover(name, path) }
  end

  config # actualize configuration
  @lookup_globs = @all_tracked_file_paths = nil
  @started = true
end

#stopObject



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

def stop
  if defined? AutoloadOverride
    AutoloadOverride.active = false
    autoload_tracker.remove_interceptors { |mod, name, path| mod.autoload_without_deep_cover(name, path) }
  end
  RequireOverride.active = false if defined? RequireOverride

  @started = false
end

#tracked_file_path?(path) ⇒ Boolean

Returns:

  • (Boolean)


150
151
152
153
154
155
# File 'lib/deep_cover/base.rb', line 150

def tracked_file_path?(path)
  # The flags are to make fnmatch match the same things as Dir.glob... This doesn't seem to be documented anywhere
  # EXTGLOB: allow matching {lib,app} as either lib or app
  # PATHNAME: Makes wildcard match not match /, and make /**/ (and pattern starting with **/) be any number of nested directory
  lookup_globs.any? { |glob| File.fnmatch?(glob, path, File::FNM_EXTGLOB | File::FNM_PATHNAME) }
end