simplecov 0.17.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +111 -427
- data/README.md +388 -94
- data/doc/alternate-formatters.md +16 -1
- data/doc/commercial-services.md +5 -0
- data/lib/minitest/simplecov_plugin.rb +15 -0
- data/lib/simplecov/combine/branches_combiner.rb +32 -0
- data/lib/simplecov/combine/files_combiner.rb +24 -0
- data/lib/simplecov/combine/lines_combiner.rb +43 -0
- data/lib/simplecov/combine/results_combiner.rb +60 -0
- data/lib/simplecov/combine.rb +30 -0
- data/lib/simplecov/command_guesser.rb +6 -3
- data/lib/simplecov/configuration.rb +210 -15
- data/lib/simplecov/coverage_statistics.rb +56 -0
- data/lib/simplecov/default_formatter.rb +20 -0
- data/lib/simplecov/defaults.rb +14 -13
- data/lib/simplecov/exit_codes/exit_code_handling.rb +29 -0
- data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +83 -0
- data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +54 -0
- data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +53 -0
- data/lib/simplecov/exit_codes.rb +5 -0
- data/lib/simplecov/file_list.rb +72 -13
- data/lib/simplecov/filter.rb +9 -6
- data/lib/simplecov/formatter/multi_formatter.rb +5 -7
- data/lib/simplecov/formatter/simple_formatter.rb +4 -4
- data/lib/simplecov/formatter.rb +2 -2
- data/lib/simplecov/last_run.rb +3 -1
- data/lib/simplecov/lines_classifier.rb +5 -5
- data/lib/simplecov/no_defaults.rb +1 -1
- data/lib/simplecov/process.rb +19 -0
- data/lib/simplecov/profiles.rb +9 -7
- data/lib/simplecov/result.rb +18 -12
- data/lib/simplecov/result_adapter.rb +30 -0
- data/lib/simplecov/result_merger.rb +130 -59
- data/lib/simplecov/simulate_coverage.rb +29 -0
- data/lib/simplecov/source_file/branch.rb +84 -0
- data/lib/simplecov/source_file/line.rb +72 -0
- data/lib/simplecov/source_file.rb +279 -127
- data/lib/simplecov/useless_results_remover.rb +18 -0
- data/lib/simplecov/version.rb +1 -1
- data/lib/simplecov.rb +296 -128
- metadata +47 -161
- data/CONTRIBUTING.md +0 -51
- data/ISSUE_TEMPLATE.md +0 -23
- data/lib/simplecov/jruby_fix.rb +0 -44
- data/lib/simplecov/railtie.rb +0 -9
- data/lib/simplecov/railties/tasks.rake +0 -13
- data/lib/simplecov/raw_coverage.rb +0 -41
data/lib/simplecov/defaults.rb
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Load default formatter gem
|
4
|
-
require "simplecov-html"
|
5
4
|
require "pathname"
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
require_relative "default_formatter"
|
6
|
+
require_relative "profiles/root_filter"
|
7
|
+
require_relative "profiles/test_frameworks"
|
8
|
+
require_relative "profiles/bundler_filter"
|
9
|
+
require_relative "profiles/hidden_filter"
|
10
|
+
require_relative "profiles/rails"
|
11
11
|
|
12
12
|
# Default configuration
|
13
13
|
SimpleCov.configure do
|
14
|
-
formatter SimpleCov::Formatter::
|
14
|
+
formatter SimpleCov::Formatter::MultiFormatter.new(
|
15
|
+
SimpleCov::Formatter.from_env(ENV)
|
16
|
+
)
|
17
|
+
|
15
18
|
load_profile "bundler_filter"
|
16
19
|
load_profile "hidden_filter"
|
17
20
|
# Exclude files outside of SimpleCov.root
|
@@ -22,15 +25,13 @@ end
|
|
22
25
|
SimpleCov::CommandGuesser.original_run_command = "#{$PROGRAM_NAME} #{ARGV.join(' ')}"
|
23
26
|
|
24
27
|
at_exit do
|
25
|
-
|
26
|
-
next if SimpleCov.pid != Process.pid
|
28
|
+
next if SimpleCov.external_at_exit?
|
27
29
|
|
28
|
-
SimpleCov.
|
29
|
-
SimpleCov.run_exit_tasks!
|
30
|
+
SimpleCov.at_exit_behavior
|
30
31
|
end
|
31
32
|
|
32
33
|
# Autoload config from ~/.simplecov if present
|
33
|
-
|
34
|
+
require_relative "load_global_config"
|
34
35
|
|
35
36
|
# Autoload config from .simplecov if present
|
36
37
|
# Recurse upwards until we find .simplecov or reach the root directory
|
@@ -42,7 +43,7 @@ loop do
|
|
42
43
|
begin
|
43
44
|
load filename
|
44
45
|
rescue LoadError, StandardError
|
45
|
-
|
46
|
+
warn "Warning: Error occurred while trying to load #{filename}. " \
|
46
47
|
"Error message: #{$!.message}"
|
47
48
|
end
|
48
49
|
break
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleCov
|
4
|
+
module ExitCodes
|
5
|
+
module ExitCodeHandling
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def call(result, coverage_limits:)
|
9
|
+
checks = coverage_checks(result, coverage_limits)
|
10
|
+
|
11
|
+
failing_check = checks.find(&:failing?)
|
12
|
+
if failing_check
|
13
|
+
failing_check.report
|
14
|
+
failing_check.exit_code
|
15
|
+
else
|
16
|
+
SimpleCov::ExitCodes::SUCCESS
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def coverage_checks(result, coverage_limits)
|
21
|
+
[
|
22
|
+
MinimumOverallCoverageCheck.new(result, coverage_limits.minimum_coverage),
|
23
|
+
MinimumCoverageByFileCheck.new(result, coverage_limits.minimum_coverage_by_file),
|
24
|
+
MaximumCoverageDropCheck.new(result, coverage_limits.maximum_coverage_drop)
|
25
|
+
]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleCov
|
4
|
+
module ExitCodes
|
5
|
+
class MaximumCoverageDropCheck
|
6
|
+
def initialize(result, maximum_coverage_drop)
|
7
|
+
@result = result
|
8
|
+
@maximum_coverage_drop = maximum_coverage_drop
|
9
|
+
end
|
10
|
+
|
11
|
+
def failing?
|
12
|
+
return false unless maximum_coverage_drop && last_run
|
13
|
+
|
14
|
+
coverage_drop_violations.any?
|
15
|
+
end
|
16
|
+
|
17
|
+
def report
|
18
|
+
coverage_drop_violations.each do |violation|
|
19
|
+
$stderr.printf(
|
20
|
+
"%<criterion>s coverage has dropped by %<drop_percent>.2f%% since the last time (maximum allowed: %<max_drop>.2f%%).\n",
|
21
|
+
criterion: violation[:criterion].capitalize,
|
22
|
+
drop_percent: SimpleCov.round_coverage(violation[:drop_percent]),
|
23
|
+
max_drop: violation[:max_drop]
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def exit_code
|
29
|
+
SimpleCov::ExitCodes::MAXIMUM_COVERAGE_DROP
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :result, :maximum_coverage_drop
|
35
|
+
|
36
|
+
def last_run
|
37
|
+
return @last_run if defined?(@last_run)
|
38
|
+
|
39
|
+
@last_run = SimpleCov::LastRun.read
|
40
|
+
end
|
41
|
+
|
42
|
+
def coverage_drop_violations
|
43
|
+
@coverage_drop_violations ||=
|
44
|
+
compute_coverage_drop_data.select do |achieved|
|
45
|
+
achieved.fetch(:max_drop) < achieved.fetch(:drop_percent)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def compute_coverage_drop_data
|
50
|
+
maximum_coverage_drop.map do |criterion, percent|
|
51
|
+
{
|
52
|
+
criterion: criterion,
|
53
|
+
max_drop: percent,
|
54
|
+
drop_percent: drop_percent(criterion)
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# if anyone says "max_coverage_drop 0.000000000000000001" I appologize. Please don't.
|
60
|
+
MAX_DROP_ACCURACY = 10
|
61
|
+
def drop_percent(criterion)
|
62
|
+
drop = last_coverage(criterion) -
|
63
|
+
SimpleCov.round_coverage(
|
64
|
+
result.coverage_statistics.fetch(criterion).percent
|
65
|
+
)
|
66
|
+
|
67
|
+
# floats, I tell ya.
|
68
|
+
# irb(main):001:0* 80.01 - 80.0
|
69
|
+
# => 0.010000000000005116
|
70
|
+
drop.floor(MAX_DROP_ACCURACY)
|
71
|
+
end
|
72
|
+
|
73
|
+
def last_coverage(criterion)
|
74
|
+
last_coverage_percent = last_run[:result][criterion]
|
75
|
+
|
76
|
+
# fallback for old file format
|
77
|
+
last_coverage_percent = last_run[:result][:covered_percent] if !last_coverage_percent && criterion == :line
|
78
|
+
|
79
|
+
last_coverage_percent || 0
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleCov
|
4
|
+
module ExitCodes
|
5
|
+
class MinimumCoverageByFileCheck
|
6
|
+
def initialize(result, minimum_coverage_by_file)
|
7
|
+
@result = result
|
8
|
+
@minimum_coverage_by_file = minimum_coverage_by_file
|
9
|
+
end
|
10
|
+
|
11
|
+
def failing?
|
12
|
+
minimum_violations.any?
|
13
|
+
end
|
14
|
+
|
15
|
+
def report
|
16
|
+
minimum_violations.each do |violation|
|
17
|
+
$stderr.printf(
|
18
|
+
"%<criterion>s coverage by file (%<covered>.2f%%) is below the expected minimum coverage (%<minimum_coverage>.2f%%).\n",
|
19
|
+
covered: SimpleCov.round_coverage(violation.fetch(:actual)),
|
20
|
+
minimum_coverage: violation.fetch(:minimum_expected),
|
21
|
+
criterion: violation.fetch(:criterion).capitalize
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def exit_code
|
27
|
+
SimpleCov::ExitCodes::MINIMUM_COVERAGE
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :result, :minimum_coverage_by_file
|
33
|
+
|
34
|
+
def minimum_violations
|
35
|
+
@minimum_violations ||=
|
36
|
+
compute_minimum_coverage_data.select do |achieved|
|
37
|
+
achieved.fetch(:actual) < achieved.fetch(:minimum_expected)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def compute_minimum_coverage_data
|
42
|
+
minimum_coverage_by_file.flat_map do |criterion, expected_percent|
|
43
|
+
result.coverage_statistics_by_file.fetch(criterion).map do |actual_coverage|
|
44
|
+
{
|
45
|
+
criterion: criterion,
|
46
|
+
minimum_expected: expected_percent,
|
47
|
+
actual: SimpleCov.round_coverage(actual_coverage.percent)
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleCov
|
4
|
+
module ExitCodes
|
5
|
+
class MinimumOverallCoverageCheck
|
6
|
+
def initialize(result, minimum_coverage)
|
7
|
+
@result = result
|
8
|
+
@minimum_coverage = minimum_coverage
|
9
|
+
end
|
10
|
+
|
11
|
+
def failing?
|
12
|
+
minimum_violations.any?
|
13
|
+
end
|
14
|
+
|
15
|
+
def report
|
16
|
+
minimum_violations.each do |violation|
|
17
|
+
$stderr.printf(
|
18
|
+
"%<criterion>s coverage (%<covered>.2f%%) is below the expected minimum coverage (%<minimum_coverage>.2f%%).\n",
|
19
|
+
covered: SimpleCov.round_coverage(violation.fetch(:actual)),
|
20
|
+
minimum_coverage: violation.fetch(:minimum_expected),
|
21
|
+
criterion: violation.fetch(:criterion).capitalize
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def exit_code
|
27
|
+
SimpleCov::ExitCodes::MINIMUM_COVERAGE
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :result, :minimum_coverage
|
33
|
+
|
34
|
+
def minimum_violations
|
35
|
+
@minimum_violations ||= calculate_minimum_violations
|
36
|
+
end
|
37
|
+
|
38
|
+
def calculate_minimum_violations
|
39
|
+
coverage_achieved = minimum_coverage.map do |criterion, percent|
|
40
|
+
{
|
41
|
+
criterion: criterion,
|
42
|
+
minimum_expected: percent,
|
43
|
+
actual: result.coverage_statistics.fetch(criterion).percent
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
coverage_achieved.select do |achieved|
|
48
|
+
achieved.fetch(:actual) < achieved.fetch(:minimum_expected)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/simplecov/exit_codes.rb
CHANGED
@@ -8,3 +8,8 @@ module SimpleCov
|
|
8
8
|
MAXIMUM_COVERAGE_DROP = 3
|
9
9
|
end
|
10
10
|
end
|
11
|
+
|
12
|
+
require_relative "exit_codes/exit_code_handling"
|
13
|
+
require_relative "exit_codes/maximum_coverage_drop_check"
|
14
|
+
require_relative "exit_codes/minimum_coverage_by_file_check"
|
15
|
+
require_relative "exit_codes/minimum_overall_coverage_check"
|
data/lib/simplecov/file_list.rb
CHANGED
@@ -1,30 +1,57 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# An array of SimpleCov SourceFile instances with additional collection helper
|
4
|
-
# methods for calculating coverage across them etc.
|
5
3
|
module SimpleCov
|
6
|
-
|
4
|
+
# An array of SimpleCov SourceFile instances with additional collection helper
|
5
|
+
# methods for calculating coverage across them etc.
|
6
|
+
class FileList
|
7
|
+
include Enumerable
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :@files,
|
11
|
+
# For Enumerable
|
12
|
+
:each,
|
13
|
+
# also delegating methods implemented in Enumerable as they have
|
14
|
+
# custom Array implementations which are presumably better/more
|
15
|
+
# resource efficient
|
16
|
+
:size, :map, :count,
|
17
|
+
# surprisingly not in Enumerable
|
18
|
+
:empty?, :length,
|
19
|
+
# still act like we're kinda an array
|
20
|
+
:to_a, :to_ary
|
21
|
+
|
22
|
+
def initialize(files)
|
23
|
+
@files = files
|
24
|
+
end
|
25
|
+
|
26
|
+
def coverage_statistics
|
27
|
+
@coverage_statistics ||= compute_coverage_statistics
|
28
|
+
end
|
29
|
+
|
30
|
+
def coverage_statistics_by_file
|
31
|
+
@coverage_statistics_by_file ||= compute_coverage_statistics_by_file
|
32
|
+
end
|
33
|
+
|
7
34
|
# Returns the count of lines that have coverage
|
8
35
|
def covered_lines
|
9
|
-
|
10
|
-
map { |f| f.covered_lines.count }.inject(:+)
|
36
|
+
coverage_statistics[:line]&.covered
|
11
37
|
end
|
12
38
|
|
13
39
|
# Returns the count of lines that have been missed
|
14
40
|
def missed_lines
|
15
|
-
|
16
|
-
map { |f| f.missed_lines.count }.inject(:+)
|
41
|
+
coverage_statistics[:line]&.missed
|
17
42
|
end
|
18
43
|
|
19
44
|
# Returns the count of lines that are not relevant for coverage
|
20
45
|
def never_lines
|
21
46
|
return 0.0 if empty?
|
47
|
+
|
22
48
|
map { |f| f.never_lines.count }.inject(:+)
|
23
49
|
end
|
24
50
|
|
25
51
|
# Returns the count of skipped lines
|
26
52
|
def skipped_lines
|
27
53
|
return 0.0 if empty?
|
54
|
+
|
28
55
|
map { |f| f.skipped_lines.count }.inject(:+)
|
29
56
|
end
|
30
57
|
|
@@ -36,26 +63,58 @@ module SimpleCov
|
|
36
63
|
|
37
64
|
# Finds the least covered file and returns that file's name
|
38
65
|
def least_covered_file
|
39
|
-
|
66
|
+
min_by(&:covered_percent).filename
|
40
67
|
end
|
41
68
|
|
42
69
|
# Returns the overall amount of relevant lines of code across all files in this list
|
43
70
|
def lines_of_code
|
44
|
-
|
71
|
+
coverage_statistics[:line]&.total
|
45
72
|
end
|
46
73
|
|
47
74
|
# Computes the coverage based upon lines covered and lines missed
|
48
75
|
# @return [Float]
|
49
76
|
def covered_percent
|
50
|
-
|
51
|
-
Float(covered_lines * 100.0 / lines_of_code)
|
77
|
+
coverage_statistics[:line]&.percent
|
52
78
|
end
|
53
79
|
|
54
80
|
# Computes the strength (hits / line) based upon lines covered and lines missed
|
55
81
|
# @return [Float]
|
56
82
|
def covered_strength
|
57
|
-
|
58
|
-
|
83
|
+
coverage_statistics[:line]&.strength
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return total count of branches in all files
|
87
|
+
def total_branches
|
88
|
+
coverage_statistics[:branch]&.total
|
89
|
+
end
|
90
|
+
|
91
|
+
# Return total count of covered branches
|
92
|
+
def covered_branches
|
93
|
+
coverage_statistics[:branch]&.covered
|
94
|
+
end
|
95
|
+
|
96
|
+
# Return total count of covered branches
|
97
|
+
def missed_branches
|
98
|
+
coverage_statistics[:branch]&.missed
|
99
|
+
end
|
100
|
+
|
101
|
+
def branch_covered_percent
|
102
|
+
coverage_statistics[:branch]&.percent
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def compute_coverage_statistics_by_file
|
108
|
+
@files.each_with_object(line: [], branch: []) do |file, together|
|
109
|
+
together[:line] << file.coverage_statistics.fetch(:line)
|
110
|
+
together[:branch] << file.coverage_statistics.fetch(:branch) if SimpleCov.branch_coverage?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def compute_coverage_statistics
|
115
|
+
coverage_statistics = {line: CoverageStatistics.from(coverage_statistics_by_file[:line])}
|
116
|
+
coverage_statistics[:branch] = CoverageStatistics.from(coverage_statistics_by_file[:branch]) if SimpleCov.branch_coverage?
|
117
|
+
coverage_statistics
|
59
118
|
end
|
60
119
|
end
|
61
120
|
end
|
data/lib/simplecov/filter.rb
CHANGED
@@ -14,11 +14,12 @@ module SimpleCov
|
|
14
14
|
#
|
15
15
|
class Filter
|
16
16
|
attr_reader :filter_argument
|
17
|
+
|
17
18
|
def initialize(filter_argument)
|
18
19
|
@filter_argument = filter_argument
|
19
20
|
end
|
20
21
|
|
21
|
-
def matches?(
|
22
|
+
def matches?(_source_file)
|
22
23
|
raise "The base filter class is not intended for direct use"
|
23
24
|
end
|
24
25
|
|
@@ -29,17 +30,19 @@ module SimpleCov
|
|
29
30
|
|
30
31
|
def self.build_filter(filter_argument)
|
31
32
|
return filter_argument if filter_argument.is_a?(SimpleCov::Filter)
|
33
|
+
|
32
34
|
class_for_argument(filter_argument).new(filter_argument)
|
33
35
|
end
|
34
36
|
|
35
37
|
def self.class_for_argument(filter_argument)
|
36
|
-
|
38
|
+
case filter_argument
|
39
|
+
when String
|
37
40
|
SimpleCov::StringFilter
|
38
|
-
|
41
|
+
when Regexp
|
39
42
|
SimpleCov::RegexFilter
|
40
|
-
|
43
|
+
when Array
|
41
44
|
SimpleCov::ArrayFilter
|
42
|
-
|
45
|
+
when Proc
|
43
46
|
SimpleCov::BlockFilter
|
44
47
|
else
|
45
48
|
raise ArgumentError, "You have provided an unrecognized filter type"
|
@@ -49,7 +52,7 @@ module SimpleCov
|
|
49
52
|
|
50
53
|
class StringFilter < SimpleCov::Filter
|
51
54
|
# Returns true when the given source file's filename matches the
|
52
|
-
# string configured when initializing this Filter with StringFilter.new('somestring)
|
55
|
+
# string configured when initializing this Filter with StringFilter.new('somestring')
|
53
56
|
def matches?(source_file)
|
54
57
|
source_file.project_filename.include?(filter_argument)
|
55
58
|
end
|
@@ -6,12 +6,10 @@ module SimpleCov
|
|
6
6
|
module InstanceMethods
|
7
7
|
def format(result)
|
8
8
|
formatters.map do |formatter|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
nil
|
14
|
-
end
|
9
|
+
formatter.new.format(result)
|
10
|
+
rescue StandardError => e
|
11
|
+
warn("Formatter #{formatter} failed with #{e.class}: #{e.message} (#{e.backtrace.first})")
|
12
|
+
nil
|
15
13
|
end
|
16
14
|
end
|
17
15
|
end
|
@@ -27,7 +25,7 @@ module SimpleCov
|
|
27
25
|
|
28
26
|
def self.[](*args)
|
29
27
|
warn "#{Kernel.caller.first}: [DEPRECATION] ::[] is deprecated. Use ::new instead."
|
30
|
-
new(Array(
|
28
|
+
new(Array(args))
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
|
-
# A ridiculously simple formatter for SimpleCov results.
|
5
|
-
#
|
6
3
|
module SimpleCov
|
7
4
|
module Formatter
|
5
|
+
#
|
6
|
+
# A ridiculously simple formatter for SimpleCov results.
|
7
|
+
#
|
8
8
|
class SimpleFormatter
|
9
9
|
# Takes a SimpleCov::Result and generates a string out of it
|
10
10
|
def format(result)
|
11
|
-
output = ""
|
11
|
+
output = +""
|
12
12
|
result.groups.each do |name, files|
|
13
13
|
output << "Group: #{name}\n"
|
14
14
|
output << "=" * 40
|
data/lib/simplecov/formatter.rb
CHANGED
data/lib/simplecov/last_run.rb
CHANGED
@@ -8,23 +8,23 @@ module SimpleCov
|
|
8
8
|
RELEVANT = 0
|
9
9
|
NOT_RELEVANT = nil
|
10
10
|
|
11
|
-
WHITESPACE_LINE = /^\s
|
12
|
-
COMMENT_LINE = /^\s
|
11
|
+
WHITESPACE_LINE = /^\s*$/.freeze
|
12
|
+
COMMENT_LINE = /^\s*#/.freeze
|
13
13
|
WHITESPACE_OR_COMMENT_LINE = Regexp.union(WHITESPACE_LINE, COMMENT_LINE)
|
14
14
|
|
15
15
|
def self.no_cov_line
|
16
|
-
/^(\s*)#(\s*)(
|
16
|
+
/^(\s*)#(\s*)(:#{SimpleCov.nocov_token}:)/o
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.no_cov_line?(line)
|
20
|
-
line
|
20
|
+
no_cov_line.match?(line)
|
21
21
|
rescue ArgumentError
|
22
22
|
# E.g., line contains an invalid byte sequence in UTF-8
|
23
23
|
false
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.whitespace_line?(line)
|
27
|
-
line
|
27
|
+
WHITESPACE_OR_COMMENT_LINE.match?(line)
|
28
28
|
rescue ArgumentError
|
29
29
|
# E.g., line contains an invalid byte sequence in UTF-8
|
30
30
|
false
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Process
|
4
|
+
class << self
|
5
|
+
def fork_with_simplecov(&block)
|
6
|
+
if defined?(SimpleCov) && SimpleCov.running
|
7
|
+
fork_without_simplecov do
|
8
|
+
SimpleCov.at_fork.call(Process.pid)
|
9
|
+
block.call if block_given?
|
10
|
+
end
|
11
|
+
else
|
12
|
+
fork_without_simplecov(&block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
alias fork_without_simplecov fork
|
17
|
+
alias fork fork_with_simplecov
|
18
|
+
end
|
19
|
+
end
|
data/lib/simplecov/profiles.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
|
-
# Profiles are SimpleCov configuration procs that can be easily
|
5
|
-
# loaded using SimpleCov.start :rails and defined using
|
6
|
-
# SimpleCov.profiles.define :foo do
|
7
|
-
# # SimpleCov configuration here, same as in SimpleCov.configure
|
8
|
-
# end
|
9
|
-
#
|
10
3
|
module SimpleCov
|
4
|
+
#
|
5
|
+
# Profiles are SimpleCov configuration procs that can be easily
|
6
|
+
# loaded using SimpleCov.start :rails and defined using
|
7
|
+
# SimpleCov.profiles.define :foo do
|
8
|
+
# # SimpleCov configuration here, same as in SimpleCov.configure
|
9
|
+
# end
|
10
|
+
#
|
11
11
|
class Profiles < Hash
|
12
12
|
#
|
13
13
|
# Define a SimpleCov profile:
|
@@ -18,6 +18,7 @@ module SimpleCov
|
|
18
18
|
def define(name, &blk)
|
19
19
|
name = name.to_sym
|
20
20
|
raise "SimpleCov Profile '#{name}' is already defined" unless self[name].nil?
|
21
|
+
|
21
22
|
self[name] = blk
|
22
23
|
end
|
23
24
|
|
@@ -27,6 +28,7 @@ module SimpleCov
|
|
27
28
|
def load(name)
|
28
29
|
name = name.to_sym
|
29
30
|
raise "Could not find SimpleCov Profile called '#{name}'" unless key?(name)
|
31
|
+
|
30
32
|
SimpleCov.configure(&self[name])
|
31
33
|
end
|
32
34
|
end
|
data/lib/simplecov/result.rb
CHANGED
@@ -5,7 +5,7 @@ require "forwardable"
|
|
5
5
|
|
6
6
|
module SimpleCov
|
7
7
|
#
|
8
|
-
# A simplecov code coverage result, initialized from the Hash Ruby
|
8
|
+
# A simplecov code coverage result, initialized from the Hash Ruby's built-in coverage
|
9
9
|
# library generates (Coverage.result).
|
10
10
|
#
|
11
11
|
class Result
|
@@ -20,15 +20,18 @@ module SimpleCov
|
|
20
20
|
# Explicitly set the command name that was used for this coverage result. Defaults to SimpleCov.command_name
|
21
21
|
attr_writer :command_name
|
22
22
|
|
23
|
-
def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength, :covered_lines, :missed_lines
|
23
|
+
def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength, :covered_lines, :missed_lines, :total_branches, :covered_branches, :missed_branches, :coverage_statistics, :coverage_statistics_by_file
|
24
24
|
def_delegator :files, :lines_of_code, :total_lines
|
25
25
|
|
26
26
|
# Initialize a new SimpleCov::Result from given Coverage.result (a Hash of filenames each containing an array of
|
27
27
|
# coverage data)
|
28
|
-
def initialize(original_result)
|
29
|
-
|
30
|
-
@
|
31
|
-
|
28
|
+
def initialize(original_result, command_name: nil, created_at: nil)
|
29
|
+
result = original_result
|
30
|
+
@original_result = result.freeze
|
31
|
+
@command_name = command_name
|
32
|
+
@created_at = created_at
|
33
|
+
@files = SimpleCov::FileList.new(result.map do |filename, coverage|
|
34
|
+
SimpleCov::SourceFile.new(filename, JSON.parse(JSON.dump(coverage))) if File.file?(filename)
|
32
35
|
end.compact.sort_by(&:filename))
|
33
36
|
filter!
|
34
37
|
end
|
@@ -61,16 +64,19 @@ module SimpleCov
|
|
61
64
|
|
62
65
|
# Returns a hash representation of this Result that can be used for marshalling it into JSON
|
63
66
|
def to_hash
|
64
|
-
{
|
67
|
+
{
|
68
|
+
command_name => {
|
69
|
+
"coverage" => coverage,
|
70
|
+
"timestamp" => created_at.to_i
|
71
|
+
}
|
72
|
+
}
|
65
73
|
end
|
66
74
|
|
67
75
|
# Loads a SimpleCov::Result#to_hash dump
|
68
76
|
def self.from_hash(hash)
|
69
|
-
command_name, data
|
70
|
-
|
71
|
-
|
72
|
-
result.created_at = Time.at(data["timestamp"])
|
73
|
-
result
|
77
|
+
hash.map do |command_name, data|
|
78
|
+
new(data.fetch("coverage"), command_name: command_name, created_at: Time.at(data["timestamp"]))
|
79
|
+
end
|
74
80
|
end
|
75
81
|
|
76
82
|
private
|