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/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, LogCollection, LogFile, LogFileIndex

Constant Summary collapse

BASE_COVERAGE =
{}
VALID_TEST_ORDERS =
[:random_seeded, :random, :ordered].freeze
VERSION =
'0.1.0'.freeze

Class Method Summary collapse

Class Method Details

.cmdline_for_run_list(run_list) ⇒ Object

Returns String.

Returns:

  • String



128
129
130
131
132
# File 'lib/covet.rb', line 128

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



137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/covet.rb', line 137

def self.coverage_before_and_after # yields
  before = CovetCoverage.peek_result
  yield if block_given?
  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

.diff_coverages(before, after) ⇒ Object

Returns Hash.

Returns:

  • Hash



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/covet.rb', line 174

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’]} }



159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/covet.rb', line 159

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



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

def self.log_collection
  @log_collection
end

.normalize_coverage_info(coverage_info) ⇒ Object



150
151
152
153
# File 'lib/covet.rb', line 150

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

.optionsObject



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

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

.register_coverage_collection!Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/covet.rb', line 112

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)
    # 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!
end

.test_directoriesObject



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

def self.test_directories; @test_directories.dup; end

.test_directories=(*dirs) ⇒ Object



91
92
93
94
95
96
97
98
99
100
# File 'lib/covet.rb', line 91

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



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

def self.test_order; @test_order; end

.test_order=(order) ⇒ Object



80
81
82
83
84
85
86
# File 'lib/covet.rb', line 80

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



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

def self.test_runner; @test_runner; end

.test_runner=(runner) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/covet.rb', line 64

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



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

def self.vcs; @vcs; end

.vcs=(vcs) ⇒ Object



54
55
56
57
58
59
# File 'lib/covet.rb', line 54

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