# frozen_string_literal: truerequire"fileutils"require"docile"require_relative"formatter/multi_formatter"moduleSimpleCov## Bundles the configuration options used for SimpleCov. All methods# defined here are usable from SimpleCov directly. Please check out# SimpleCov documentation for further info.#moduleConfigurationattr_writer:filters,:groups,:formatter,:print_error_status## The root for the project. This defaults to the# current working directory.## Configure with SimpleCov.root('/my/project/path')#defroot(root=nil)return@rootifdefined?(@root)&&root.nil?@coverage_path=nil# invalidate cache@root=File.expand_path(root||Dir.getwd)end## The name of the output and cache directory. Defaults to 'coverage'## Configure with SimpleCov.coverage_dir('cov')#defcoverage_dir(dir=nil)return@coverage_dirifdefined?(@coverage_dir)&&dir.nil?@coverage_path=nil# invalidate cache@coverage_dir=(dir||"coverage")end## Returns the full path to the output directory using SimpleCov.root# and SimpleCov.coverage_dir, so you can adjust this by configuring those# values. Will create the directory if it's missing#defcoverage_path@coverage_path||=begincoverage_path=File.expand_path(coverage_dir,root)FileUtils.mkdir_pcoverage_pathcoverage_pathendend## Coverage results will always include files matched by this glob, whether# or not they were explicitly required. Without this, un-required files# will not be present in the final report.#deftrack_files(glob)@tracked_files=globend## Returns the glob that will be used to include files that were not# explicitly required.#deftracked_files@tracked_filesifdefined?(@tracked_files)end## Returns the list of configured filters. Add filters using SimpleCov.add_filter.#deffilters@filters||=[]end# The name of the command (a.k.a. Test Suite) currently running. Used for result# merging and caching. It first tries to make a guess based upon the command line# arguments the current test suite is running on and should automatically detect# unit tests, functional tests, integration tests, rpsec and cucumber and label# them properly. If it fails to recognize the current command, the command name# is set to the shell command that the current suite is running on.## You can specify it manually with SimpleCov.command_name("test:units") - please# also check out the corresponding section in README.rdocdefcommand_name(name=nil)@name=nameunlessname.nil?@name||=SimpleCov::CommandGuesser.guess@nameend## Gets or sets the configured formatter.## Configure with: SimpleCov.formatter(SimpleCov::Formatter::SimpleFormatter)#defformatter(formatter=nil)return@formatterifdefined?(@formatter)&&formatter.nil?@formatter=formatterraise"No formatter configured. Please specify a formatter using SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter"unless@formatter@formatterend## Sets the configured formatters.#defformatters=(formatters)@formatter=SimpleCov::Formatter::MultiFormatter.new(formatters)end## Gets the configured formatters.#defformattersif@formatter.is_a?(SimpleCov::Formatter::MultiFormatter)@formatter.formatterselseArray(formatter)endend## Whether we should print non-success status codes. This can be# configured with the #print_error_status= method.#defprint_error_statusdefined?(@print_error_status)?@print_error_status:trueend## Certain code blocks (i.e. Ruby-implementation specific code) can be excluded from# the coverage metrics by wrapping it inside # :nocov: comment blocks. The nocov token# can be configured to be any other string using this.## Configure with SimpleCov.nocov_token('skip') or it's alias SimpleCov.skip_token('skip')#defnocov_token(nocov_token=nil)return@nocov_tokenifdefined?(@nocov_token)&&nocov_token.nil?@nocov_token=(nocov_token||"nocov")endaliasskip_tokennocov_token## Returns the configured groups. Add groups using SimpleCov.add_group#defgroups@groups||={}end## Returns the hash of available profiles#defprofiles@profiles||=SimpleCov::Profiles.newenddefadapterswarn"#{Kernel.caller.first}: [DEPRECATION] #adapters is deprecated. Use #profiles instead."profilesend## Allows you to configure simplecov in a block instead of prepending SimpleCov to all config methods# you're calling.## SimpleCov.configure do# add_filter 'foobar'# end## This is equivalent to SimpleCov.add_filter 'foobar' and thus makes it easier to set a bunch of configure# options at once.#defconfigure(&block)Docile.dsl_eval(self,&block)end## Gets or sets the behavior to process coverage results.## By default, it will call SimpleCov.result.format!## Configure with:## SimpleCov.at_exit do# puts "Coverage done"# SimpleCov.result.format!# end#defat_exit(&block)returnProc.newunlessrunning||block_given?@at_exit=blockifblock_given?@at_exit||=proc{SimpleCov.result.format!}end# gets or sets the enabled_for_subprocess configuration# when true, this will inject SimpleCov code into Process.forkdefenable_for_subprocesses(value=nil)return@enable_for_subprocessesifdefined?(@enable_for_subprocesses)&&value.nil?@enable_for_subprocesses=value||falseend# gets the enabled_for_subprocess configurationdefenabled_for_subprocesses?enable_for_subprocessesend## Gets or sets the behavior to start a new forked Process.## By default, it will add " (Process #{pid})" to the command_name, and start SimpleCov in quiet mode## Configure with:## SimpleCov.at_fork do |pid|# SimpleCov.start do# # This needs a unique name so it won't be ovewritten# SimpleCov.command_name "#{SimpleCov.command_name} (subprocess: #{pid})"# # be quiet, the parent process will be in charge of using the regular formatter and checking coverage totals# SimpleCov.print_error_status = false# SimpleCov.formatter SimpleCov::Formatter::SimpleFormatter# SimpleCov.minimum_coverage 0# # start# SimpleCov.start# end# end#defat_fork(&block)@at_fork=blockifblock_given?@at_fork||=lambda{|pid|# This needs a unique name so it won't be ovewrittenSimpleCov.command_name"#{SimpleCov.command_name} (subprocess: #{pid})"# be quiet, the parent process will be in charge of using the regular formatter and checking coverage totalsSimpleCov.print_error_status=falseSimpleCov.formatterSimpleCov::Formatter::SimpleFormatterSimpleCov.minimum_coverage0# startSimpleCov.start}end## Returns the project name - currently assuming the last dirname in# the SimpleCov.root is this.#defproject_name(new_name=nil)return@project_nameifdefined?(@project_name)&&@project_name&&new_name.nil?@project_name=new_nameifnew_name.is_a?(String)@project_name||=File.basename(root.split("/").last).capitalize.tr("_"," ")end## Defines whether to use result merging so all your test suites (test:units, test:functionals, cucumber, ...)# are joined and combined into a single coverage report#defuse_merging(use=nil)@use_merging=useunlessuse.nil?@use_merging=trueunlessdefined?(@use_merging)&&@use_merging==falseend## Defines the maximum age (in seconds) of a resultset to still be included in merged results.# i.e. If you run cucumber features, then later rake test, if the stored cucumber resultset is# more seconds ago than specified here, it won't be taken into account when merging (and is also# purged from the resultset cache)## Of course, this only applies when merging is active (e.g. SimpleCov.use_merging is not false!)## Default is 600 seconds (10 minutes)## Configure with SimpleCov.merge_timeout(3600) # 1hr#defmerge_timeout(seconds=nil)@merge_timeout=secondsifseconds.is_a?(Integer)@merge_timeout||=600end## Defines the minimum overall coverage required for the testsuite to pass.# SimpleCov will return non-zero if the current coverage is below this threshold.## Default is 0% (disabled)#defminimum_coverage(coverage=nil)return@minimum_coverage||={}unlesscoveragecoverage={primary_coverage=>coverage}ifcoverage.is_a?(Numeric)raise_on_invalid_coverage(coverage,"minimum_coverage")@minimum_coverage=coverageenddefraise_on_invalid_coverage(coverage,coverage_setting)coverage.each_key{|criterion|raise_if_criterion_disabled(criterion)}coverage.each_valuedo|percent|minimum_possible_coverage_exceeded(coverage_setting)ifpercent&&percent>100endend## Defines the maximum coverage drop at once allowed for the testsuite to pass.# SimpleCov will return non-zero if the coverage decreases by more than this threshold.## Default is 100% (disabled)#defmaximum_coverage_drop(coverage_drop=nil)return@maximum_coverage_drop||={}unlesscoverage_dropcoverage_drop={primary_coverage=>coverage_drop}ifcoverage_drop.is_a?(Numeric)raise_on_invalid_coverage(coverage_drop,"maximum_coverage_drop")@maximum_coverage_drop=coverage_dropend## Defines the minimum coverage per file required for the testsuite to pass.# SimpleCov will return non-zero if the current coverage of the least covered file# is below this threshold.## Default is 0% (disabled)#defminimum_coverage_by_file(coverage=nil)return@minimum_coverage_by_file||={}unlesscoveragecoverage={primary_coverage=>coverage}ifcoverage.is_a?(Numeric)raise_on_invalid_coverage(coverage,"minimum_coverage_by_file")@minimum_coverage_by_file=coverageend## Refuses any coverage drop. That is, coverage is only allowed to increase.# SimpleCov will return non-zero if the coverage decreases.#defrefuse_coverage_drop(*criteria)criteria=coverage_criteriaifcriteria.empty?maximum_coverage_drop(criteria.map{|c|[c,0]}.to_h)end## Add a filter to the processing chain.# There are four ways to define a filter:## * as a String that will then be matched against all source files' file paths,# SimpleCov.add_filter 'app/models' # will reject all your models# * as a block which will be passed the source file in question and should either# return a true or false value, depending on whether the file should be removed# SimpleCov.add_filter do |src_file|# File.basename(src_file.filename) == 'environment.rb'# end # Will exclude environment.rb files from the results# * as an array of strings that are matched against all sorce files' file# paths and then ignored (basically string filter multiple times)# SimpleCov.add_filter ['app/models', 'app/helpers'] # ignores both dirs# * as an instance of a subclass of SimpleCov::Filter. See the documentation there# on how to define your own filter classes#defadd_filter(filter_argument=nil,&filter_proc)filters<<parse_filter(filter_argument,&filter_proc)end## Define a group for files. Works similar to add_filter, only that the first# argument is the desired group name and files PASSING the filter end up in the group# (while filters exclude when the filter is applicable).#defadd_group(group_name,filter_argument=nil,&filter_proc)groups[group_name]=parse_filter(filter_argument,&filter_proc)endSUPPORTED_COVERAGE_CRITERIA=%i[line branch].freezeDEFAULT_COVERAGE_CRITERION=:line## Define which coverage criterion should be evaluated.## Possible coverage criteria:# * :line - coverage based on lines aka has this line been executed?# * :branch - coverage based on branches aka has this branch (think conditions) been executed?## If not set the default is `:line`## @param [Symbol] criterion#defcoverage_criterion(criterion=nil)return@coverage_criterion||=primary_coverageunlesscriterionraise_if_criterion_unsupported(criterion)@coverage_criterion=criterionenddefenable_coverage(criterion)raise_if_criterion_unsupported(criterion)coverage_criteria<<criterionenddefprimary_coverage(criterion=nil)ifcriterion.nil?@primary_coverage||=DEFAULT_COVERAGE_CRITERIONelseraise_if_criterion_disabled(criterion)@primary_coverage=criterionendenddefcoverage_criteria@coverage_criteria||=Set[primary_coverage]enddefcoverage_criterion_enabled?(criterion)coverage_criteria.member?(criterion)enddefclear_coverage_criteria@coverage_criteria=nilenddefbranch_coverage?branch_coverage_supported?&&coverage_criterion_enabled?(:branch)enddefcoverage_start_arguments_supported?# safe to cache as within one process this value should never# changereturn@coverage_start_arguments_supportedifdefined?(@coverage_start_arguments_supported)@coverage_start_arguments_supported=beginrequire"coverage"!Coverage.method(:start).arity.zero?endenddefbranch_coverage_supported?coverage_start_arguments_supported?&&RUBY_ENGINE!="jruby"enddefcoverage_for_eval_supported?require"coverage"defined?(Coverage.supported?)&&Coverage.supported?(:eval)enddefcoverage_for_eval_enabled?@coverage_for_eval_enabled||=falseenddefenable_coverage_for_evalifcoverage_for_eval_supported?@coverage_for_eval_enabled=trueelsewarn"Coverage for eval is not available; Use Ruby 3.2.0 or later"endendprivatedefraise_if_criterion_disabled(criterion)raise_if_criterion_unsupported(criterion)# rubocop:disable Style/IfUnlessModifierunlesscoverage_criterion_enabled?(criterion)raise"Coverage criterion #{criterion}, is disabled! Please enable it first through enable_coverage #{criterion} (if supported)"end# rubocop:enable Style/IfUnlessModifierenddefraise_if_criterion_unsupported(criterion)# rubocop:disable Style/IfUnlessModifierunlessSUPPORTED_COVERAGE_CRITERIA.member?(criterion)raise"Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"end# rubocop:enable Style/IfUnlessModifierenddefminimum_possible_coverage_exceeded(coverage_option)warn"The coverage you set for #{coverage_option} is greater than 100%"end## The actual filter processor. Not meant for direct use#defparse_filter(filter_argument=nil,&filter_proc)filter=filter_argument||filter_prociffilterSimpleCov::Filter.build_filter(filter)elseraiseArgumentError,"Please specify either a filter or a block to filter with"endendendend