simplecov 0.8.0.pre2 → 0.18.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +368 -27
  3. data/CODE_OF_CONDUCT.md +76 -0
  4. data/CONTRIBUTING.md +38 -7
  5. data/ISSUE_TEMPLATE.md +23 -0
  6. data/{MIT-LICENSE → LICENSE} +1 -1
  7. data/README.md +449 -230
  8. data/doc/alternate-formatters.md +61 -0
  9. data/doc/commercial-services.md +20 -0
  10. data/doc/editor-integration.md +18 -0
  11. data/lib/simplecov.rb +294 -59
  12. data/lib/simplecov/combine.rb +30 -0
  13. data/lib/simplecov/combine/branches_combiner.rb +32 -0
  14. data/lib/simplecov/combine/files_combiner.rb +25 -0
  15. data/lib/simplecov/combine/lines_combiner.rb +43 -0
  16. data/lib/simplecov/combine/results_combiner.rb +60 -0
  17. data/lib/simplecov/command_guesser.rb +46 -40
  18. data/lib/simplecov/configuration.rb +346 -221
  19. data/lib/simplecov/defaults.rb +35 -75
  20. data/lib/simplecov/exit_codes.rb +9 -4
  21. data/lib/simplecov/file_list.rb +80 -36
  22. data/lib/simplecov/filter.rb +51 -3
  23. data/lib/simplecov/formatter.rb +4 -2
  24. data/lib/simplecov/formatter/multi_formatter.rb +28 -19
  25. data/lib/simplecov/formatter/simple_formatter.rb +21 -15
  26. data/lib/simplecov/last_run.rb +21 -13
  27. data/lib/simplecov/lines_classifier.rb +48 -0
  28. data/lib/simplecov/load_global_config.rb +8 -0
  29. data/lib/simplecov/no_defaults.rb +4 -2
  30. data/lib/simplecov/profiles.rb +29 -23
  31. data/lib/simplecov/profiles/bundler_filter.rb +5 -0
  32. data/lib/simplecov/profiles/hidden_filter.rb +5 -0
  33. data/lib/simplecov/profiles/rails.rb +18 -0
  34. data/lib/simplecov/profiles/root_filter.rb +10 -0
  35. data/lib/simplecov/profiles/test_frameworks.rb +8 -0
  36. data/lib/simplecov/result.rb +39 -68
  37. data/lib/simplecov/result_adapter.rb +30 -0
  38. data/lib/simplecov/result_merger.rb +110 -60
  39. data/lib/simplecov/simulate_coverage.rb +29 -0
  40. data/lib/simplecov/source_file.rb +261 -135
  41. data/lib/simplecov/source_file/branch.rb +106 -0
  42. data/lib/simplecov/source_file/line.rb +72 -0
  43. data/lib/simplecov/useless_results_remover.rb +16 -0
  44. data/lib/simplecov/version.rb +4 -2
  45. metadata +48 -197
  46. data/.gitignore +0 -32
  47. data/.travis.yml +0 -28
  48. data/.yardopts +0 -1
  49. data/Appraisals +0 -8
  50. data/Gemfile +0 -16
  51. data/Rakefile +0 -32
  52. data/cucumber.yml +0 -13
  53. data/features/config_autoload.feature +0 -46
  54. data/features/config_command_name.feature +0 -45
  55. data/features/config_coverage_dir.feature +0 -33
  56. data/features/config_deactivate_merging.feature +0 -42
  57. data/features/config_formatters.feature +0 -52
  58. data/features/config_merge_timeout.feature +0 -39
  59. data/features/config_nocov_token.feature +0 -79
  60. data/features/config_profiles.feature +0 -44
  61. data/features/config_project_name.feature +0 -27
  62. data/features/config_styles.feature +0 -121
  63. data/features/cucumber_basic.feature +0 -29
  64. data/features/maximum_coverage_drop.feature +0 -36
  65. data/features/merging_test_unit_and_rspec.feature +0 -44
  66. data/features/minimum_coverage.feature +0 -59
  67. data/features/refuse_coverage_drop.feature +0 -35
  68. data/features/rspec_basic.feature +0 -31
  69. data/features/rspec_fails_on_initialization.feature +0 -14
  70. data/features/rspec_groups_and_filters_basic.feature +0 -29
  71. data/features/rspec_groups_and_filters_complex.feature +0 -35
  72. data/features/rspec_groups_using_filter_class.feature +0 -40
  73. data/features/rspec_without_simplecov.feature +0 -20
  74. data/features/skipping_code_blocks_manually.feature +0 -70
  75. data/features/step_definitions/html_steps.rb +0 -45
  76. data/features/step_definitions/simplecov_steps.rb +0 -66
  77. data/features/step_definitions/transformers.rb +0 -13
  78. data/features/step_definitions/web_steps.rb +0 -64
  79. data/features/support/env.rb +0 -44
  80. data/features/test_unit_basic.feature +0 -34
  81. data/features/test_unit_groups_and_filters_basic.feature +0 -29
  82. data/features/test_unit_groups_and_filters_complex.feature +0 -35
  83. data/features/test_unit_groups_using_filter_class.feature +0 -40
  84. data/features/test_unit_without_simplecov.feature +0 -20
  85. data/features/unicode_compatiblity.feature +0 -67
  86. data/gemfiles/multi_json_legacy.gemfile +0 -12
  87. data/gemfiles/multi_json_new.gemfile +0 -12
  88. data/lib/simplecov/jruby16_fix.rb +0 -43
  89. data/lib/simplecov/json.rb +0 -27
  90. data/lib/simplecov/merge_helpers.rb +0 -39
  91. data/lib/simplecov/railtie.rb +0 -7
  92. data/lib/simplecov/railties/tasks.rake +0 -11
  93. data/simplecov.gemspec +0 -30
  94. data/test/faked_project/Gemfile +0 -6
  95. data/test/faked_project/Rakefile +0 -8
  96. data/test/faked_project/cucumber.yml +0 -13
  97. data/test/faked_project/features/step_definitions/my_steps.rb +0 -23
  98. data/test/faked_project/features/support/env.rb +0 -12
  99. data/test/faked_project/features/test_stuff.feature +0 -6
  100. data/test/faked_project/lib/faked_project.rb +0 -11
  101. data/test/faked_project/lib/faked_project/framework_specific.rb +0 -18
  102. data/test/faked_project/lib/faked_project/meta_magic.rb +0 -24
  103. data/test/faked_project/lib/faked_project/some_class.rb +0 -29
  104. data/test/faked_project/spec/faked_spec.rb +0 -11
  105. data/test/faked_project/spec/meta_magic_spec.rb +0 -10
  106. data/test/faked_project/spec/some_class_spec.rb +0 -10
  107. data/test/faked_project/spec/spec_helper.rb +0 -15
  108. data/test/faked_project/test/faked_test.rb +0 -11
  109. data/test/faked_project/test/meta_magic_test.rb +0 -13
  110. data/test/faked_project/test/some_class_test.rb +0 -15
  111. data/test/faked_project/test/test_helper.rb +0 -16
  112. data/test/fixtures/app/controllers/sample_controller.rb +0 -10
  113. data/test/fixtures/app/models/user.rb +0 -10
  114. data/test/fixtures/deleted_source_sample.rb +0 -15
  115. data/test/fixtures/frameworks/rspec_bad.rb +0 -9
  116. data/test/fixtures/frameworks/rspec_good.rb +0 -9
  117. data/test/fixtures/frameworks/testunit_bad.rb +0 -9
  118. data/test/fixtures/frameworks/testunit_good.rb +0 -9
  119. data/test/fixtures/iso-8859.rb +0 -3
  120. data/test/fixtures/resultset1.rb +0 -4
  121. data/test/fixtures/resultset2.rb +0 -5
  122. data/test/fixtures/sample.rb +0 -16
  123. data/test/fixtures/utf-8.rb +0 -3
  124. data/test/helper.rb +0 -34
  125. data/test/shoulda_macros.rb +0 -19
  126. data/test/test_1_8_fallbacks.rb +0 -31
  127. data/test/test_command_guesser.rb +0 -19
  128. data/test/test_deleted_source.rb +0 -14
  129. data/test/test_file_list.rb +0 -22
  130. data/test/test_filters.rb +0 -78
  131. data/test/test_merge_helpers.rb +0 -105
  132. data/test/test_result.rb +0 -160
  133. data/test/test_return_codes.rb +0 -37
  134. data/test/test_source_file.rb +0 -106
  135. data/test/test_source_file_line.rb +0 -106
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # Functionally for combining coverage results
5
+ #
6
+ module Combine
7
+ module_function
8
+
9
+ #
10
+ # Combine two coverage based on the given combiner_module.
11
+ #
12
+ # Combiners should always be called throught his interface,
13
+ # as it takes care of short circuting of one of the coverages is nil.
14
+ #
15
+ # @return [Hash]
16
+ def combine(combiner_module, coverage_a, coverage_b)
17
+ return existing_coverage(coverage_a, coverage_b) if empty_coverage?(coverage_a, coverage_b)
18
+
19
+ combiner_module.combine(coverage_a, coverage_b)
20
+ end
21
+
22
+ def empty_coverage?(coverage_a, coverage_b)
23
+ !(coverage_a && coverage_b)
24
+ end
25
+
26
+ def existing_coverage(coverage_a, coverage_b)
27
+ coverage_a || coverage_b
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ module Combine
5
+ #
6
+ # Combine different branch coverage results on single file.
7
+ #
8
+ # Should be called through `SimpleCov.combine`.
9
+ module BranchesCombiner
10
+ module_function
11
+
12
+ #
13
+ # Return merged branches or the existed branche if other is missing.
14
+ #
15
+ # Branches inside files are always same if they exists, the difference only in coverage count.
16
+ # Branch coverage report for any conditional case is built from hash, it's key is a condition and
17
+ # it's body is a hash << keys from condition and value is coverage rate >>.
18
+ # ex: branches =>{ [:if, 3, 8, 6, 8, 36] => {[:then, 4, 8, 6, 8, 12] => 1, [:else, 5, 8, 6, 8, 36]=>2}, other conditions...}
19
+ # We create copy of result and update it values depending on the combined branches coverage values.
20
+ #
21
+ # @return [Hash]
22
+ #
23
+ def combine(coverage_a, coverage_b)
24
+ coverage_a.merge(coverage_b) do |_condition, branches_inside_a, branches_inside_b|
25
+ branches_inside_a.merge(branches_inside_b) do |_branch, a_count, b_count|
26
+ a_count + b_count
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ module Combine
5
+ #
6
+ # Handle combining two coverage results for same file
7
+ #
8
+ # Should be called through `SimpleCov.combine`.
9
+ module FilesCombiner
10
+ module_function
11
+
12
+ #
13
+ # Combines the results for 2 coverages of a file.
14
+ #
15
+ # @return [Hash]
16
+ #
17
+ def combine(coverage_a, coverage_b)
18
+ {
19
+ :lines => Combine.combine(LinesCombiner, coverage_a[:lines], coverage_b[:lines]),
20
+ :branches => Combine.combine(BranchesCombiner, coverage_a[:branches], coverage_b[:branches])
21
+ }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ module Combine
5
+ #
6
+ # Combine two different lines coverage results on same file
7
+ #
8
+ # Should be called through `SimpleCov.combine`.
9
+ module LinesCombiner
10
+ module_function
11
+
12
+ def combine(coverage_a, coverage_b)
13
+ coverage_a
14
+ .zip(coverage_b)
15
+ .map do |coverage_a_val, coverage_b_val|
16
+ merge_line_coverage(coverage_a_val, coverage_b_val)
17
+ end
18
+ end
19
+
20
+ # Return depends on coverage in a specific line
21
+ #
22
+ # @param [Integer || nil] first_val
23
+ # @param [Integer || nil] second_val
24
+ #
25
+ # Logic:
26
+ #
27
+ # => nil + 0 = nil
28
+ # => nil + nil = nil
29
+ # => int + int = int
30
+ #
31
+ # @return [Integer || nil]
32
+ def merge_line_coverage(first_val, second_val)
33
+ sum = first_val.to_i + second_val.to_i
34
+
35
+ if sum.zero? && (first_val.nil? || second_val.nil?)
36
+ nil
37
+ else
38
+ sum
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ module Combine
5
+ # There might be reports from different kinds of tests,
6
+ # e.g. RSpec and Cucumber. We need to combine their results
7
+ # into unified one. This class does that.
8
+ # To unite the results on file basis, it leverages
9
+ # the combine of lines and branches inside each file within given results.
10
+ module ResultsCombiner
11
+ module_function
12
+
13
+ #
14
+ # Combine process explanation
15
+ # => ResultCombiner: define all present files between results and start combine on file level.
16
+ # ==> FileCombiner: collect result of next combine levels lines and branches.
17
+ # ===> LinesCombiner: combine lines results.
18
+ # ===> BranchesCombiner: combine branches results.
19
+ #
20
+ # @return [Hash]
21
+ #
22
+ def combine(*results)
23
+ results.reduce({}) do |result, next_result|
24
+ combine_result_sets(result, next_result)
25
+ end
26
+ end
27
+
28
+ #
29
+ # Manage combining results on files level
30
+ #
31
+ # @param [Hash] result_a
32
+ # @param [Hash] result_b
33
+ #
34
+ # @return [Hash]
35
+ #
36
+ def combine_result_sets(result_a, result_b)
37
+ results_files = result_a.keys | result_b.keys
38
+
39
+ results_files.each_with_object({}) do |file_name, combined_results|
40
+ combined_results[file_name] = combine_file_coverage(
41
+ result_a[file_name],
42
+ result_b[file_name]
43
+ )
44
+ end
45
+ end
46
+
47
+ #
48
+ # Combine two files coverage results
49
+ #
50
+ # @param [Hash] coverage_a
51
+ # @param [Hash] coverage_b
52
+ #
53
+ # @return [Hash]
54
+ #
55
+ def combine_file_coverage(coverage_a, coverage_b)
56
+ Combine.combine(Combine::FilesCombiner, coverage_a, coverage_b)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,33 +1,36 @@
1
- #
2
- # Helper that tries to find out what test suite is running (for SimpleCov.command_name)
3
- #
4
- module SimpleCov::CommandGuesser
5
- class << self
6
- # Storage for the original command line call that invoked the test suite.
7
- # This has got to be stored as early as possible because i.e. rake and test/unit 2
8
- # have a habit of tampering with ARGV, which makes i.e. the automatic distinction
9
- # between rails unit/functional/integration tests impossible without this cached
10
- # item.
11
- attr_accessor :original_run_command
12
-
13
- def guess
14
- from_env || from_command_line_options || from_defined_constants
15
- end
16
-
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ #
5
+ # Helper that tries to find out what test suite is running (for SimpleCov.command_name)
6
+ #
7
+ module CommandGuesser
8
+ class << self
9
+ # Storage for the original command line call that invoked the test suite.
10
+ # This has got to be stored as early as possible because i.e. rake and test/unit 2
11
+ # have a habit of tampering with ARGV, which makes i.e. the automatic distinction
12
+ # between rails unit/functional/integration tests impossible without this cached
13
+ # item.
14
+ attr_accessor :original_run_command
15
+
16
+ def guess
17
+ from_env || from_command_line_options || from_defined_constants
18
+ end
19
+
17
20
  private
18
21
 
19
- def from_env
20
- # If being run from inside parallel_tests set the command name according to the process number
21
- if ENV['PARALLEL_TEST_GROUPS'] && ENV['TEST_ENV_NUMBER']
22
- number = ENV['TEST_ENV_NUMBER']
23
- number = '1' if number == ''
22
+ def from_env
23
+ # If being run from inside parallel_tests set the command name according to the process number
24
+ return unless ENV["PARALLEL_TEST_GROUPS"] && ENV["TEST_ENV_NUMBER"]
25
+
26
+ number = ENV["TEST_ENV_NUMBER"]
27
+ number = "1" if number.empty?
24
28
  "(#{number}/#{ENV['PARALLEL_TEST_GROUPS']})"
25
29
  end
26
- end
27
-
28
- def from_command_line_options
29
- case original_run_command
30
- when /test\/functional\//, /test\/{.*?functional.*?}\//
30
+
31
+ def from_command_line_options
32
+ case original_run_command
33
+ when /test\/functional\//, /test\/\{.*functional.*\}\//
31
34
  "Functional Tests"
32
35
  when /test\/integration\//
33
36
  "Integration Tests"
@@ -37,21 +40,24 @@ module SimpleCov::CommandGuesser
37
40
  "RSpec"
38
41
  when /cucumber/, /features/
39
42
  "Cucumber Features"
40
- else
41
- nil
43
+ end
42
44
  end
43
- end
44
-
45
- def from_defined_constants
46
- # If the command regexps fail, let's try checking defined constants.
47
- if defined?(RSpec)
48
- "RSpec"
49
- elsif defined?(Test::Unit)
50
- "Unit Tests"
51
- else
52
- # TODO: Provide link to docs/wiki article
53
- warn "SimpleCov failed to recognize the test framework and/or suite used. Please specify manually using SimpleCov.command_name 'Unit Tests'."
54
- 'Unknown Test Framework'
45
+
46
+ def from_defined_constants
47
+ # If the command regexps fail, let's try checking defined constants.
48
+ if defined?(RSpec)
49
+ "RSpec"
50
+ elsif defined?(Test::Unit)
51
+ "Unit Tests"
52
+ elsif defined?(Minitest)
53
+ "Minitest"
54
+ elsif defined?(MiniTest)
55
+ "MiniTest"
56
+ else
57
+ # TODO: Provide link to docs/wiki article
58
+ warn "SimpleCov failed to recognize the test framework and/or suite used. Please specify manually using SimpleCov.command_name 'Unit Tests'."
59
+ "Unknown Test Framework"
60
+ end
55
61
  end
56
62
  end
57
63
  end
@@ -1,249 +1,374 @@
1
- require 'fileutils'
2
- require 'docile'
3
- #
4
- # Bundles the configuration options used for SimpleCov. All methods
5
- # defined here are usable from SimpleCov directly. Please check out
6
- # SimpleCov documentation for further info.
7
- #
8
- module SimpleCov::Configuration
9
- attr_writer :filters, :groups, :formatter
1
+ # frozen_string_literal: true
10
2
 
11
- #
12
- # The root for the project. This defaults to the
13
- # current working directory.
14
- #
15
- # Configure with SimpleCov.root('/my/project/path')
16
- #
17
- def root(root=nil)
18
- return @root if defined? @root and root.nil?
19
- @root = File.expand_path(root || Dir.getwd)
20
- end
3
+ require "fileutils"
4
+ require "docile"
5
+ require "simplecov/formatter/multi_formatter"
21
6
 
7
+ module SimpleCov
22
8
  #
23
- # The name of the output and cache directory. Defaults to 'coverage'
24
- #
25
- # Configure with SimpleCov.coverage_dir('cov')
9
+ # Bundles the configuration options used for SimpleCov. All methods
10
+ # defined here are usable from SimpleCov directly. Please check out
11
+ # SimpleCov documentation for further info.
26
12
  #
27
- def coverage_dir(dir=nil)
28
- return @coverage_dir if defined? @coverage_dir and dir.nil?
29
- @coverage_dir = (dir || 'coverage')
30
- end
13
+ module Configuration # rubocop:disable Metrics/ModuleLength
14
+ attr_writer :filters, :groups, :formatter, :print_error_status
31
15
 
32
- #
33
- # Returns the full path to the output directory using SimpleCov.root
34
- # and SimpleCov.coverage_dir, so you can adjust this by configuring those
35
- # values. Will create the directory if it's missing
36
- #
37
- def coverage_path
38
- coverage_path = File.expand_path(coverage_dir, root)
39
- FileUtils.mkdir_p coverage_path
40
- coverage_path
41
- end
16
+ #
17
+ # The root for the project. This defaults to the
18
+ # current working directory.
19
+ #
20
+ # Configure with SimpleCov.root('/my/project/path')
21
+ #
22
+ def root(root = nil)
23
+ return @root if defined?(@root) && root.nil?
42
24
 
43
- #
44
- # Returns the list of configured filters. Add filters using SimpleCov.add_filter.
45
- #
46
- def filters
47
- @filters ||= []
48
- end
25
+ @root = File.expand_path(root || Dir.getwd)
26
+ end
49
27
 
50
- # The name of the command (a.k.a. Test Suite) currently running. Used for result
51
- # merging and caching. It first tries to make a guess based upon the command line
52
- # arguments the current test suite is running on and should automatically detect
53
- # unit tests, functional tests, integration tests, rpsec and cucumber and label
54
- # them properly. If it fails to recognize the current command, the command name
55
- # is set to the shell command that the current suite is running on.
56
- #
57
- # You can specify it manually with SimpleCov.command_name("test:units") - please
58
- # also check out the corresponding section in README.rdoc
59
- def command_name(name=nil)
60
- @name = name unless name.nil?
61
- @name ||= SimpleCov::CommandGuesser.guess
62
- @name
63
- end
28
+ #
29
+ # The name of the output and cache directory. Defaults to 'coverage'
30
+ #
31
+ # Configure with SimpleCov.coverage_dir('cov')
32
+ #
33
+ def coverage_dir(dir = nil)
34
+ return @coverage_dir if defined?(@coverage_dir) && dir.nil?
64
35
 
65
- #
66
- # Gets or sets the configured formatter.
67
- #
68
- # Configure with: SimpleCov.formatter(SimpleCov::Formatter::SimpleFormatter)
69
- #
70
- def formatter(formatter=nil)
71
- return @formatter if defined? @formatter and formatter.nil?
72
- @formatter = formatter
73
- raise "No formatter configured. Please specify a formatter using SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter" unless @formatter
74
- @formatter
75
- end
36
+ @coverage_path = nil # invalidate cache
37
+ @coverage_dir = (dir || "coverage")
38
+ end
76
39
 
77
- #
78
- # Certain code blocks (i.e. Ruby-implementation specific code) can be excluded from
79
- # the coverage metrics by wrapping it inside # :nocov: comment blocks. The nocov token
80
- # can be configured to be any other string using this.
81
- #
82
- # Configure with SimpleCov.nocov_token('skip') or it's alias SimpleCov.skip_token('skip')
83
- #
84
- def nocov_token(nocov_token=nil)
85
- return @nocov_token if defined? @nocov_token and nocov_token.nil?
86
- @nocov_token = (nocov_token || 'nocov')
87
- end
88
- alias_method :skip_token, :nocov_token
40
+ #
41
+ # Returns the full path to the output directory using SimpleCov.root
42
+ # and SimpleCov.coverage_dir, so you can adjust this by configuring those
43
+ # values. Will create the directory if it's missing
44
+ #
45
+ def coverage_path
46
+ @coverage_path ||= begin
47
+ coverage_path = File.expand_path(coverage_dir, root)
48
+ FileUtils.mkdir_p coverage_path
49
+ coverage_path
50
+ end
51
+ end
89
52
 
90
- #
91
- # Returns the configured groups. Add groups using SimpleCov.add_group
92
- #
93
- def groups
94
- @groups ||= {}
95
- end
53
+ #
54
+ # Coverage results will always include files matched by this glob, whether
55
+ # or not they were explicitly required. Without this, un-required files
56
+ # will not be present in the final report.
57
+ #
58
+ def track_files(glob)
59
+ @tracked_files = glob
60
+ end
96
61
 
97
- #
98
- # Returns the hash of available profiles
99
- #
100
- def profiles
101
- @profiles ||= SimpleCov::Profiles.new
102
- end
62
+ #
63
+ # Returns the glob that will be used to include files that were not
64
+ # explicitly required.
65
+ #
66
+ def tracked_files
67
+ @tracked_files if defined?(@tracked_files)
68
+ end
103
69
 
104
- def adapters
105
- warn "method adapters is deprecated. use profiles instead"
106
- profiles
107
- end
70
+ #
71
+ # Returns the list of configured filters. Add filters using SimpleCov.add_filter.
72
+ #
73
+ def filters
74
+ @filters ||= []
75
+ end
108
76
 
109
- #
110
- # Allows you to configure simplecov in a block instead of prepending SimpleCov to all config methods
111
- # you're calling.
112
- #
113
- # SimpleCov.configure do
114
- # add_filter 'foobar'
115
- # end
116
- #
117
- # This is equivalent to SimpleCov.add_filter 'foobar' and thus makes it easier to set a bunch of configure
118
- # options at once.
119
- #
120
- def configure(&block)
121
- return false unless SimpleCov.usable?
122
- Docile.dsl_eval(self, &block)
123
- end
77
+ # The name of the command (a.k.a. Test Suite) currently running. Used for result
78
+ # merging and caching. It first tries to make a guess based upon the command line
79
+ # arguments the current test suite is running on and should automatically detect
80
+ # unit tests, functional tests, integration tests, rpsec and cucumber and label
81
+ # them properly. If it fails to recognize the current command, the command name
82
+ # is set to the shell command that the current suite is running on.
83
+ #
84
+ # You can specify it manually with SimpleCov.command_name("test:units") - please
85
+ # also check out the corresponding section in README.rdoc
86
+ def command_name(name = nil)
87
+ @name = name unless name.nil?
88
+ @name ||= SimpleCov::CommandGuesser.guess
89
+ @name
90
+ end
124
91
 
125
- #
126
- # Gets or sets the behavior to process coverage results.
127
- #
128
- # By default, it will call SimpleCov.result.format!
129
- #
130
- # Configure with:
131
- # SimpleCov.at_exit do
132
- # puts "Coverage done"
133
- # SimpleCov.result.format!
134
- # end
135
- #
136
- def at_exit(&block)
137
- return Proc.new {} unless running or block_given?
138
- @at_exit = block if block_given?
139
- @at_exit ||= Proc.new { SimpleCov.result.format! }
140
- end
92
+ #
93
+ # Gets or sets the configured formatter.
94
+ #
95
+ # Configure with: SimpleCov.formatter(SimpleCov::Formatter::SimpleFormatter)
96
+ #
97
+ def formatter(formatter = nil)
98
+ return @formatter if defined?(@formatter) && formatter.nil?
141
99
 
142
- #
143
- # Returns the project name - currently assuming the last dirname in
144
- # the SimpleCov.root is this.
145
- #
146
- def project_name(new_name=nil)
147
- return @project_name if defined? @project_name and @project_name and new_name.nil?
148
- @project_name = new_name if new_name.kind_of?(String)
149
- @project_name ||= File.basename(root.split('/').last).capitalize.gsub('_', ' ')
150
- end
100
+ @formatter = formatter
101
+ raise "No formatter configured. Please specify a formatter using SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter" unless @formatter
151
102
 
152
- #
153
- # Defines whether to use result merging so all your test suites (test:units, test:functionals, cucumber, ...)
154
- # are joined and combined into a single coverage report
155
- #
156
- def use_merging(use=nil)
157
- @use_merging = use unless use.nil?
158
- @use_merging = true unless defined? @use_merging and @use_merging == false
159
- end
103
+ @formatter
104
+ end
160
105
 
161
- #
162
- # Defines them maximum age (in seconds) of a resultset to still be included in merged results.
163
- # i.e. If you run cucumber features, then later rake test, if the stored cucumber resultset is
164
- # more seconds ago than specified here, it won't be taken into account when merging (and is also
165
- # purged from the resultset cache)
166
- #
167
- # Of course, this only applies when merging is active (e.g. SimpleCov.use_merging is not false!)
168
- #
169
- # Default is 600 seconds (10 minutes)
170
- #
171
- # Configure with SimpleCov.merge_timeout(3600) # 1hr
172
- #
173
- def merge_timeout(seconds=nil)
174
- @merge_timeout = seconds if seconds.kind_of?(Fixnum)
175
- @merge_timeout ||= 600
176
- end
106
+ #
107
+ # Sets the configured formatters.
108
+ #
109
+ def formatters=(formatters)
110
+ @formatter = SimpleCov::Formatter::MultiFormatter.new(formatters)
111
+ end
177
112
 
178
- #
179
- # Defines the minimum overall coverage required for the testsuite to pass.
180
- # SimpleCov will return non-zero if the current coverage is below this threshold.
181
- #
182
- # Default is 0% (disabled)
183
- #
184
- def minimum_coverage(coverage=nil)
185
- @minimum_coverage ||= (coverage || 0).to_f.round(2)
186
- end
113
+ #
114
+ # Gets the configured formatters.
115
+ #
116
+ def formatters
117
+ if @formatter.is_a?(SimpleCov::Formatter::MultiFormatter)
118
+ @formatter.formatters
119
+ else
120
+ Array(formatter)
121
+ end
122
+ end
187
123
 
188
- #
189
- # Defines the maximum coverage drop at once allowed for the testsuite to pass.
190
- # SimpleCov will return non-zero if the coverage decreases by more than this threshold.
191
- #
192
- # Default is 100% (disabled)
193
- #
194
- def maximum_coverage_drop(coverage_drop=nil)
195
- @maximum_coverage_drop ||= (coverage_drop || 100).to_f.round(2)
196
- end
124
+ #
125
+ # Whether we should print non-success status codes. This can be
126
+ # configured with the #print_error_status= method.
127
+ #
128
+ def print_error_status
129
+ defined?(@print_error_status) ? @print_error_status : true
130
+ end
197
131
 
198
- #
199
- # Refuses any coverage drop. That is, coverage is only allowed to increase.
200
- # SimpleCov will return non-zero if the coverage decreases.
201
- #
202
- def refuse_coverage_drop
203
- maximum_coverage_drop 0
204
- end
132
+ #
133
+ # Certain code blocks (i.e. Ruby-implementation specific code) can be excluded from
134
+ # the coverage metrics by wrapping it inside # :nocov: comment blocks. The nocov token
135
+ # can be configured to be any other string using this.
136
+ #
137
+ # Configure with SimpleCov.nocov_token('skip') or it's alias SimpleCov.skip_token('skip')
138
+ #
139
+ def nocov_token(nocov_token = nil)
140
+ return @nocov_token if defined?(@nocov_token) && nocov_token.nil?
205
141
 
206
- #
207
- # Add a filter to the processing chain.
208
- # There are three ways to define a filter:
209
- #
210
- # * as a String that will then be matched against all source files' file paths,
211
- # SimpleCov.add_filter 'app/models' # will reject all your models
212
- # * as a block which will be passed the source file in question and should either
213
- # return a true or false value, depending on whether the file should be removed
214
- # SimpleCov.add_filter do |src_file|
215
- # File.basename(src_file.filename) == 'environment.rb'
216
- # end # Will exclude environment.rb files from the results
217
- # * as an instance of a subclass of SimpleCov::Filter. See the documentation there
218
- # on how to define your own filter classes
219
- #
220
- def add_filter(filter_argument=nil, &filter_proc)
221
- filters << parse_filter(filter_argument, &filter_proc)
222
- end
142
+ @nocov_token = (nocov_token || "nocov")
143
+ end
144
+ alias skip_token nocov_token
223
145
 
224
- #
225
- # Define a group for files. Works similar to add_filter, only that the first
226
- # argument is the desired group name and files PASSING the filter end up in the group
227
- # (while filters exclude when the filter is applicable).
228
- #
229
- def add_group(group_name, filter_argument=nil, &filter_proc)
230
- groups[group_name] = parse_filter(filter_argument, &filter_proc)
231
- end
146
+ #
147
+ # Returns the configured groups. Add groups using SimpleCov.add_group
148
+ #
149
+ def groups
150
+ @groups ||= {}
151
+ end
152
+
153
+ #
154
+ # Returns the hash of available profiles
155
+ #
156
+ def profiles
157
+ @profiles ||= SimpleCov::Profiles.new
158
+ end
159
+
160
+ def adapters
161
+ warn "#{Kernel.caller.first}: [DEPRECATION] #adapters is deprecated. Use #profiles instead."
162
+ profiles
163
+ end
164
+
165
+ #
166
+ # Allows you to configure simplecov in a block instead of prepending SimpleCov to all config methods
167
+ # you're calling.
168
+ #
169
+ # SimpleCov.configure do
170
+ # add_filter 'foobar'
171
+ # end
172
+ #
173
+ # This is equivalent to SimpleCov.add_filter 'foobar' and thus makes it easier to set a bunch of configure
174
+ # options at once.
175
+ #
176
+ def configure(&block)
177
+ Docile.dsl_eval(self, &block)
178
+ end
179
+
180
+ #
181
+ # Gets or sets the behavior to process coverage results.
182
+ #
183
+ # By default, it will call SimpleCov.result.format!
184
+ #
185
+ # Configure with:
186
+ #
187
+ # SimpleCov.at_exit do
188
+ # puts "Coverage done"
189
+ # SimpleCov.result.format!
190
+ # end
191
+ #
192
+ def at_exit(&block)
193
+ return proc {} unless running || block_given?
194
+
195
+ @at_exit = block if block_given?
196
+ @at_exit ||= proc { SimpleCov.result.format! }
197
+ end
198
+
199
+ #
200
+ # Returns the project name - currently assuming the last dirname in
201
+ # the SimpleCov.root is this.
202
+ #
203
+ def project_name(new_name = nil)
204
+ return @project_name if defined?(@project_name) && @project_name && new_name.nil?
205
+
206
+ @project_name = new_name if new_name.is_a?(String)
207
+ @project_name ||= File.basename(root.split("/").last).capitalize.tr("_", " ")
208
+ end
209
+
210
+ #
211
+ # Defines whether to use result merging so all your test suites (test:units, test:functionals, cucumber, ...)
212
+ # are joined and combined into a single coverage report
213
+ #
214
+ def use_merging(use = nil)
215
+ @use_merging = use unless use.nil?
216
+ @use_merging = true unless defined?(@use_merging) && @use_merging == false
217
+ end
218
+
219
+ #
220
+ # Defines the maximum age (in seconds) of a resultset to still be included in merged results.
221
+ # i.e. If you run cucumber features, then later rake test, if the stored cucumber resultset is
222
+ # more seconds ago than specified here, it won't be taken into account when merging (and is also
223
+ # purged from the resultset cache)
224
+ #
225
+ # Of course, this only applies when merging is active (e.g. SimpleCov.use_merging is not false!)
226
+ #
227
+ # Default is 600 seconds (10 minutes)
228
+ #
229
+ # Configure with SimpleCov.merge_timeout(3600) # 1hr
230
+ #
231
+ def merge_timeout(seconds = nil)
232
+ @merge_timeout = seconds if seconds.is_a?(Integer)
233
+ @merge_timeout ||= 600
234
+ end
235
+
236
+ #
237
+ # Defines the minimum overall coverage required for the testsuite to pass.
238
+ # SimpleCov will return non-zero if the current coverage is below this threshold.
239
+ #
240
+ # Default is 0% (disabled)
241
+ #
242
+ def minimum_coverage(coverage = nil)
243
+ minimum_possible_coverage_exceeded("minimum_coverage") if coverage && coverage > 100
244
+ @minimum_coverage ||= (coverage || 0).to_f.round(2)
245
+ end
246
+
247
+ #
248
+ # Defines the maximum coverage drop at once allowed for the testsuite to pass.
249
+ # SimpleCov will return non-zero if the coverage decreases by more than this threshold.
250
+ #
251
+ # Default is 100% (disabled)
252
+ #
253
+ def maximum_coverage_drop(coverage_drop = nil)
254
+ @maximum_coverage_drop ||= (coverage_drop || 100).to_f.round(2)
255
+ end
256
+
257
+ #
258
+ # Defines the minimum coverage per file required for the testsuite to pass.
259
+ # SimpleCov will return non-zero if the current coverage of the least covered file
260
+ # is below this threshold.
261
+ #
262
+ # Default is 0% (disabled)
263
+ #
264
+ def minimum_coverage_by_file(coverage = nil)
265
+ minimum_possible_coverage_exceeded("minimum_coverage_by_file") if coverage && coverage > 100
266
+ @minimum_coverage_by_file ||= (coverage || 0).to_f.round(2)
267
+ end
268
+
269
+ #
270
+ # Refuses any coverage drop. That is, coverage is only allowed to increase.
271
+ # SimpleCov will return non-zero if the coverage decreases.
272
+ #
273
+ def refuse_coverage_drop
274
+ maximum_coverage_drop 0
275
+ end
276
+
277
+ #
278
+ # Add a filter to the processing chain.
279
+ # There are four ways to define a filter:
280
+ #
281
+ # * as a String that will then be matched against all source files' file paths,
282
+ # SimpleCov.add_filter 'app/models' # will reject all your models
283
+ # * as a block which will be passed the source file in question and should either
284
+ # return a true or false value, depending on whether the file should be removed
285
+ # SimpleCov.add_filter do |src_file|
286
+ # File.basename(src_file.filename) == 'environment.rb'
287
+ # end # Will exclude environment.rb files from the results
288
+ # * as an array of strings that are matched against all sorce files' file
289
+ # paths and then ignored (basically string filter multiple times)
290
+ # SimpleCov.add_filter ['app/models', 'app/helpers'] # ignores both dirs
291
+ # * as an instance of a subclass of SimpleCov::Filter. See the documentation there
292
+ # on how to define your own filter classes
293
+ #
294
+ def add_filter(filter_argument = nil, &filter_proc)
295
+ filters << parse_filter(filter_argument, &filter_proc)
296
+ end
297
+
298
+ #
299
+ # Define a group for files. Works similar to add_filter, only that the first
300
+ # argument is the desired group name and files PASSING the filter end up in the group
301
+ # (while filters exclude when the filter is applicable).
302
+ #
303
+ def add_group(group_name, filter_argument = nil, &filter_proc)
304
+ groups[group_name] = parse_filter(filter_argument, &filter_proc)
305
+ end
306
+
307
+ SUPPORTED_COVERAGE_CRITERIA = %i[line branch].freeze
308
+ DEFAULT_COVERAGE_CRITERION = :line
309
+ #
310
+ # Define which coverage criterion should be evaluated.
311
+ #
312
+ # Possible coverage criteria:
313
+ # * :line - coverage based on lines aka has this line been executed?
314
+ # * :branch - coverage based on branches aka has this branch (think conditions) been executed?
315
+ #
316
+ # If not set the default is is `:line`
317
+ #
318
+ # @param [Symbol] criterion
319
+ #
320
+ def coverage_criterion(criterion = nil)
321
+ return @coverage_criterion ||= DEFAULT_COVERAGE_CRITERION unless criterion
322
+
323
+ raise_if_criterion_unsupported(criterion)
324
+
325
+ @coverage_criterion = criterion
326
+ end
327
+
328
+ def enable_coverage(criterion)
329
+ raise_if_criterion_unsupported(criterion)
330
+
331
+ coverage_criteria << criterion
332
+ end
333
+
334
+ def coverage_criteria
335
+ @coverage_criteria ||= Set[DEFAULT_COVERAGE_CRITERION]
336
+ end
337
+
338
+ def branch_coverage?
339
+ branch_coverage_supported? && coverage_criteria.member?(:branch)
340
+ end
341
+
342
+ def branch_coverage_supported?
343
+ require "coverage"
344
+ !Coverage.method(:start).arity.zero?
345
+ end
232
346
 
233
347
  private
234
348
 
235
- #
236
- # The actal filter processor. Not meant for direct use
237
- #
238
- def parse_filter(filter_argument=nil, &filter_proc)
239
- if filter_argument.kind_of?(SimpleCov::Filter)
240
- filter_argument
241
- elsif filter_argument.kind_of?(String)
242
- SimpleCov::StringFilter.new(filter_argument)
243
- elsif filter_proc
244
- SimpleCov::BlockFilter.new(filter_proc)
245
- else
246
- raise ArgumentError, "Please specify either a string or a block to filter with"
349
+ def raise_if_criterion_unsupported(criterion)
350
+ raise_criterion_unsupported(criterion) unless SUPPORTED_COVERAGE_CRITERIA.member?(criterion)
351
+ end
352
+
353
+ def raise_criterion_unsupported(criterion)
354
+ raise "Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"
355
+ end
356
+
357
+ def minimum_possible_coverage_exceeded(coverage_option)
358
+ warn "The coverage you set for #{coverage_option} is greater than 100%"
359
+ end
360
+
361
+ #
362
+ # The actual filter processor. Not meant for direct use
363
+ #
364
+ def parse_filter(filter_argument = nil, &filter_proc)
365
+ filter = filter_argument || filter_proc
366
+
367
+ if filter
368
+ SimpleCov::Filter.build_filter(filter)
369
+ else
370
+ raise ArgumentError, "Please specify either a filter or a block to filter with"
371
+ end
247
372
  end
248
373
  end
249
374
  end