module SimpleCov

Code coverage for ruby. Please check out README for a full introduction.

Constants

CRITERION_TO_RUBY_COVERAGE
CoverageLimits

@api private

VERSION

Attributes

external_at_exit[RW]

Basically, should we take care of at_exit behavior or something else? Used by the minitest plugin. See lib/minitest/simplecov_plugin.rb

external_at_exit?[RW]

Basically, should we take care of at_exit behavior or something else? Used by the minitest plugin. See lib/minitest/simplecov_plugin.rb

pid[RW]
running[RW]

Public Class Methods

at_exit_behavior() click to toggle source
# File lib/simplecov.rb, line 174
def at_exit_behavior
  # If we are in a different process than called start, don't interfere.
  return if SimpleCov.pid != Process.pid

  # If SimpleCov is no longer running then don't run exit tasks
  SimpleCov.run_exit_tasks! if SimpleCov.running
end
clear_result() click to toggle source

Clear out the previously cached .result. Primarily useful in testing

# File lib/simplecov.rb, line 170
def clear_result
  @result = nil
end
collate(result_filenames, profile = nil, ignore_timeout: true, &block) click to toggle source

Collate a series of SimpleCov result files into a single SimpleCov output.

You can optionally specify configuration with a block:

SimpleCov.collate Dir["simplecov-resultset-*/.resultset.json"]
 OR
SimpleCov.collate Dir["simplecov-resultset-*/.resultset.json"], 'rails' # using rails profile
 OR
SimpleCov.collate Dir["simplecov-resultset-*/.resultset.json"] do
  add_filter 'test'
end
 OR
SimpleCov.collate Dir["simplecov-resultset-*/.resultset.json"], 'rails' do
  add_filter 'test'
end

Please check out the RDoc for SimpleCov::Configuration to find about available config options, or checkout the README for more in-depth information about coverage collation

By default ‘collate` ignores the merge_timeout so all results of all files specified will be merged together. If you want to honor the merge_timeout then provide the keyword argument `ignore_timeout: false`.

# File lib/simplecov.rb, line 86
def collate(result_filenames, profile = nil, ignore_timeout: true, &block)
  raise "There are no reports to be merged" if result_filenames.empty?

  initial_setup(profile, &block)

  # Use the ResultMerger to produce a single, merged result, ready to use.
  @result = ResultMerger.merge_and_store(*result_filenames, ignore_timeout: ignore_timeout)

  run_exit_tasks!
end
exit_and_report_previous_error(exit_status) click to toggle source

@api private

Thinking: Move this behavior earlier so if there was an error we do nothing?

# File lib/simplecov.rb, line 223
def exit_and_report_previous_error(exit_status)
  warn("Stopped processing SimpleCov as a previous error not related to SimpleCov has been detected") if print_error_status
  Kernel.exit(exit_status)
end
exit_status_from_exception() click to toggle source

@api private

Returns the exit status from the exit exception

# File lib/simplecov.rb, line 200
def exit_status_from_exception
  # Capture the current exception if it exists
  @exit_exception = $ERROR_INFO
  return nil unless @exit_exception

  if @exit_exception.is_a?(SystemExit)
    @exit_exception.status
  else
    SimpleCov::ExitCodes::EXCEPTION
  end
end
filtered(files) click to toggle source

Applies the configured filters to the given array of SimpleCov::SourceFile items

# File lib/simplecov.rb, line 131
def filtered(files)
  result = files.clone
  filters.each do |filter|
    result = result.reject { |source_file| filter.matches?(source_file) }
  end
  SimpleCov::FileList.new result
end
final_result_process?() click to toggle source

@api private

# File lib/simplecov.rb, line 268
def final_result_process?
  # checking for ENV["TEST_ENV_NUMBER"] to determine if the tests are being run in parallel
  !defined?(ParallelTests) || !ENV["TEST_ENV_NUMBER"] || ParallelTests.last_process?
end
grouped(files) click to toggle source

Applies the configured groups to the given array of SimpleCov::SourceFile items

# File lib/simplecov.rb, line 142
def grouped(files)
  grouped = {}
  grouped_files = []
  groups.each do |name, filter|
    grouped[name] = SimpleCov::FileList.new(files.select { |source_file| filter.matches?(source_file) })
    grouped_files += grouped[name]
  end
  if !groups.empty? && !(other_files = files.reject { |source_file| grouped_files.include?(source_file) }).empty?
    grouped["Ungrouped"] = SimpleCov::FileList.new(other_files)
  end
  grouped
end
load_adapter(name) click to toggle source
# File lib/simplecov.rb, line 162
def load_adapter(name)
  warn "#{Kernel.caller.first}: [DEPRECATION] #load_adapter is deprecated. Use #load_profile instead."
  load_profile(name)
end
load_profile(name) click to toggle source

Applies the profile of given name on SimpleCov configuration

# File lib/simplecov.rb, line 158
def load_profile(name)
  profiles.load(name)
end
previous_error?(error_exit_status) click to toggle source

@api private

# File lib/simplecov.rb, line 213
def previous_error?(error_exit_status)
  # Normally it'd be enough to check for previous error but when running test_unit
  # status is 0
  error_exit_status && error_exit_status != SimpleCov::ExitCodes::SUCCESS
end
process_result(result) click to toggle source

@api private

Usage:

exit_status = SimpleCov.process_result(SimpleCov.result, exit_status)
# File lib/simplecov.rb, line 248
def process_result(result)
  result_exit_status = result_exit_status(result)
  write_last_run(result) if result_exit_status == SimpleCov::ExitCodes::SUCCESS
  result_exit_status
end
process_results_and_report_error() click to toggle source
# File lib/simplecov.rb, line 233
def process_results_and_report_error
  exit_status = process_result(result)

  # Force exit with stored status (see github issue #5)
  if exit_status.positive?
    warn("SimpleCov failed with exit #{exit_status} due to a coverage related error") if print_error_status
    Kernel.exit exit_status
  end
end
ready_to_process_results?() click to toggle source

@api private

# File lib/simplecov.rb, line 229
def ready_to_process_results?
  final_result_process? && result?
end
result() click to toggle source

Returns the result for the current coverage run, merging it across test suites from cache using SimpleCov::ResultMerger if use_merging is activated (default)

# File lib/simplecov.rb, line 101
def result
  return @result if result?

  # Collect our coverage result
  process_coverage_result if running

  # If we're using merging of results, store the current result
  # first (if there is one), then merge the results and return those
  if use_merging
    wait_for_other_processes
    SimpleCov::ResultMerger.store_result(@result) if result?
    @result = SimpleCov::ResultMerger.merged_result
  end

  @result
ensure
  self.running = false
end
result?() click to toggle source

Returns nil if the result has not been computed Otherwise, returns the result

# File lib/simplecov.rb, line 124
def result?
  defined?(@result) && @result
end
result_exit_status(result) click to toggle source
# File lib/simplecov.rb, line 256
def result_exit_status(result)
  coverage_limits = CoverageLimits.new(
    minimum_coverage: minimum_coverage, minimum_coverage_by_file: minimum_coverage_by_file,
    maximum_coverage_drop: maximum_coverage_drop
  )

  ExitCodes::ExitCodeHandling.call(result, coverage_limits: coverage_limits)
end
round_coverage(coverage) click to toggle source

@api private

Rounding down to be extra strict, see #679

# File lib/simplecov.rb, line 296
def round_coverage(coverage)
  coverage.floor(2)
end
run_exit_tasks!() click to toggle source

@api private

Called from at_exit block

# File lib/simplecov.rb, line 186
def run_exit_tasks!
  error_exit_status = exit_status_from_exception

  at_exit.call

  exit_and_report_previous_error(error_exit_status) if previous_error?(error_exit_status)
  process_results_and_report_error if ready_to_process_results?
end
start(profile = nil, &block) click to toggle source

Sets up SimpleCov to run against your project. You can optionally specify a profile to use as well as configuration with a block:

SimpleCov.start
 OR
SimpleCov.start 'rails' # using rails profile
 OR
SimpleCov.start do
  add_filter 'test'
end
  OR
SimpleCov.start 'rails' do
  add_filter 'test'
end

Please check out the RDoc for SimpleCov::Configuration to find about available config options

# File lib/simplecov.rb, line 48
def start(profile = nil, &block)
  require "coverage"
  initial_setup(profile, &block)
  require_relative "./simplecov/process" if SimpleCov.enabled_for_subprocesses? &&
                                            ::Process.respond_to?(:fork)

  make_parallel_tests_available

  @result = nil
  self.pid = Process.pid

  start_coverage_measurement
end
wait_for_other_processes() click to toggle source

@api private

# File lib/simplecov.rb, line 276
def wait_for_other_processes
  return unless defined?(ParallelTests) && final_result_process?

  ParallelTests.wait_for_other_processes_to_finish
end
write_last_run(result) click to toggle source

@api private

# File lib/simplecov.rb, line 285
def write_last_run(result)
  SimpleCov::LastRun.write(result:
    result.coverage_statistics.transform_values do |stats|
      round_coverage(stats.percent)
    end)
end

Private Class Methods

adapt_coverage_result() click to toggle source

Unite the result so it wouldn’t matter what coverage type was called

@return [Hash]

# File lib/simplecov.rb, line 399
def adapt_coverage_result
  @result = SimpleCov::ResultAdapter.call(Coverage.result)
end
add_not_loaded_files(result) click to toggle source

Finds files that were to be tracked but were not loaded and initializes the line-by-line coverage to zero (if relevant) or nil (comments / whitespace etc).

# File lib/simplecov.rb, line 371
def add_not_loaded_files(result)
  if tracked_files
    result = result.dup
    Dir[tracked_files].each do |file|
      absolute_path = File.expand_path(file)
      result[absolute_path] ||= SimulateCoverage.call(absolute_path)
    end
  end

  result
end
initial_setup(profile, &block) click to toggle source
# File lib/simplecov.rb, line 302
def initial_setup(profile, &block)
  load_profile(profile) if profile
  configure(&block) if block_given?
  self.running = true
end
lookup_corresponding_ruby_coverage_name(criterion) click to toggle source
# File lib/simplecov.rb, line 363
def lookup_corresponding_ruby_coverage_name(criterion)
  CRITERION_TO_RUBY_COVERAGE.fetch(criterion)
end
make_parallel_tests_available() click to toggle source

parallel_tests isn’t always available, see: github.com/grosser/parallel_tests/issues/772

# File lib/simplecov.rb, line 425
def make_parallel_tests_available
  return if defined?(ParallelTests)
  return unless probably_running_parallel_tests?

  require "parallel_tests"
rescue LoadError
  warn("SimpleCov guessed you were running inside parallel tests but couldn't load it. Please file a bug report with us!")
end
probably_running_parallel_tests?() click to toggle source
# File lib/simplecov.rb, line 434
def probably_running_parallel_tests?
  ENV["TEST_ENV_NUMBER"] && ENV["PARALLEL_TEST_GROUPS"]
end
process_coverage_result() click to toggle source

Call steps that handle process coverage result

@return [Hash]

# File lib/simplecov.rb, line 388
def process_coverage_result
  adapt_coverage_result
  remove_useless_results
  result_with_not_loaded_files
end
remove_useless_results() click to toggle source

Filter coverage result The result before filter also has result of coverage for files are not related to the project like loaded gems coverage.

@return [Hash]

# File lib/simplecov.rb, line 410
def remove_useless_results
  @result = SimpleCov::UselessResultsRemover.call(@result)
end
result_with_not_loaded_files() click to toggle source

Initialize result with files that are not included by coverage and added inside the config block

@return [Hash]

# File lib/simplecov.rb, line 420
def result_with_not_loaded_files
  @result = SimpleCov::Result.new(add_not_loaded_files(@result))
end
start_coverage_measurement() click to toggle source

Trigger Coverage.start depends on given config coverage_criterion

With Positive branch it supports all coverage measurement types With Negative branch it supports only line coverage measurement type

# File lib/simplecov.rb, line 314
def start_coverage_measurement
  # This blog post gives a good run down of the coverage criterias introduced
  # in Ruby 2.5: https://blog.bigbinary.com/2018/04/11/ruby-2-5-supports-measuring-branch-and-method-coverages.html
  # There is also a nice writeup of the different coverage criteria made in this
  # comment  https://github.com/simplecov-ruby/simplecov/pull/692#discussion_r281836176 :
  # Ruby < 2.5:
  # https://github.com/ruby/ruby/blob/v1_9_3_374/ext/coverage/coverage.c
  # traditional mode (Array)
  #
  # Ruby 2.5:
  # https://bugs.ruby-lang.org/issues/13901
  # https://github.com/ruby/ruby/blob/v2_5_3/ext/coverage/coverage.c
  # default: traditional/compatible mode (Array)
  # :lines - like traditional mode but using Hash
  # :branches
  # :methods
  # :all - same as lines + branches + methods
  #
  # Ruby >= 2.6:
  # https://bugs.ruby-lang.org/issues/15022
  # https://github.com/ruby/ruby/blob/v2_6_3/ext/coverage/coverage.c
  # default: traditional/compatible mode (Array)
  # :lines - like traditional mode but using Hash
  # :branches
  # :methods
  # :oneshot_lines - can not be combined with lines
  # :all - same as lines + branches + methods
  #
  if coverage_start_arguments_supported?
    start_coverage_with_criteria
  else
    Coverage.start unless Coverage.running?
  end
end
start_coverage_with_criteria() click to toggle source
# File lib/simplecov.rb, line 349
def start_coverage_with_criteria
  start_arguments = coverage_criteria.map do |criterion|
    [lookup_corresponding_ruby_coverage_name(criterion), true]
  end.to_h

  start_arguments[:eval] = true if coverage_for_eval_enabled?

  Coverage.start(start_arguments) unless Coverage.running?
end