simplecov 0.17.1 → 0.22.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|