simplecov 0.16.0 → 0.18.1
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 +5 -5
- data/CHANGELOG.md +110 -1
- data/CODE_OF_CONDUCT.md +76 -0
- data/LICENSE +20 -0
- data/README.md +281 -112
- data/doc/alternate-formatters.md +10 -0
- data/lib/simplecov.rb +248 -63
- data/lib/simplecov/combine.rb +30 -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/command_guesser.rb +6 -3
- data/lib/simplecov/configuration.rb +110 -9
- data/lib/simplecov/coverage_statistics.rb +56 -0
- data/lib/simplecov/defaults.rb +6 -2
- data/lib/simplecov/file_list.rb +66 -13
- data/lib/simplecov/filter.rb +2 -1
- data/lib/simplecov/formatter/multi_formatter.rb +2 -2
- data/lib/simplecov/formatter/simple_formatter.rb +4 -4
- data/lib/simplecov/last_run.rb +3 -1
- data/lib/simplecov/lines_classifier.rb +2 -2
- data/lib/simplecov/profiles.rb +9 -7
- data/lib/simplecov/profiles/hidden_filter.rb +5 -0
- data/lib/simplecov/profiles/rails.rb +1 -1
- data/lib/simplecov/result.rb +39 -6
- data/lib/simplecov/result_adapter.rb +30 -0
- data/lib/simplecov/result_merger.rb +18 -11
- data/lib/simplecov/simulate_coverage.rb +29 -0
- data/lib/simplecov/source_file.rb +227 -126
- data/lib/simplecov/source_file/branch.rb +84 -0
- data/lib/simplecov/source_file/line.rb +72 -0
- data/lib/simplecov/useless_results_remover.rb +16 -0
- data/lib/simplecov/version.rb +1 -1
- metadata +32 -53
- 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
@@ -6,12 +6,14 @@ require "pathname"
|
|
6
6
|
require "simplecov/profiles/root_filter"
|
7
7
|
require "simplecov/profiles/test_frameworks"
|
8
8
|
require "simplecov/profiles/bundler_filter"
|
9
|
+
require "simplecov/profiles/hidden_filter"
|
9
10
|
require "simplecov/profiles/rails"
|
10
11
|
|
11
12
|
# Default configuration
|
12
13
|
SimpleCov.configure do
|
13
14
|
formatter SimpleCov::Formatter::HTMLFormatter
|
14
15
|
load_profile "bundler_filter"
|
16
|
+
load_profile "hidden_filter"
|
15
17
|
# Exclude files outside of SimpleCov.root
|
16
18
|
load_profile "root_filter"
|
17
19
|
end
|
@@ -23,7 +25,9 @@ at_exit do
|
|
23
25
|
# If we are in a different process than called start, don't interfere.
|
24
26
|
next if SimpleCov.pid != Process.pid
|
25
27
|
|
26
|
-
SimpleCov
|
28
|
+
# If SimpleCov is no longer running then don't run exit tasks
|
29
|
+
next unless SimpleCov.running
|
30
|
+
|
27
31
|
SimpleCov.run_exit_tasks!
|
28
32
|
end
|
29
33
|
|
@@ -40,7 +44,7 @@ loop do
|
|
40
44
|
begin
|
41
45
|
load filename
|
42
46
|
rescue LoadError, StandardError
|
43
|
-
|
47
|
+
warn "Warning: Error occurred while trying to load #{filename}. " \
|
44
48
|
"Error message: #{$!.message}"
|
45
49
|
end
|
46
50
|
break
|
data/lib/simplecov/file_list.rb
CHANGED
@@ -1,30 +1,53 @@
|
|
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
|
+
|
7
30
|
# Returns the count of lines that have coverage
|
8
31
|
def covered_lines
|
9
|
-
|
10
|
-
map { |f| f.covered_lines.count }.inject(:+)
|
32
|
+
coverage_statistics[:line]&.covered
|
11
33
|
end
|
12
34
|
|
13
35
|
# Returns the count of lines that have been missed
|
14
36
|
def missed_lines
|
15
|
-
|
16
|
-
map { |f| f.missed_lines.count }.inject(:+)
|
37
|
+
coverage_statistics[:line]&.missed
|
17
38
|
end
|
18
39
|
|
19
40
|
# Returns the count of lines that are not relevant for coverage
|
20
41
|
def never_lines
|
21
42
|
return 0.0 if empty?
|
43
|
+
|
22
44
|
map { |f| f.never_lines.count }.inject(:+)
|
23
45
|
end
|
24
46
|
|
25
47
|
# Returns the count of skipped lines
|
26
48
|
def skipped_lines
|
27
49
|
return 0.0 if empty?
|
50
|
+
|
28
51
|
map { |f| f.skipped_lines.count }.inject(:+)
|
29
52
|
end
|
30
53
|
|
@@ -36,26 +59,56 @@ module SimpleCov
|
|
36
59
|
|
37
60
|
# Finds the least covered file and returns that file's name
|
38
61
|
def least_covered_file
|
39
|
-
|
62
|
+
min_by(&:covered_percent).filename
|
40
63
|
end
|
41
64
|
|
42
65
|
# Returns the overall amount of relevant lines of code across all files in this list
|
43
66
|
def lines_of_code
|
44
|
-
|
67
|
+
coverage_statistics[:line]&.total
|
45
68
|
end
|
46
69
|
|
47
70
|
# Computes the coverage based upon lines covered and lines missed
|
48
71
|
# @return [Float]
|
49
72
|
def covered_percent
|
50
|
-
|
51
|
-
Float(covered_lines * 100.0 / lines_of_code)
|
73
|
+
coverage_statistics[:line]&.percent
|
52
74
|
end
|
53
75
|
|
54
76
|
# Computes the strength (hits / line) based upon lines covered and lines missed
|
55
77
|
# @return [Float]
|
56
78
|
def covered_strength
|
57
|
-
|
58
|
-
|
79
|
+
coverage_statistics[:line]&.strength
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return total count of branches in all files
|
83
|
+
def total_branches
|
84
|
+
coverage_statistics[:branch]&.total
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return total count of covered branches
|
88
|
+
def covered_branches
|
89
|
+
coverage_statistics[:branch]&.covered
|
90
|
+
end
|
91
|
+
|
92
|
+
# Return total count of covered branches
|
93
|
+
def missed_branches
|
94
|
+
coverage_statistics[:branch]&.missed
|
95
|
+
end
|
96
|
+
|
97
|
+
def branch_covered_percent
|
98
|
+
coverage_statistics[:branch]&.percent
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def compute_coverage_statistics
|
104
|
+
total_coverage_statistics = @files.each_with_object(line: [], branch: []) do |file, together|
|
105
|
+
together[:line] << file.coverage_statistics[:line]
|
106
|
+
together[:branch] << file.coverage_statistics[:branch] if SimpleCov.branch_coverage?
|
107
|
+
end
|
108
|
+
|
109
|
+
coverage_statistics = {line: CoverageStatistics.from(total_coverage_statistics[:line])}
|
110
|
+
coverage_statistics[:branch] = CoverageStatistics.from(total_coverage_statistics[:branch]) if SimpleCov.branch_coverage?
|
111
|
+
coverage_statistics
|
59
112
|
end
|
60
113
|
end
|
61
114
|
end
|
data/lib/simplecov/filter.rb
CHANGED
@@ -18,7 +18,7 @@ module SimpleCov
|
|
18
18
|
@filter_argument = filter_argument
|
19
19
|
end
|
20
20
|
|
21
|
-
def matches?(
|
21
|
+
def matches?(_source_file)
|
22
22
|
raise "The base filter class is not intended for direct use"
|
23
23
|
end
|
24
24
|
|
@@ -29,6 +29,7 @@ module SimpleCov
|
|
29
29
|
|
30
30
|
def self.build_filter(filter_argument)
|
31
31
|
return filter_argument if filter_argument.is_a?(SimpleCov::Filter)
|
32
|
+
|
32
33
|
class_for_argument(filter_argument).new(filter_argument)
|
33
34
|
end
|
34
35
|
|
@@ -8,8 +8,8 @@ module SimpleCov
|
|
8
8
|
formatters.map do |formatter|
|
9
9
|
begin
|
10
10
|
formatter.new.format(result)
|
11
|
-
rescue => e
|
12
|
-
|
11
|
+
rescue StandardError => e
|
12
|
+
warn("Formatter #{formatter} failed with #{e.class}: #{e.message} (#{e.backtrace.first})")
|
13
13
|
nil
|
14
14
|
end
|
15
15
|
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/last_run.rb
CHANGED
@@ -8,8 +8,8 @@ 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
|
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
|
@@ -7,7 +7,7 @@ SimpleCov.profiles.define "rails" do
|
|
7
7
|
add_filter %r{^/db/}
|
8
8
|
|
9
9
|
add_group "Controllers", "app/controllers"
|
10
|
-
add_group "Channels", "app/channels"
|
10
|
+
add_group "Channels", "app/channels"
|
11
11
|
add_group "Models", "app/models"
|
12
12
|
add_group "Mailers", "app/mailers"
|
13
13
|
add_group "Helpers", "app/helpers"
|
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,16 @@ 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
|
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
28
|
def initialize(original_result)
|
29
|
-
|
30
|
-
@
|
31
|
-
|
29
|
+
result = adapt_result(original_result)
|
30
|
+
@original_result = result.freeze
|
31
|
+
@files = SimpleCov::FileList.new(result.map do |filename, coverage|
|
32
|
+
SimpleCov::SourceFile.new(filename, JSON.parse(JSON.dump(coverage))) if File.file?(filename)
|
32
33
|
end.compact.sort_by(&:filename))
|
33
34
|
filter!
|
34
35
|
end
|
@@ -61,13 +62,20 @@ module SimpleCov
|
|
61
62
|
|
62
63
|
# Returns a hash representation of this Result that can be used for marshalling it into JSON
|
63
64
|
def to_hash
|
64
|
-
{
|
65
|
+
{
|
66
|
+
command_name => {
|
67
|
+
"coverage" => coverage,
|
68
|
+
"timestamp" => created_at.to_i
|
69
|
+
}
|
70
|
+
}
|
65
71
|
end
|
66
72
|
|
67
73
|
# Loads a SimpleCov::Result#to_hash dump
|
68
74
|
def self.from_hash(hash)
|
69
75
|
command_name, data = hash.first
|
76
|
+
|
70
77
|
result = SimpleCov::Result.new(data["coverage"])
|
78
|
+
|
71
79
|
result.command_name = command_name
|
72
80
|
result.created_at = Time.at(data["timestamp"])
|
73
81
|
result
|
@@ -75,6 +83,31 @@ module SimpleCov
|
|
75
83
|
|
76
84
|
private
|
77
85
|
|
86
|
+
# We changed the format of the raw result data in simplecov, as people are likely
|
87
|
+
# to have "old" resultsets lying around (but not too old so that they're still
|
88
|
+
# considered we can adapt them).
|
89
|
+
# See https://github.com/colszowka/simplecov/pull/824#issuecomment-576049747
|
90
|
+
def adapt_result(result)
|
91
|
+
if pre_simplecov_0_18_result?(result)
|
92
|
+
adapt_pre_simplecov_0_18_result(result)
|
93
|
+
else
|
94
|
+
result
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# pre 0.18 coverage data pointed from file directly to an array of line coverage
|
99
|
+
def pre_simplecov_0_18_result?(result)
|
100
|
+
_key, data = result.first
|
101
|
+
|
102
|
+
data.is_a?(Array)
|
103
|
+
end
|
104
|
+
|
105
|
+
def adapt_pre_simplecov_0_18_result(result)
|
106
|
+
result.map do |file_path, line_coverage_data|
|
107
|
+
[file_path, {"lines" => line_coverage_data}]
|
108
|
+
end.to_h
|
109
|
+
end
|
110
|
+
|
78
111
|
def coverage
|
79
112
|
keys = original_result.keys & filenames
|
80
113
|
Hash[keys.zip(original_result.values_at(*keys))]
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleCov
|
4
|
+
#
|
5
|
+
# Responsible for adapting the format of the coverage result whether it's default or with statistics
|
6
|
+
#
|
7
|
+
class ResultAdapter
|
8
|
+
attr_reader :result
|
9
|
+
|
10
|
+
def initialize(result)
|
11
|
+
@result = result
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.call(*args)
|
15
|
+
new(*args).adapt
|
16
|
+
end
|
17
|
+
|
18
|
+
def adapt
|
19
|
+
return unless result
|
20
|
+
|
21
|
+
result.each_with_object({}) do |(file_name, cover_statistic), adapted_result|
|
22
|
+
if cover_statistic.is_a?(Array)
|
23
|
+
adapted_result.merge!(file_name => {"lines" => cover_statistic})
|
24
|
+
else
|
25
|
+
adapted_result.merge!(file_name => cover_statistic)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
require "json"
|
4
4
|
|
5
|
-
#
|
6
|
-
# Singleton that is responsible for caching, loading and merging
|
7
|
-
# SimpleCov::Results into a single result for coverage analysis based
|
8
|
-
# upon multiple test suites.
|
9
|
-
#
|
10
5
|
module SimpleCov
|
6
|
+
#
|
7
|
+
# Singleton that is responsible for caching, loading and merging
|
8
|
+
# SimpleCov::Results into a single result for coverage analysis based
|
9
|
+
# upon multiple test suites.
|
10
|
+
#
|
11
11
|
module ResultMerger
|
12
12
|
class << self
|
13
13
|
# The path to the .resultset.json cache file
|
@@ -27,7 +27,7 @@ module SimpleCov
|
|
27
27
|
if data
|
28
28
|
begin
|
29
29
|
JSON.parse(data) || {}
|
30
|
-
rescue
|
30
|
+
rescue StandardError
|
31
31
|
{}
|
32
32
|
end
|
33
33
|
else
|
@@ -40,8 +40,10 @@ module SimpleCov
|
|
40
40
|
def stored_data
|
41
41
|
synchronize_resultset do
|
42
42
|
return unless File.exist?(resultset_path)
|
43
|
+
|
43
44
|
data = File.read(resultset_path)
|
44
45
|
return if data.nil? || data.length < 2
|
46
|
+
|
45
47
|
data
|
46
48
|
end
|
47
49
|
end
|
@@ -55,19 +57,24 @@ module SimpleCov
|
|
55
57
|
resultset.each do |command_name, data|
|
56
58
|
result = SimpleCov::Result.from_hash(command_name => data)
|
57
59
|
# Only add result if the timeout is above the configured threshold
|
58
|
-
if (Time.now - result.created_at) < SimpleCov.merge_timeout
|
59
|
-
results << result
|
60
|
-
end
|
60
|
+
results << result if (Time.now - result.created_at) < SimpleCov.merge_timeout
|
61
61
|
end
|
62
62
|
results
|
63
63
|
end
|
64
64
|
|
65
|
+
def merge_and_store(*results)
|
66
|
+
result = merge_results(*results)
|
67
|
+
store_result(result) if result
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
65
71
|
# Merge two or more SimpleCov::Results into a new one with merged
|
66
72
|
# coverage data and the command_name for the result consisting of a join
|
67
73
|
# on all source result's names
|
68
74
|
def merge_results(*results)
|
69
|
-
|
70
|
-
|
75
|
+
parsed_results = JSON.parse(JSON.dump(results.map(&:original_result)))
|
76
|
+
combined_result = SimpleCov::Combine::ResultsCombiner.combine(*parsed_results)
|
77
|
+
result = SimpleCov::Result.new(combined_result)
|
71
78
|
# Specify the command name
|
72
79
|
result.command_name = results.map(&:command_name).sort.join(", ")
|
73
80
|
result
|