Module: Covet

Defined in:
lib/covet.rb,
lib/covet/cli.rb,
lib/covet/vcs/git.rb,
lib/covet/version.rb,
lib/covet/log_file.rb,
lib/covet/log_collection.rb,
lib/covet/collection_task.rb,
lib/covet/line_changes_vcs.rb,
lib/covet/collection_filter.rb,
lib/covet/test_runners/rspec.rb,
lib/covet/collection_compressor.rb,
lib/covet/test_runners/minitest.rb

Defined Under Namespace

Modules: CollectionCompressor, CollectionFilter, LineChangesVCS, Options, TestRunners, VCS Classes: CLI, CollectionTask, LogCollection, LogFile, LogFileIndex

Constant Summary collapse

BASE_COVERAGE =
{}
VALID_TEST_ORDERS =

Tell ‘covet` the order in which your tests are run, which allows it to save space and time during the coverage collection phase in certain situations.

[:random_seeded, :random, :ordered].freeze
VERSION =
'0.1.1'.freeze

Class Method Summary collapse

Class Method Details

.cmdline_for_run_list(run_list) ⇒ Object

Returns the command line to run the tests given in ‘run_list`.

Returns:

  • String



145
146
147
148
149
# File 'lib/covet.rb', line 145

def self.cmdline_for_run_list(run_list)
  Covet::TestRunners.const_get(
    @test_runner.to_s.capitalize
  ).cmdline_for_run_list(run_list)
end

.coverage_before_and_afterObject

Returns coverage information for before block ran, and after block ran for the codebase in its current state.

Returns:

  • Array



154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/covet.rb', line 154

def self.coverage_before_and_after # yields
  before = CovetCoverage.peek_result
  yield
  after = CovetCoverage.peek_result
  before = normalize_coverage_info(before)
  if Covet::BASE_COVERAGE.any?
    before = diff_coverages(Covet::BASE_COVERAGE, before)
  end
  after = normalize_coverage_info(after)
  after = diff_coverages(before, after)
  [before, after]
end

.coverage_collection_registered?Boolean

Returns:

  • (Boolean)


139
140
141
# File 'lib/covet.rb', line 139

def self.coverage_collection_registered?
  @coverage_collection_registered
end

.diff_coverages(before, after) ⇒ Object

Get the difference between ‘before`’s coverage info and ‘after`’s coverage info.

Parameters:

  • before (Hash)
  • after (Hash)

Returns:

  • Hash



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/covet.rb', line 197

def self.diff_coverages(before, after)
  ret = after.each_with_object({}) do |(file_name, after_line_cov), res|
    before_line_cov = before[file_name] || {}
    next if before_line_cov == after_line_cov

    cov = {}

    after_line_cov.each do |lineno, exec_times|
      # no change
      if (before_exec_times = before_line_cov[lineno]) == exec_times
        next
      end

      # execution of previous line number
      if before_exec_times && exec_times
        cov[lineno] = exec_times - before_exec_times
      elsif exec_times
        cov[lineno] = exec_times
      else
        raise "shouldn't get here"
      end
    end

    # add the "diffed" coverage to the hash
    res[file_name] = cov
  end
  ret
end

.generate_run_list_for_method(before, after, options = {}) ⇒ Object

Generates a mapping of filenames to the lines and test methods that caused the changes.

Returns:

  • Hash, example: { “/home/me/workspace/myproj/myproj.rb” => { 1 => [‘test_method_that_caused_changed’]} }



178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/covet.rb', line 178

def self.generate_run_list_for_method(before, after, options = {})
  cov_map = Hash.new { |h, file| h[file] = Hash.new { |i, line| i[line] = [] } }
  after.each do |file, lines_hash|
    file_map = cov_map[file]

    lines_hash.each do |lineno, exec_times|
      # add the test name to the map. Multiple tests can execute the same
      # line, so we need to use an array.
      file_map[lineno] << (options[:method_name] || '???').to_s
    end
  end
  cov_map
end

.log_collectionObject

Singleton for collecting and writing log information during the collection phase.



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

def self.log_collection
  @log_collection
end

.normalize_coverage_info(coverage_info) ⇒ Object

Filter and compress ‘coverage_info` to make it more manageable to log to the collection file, and so that processing it will be faster.



169
170
171
172
# File 'lib/covet.rb', line 169

def self.normalize_coverage_info(coverage_info)
  filtered = CollectionFilter.filter(coverage_info)
  CollectionCompressor.compress(filtered)
end

.optionsObject

Returns Hash.

Returns:

  • Hash



39
40
41
# File 'lib/covet.rb', line 39

def self.options
  CLI.options || Options::DEFAULTS
end

.register_coverage_collection!Object

Register coverage collection with the test library ‘Covet.test_runner`. This happens during the collection phase.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/covet.rb', line 122

def self.register_coverage_collection!
  # stdlib Coverage can't run at the same time as CovetCoverage or
  # bad things will happen
  if defined?(Coverage) && !Coverage.respond_to?(:peek_result)
    warn "The 'coverage' library is already loaded. It could cause issues with this library."
    # There's no way to tell if coverage is enabled or not, and
    # if we try stopping the coverage and it's not enabled, it raises
    # a RuntimeError.
    Coverage.stop rescue nil
  end
  CovetCoverage.start # needs to be called before any application code gets required
  Covet::TestRunners.const_get(
    @test_runner.to_s.capitalize
  ).hook_into_test_methods!
  @coverage_collection_registered = true
end

.test_directoriesObject



108
# File 'lib/covet.rb', line 108

def self.test_directories; @test_directories.dup; end

.test_directories=(*dirs) ⇒ Object



98
99
100
101
102
103
104
105
106
107
# File 'lib/covet.rb', line 98

def self.test_directories=(*dirs)
  dirs = dirs.flatten
  dirs.each do |dir|
    unless Dir.exist?(dir)
      raise Errno::ENOENT, %Q(invalid directory given: "#{dir}" ) +
        %Q{("#{File.join(Dir.pwd, dir)}")}
    end
  end
  @test_directories = dirs
end

.test_orderObject



94
# File 'lib/covet.rb', line 94

def self.test_order; @test_order; end

.test_order=(order) ⇒ Object



87
88
89
90
91
92
93
# File 'lib/covet.rb', line 87

def self.test_order=(order)
  unless VALID_TEST_ORDERS.include?(order.intern)
    raise ArgumentError, "Invalid test order given. Expected one of " \
      "#{VALID_TEST_ORDERS.map(&:inspect).join(", ")} - #{order.intern.inspect} given"
  end
  @test_order = order
end

.test_runnerObject



76
# File 'lib/covet.rb', line 76

def self.test_runner; @test_runner; end

.test_runner=(runner) ⇒ Object

Set the test runner library to hook into, gathering and logging coverage information during the collection phase for each test method.



69
70
71
72
73
74
75
# File 'lib/covet.rb', line 69

def self.test_runner=(runner)
  @test_runner = runner.intern
  require_relative "covet/test_runners/#{runner}"
rescue LoadError
  raise ArgumentError, "invalid test runner given: '#{runner}'. " \
    "Expected 'rspec' or 'minitest'"
end

.vcsObject



63
# File 'lib/covet.rb', line 63

def self.vcs; @vcs; end

.vcs=(vcs) ⇒ Object

Set the version control system to use for seeing which files have changed since a certain version.



57
58
59
60
61
62
# File 'lib/covet.rb', line 57

def self.vcs=(vcs)
  @vcs = vcs.intern
  if @vcs != :git
    raise NotImplementedError, "Can only use git as the VCS for now."
  end
end