simplecov 0.18.0.beta1 → 0.18.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.
@@ -62,11 +62,17 @@ module SimpleCov
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
- parsed_results = JSON.parse(JSON.dump(results.map(&:original_result)), :symbolize_names => true)
75
+ parsed_results = JSON.parse(JSON.dump(results.map(&:original_result)))
70
76
  combined_result = SimpleCov::Combine::ResultsCombiner.combine(*parsed_results)
71
77
  result = SimpleCov::Result.new(combined_result)
72
78
  # Specify the command name
@@ -19,10 +19,10 @@ module SimpleCov
19
19
  lines = File.foreach(absolute_path)
20
20
 
21
21
  {
22
- :lines => LinesClassifier.new.classify(lines),
22
+ "lines" => LinesClassifier.new.classify(lines),
23
23
  # we don't want to parse branches ourselves...
24
24
  # requiring files can have side effects and we don't want to trigger that
25
- :branches => {}
25
+ "branches" => {}
26
26
  }
27
27
  end
28
28
  end
@@ -6,15 +6,15 @@ module SimpleCov
6
6
  # Representing single branch that has been detected in coverage report.
7
7
  # Give us support methods that handle needed calculations.
8
8
  class Branch
9
- attr_reader :start_line, :end_line, :coverage
9
+ attr_reader :start_line, :end_line, :coverage, :type
10
10
 
11
11
  # rubocop:disable Metrics/ParameterLists
12
- def initialize(start_line:, end_line:, coverage:, inline:, positive:)
12
+ def initialize(start_line:, end_line:, coverage:, inline:, type:)
13
13
  @start_line = start_line
14
14
  @end_line = end_line
15
15
  @coverage = coverage
16
16
  @inline = inline
17
- @positive = positive
17
+ @type = type
18
18
  @skipped = false
19
19
  end
20
20
  # rubocop:enable Metrics/ParameterLists
@@ -23,19 +23,6 @@ module SimpleCov
23
23
  @inline
24
24
  end
25
25
 
26
- def positive?
27
- @positive
28
- end
29
-
30
- #
31
- # Branch is negative
32
- #
33
- # @return [Boolean]
34
- #
35
- def negative?
36
- !positive?
37
- end
38
-
39
26
  #
40
27
  # Return true if there is relevant count defined > 0
41
28
  #
@@ -54,15 +41,6 @@ module SimpleCov
54
41
  !skipped? && coverage.zero?
55
42
  end
56
43
 
57
- #
58
- # Return the sign depends on branch is positive or negative
59
- #
60
- # @return [String]
61
- #
62
- def badge
63
- positive? ? "+" : "-"
64
- end
65
-
66
44
  # The line on which we want to report the coverage
67
45
  #
68
46
  # Usually we choose the line above the start of the branch (so that it shows up
@@ -99,7 +77,7 @@ module SimpleCov
99
77
  # @return [Array]
100
78
  #
101
79
  def report
102
- [coverage, badge]
80
+ [type, coverage]
103
81
  end
104
82
  end
105
83
  end
@@ -9,11 +9,11 @@ module SimpleCov
9
9
  # The full path to this source file (e.g. /User/colszowka/projects/simplecov/lib/simplecov/source_file.rb)
10
10
  attr_reader :filename
11
11
  # The array of coverage data received from the Coverage.result
12
- attr_reader :coverage
12
+ attr_reader :coverage_data
13
13
 
14
- def initialize(filename, coverage)
15
- @filename = filename.to_s
16
- @coverage = coverage
14
+ def initialize(filename, coverage_data)
15
+ @filename = filename
16
+ @coverage_data = coverage_data
17
17
  end
18
18
 
19
19
  # The path to this source file relative to the projects directory
@@ -29,6 +29,14 @@ module SimpleCov
29
29
  end
30
30
  alias source src
31
31
 
32
+ def coverage_statistics
33
+ @coverage_statistics ||=
34
+ {
35
+ **line_coverage_statistics,
36
+ **branch_coverage_statistics
37
+ }
38
+ end
39
+
32
40
  # Returns all source lines for this file as instances of SimpleCov::SourceFile::Line,
33
41
  # and thus including coverage data. Aliased as :source_lines
34
42
  def lines
@@ -60,44 +68,7 @@ module SimpleCov
60
68
 
61
69
  # Returns the number of relevant lines (covered + missed)
62
70
  def lines_of_code
63
- covered_lines.size + missed_lines.size
64
- end
65
-
66
- def build_lines
67
- coverage_exceeding_source_warn if coverage[:lines].size > src.size
68
- lines = src.map.with_index(1) do |src, i|
69
- SimpleCov::SourceFile::Line.new(src, i, coverage[:lines][i - 1])
70
- end
71
- process_skipped_lines(lines)
72
- end
73
-
74
- # no_cov_chunks is zero indexed to work directly with the array holding the lines
75
- def no_cov_chunks
76
- @no_cov_chunks ||= build_no_cov_chunks
77
- end
78
-
79
- def build_no_cov_chunks
80
- no_cov_lines = src.map.with_index(1).select { |line, _index| LinesClassifier.no_cov_line?(line) }
81
-
82
- warn "uneven number of nocov comments detected" if no_cov_lines.size.odd?
83
-
84
- no_cov_lines.each_slice(2).map do |(_line_start, index_start), (_line_end, index_end)|
85
- index_start..index_end
86
- end
87
- end
88
-
89
- def process_skipped_lines(lines)
90
- # the array the lines are kept in is 0-based whereas the line numbers in the nocov
91
- # chunks are 1-based and are expected to be like this in other parts (and it's also
92
- # arguably more understandable)
93
- no_cov_chunks.each { |chunk| lines[(chunk.begin - 1)..(chunk.end - 1)].each(&:skipped!) }
94
-
95
- lines
96
- end
97
-
98
- # Warning to identify condition from Issue #56
99
- def coverage_exceeding_source_warn
100
- warn "Warning: coverage data provided by Coverage [#{coverage[:lines].size}] exceeds number of lines in #{filename} [#{src.size}]"
71
+ coverage_statistics[:line]&.total
101
72
  end
102
73
 
103
74
  # Access SimpleCov::SourceFile::Line source lines by line number
@@ -107,27 +78,17 @@ module SimpleCov
107
78
 
108
79
  # The coverage for this file in percent. 0 if the file has no coverage lines
109
80
  def covered_percent
110
- return 100.0 if no_lines?
111
-
112
- return 0.0 if relevant_lines.zero?
113
-
114
- Float(covered_lines.size * 100.0 / relevant_lines.to_f)
81
+ coverage_statistics[:line]&.percent
115
82
  end
116
83
 
117
84
  def covered_strength
118
- return 0.0 if relevant_lines.zero?
119
-
120
- (lines_strength / relevant_lines.to_f).round(1)
85
+ coverage_statistics[:line]&.strength
121
86
  end
122
87
 
123
88
  def no_lines?
124
89
  lines.length.zero? || (lines.length == never_lines.size)
125
90
  end
126
91
 
127
- def lines_strength
128
- lines.map(&:coverage).compact.reduce(:+)
129
- end
130
-
131
92
  def relevant_lines
132
93
  lines.size - never_lines.size - skipped_lines.size
133
94
  end
@@ -143,16 +104,13 @@ module SimpleCov
143
104
  end
144
105
 
145
106
  def branches_coverage_percent
146
- return 100.0 if no_branches?
147
- return 0.0 if covered_branches.empty?
148
-
149
- Float(covered_branches.size * 100.0 / total_branches.size.to_f)
107
+ coverage_statistics[:branch]&.percent
150
108
  end
151
109
 
152
110
  #
153
111
  # Return the relevant branches to source file
154
112
  def total_branches
155
- covered_branches + missed_branches
113
+ @total_branches ||= covered_branches + missed_branches
156
114
  end
157
115
 
158
116
  #
@@ -161,14 +119,104 @@ module SimpleCov
161
119
  @branches_report ||= build_branches_report
162
120
  end
163
121
 
164
- ## Related to source file branches statistics
122
+ #
123
+ # Select the covered branches
124
+ # Here we user tree schema because some conditions like case may have additional
125
+ # else that is not in declared inside the code but given by default by coverage report
126
+ #
127
+ # @return [Array]
128
+ #
129
+ def covered_branches
130
+ @covered_branches ||= branches.select(&:covered?)
131
+ end
132
+
133
+ #
134
+ # Select the missed branches with coverage equal to zero
135
+ #
136
+ # @return [Array]
137
+ #
138
+ def missed_branches
139
+ @missed_branches ||= branches.select(&:missed?)
140
+ end
141
+
142
+ def branches_for_line(line_number)
143
+ branches_report.fetch(line_number, [])
144
+ end
145
+
146
+ #
147
+ # Check if any branches missing on given line number
148
+ #
149
+ # @param [Integer] line_number
150
+ #
151
+ # @return [Boolean]
152
+ #
153
+ def line_with_missed_branch?(line_number)
154
+ branches_for_line(line_number).select { |_type, count| count.zero? }.any?
155
+ end
156
+
157
+ private
158
+
159
+ # no_cov_chunks is zero indexed to work directly with the array holding the lines
160
+ def no_cov_chunks
161
+ @no_cov_chunks ||= build_no_cov_chunks
162
+ end
163
+
164
+ def build_no_cov_chunks
165
+ no_cov_lines = src.map.with_index(1).select { |line, _index| LinesClassifier.no_cov_line?(line) }
166
+
167
+ warn "uneven number of nocov comments detected" if no_cov_lines.size.odd?
168
+
169
+ no_cov_lines.each_slice(2).map do |(_line_start, index_start), (_line_end, index_end)|
170
+ index_start..index_end
171
+ end
172
+ end
173
+
174
+ def build_lines
175
+ coverage_exceeding_source_warn if coverage_data["lines"].size > src.size
176
+ lines = src.map.with_index(1) do |src, i|
177
+ SimpleCov::SourceFile::Line.new(src, i, coverage_data["lines"][i - 1])
178
+ end
179
+ process_skipped_lines(lines)
180
+ end
181
+
182
+ def process_skipped_lines(lines)
183
+ # the array the lines are kept in is 0-based whereas the line numbers in the nocov
184
+ # chunks are 1-based and are expected to be like this in other parts (and it's also
185
+ # arguably more understandable)
186
+ no_cov_chunks.each { |chunk| lines[(chunk.begin - 1)..(chunk.end - 1)].each(&:skipped!) }
187
+
188
+ lines
189
+ end
190
+
191
+ def lines_strength
192
+ lines.map(&:coverage).compact.reduce(:+)
193
+ end
194
+
195
+ # Warning to identify condition from Issue #56
196
+ def coverage_exceeding_source_warn
197
+ warn "Warning: coverage data provided by Coverage [#{coverage_data['lines'].size}] exceeds number of lines in #{filename} [#{src.size}]"
198
+ end
199
+
200
+ #
201
+ # Build full branches report
202
+ # Root branches represent the wrapper of all condition state that
203
+ # have inside the branches
204
+ #
205
+ # @return [Hash]
206
+ #
207
+ def build_branches_report
208
+ branches.reject(&:skipped?).each_with_object({}) do |branch, coverage_statistics|
209
+ coverage_statistics[branch.report_line] ||= []
210
+ coverage_statistics[branch.report_line] << branch.report
211
+ end
212
+ end
165
213
 
166
214
  #
167
215
  # Call recursive method that transform our static hash to array of objects
168
216
  # @return [Array]
169
217
  #
170
218
  def build_branches
171
- coverage_branch_data = coverage.fetch(:branches, {})
219
+ coverage_branch_data = coverage_data.fetch("branches", {})
172
220
  branches = coverage_branch_data.flat_map do |condition, coverage_branches|
173
221
  build_branches_from(condition, coverage_branches)
174
222
  end
@@ -187,7 +235,7 @@ module SimpleCov
187
235
  end
188
236
 
189
237
  # Since we are dumping to and loading from JSON, and we have arrays as keys those
190
- # don't make their way back to us intact e.g. just as a string or a symbol (currently keys are symbolized).
238
+ # don't make their way back to us intact e.g. just as a string
191
239
  #
192
240
  # We should probably do something different here, but as it stands these are
193
241
  # our data structures that we write so eval isn't _too_ bad.
@@ -199,9 +247,8 @@ module SimpleCov
199
247
  # put them through here.
200
248
  return structure if structure.is_a?(Array)
201
249
 
202
- # as of right now the keys are still symbolized
203
250
  # rubocop:disable Security/Eval
204
- eval structure.to_s
251
+ eval structure
205
252
  # rubocop:enable Security/Eval
206
253
  end
207
254
 
@@ -211,103 +258,43 @@ module SimpleCov
211
258
  # [:then, 4, 6, 6, 6, 10]
212
259
  #
213
260
  # which is [type, id, start_line, start_col, end_line, end_col]
214
- condition_type, condition_id, condition_start_line, * = restore_ruby_data_structure(condition)
261
+ _condition_type, _condition_id, condition_start_line, * = restore_ruby_data_structure(condition)
215
262
 
216
- branches
217
- .map { |branch_data, hit_count| [restore_ruby_data_structure(branch_data), hit_count] }
218
- .reject { |branch_data, _hit_count| ignore_branch?(branch_data, condition_type, condition_start_line) }
219
- .map { |branch_data, hit_count| build_branch(branch_data, hit_count, condition_start_line, condition_id) }
263
+ branches.map do |branch_data, hit_count|
264
+ branch_data = restore_ruby_data_structure(branch_data)
265
+ build_branch(branch_data, hit_count, condition_start_line)
266
+ end
220
267
  end
221
268
 
222
- def build_branch(branch_data, hit_count, condition_start_line, condition_id)
223
- type, id, start_line, _start_col, end_line, _end_col = branch_data
269
+ def build_branch(branch_data, hit_count, condition_start_line)
270
+ type, _id, start_line, _start_col, end_line, _end_col = branch_data
224
271
 
225
272
  SourceFile::Branch.new(
226
- # rubocop these are keyword args please let me keep them, thank you
227
- # rubocop:disable Style/HashSyntax
228
273
  start_line: start_line,
229
274
  end_line: end_line,
230
275
  coverage: hit_count,
231
276
  inline: start_line == condition_start_line,
232
- positive: positive_branch?(condition_id, id, type)
233
- # rubocop:enable Style/HashSyntax
277
+ type: type
234
278
  )
235
279
  end
236
280
 
237
- def ignore_branch?(branch_data, condition_type, condition_start_line)
238
- branch_type = branch_data[0]
239
- branch_start_line = branch_data[2]
240
-
241
- # branch coverage always reports case to be with an else branch even when
242
- # there is no else branch to be covered, it's noticable by the reported start
243
- # line being the same as that of the condition/case
244
- condition_type == :case &&
245
- branch_type == :else &&
246
- condition_start_line == branch_start_line
247
- end
248
-
249
- #
250
- # Branch is positive or negative.
251
- # For `case` conditions, `when` always supposed as positive branch.
252
- # For `if, else` conditions:
253
- # coverage returns matrices ex: [:if, 0,..] => {[:then, 1,..], [:else, 2,..]},
254
- # positive branch always has id equals to condition id incremented by 1.
255
- #
256
- # @return [Boolean]
257
- #
258
- def positive_branch?(condition_id, branch_id, branch_type)
259
- return true if branch_type == :when
260
-
261
- branch_id == (1 + condition_id)
281
+ def line_coverage_statistics
282
+ {
283
+ line: CoverageStatistics.new(
284
+ total_strength: lines_strength,
285
+ covered: covered_lines.size,
286
+ missed: missed_lines.size
287
+ )
288
+ }
262
289
  end
263
290
 
264
- #
265
- # Select the covered branches
266
- # Here we user tree schema because some conditions like case may have additional
267
- # else that is not in declared inside the code but given by default by coverage report
268
- #
269
- # @return [Array]
270
- #
271
- def covered_branches
272
- @covered_branches ||= branches.select(&:covered?)
273
- end
274
-
275
- #
276
- # Select the missed branches with coverage equal to zero
277
- #
278
- # @return [Array]
279
- #
280
- def missed_branches
281
- @missed_branches ||= branches.select(&:missed?)
282
- end
283
-
284
- def branches_for_line(line_number)
285
- branches_report.fetch(line_number, [])
286
- end
287
-
288
- #
289
- # Check if any branches missing on given line number
290
- #
291
- # @param [Integer] line_number
292
- #
293
- # @return [Boolean]
294
- #
295
- def line_with_missed_branch?(line_number)
296
- branches_for_line(line_number).select { |count, _sign| count.zero? }.any?
297
- end
298
-
299
- #
300
- # Build full branches report
301
- # Root branches represent the wrapper of all condition state that
302
- # have inside the branches
303
- #
304
- # @return [Hash]
305
- #
306
- def build_branches_report
307
- branches.reject(&:skipped?).each_with_object({}) do |branch, coverage_statistics|
308
- coverage_statistics[branch.report_line] ||= []
309
- coverage_statistics[branch.report_line] << branch.report
310
- end
291
+ def branch_coverage_statistics
292
+ {
293
+ branch: CoverageStatistics.new(
294
+ covered: covered_branches.size,
295
+ missed: missed_branches.size
296
+ )
297
+ }
311
298
  end
312
299
  end
313
300
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module SimpleCov
4
4
  #
5
- # Selection the files that related to working scope directory of SimpleCov
5
+ # Select the files that related to working scope directory of SimpleCov
6
6
  #
7
7
  module UselessResultsRemover
8
8
  ROOT_REGX = /\A#{Regexp.escape(SimpleCov.root + File::SEPARATOR)}/io.freeze
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCov
4
- VERSION = "0.18.0.beta1"
4
+ VERSION = "0.18.0"
5
5
  end