simplecov 0.22.0 → 1.0.0.rc2

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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +89 -1
  3. data/LICENSE +1 -1
  4. data/README.md +1009 -511
  5. data/doc/alternate-formatters.md +0 -5
  6. data/doc/commercial-services.md +5 -5
  7. data/exe/simplecov +11 -0
  8. data/lib/minitest/simplecov_plugin.rb +13 -5
  9. data/lib/simplecov/autostart.rb +11 -0
  10. data/lib/simplecov/cli/clean.rb +47 -0
  11. data/lib/simplecov/cli/coverage.rb +91 -0
  12. data/lib/simplecov/cli/diff.rb +151 -0
  13. data/lib/simplecov/cli/dotfile.rb +100 -0
  14. data/lib/simplecov/cli/merge.rb +116 -0
  15. data/lib/simplecov/cli/open.rb +50 -0
  16. data/lib/simplecov/cli/report.rb +84 -0
  17. data/lib/simplecov/cli/run.rb +36 -0
  18. data/lib/simplecov/cli/serve.rb +139 -0
  19. data/lib/simplecov/cli/uncovered.rb +107 -0
  20. data/lib/simplecov/cli.rb +150 -0
  21. data/lib/simplecov/color.rb +74 -0
  22. data/lib/simplecov/combine/branches_combiner.rb +3 -2
  23. data/lib/simplecov/combine/files_combiner.rb +7 -1
  24. data/lib/simplecov/combine/lines_combiner.rb +19 -17
  25. data/lib/simplecov/combine/methods_combiner.rb +26 -0
  26. data/lib/simplecov/combine/results_combiner.rb +5 -4
  27. data/lib/simplecov/command_guesser.rb +46 -32
  28. data/lib/simplecov/configuration/coverage.rb +171 -0
  29. data/lib/simplecov/configuration/coverage_criteria.rb +156 -0
  30. data/lib/simplecov/configuration/filters.rb +197 -0
  31. data/lib/simplecov/configuration/formatting.rb +119 -0
  32. data/lib/simplecov/configuration/ignored_entries.rb +63 -0
  33. data/lib/simplecov/configuration/merging.rb +74 -0
  34. data/lib/simplecov/configuration/thresholds.rb +174 -0
  35. data/lib/simplecov/configuration.rb +86 -407
  36. data/lib/simplecov/coverage_statistics.rb +12 -9
  37. data/lib/simplecov/coverage_violations.rb +148 -0
  38. data/lib/simplecov/defaults.rb +27 -20
  39. data/lib/simplecov/deprecation.rb +47 -0
  40. data/lib/simplecov/directive.rb +162 -0
  41. data/lib/simplecov/exit_codes/exit_code_handling.rb +8 -2
  42. data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +19 -57
  43. data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +45 -0
  44. data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +17 -27
  45. data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +41 -0
  46. data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +38 -21
  47. data/lib/simplecov/exit_codes.rb +3 -0
  48. data/lib/simplecov/exit_handling.rb +158 -0
  49. data/lib/simplecov/file_list.rb +61 -17
  50. data/lib/simplecov/filter.rb +69 -24
  51. data/lib/simplecov/formatter/base.rb +101 -0
  52. data/lib/simplecov/formatter/html_formatter/public/application.css +1 -0
  53. data/lib/simplecov/formatter/html_formatter/public/application.js +18 -0
  54. data/lib/simplecov/formatter/html_formatter/public/favicon_green.png +0 -0
  55. data/lib/simplecov/formatter/html_formatter/public/favicon_red.png +0 -0
  56. data/lib/simplecov/formatter/html_formatter/public/favicon_yellow.png +0 -0
  57. data/lib/simplecov/formatter/html_formatter/public/index.html +56 -0
  58. data/lib/simplecov/formatter/html_formatter.rb +79 -0
  59. data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +84 -0
  60. data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +127 -0
  61. data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +99 -0
  62. data/lib/simplecov/formatter/json_formatter.rb +77 -0
  63. data/lib/simplecov/formatter/multi_formatter.rb +4 -5
  64. data/lib/simplecov/formatter/simple_formatter.rb +9 -11
  65. data/lib/simplecov/formatter.rb +4 -0
  66. data/lib/simplecov/last_run.rb +10 -3
  67. data/lib/simplecov/lines_classifier.rb +26 -13
  68. data/lib/simplecov/load_global_config.rb +9 -4
  69. data/lib/simplecov/parallel_adapters/base.rb +51 -0
  70. data/lib/simplecov/parallel_adapters/generic.rb +42 -0
  71. data/lib/simplecov/parallel_adapters/parallel_tests.rb +77 -0
  72. data/lib/simplecov/parallel_adapters.rb +83 -0
  73. data/lib/simplecov/parallel_coordination.rb +95 -0
  74. data/lib/simplecov/process.rb +26 -14
  75. data/lib/simplecov/profiles/bundler_filter.rb +1 -1
  76. data/lib/simplecov/profiles/hidden_filter.rb +1 -1
  77. data/lib/simplecov/profiles/rails.rb +24 -10
  78. data/lib/simplecov/profiles/root_filter.rb +6 -5
  79. data/lib/simplecov/profiles/strict.rb +32 -0
  80. data/lib/simplecov/profiles/test_frameworks.rb +1 -4
  81. data/lib/simplecov/profiles.rb +32 -3
  82. data/lib/simplecov/result/missing_source_files_reporter.rb +49 -0
  83. data/lib/simplecov/result/source_file_builder.rb +51 -0
  84. data/lib/simplecov/result.rb +97 -19
  85. data/lib/simplecov/result_adapter.rb +68 -6
  86. data/lib/simplecov/result_merger/legacy_format_adapter.rb +28 -0
  87. data/lib/simplecov/result_merger/resultset_file.rb +38 -0
  88. data/lib/simplecov/result_merger/resultset_store.rb +50 -0
  89. data/lib/simplecov/result_merger.rb +54 -90
  90. data/lib/simplecov/result_processing.rb +162 -0
  91. data/lib/simplecov/simulate_coverage.rb +54 -8
  92. data/lib/simplecov/source_file/branch.rb +1 -3
  93. data/lib/simplecov/source_file/branch_builder.rb +114 -0
  94. data/lib/simplecov/source_file/builder_context.rb +28 -0
  95. data/lib/simplecov/source_file/line.rb +7 -2
  96. data/lib/simplecov/source_file/line_builder.rb +43 -0
  97. data/lib/simplecov/source_file/method.rb +52 -0
  98. data/lib/simplecov/source_file/method_builder.rb +58 -0
  99. data/lib/simplecov/source_file/ruby_data_parser.rb +88 -0
  100. data/lib/simplecov/source_file/skip_chunks.rb +77 -0
  101. data/lib/simplecov/source_file/source_loader.rb +63 -0
  102. data/lib/simplecov/source_file/statistics.rb +57 -0
  103. data/lib/simplecov/source_file.rb +66 -232
  104. data/lib/simplecov/static_coverage_extractor/visitor.rb +193 -0
  105. data/lib/simplecov/static_coverage_extractor.rb +111 -0
  106. data/lib/simplecov/useless_results_remover.rb +16 -7
  107. data/lib/simplecov/version.rb +1 -1
  108. data/lib/simplecov-html.rb +4 -0
  109. data/lib/simplecov.rb +148 -377
  110. data/lib/simplecov_json_formatter.rb +4 -0
  111. data/schemas/coverage-v1.0.schema.json +300 -0
  112. data/schemas/coverage.schema.json +300 -0
  113. metadata +89 -56
  114. data/lib/simplecov/default_formatter.rb +0 -20
@@ -0,0 +1,300 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/simplecov-ruby/simplecov/main/schemas/coverage-v1.0.schema.json",
4
+ "title": "SimpleCov coverage.json",
5
+ "description": "Schema for the coverage.json file emitted by SimpleCov's JSONFormatter. Versioned independently of the gem. Non-breaking additions bump the minor segment of schema_version, breaking changes bump the major segment. The versioned file at schemas/coverage-vX.Y.schema.json is the canonical artifact for that version, schemas/coverage.schema.json is a convenience alias for the latest.",
6
+ "type": "object",
7
+ "required": ["$schema", "meta", "total", "coverage", "groups", "errors"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "$schema": {
11
+ "type": "string",
12
+ "format": "uri",
13
+ "description": "URL of the JSON Schema this document conforms to. Pinned to the versioned canonical URL so documents stay validatable against the exact contract they were emitted under, even after the schema evolves."
14
+ },
15
+ "meta": {
16
+ "type": "object",
17
+ "required": [
18
+ "schema_version",
19
+ "simplecov_version",
20
+ "command_name",
21
+ "project_name",
22
+ "timestamp",
23
+ "root",
24
+ "commit",
25
+ "line_coverage",
26
+ "branch_coverage",
27
+ "method_coverage"
28
+ ],
29
+ "additionalProperties": false,
30
+ "properties": {
31
+ "schema_version": {
32
+ "type": "string",
33
+ "const": "1.0",
34
+ "description": "Schema major.minor. Additive changes bump minor; breaking changes bump major. Update this `const` whenever the schema version is bumped so documents claiming a different version don't quietly validate against this contract."
35
+ },
36
+ "simplecov_version": {
37
+ "type": "string",
38
+ "description": "The version of the SimpleCov gem that produced this file."
39
+ },
40
+ "command_name": {"type": "string"},
41
+ "project_name": {"type": "string"},
42
+ "timestamp": {
43
+ "type": "string",
44
+ "format": "date-time",
45
+ "description": "ISO 8601 timestamp with millisecond precision."
46
+ },
47
+ "root": {
48
+ "type": "string",
49
+ "description": "Absolute path to SimpleCov.root at the time of write."
50
+ },
51
+ "commit": {
52
+ "type": ["string", "null"],
53
+ "description": "Full git commit SHA of `root`'s HEAD when the report was generated, or null when the project isn't a git checkout (or git isn't available). Lets tools recover the exact source a report reflects, which matters most when `source_in_json` is false and the per-file `source` arrays are omitted."
54
+ },
55
+ "line_coverage": {"type": "boolean"},
56
+ "branch_coverage": {"type": "boolean"},
57
+ "method_coverage": {"type": "boolean"}
58
+ }
59
+ },
60
+ "total": {"$ref": "#/definitions/totals"},
61
+ "coverage": {
62
+ "type": "object",
63
+ "description": "Map of project-relative file paths to per-file coverage data.",
64
+ "additionalProperties": {"$ref": "#/definitions/source_file"}
65
+ },
66
+ "groups": {
67
+ "type": "object",
68
+ "description": "Map of group names to per-group totals plus the list of files in the group.",
69
+ "additionalProperties": {"$ref": "#/definitions/group"}
70
+ },
71
+ "errors": {
72
+ "type": "object",
73
+ "description": "Threshold violations from minimum_coverage, minimum_coverage_by_file, minimum_coverage_by_group, maximum_coverage, and maximum_coverage_drop. Empty object when no thresholds were violated.",
74
+ "additionalProperties": false,
75
+ "properties": {
76
+ "minimum_coverage": {
77
+ "type": "object",
78
+ "description": "Keyed by criterion: lines, branches, methods.",
79
+ "additionalProperties": {"$ref": "#/definitions/expected_actual"}
80
+ },
81
+ "minimum_coverage_by_file": {
82
+ "type": "object",
83
+ "description": "Keyed by project-relative filename, then by criterion (lines, branches, methods).",
84
+ "additionalProperties": {
85
+ "type": "object",
86
+ "additionalProperties": {"$ref": "#/definitions/expected_actual"}
87
+ }
88
+ },
89
+ "minimum_coverage_by_group": {
90
+ "type": "object",
91
+ "description": "Keyed by group name, then by criterion (lines, branches, methods).",
92
+ "additionalProperties": {
93
+ "type": "object",
94
+ "additionalProperties": {"$ref": "#/definitions/expected_actual"}
95
+ }
96
+ },
97
+ "maximum_coverage": {
98
+ "type": "object",
99
+ "description": "Keyed by criterion: lines, branches, methods. `expected` is the configured maximum, `actual` is the measured value that exceeded it.",
100
+ "additionalProperties": {"$ref": "#/definitions/expected_actual"}
101
+ },
102
+ "maximum_coverage_drop": {
103
+ "type": "object",
104
+ "description": "Keyed by criterion: lines, branches, methods.",
105
+ "additionalProperties": {"$ref": "#/definitions/maximum_actual"}
106
+ }
107
+ }
108
+ }
109
+ },
110
+ "definitions": {
111
+ "totals": {
112
+ "type": "object",
113
+ "additionalProperties": false,
114
+ "anyOf": [
115
+ {"required": ["lines"]},
116
+ {"required": ["branches"]},
117
+ {"required": ["methods"]}
118
+ ],
119
+ "properties": {
120
+ "lines": {"$ref": "#/definitions/line_statistic"},
121
+ "branches": {"$ref": "#/definitions/coverage_statistic"},
122
+ "methods": {"$ref": "#/definitions/coverage_statistic"}
123
+ }
124
+ },
125
+ "source_file": {
126
+ "type": "object",
127
+ "additionalProperties": false,
128
+ "anyOf": [
129
+ {"required": ["lines"]},
130
+ {"required": ["branches"]},
131
+ {"required": ["methods"]}
132
+ ],
133
+ "dependentRequired": {
134
+ "lines": ["lines_covered_percent", "covered_lines", "missed_lines", "omitted_lines", "total_lines"],
135
+ "lines_covered_percent": ["lines", "covered_lines", "missed_lines", "omitted_lines", "total_lines"],
136
+ "covered_lines": ["lines", "lines_covered_percent", "missed_lines", "omitted_lines", "total_lines"],
137
+ "missed_lines": ["lines", "lines_covered_percent", "covered_lines", "omitted_lines", "total_lines"],
138
+ "omitted_lines": ["lines", "lines_covered_percent", "covered_lines", "missed_lines", "total_lines"],
139
+ "total_lines": ["lines", "lines_covered_percent", "covered_lines", "missed_lines", "omitted_lines"],
140
+ "branches": ["branches_covered_percent", "covered_branches", "missed_branches", "total_branches"],
141
+ "branches_covered_percent": ["branches", "covered_branches", "missed_branches", "total_branches"],
142
+ "covered_branches": ["branches", "branches_covered_percent", "missed_branches", "total_branches"],
143
+ "missed_branches": ["branches", "branches_covered_percent", "covered_branches", "total_branches"],
144
+ "total_branches": ["branches", "branches_covered_percent", "covered_branches", "missed_branches"],
145
+ "methods": ["methods_covered_percent", "covered_methods", "missed_methods", "total_methods"],
146
+ "methods_covered_percent": ["methods", "covered_methods", "missed_methods", "total_methods"],
147
+ "covered_methods": ["methods", "methods_covered_percent", "missed_methods", "total_methods"],
148
+ "missed_methods": ["methods", "methods_covered_percent", "covered_methods", "total_methods"],
149
+ "total_methods": ["methods", "methods_covered_percent", "covered_methods", "missed_methods"]
150
+ },
151
+ "properties": {
152
+ "lines": {
153
+ "type": "array",
154
+ "description": "Per-source-line coverage. Element index N corresponds to source line N+1. Integer hit-count, null for non-relevant (blank/comment) lines, or the string \"ignored\" for lines inside a simplecov:disable / :nocov: region. Present if and only if `meta.line_coverage` is true. When present, the five line stat fields (`lines_covered_percent`, `covered_lines`, `missed_lines`, `omitted_lines`, `total_lines`) are guaranteed to be present too.",
155
+ "items": {"$ref": "#/definitions/line_coverage"}
156
+ },
157
+ "source": {
158
+ "type": "array",
159
+ "description": "Source lines of the file, in order (one entry per physical line). When the per-file `lines` array is present it has the same length, but `source` is emitted independently of line coverage. Present only when SimpleCov.source_in_json is true (the default); omitted when downstream tools opt out via `source_in_json false` to keep coverage.json smaller.",
160
+ "items": {"type": "string"}
161
+ },
162
+ "lines_covered_percent": {"type": "number"},
163
+ "covered_lines": {"type": "integer", "minimum": 0, "description": "Count of executable lines that were hit at least once."},
164
+ "missed_lines": {"type": "integer", "minimum": 0, "description": "Count of executable lines that were never hit."},
165
+ "omitted_lines": {"type": "integer", "minimum": 0, "description": "Count of non-executable lines (blank lines, comments). Mirrors `total.lines.omitted` at the per-file level. Excludes lines inside simplecov:disable / :nocov: regions, which surface as `\"ignored\"` in the `lines` array."},
166
+ "total_lines": {"type": "integer", "minimum": 0, "description": "Count of executable lines: `covered_lines + missed_lines`. Does not include non-executable (omitted) lines or lines inside simplecov:disable / :nocov: regions, so this can be smaller than `lines.length`."},
167
+ "branches": {
168
+ "type": "array",
169
+ "description": "Per-branch coverage. Present if and only if `meta.branch_coverage` is true. When present, the four branch stat fields (`branches_covered_percent`, `covered_branches`, `missed_branches`, `total_branches`) are guaranteed to be present too.",
170
+ "items": {"$ref": "#/definitions/branch"}
171
+ },
172
+ "branches_covered_percent": {"type": "number"},
173
+ "covered_branches": {"type": "integer", "minimum": 0},
174
+ "missed_branches": {"type": "integer", "minimum": 0},
175
+ "total_branches": {"type": "integer", "minimum": 0, "description": "Count of branches: `covered_branches + missed_branches`."},
176
+ "methods": {
177
+ "type": "array",
178
+ "description": "Per-method coverage. Present if and only if `meta.method_coverage` is true. When present, the four method stat fields (`methods_covered_percent`, `covered_methods`, `missed_methods`, `total_methods`) are guaranteed to be present too.",
179
+ "items": {"$ref": "#/definitions/method"}
180
+ },
181
+ "methods_covered_percent": {"type": "number"},
182
+ "covered_methods": {"type": "integer", "minimum": 0},
183
+ "missed_methods": {"type": "integer", "minimum": 0},
184
+ "total_methods": {"type": "integer", "minimum": 0, "description": "Count of methods: `covered_methods + missed_methods`."}
185
+ }
186
+ },
187
+ "branch": {
188
+ "type": "object",
189
+ "required": ["type", "start_line", "end_line", "coverage", "inline", "report_line"],
190
+ "additionalProperties": false,
191
+ "properties": {
192
+ "type": {
193
+ "type": "string",
194
+ "description": "Branch kind from Ruby's Coverage library (e.g. \"then\", \"else\", \"when\")."
195
+ },
196
+ "start_line": {"type": "integer", "minimum": 1},
197
+ "end_line": {"type": "integer", "minimum": 1},
198
+ "coverage": {"$ref": "#/definitions/branch_method_coverage"},
199
+ "inline": {"type": "boolean"},
200
+ "report_line": {"type": "integer", "minimum": 1, "description": "Line of the conditional that owns this branch (the `if`, `case`, or `&&` line), not the start of the branch body. Useful for rendering annotations at the decision point."}
201
+ }
202
+ },
203
+ "method": {
204
+ "type": "object",
205
+ "required": ["name", "start_line", "end_line", "coverage"],
206
+ "additionalProperties": false,
207
+ "properties": {
208
+ "name": {
209
+ "type": "string",
210
+ "description": "Qualified method name, e.g. \"Foo#bar\", \"Foo.bar\", or \"Foo::Bar#baz\"."
211
+ },
212
+ "start_line": {"type": "integer", "minimum": 1},
213
+ "end_line": {"type": "integer", "minimum": 1},
214
+ "coverage": {"$ref": "#/definitions/branch_method_coverage"}
215
+ }
216
+ },
217
+ "group": {
218
+ "type": "object",
219
+ "required": ["files"],
220
+ "additionalProperties": false,
221
+ "anyOf": [
222
+ {"required": ["lines"]},
223
+ {"required": ["branches"]},
224
+ {"required": ["methods"]}
225
+ ],
226
+ "properties": {
227
+ "lines": {"$ref": "#/definitions/line_statistic"},
228
+ "branches": {"$ref": "#/definitions/coverage_statistic"},
229
+ "methods": {"$ref": "#/definitions/coverage_statistic"},
230
+ "files": {
231
+ "type": "array",
232
+ "description": "Project-relative paths of the files that fell into this group.",
233
+ "items": {"type": "string"}
234
+ }
235
+ }
236
+ },
237
+ "line_statistic": {
238
+ "type": "object",
239
+ "required": ["covered", "missed", "omitted", "total", "percent", "strength"],
240
+ "additionalProperties": false,
241
+ "properties": {
242
+ "covered": {"type": "integer", "minimum": 0},
243
+ "missed": {"type": "integer", "minimum": 0},
244
+ "omitted": {
245
+ "type": "integer",
246
+ "minimum": 0,
247
+ "description": "Lines that cannot be covered (blank, comment, etc.). Only present on line stats."
248
+ },
249
+ "total": {"type": "integer", "minimum": 0, "description": "Executable lines: `covered + missed`. Does not include `omitted`."},
250
+ "percent": {"type": "number"},
251
+ "strength": {"type": "number", "description": "Average number of executions across covered lines (hits per covered line)."}
252
+ }
253
+ },
254
+ "coverage_statistic": {
255
+ "type": "object",
256
+ "required": ["covered", "missed", "total", "percent", "strength"],
257
+ "additionalProperties": false,
258
+ "properties": {
259
+ "covered": {"type": "integer", "minimum": 0},
260
+ "missed": {"type": "integer", "minimum": 0},
261
+ "total": {"type": "integer", "minimum": 0, "description": "Total branches or methods: `covered + missed`."},
262
+ "percent": {"type": "number"},
263
+ "strength": {"type": "number", "description": "Average number of executions across covered branches or methods (hits per covered item)."}
264
+ }
265
+ },
266
+ "line_coverage": {
267
+ "description": "Coverage value for a source line in the per-file `lines` array. Integer hit count, `null` for a non-executable line (blank or comment), or the literal string `\"ignored\"` for a line inside a simplecov:disable / :nocov: region.",
268
+ "oneOf": [
269
+ {"type": "integer", "minimum": 0},
270
+ {"type": "null"},
271
+ {"type": "string", "const": "ignored"}
272
+ ]
273
+ },
274
+ "branch_method_coverage": {
275
+ "description": "Coverage value for a branch or method `coverage` field. Integer hit count, or the literal string `\"ignored\"` for code inside a simplecov:disable / :nocov: region. Unlike line coverage, `null` never occurs: every branch and method maps to an executable construct.",
276
+ "oneOf": [
277
+ {"type": "integer", "minimum": 0},
278
+ {"type": "string", "const": "ignored"}
279
+ ]
280
+ },
281
+ "expected_actual": {
282
+ "type": "object",
283
+ "required": ["expected", "actual"],
284
+ "additionalProperties": false,
285
+ "properties": {
286
+ "expected": {"type": "number"},
287
+ "actual": {"type": "number"}
288
+ }
289
+ },
290
+ "maximum_actual": {
291
+ "type": "object",
292
+ "required": ["maximum", "actual"],
293
+ "additionalProperties": false,
294
+ "properties": {
295
+ "maximum": {"type": "number"},
296
+ "actual": {"type": "number"}
297
+ }
298
+ }
299
+ }
300
+ }
@@ -0,0 +1,300 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/simplecov-ruby/simplecov/main/schemas/coverage.schema.json",
4
+ "title": "SimpleCov coverage.json (latest)",
5
+ "description": "Convenience alias for the latest coverage.json schema. Mirrors schemas/coverage-v1.0.schema.json except for the $id (which is the unversioned URL). For long-lived integrations, pin to the versioned canonical (schemas/coverage-vX.Y.schema.json) so the contract you validate against does not silently shift when a new SimpleCov release bumps the schema.",
6
+ "type": "object",
7
+ "required": ["$schema", "meta", "total", "coverage", "groups", "errors"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "$schema": {
11
+ "type": "string",
12
+ "format": "uri",
13
+ "description": "URL of the JSON Schema this document conforms to. Pinned to the versioned canonical URL so documents stay validatable against the exact contract they were emitted under, even after the schema evolves."
14
+ },
15
+ "meta": {
16
+ "type": "object",
17
+ "required": [
18
+ "schema_version",
19
+ "simplecov_version",
20
+ "command_name",
21
+ "project_name",
22
+ "timestamp",
23
+ "root",
24
+ "commit",
25
+ "line_coverage",
26
+ "branch_coverage",
27
+ "method_coverage"
28
+ ],
29
+ "additionalProperties": false,
30
+ "properties": {
31
+ "schema_version": {
32
+ "type": "string",
33
+ "const": "1.0",
34
+ "description": "Schema major.minor. Additive changes bump minor; breaking changes bump major. Update this `const` whenever the schema version is bumped so documents claiming a different version don't quietly validate against this contract."
35
+ },
36
+ "simplecov_version": {
37
+ "type": "string",
38
+ "description": "The version of the SimpleCov gem that produced this file."
39
+ },
40
+ "command_name": {"type": "string"},
41
+ "project_name": {"type": "string"},
42
+ "timestamp": {
43
+ "type": "string",
44
+ "format": "date-time",
45
+ "description": "ISO 8601 timestamp with millisecond precision."
46
+ },
47
+ "root": {
48
+ "type": "string",
49
+ "description": "Absolute path to SimpleCov.root at the time of write."
50
+ },
51
+ "commit": {
52
+ "type": ["string", "null"],
53
+ "description": "Full git commit SHA of `root`'s HEAD when the report was generated, or null when the project isn't a git checkout (or git isn't available). Lets tools recover the exact source a report reflects, which matters most when `source_in_json` is false and the per-file `source` arrays are omitted."
54
+ },
55
+ "line_coverage": {"type": "boolean"},
56
+ "branch_coverage": {"type": "boolean"},
57
+ "method_coverage": {"type": "boolean"}
58
+ }
59
+ },
60
+ "total": {"$ref": "#/definitions/totals"},
61
+ "coverage": {
62
+ "type": "object",
63
+ "description": "Map of project-relative file paths to per-file coverage data.",
64
+ "additionalProperties": {"$ref": "#/definitions/source_file"}
65
+ },
66
+ "groups": {
67
+ "type": "object",
68
+ "description": "Map of group names to per-group totals plus the list of files in the group.",
69
+ "additionalProperties": {"$ref": "#/definitions/group"}
70
+ },
71
+ "errors": {
72
+ "type": "object",
73
+ "description": "Threshold violations from minimum_coverage, minimum_coverage_by_file, minimum_coverage_by_group, maximum_coverage, and maximum_coverage_drop. Empty object when no thresholds were violated.",
74
+ "additionalProperties": false,
75
+ "properties": {
76
+ "minimum_coverage": {
77
+ "type": "object",
78
+ "description": "Keyed by criterion: lines, branches, methods.",
79
+ "additionalProperties": {"$ref": "#/definitions/expected_actual"}
80
+ },
81
+ "minimum_coverage_by_file": {
82
+ "type": "object",
83
+ "description": "Keyed by project-relative filename, then by criterion (lines, branches, methods).",
84
+ "additionalProperties": {
85
+ "type": "object",
86
+ "additionalProperties": {"$ref": "#/definitions/expected_actual"}
87
+ }
88
+ },
89
+ "minimum_coverage_by_group": {
90
+ "type": "object",
91
+ "description": "Keyed by group name, then by criterion (lines, branches, methods).",
92
+ "additionalProperties": {
93
+ "type": "object",
94
+ "additionalProperties": {"$ref": "#/definitions/expected_actual"}
95
+ }
96
+ },
97
+ "maximum_coverage": {
98
+ "type": "object",
99
+ "description": "Keyed by criterion: lines, branches, methods. `expected` is the configured maximum, `actual` is the measured value that exceeded it.",
100
+ "additionalProperties": {"$ref": "#/definitions/expected_actual"}
101
+ },
102
+ "maximum_coverage_drop": {
103
+ "type": "object",
104
+ "description": "Keyed by criterion: lines, branches, methods.",
105
+ "additionalProperties": {"$ref": "#/definitions/maximum_actual"}
106
+ }
107
+ }
108
+ }
109
+ },
110
+ "definitions": {
111
+ "totals": {
112
+ "type": "object",
113
+ "additionalProperties": false,
114
+ "anyOf": [
115
+ {"required": ["lines"]},
116
+ {"required": ["branches"]},
117
+ {"required": ["methods"]}
118
+ ],
119
+ "properties": {
120
+ "lines": {"$ref": "#/definitions/line_statistic"},
121
+ "branches": {"$ref": "#/definitions/coverage_statistic"},
122
+ "methods": {"$ref": "#/definitions/coverage_statistic"}
123
+ }
124
+ },
125
+ "source_file": {
126
+ "type": "object",
127
+ "additionalProperties": false,
128
+ "anyOf": [
129
+ {"required": ["lines"]},
130
+ {"required": ["branches"]},
131
+ {"required": ["methods"]}
132
+ ],
133
+ "dependentRequired": {
134
+ "lines": ["lines_covered_percent", "covered_lines", "missed_lines", "omitted_lines", "total_lines"],
135
+ "lines_covered_percent": ["lines", "covered_lines", "missed_lines", "omitted_lines", "total_lines"],
136
+ "covered_lines": ["lines", "lines_covered_percent", "missed_lines", "omitted_lines", "total_lines"],
137
+ "missed_lines": ["lines", "lines_covered_percent", "covered_lines", "omitted_lines", "total_lines"],
138
+ "omitted_lines": ["lines", "lines_covered_percent", "covered_lines", "missed_lines", "total_lines"],
139
+ "total_lines": ["lines", "lines_covered_percent", "covered_lines", "missed_lines", "omitted_lines"],
140
+ "branches": ["branches_covered_percent", "covered_branches", "missed_branches", "total_branches"],
141
+ "branches_covered_percent": ["branches", "covered_branches", "missed_branches", "total_branches"],
142
+ "covered_branches": ["branches", "branches_covered_percent", "missed_branches", "total_branches"],
143
+ "missed_branches": ["branches", "branches_covered_percent", "covered_branches", "total_branches"],
144
+ "total_branches": ["branches", "branches_covered_percent", "covered_branches", "missed_branches"],
145
+ "methods": ["methods_covered_percent", "covered_methods", "missed_methods", "total_methods"],
146
+ "methods_covered_percent": ["methods", "covered_methods", "missed_methods", "total_methods"],
147
+ "covered_methods": ["methods", "methods_covered_percent", "missed_methods", "total_methods"],
148
+ "missed_methods": ["methods", "methods_covered_percent", "covered_methods", "total_methods"],
149
+ "total_methods": ["methods", "methods_covered_percent", "covered_methods", "missed_methods"]
150
+ },
151
+ "properties": {
152
+ "lines": {
153
+ "type": "array",
154
+ "description": "Per-source-line coverage. Element index N corresponds to source line N+1. Integer hit-count, null for non-relevant (blank/comment) lines, or the string \"ignored\" for lines inside a simplecov:disable / :nocov: region. Present if and only if `meta.line_coverage` is true. When present, the five line stat fields (`lines_covered_percent`, `covered_lines`, `missed_lines`, `omitted_lines`, `total_lines`) are guaranteed to be present too.",
155
+ "items": {"$ref": "#/definitions/line_coverage"}
156
+ },
157
+ "source": {
158
+ "type": "array",
159
+ "description": "Source lines of the file, in order (one entry per physical line). When the per-file `lines` array is present it has the same length, but `source` is emitted independently of line coverage. Present only when SimpleCov.source_in_json is true (the default); omitted when downstream tools opt out via `source_in_json false` to keep coverage.json smaller.",
160
+ "items": {"type": "string"}
161
+ },
162
+ "lines_covered_percent": {"type": "number"},
163
+ "covered_lines": {"type": "integer", "minimum": 0, "description": "Count of executable lines that were hit at least once."},
164
+ "missed_lines": {"type": "integer", "minimum": 0, "description": "Count of executable lines that were never hit."},
165
+ "omitted_lines": {"type": "integer", "minimum": 0, "description": "Count of non-executable lines (blank lines, comments). Mirrors `total.lines.omitted` at the per-file level. Excludes lines inside simplecov:disable / :nocov: regions, which surface as `\"ignored\"` in the `lines` array."},
166
+ "total_lines": {"type": "integer", "minimum": 0, "description": "Count of executable lines: `covered_lines + missed_lines`. Does not include non-executable (omitted) lines or lines inside simplecov:disable / :nocov: regions, so this can be smaller than `lines.length`."},
167
+ "branches": {
168
+ "type": "array",
169
+ "description": "Per-branch coverage. Present if and only if `meta.branch_coverage` is true. When present, the four branch stat fields (`branches_covered_percent`, `covered_branches`, `missed_branches`, `total_branches`) are guaranteed to be present too.",
170
+ "items": {"$ref": "#/definitions/branch"}
171
+ },
172
+ "branches_covered_percent": {"type": "number"},
173
+ "covered_branches": {"type": "integer", "minimum": 0},
174
+ "missed_branches": {"type": "integer", "minimum": 0},
175
+ "total_branches": {"type": "integer", "minimum": 0, "description": "Count of branches: `covered_branches + missed_branches`."},
176
+ "methods": {
177
+ "type": "array",
178
+ "description": "Per-method coverage. Present if and only if `meta.method_coverage` is true. When present, the four method stat fields (`methods_covered_percent`, `covered_methods`, `missed_methods`, `total_methods`) are guaranteed to be present too.",
179
+ "items": {"$ref": "#/definitions/method"}
180
+ },
181
+ "methods_covered_percent": {"type": "number"},
182
+ "covered_methods": {"type": "integer", "minimum": 0},
183
+ "missed_methods": {"type": "integer", "minimum": 0},
184
+ "total_methods": {"type": "integer", "minimum": 0, "description": "Count of methods: `covered_methods + missed_methods`."}
185
+ }
186
+ },
187
+ "branch": {
188
+ "type": "object",
189
+ "required": ["type", "start_line", "end_line", "coverage", "inline", "report_line"],
190
+ "additionalProperties": false,
191
+ "properties": {
192
+ "type": {
193
+ "type": "string",
194
+ "description": "Branch kind from Ruby's Coverage library (e.g. \"then\", \"else\", \"when\")."
195
+ },
196
+ "start_line": {"type": "integer", "minimum": 1},
197
+ "end_line": {"type": "integer", "minimum": 1},
198
+ "coverage": {"$ref": "#/definitions/branch_method_coverage"},
199
+ "inline": {"type": "boolean"},
200
+ "report_line": {"type": "integer", "minimum": 1, "description": "Line of the conditional that owns this branch (the `if`, `case`, or `&&` line), not the start of the branch body. Useful for rendering annotations at the decision point."}
201
+ }
202
+ },
203
+ "method": {
204
+ "type": "object",
205
+ "required": ["name", "start_line", "end_line", "coverage"],
206
+ "additionalProperties": false,
207
+ "properties": {
208
+ "name": {
209
+ "type": "string",
210
+ "description": "Qualified method name, e.g. \"Foo#bar\", \"Foo.bar\", or \"Foo::Bar#baz\"."
211
+ },
212
+ "start_line": {"type": "integer", "minimum": 1},
213
+ "end_line": {"type": "integer", "minimum": 1},
214
+ "coverage": {"$ref": "#/definitions/branch_method_coverage"}
215
+ }
216
+ },
217
+ "group": {
218
+ "type": "object",
219
+ "required": ["files"],
220
+ "additionalProperties": false,
221
+ "anyOf": [
222
+ {"required": ["lines"]},
223
+ {"required": ["branches"]},
224
+ {"required": ["methods"]}
225
+ ],
226
+ "properties": {
227
+ "lines": {"$ref": "#/definitions/line_statistic"},
228
+ "branches": {"$ref": "#/definitions/coverage_statistic"},
229
+ "methods": {"$ref": "#/definitions/coverage_statistic"},
230
+ "files": {
231
+ "type": "array",
232
+ "description": "Project-relative paths of the files that fell into this group.",
233
+ "items": {"type": "string"}
234
+ }
235
+ }
236
+ },
237
+ "line_statistic": {
238
+ "type": "object",
239
+ "required": ["covered", "missed", "omitted", "total", "percent", "strength"],
240
+ "additionalProperties": false,
241
+ "properties": {
242
+ "covered": {"type": "integer", "minimum": 0},
243
+ "missed": {"type": "integer", "minimum": 0},
244
+ "omitted": {
245
+ "type": "integer",
246
+ "minimum": 0,
247
+ "description": "Lines that cannot be covered (blank, comment, etc.). Only present on line stats."
248
+ },
249
+ "total": {"type": "integer", "minimum": 0, "description": "Executable lines: `covered + missed`. Does not include `omitted`."},
250
+ "percent": {"type": "number"},
251
+ "strength": {"type": "number", "description": "Average number of executions across covered lines (hits per covered line)."}
252
+ }
253
+ },
254
+ "coverage_statistic": {
255
+ "type": "object",
256
+ "required": ["covered", "missed", "total", "percent", "strength"],
257
+ "additionalProperties": false,
258
+ "properties": {
259
+ "covered": {"type": "integer", "minimum": 0},
260
+ "missed": {"type": "integer", "minimum": 0},
261
+ "total": {"type": "integer", "minimum": 0, "description": "Total branches or methods: `covered + missed`."},
262
+ "percent": {"type": "number"},
263
+ "strength": {"type": "number", "description": "Average number of executions across covered branches or methods (hits per covered item)."}
264
+ }
265
+ },
266
+ "line_coverage": {
267
+ "description": "Coverage value for a source line in the per-file `lines` array. Integer hit count, `null` for a non-executable line (blank or comment), or the literal string `\"ignored\"` for a line inside a simplecov:disable / :nocov: region.",
268
+ "oneOf": [
269
+ {"type": "integer", "minimum": 0},
270
+ {"type": "null"},
271
+ {"type": "string", "const": "ignored"}
272
+ ]
273
+ },
274
+ "branch_method_coverage": {
275
+ "description": "Coverage value for a branch or method `coverage` field. Integer hit count, or the literal string `\"ignored\"` for code inside a simplecov:disable / :nocov: region. Unlike line coverage, `null` never occurs: every branch and method maps to an executable construct.",
276
+ "oneOf": [
277
+ {"type": "integer", "minimum": 0},
278
+ {"type": "string", "const": "ignored"}
279
+ ]
280
+ },
281
+ "expected_actual": {
282
+ "type": "object",
283
+ "required": ["expected", "actual"],
284
+ "additionalProperties": false,
285
+ "properties": {
286
+ "expected": {"type": "number"},
287
+ "actual": {"type": "number"}
288
+ }
289
+ },
290
+ "maximum_actual": {
291
+ "type": "object",
292
+ "required": ["maximum", "actual"],
293
+ "additionalProperties": false,
294
+ "properties": {
295
+ "maximum": {"type": "number"},
296
+ "actual": {"type": "number"}
297
+ }
298
+ }
299
+ }
300
+ }