Skip to content

Commit 557dd15

Browse files
Ryuta1005nedbat
andauthored
feat: add statement and branch coverage percentages to JSON report (#2090)
* feat: add Statement/Branch coverage stats to JSON report * test: update JSON report tests for Statement/Branch coverage stats * Docs: Update FAQ for new JSON report metrics * update the changelog --------- Co-authored-by: Ned Batchelder <ned@nedbatchelder.com>
1 parent e18359c commit 557dd15

4 files changed

Lines changed: 86 additions & 2 deletions

File tree

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ Unreleased
2828
Otsuka for the `discussion <issue 2081_>`_ and the `implementation
2929
<pull 2085_>`_.
3030

31+
- The JSON report now include separate coverage totals for statements and
32+
branches, thanks to `Ryuta Otsuka <pull 2090_>`_.
33+
3134
- Fix: ``except*`` clauses were not handled properly under the "sysmon"
3235
measurement core, causing KeyError exceptions as described in `issue 2086`_.
3336
This is now fixed.
@@ -47,6 +50,7 @@ Unreleased
4750
.. _issue 2083: https://github.com/coveragepy/coveragepy/issues/2083
4851
.. _pull 2085: https://github.com/coveragepy/coveragepy/pull/2085
4952
.. _issue 2086: https://github.com/coveragepy/coveragepy/issues/2086
53+
.. _pull 2090: https://github.com/coveragepy/coveragepy/pull/2090
5054
.. _issue 2091: https://github.com/coveragepy/coveragepy/issues/2091
5155

5256

coverage/jsonreport.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ def make_summary(self, nums: Numbers) -> JsonObj:
5151
"percent_covered_display": nums.pc_covered_str,
5252
"missing_lines": nums.n_missing,
5353
"excluded_lines": nums.n_excluded,
54+
"percent_statements_covered": nums.pc_statements,
55+
"percent_statements_covered_display": nums.pc_statements_str,
5456
}
5557

5658
def make_branch_summary(self, nums: Numbers) -> JsonObj:
@@ -60,6 +62,8 @@ def make_branch_summary(self, nums: Numbers) -> JsonObj:
6062
"num_partial_branches": nums.n_partial_branches,
6163
"covered_branches": nums.n_executed_branches,
6264
"missing_branches": nums.n_missing_branches,
65+
"percent_branches_covered": nums.pc_branches,
66+
"percent_branches_covered_display": nums.pc_branches_str,
6367
}
6468

6569
def report(self, morfs: Iterable[TMorf] | None, outfile: IO[str]) -> float:

doc/faq.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ did not execute all of their exits.
116116

117117
The :ref:`JSON report <cmd_json>` includes more data that can be used to
118118
re-calculate the total percentage. Individual files have a ``summary`` key,
119-
and the report as a whole has a ``totals`` key that include items like these:
119+
and the report as a whole has a ``totals`` key that include items like these.
120+
The ``percent_statements_covered`` value is always included, and when branch
121+
coverage is measured there are matching branch values:
120122

121123
.. code-block:: json
122124
@@ -130,7 +132,11 @@ and the report as a whole has a ``totals`` key that include items like these:
130132
"num_partial_branches": 5,
131133
"num_statements": 114,
132134
"percent_covered": 10.76923076923077,
133-
"percent_covered_display": "11"
135+
"percent_covered_display": "11",
136+
"percent_statements_covered": 7.894736842105263,
137+
"percent_statements_covered_display": "8",
138+
"percent_branches_covered": 31.25,
139+
"percent_branches_covered_display": "31"
134140
}
135141
136142
The total percentage is calculated as::

tests/test_json.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ def test_branch_coverage(self) -> None:
129129
"missing_branches": 3,
130130
"percent_covered": 57.142857142857146,
131131
"percent_covered_display": "57",
132+
"percent_statements_covered": 62.5,
133+
"percent_statements_covered_display": "62",
134+
"percent_branches_covered": 50.0,
135+
"percent_branches_covered_display": "50",
132136
},
133137
}
134138
expected_result = {
@@ -151,6 +155,10 @@ def test_branch_coverage(self) -> None:
151155
"percent_covered_display": "57",
152156
"covered_branches": 3,
153157
"missing_branches": 3,
158+
"percent_statements_covered": 62.5,
159+
"percent_statements_covered_display": "62",
160+
"percent_branches_covered": 50.0,
161+
"percent_branches_covered_display": "50",
154162
},
155163
}
156164
# With regions, a lot of data is duplicated.
@@ -171,6 +179,8 @@ def test_simple_line_coverage(self) -> None:
171179
"num_statements": 8,
172180
"percent_covered": 62.5,
173181
"percent_covered_display": "62",
182+
"percent_statements_covered": 62.5,
183+
"percent_statements_covered_display": "62",
174184
},
175185
}
176186
expected_result = {
@@ -189,6 +199,8 @@ def test_simple_line_coverage(self) -> None:
189199
"num_statements": 8,
190200
"percent_covered": 62.5,
191201
"percent_covered_display": "62",
202+
"percent_statements_covered": 62.5,
203+
"percent_statements_covered_display": "62",
192204
},
193205
}
194206
# With regions, a lot of data is duplicated.
@@ -213,6 +225,8 @@ def test_regions_coverage(self) -> None:
213225
"num_statements": 8,
214226
"percent_covered": 87.5,
215227
"percent_covered_display": "88",
228+
"percent_statements_covered": 87.5,
229+
"percent_statements_covered_display": "88",
216230
},
217231
},
218232
"C": {
@@ -226,6 +240,8 @@ def test_regions_coverage(self) -> None:
226240
"num_statements": 0,
227241
"percent_covered": 100.0,
228242
"percent_covered_display": "100",
243+
"percent_statements_covered": 100.0,
244+
"percent_statements_covered_display": "100",
229245
},
230246
},
231247
"D": {
@@ -239,6 +255,8 @@ def test_regions_coverage(self) -> None:
239255
"num_statements": 4,
240256
"percent_covered": 0.0,
241257
"percent_covered_display": "0",
258+
"percent_statements_covered": 0.0,
259+
"percent_statements_covered_display": "0",
242260
},
243261
},
244262
},
@@ -256,6 +274,8 @@ def test_regions_coverage(self) -> None:
256274
"num_statements": 7,
257275
"percent_covered": 100.0,
258276
"percent_covered_display": "100",
277+
"percent_statements_covered": 100.0,
278+
"percent_statements_covered_display": "100",
259279
},
260280
},
261281
"c": {
@@ -269,6 +289,8 @@ def test_regions_coverage(self) -> None:
269289
"num_statements": 1,
270290
"percent_covered": 0.0,
271291
"percent_covered_display": "0",
292+
"percent_statements_covered": 0.0,
293+
"percent_statements_covered_display": "0",
272294
},
273295
},
274296
"D.e": {
@@ -282,6 +304,8 @@ def test_regions_coverage(self) -> None:
282304
"num_statements": 3,
283305
"percent_covered": 0.0,
284306
"percent_covered_display": "0",
307+
"percent_statements_covered": 0.0,
308+
"percent_statements_covered_display": "0",
285309
},
286310
},
287311
"D.f": {
@@ -295,6 +319,8 @@ def test_regions_coverage(self) -> None:
295319
"num_statements": 1,
296320
"percent_covered": 0.0,
297321
"percent_covered_display": "0",
322+
"percent_statements_covered": 0.0,
323+
"percent_statements_covered_display": "0",
298324
},
299325
},
300326
},
@@ -306,6 +332,8 @@ def test_regions_coverage(self) -> None:
306332
"num_statements": 12,
307333
"percent_covered": 58.333333333333336,
308334
"percent_covered_display": "58",
335+
"percent_statements_covered": 58.333333333333336,
336+
"percent_statements_covered_display": "58",
309337
},
310338
},
311339
},
@@ -321,6 +349,8 @@ def test_regions_coverage(self) -> None:
321349
"num_statements": 12,
322350
"percent_covered": 58.333333333333336,
323351
"percent_covered_display": "58",
352+
"percent_statements_covered": 58.333333333333336,
353+
"percent_statements_covered_display": "58",
324354
},
325355
}
326356
self._assert_expected_json_report_with_regions(cov, expected_result)
@@ -348,6 +378,10 @@ def test_branch_regions_coverage(self) -> None:
348378
"num_statements": 8,
349379
"percent_covered": 87.5,
350380
"percent_covered_display": "88",
381+
"percent_statements_covered": 87.5,
382+
"percent_statements_covered_display": "88",
383+
"percent_branches_covered": 100.0,
384+
"percent_branches_covered_display": "100",
351385
},
352386
},
353387
"C": {
@@ -367,6 +401,10 @@ def test_branch_regions_coverage(self) -> None:
367401
"num_statements": 0,
368402
"percent_covered": 100.0,
369403
"percent_covered_display": "100",
404+
"percent_statements_covered": 100.0,
405+
"percent_statements_covered_display": "100",
406+
"percent_branches_covered": 100.0,
407+
"percent_branches_covered_display": "100",
370408
},
371409
},
372410
"D": {
@@ -386,6 +424,10 @@ def test_branch_regions_coverage(self) -> None:
386424
"num_statements": 4,
387425
"percent_covered": 0.0,
388426
"percent_covered_display": "0",
427+
"percent_statements_covered": 0.0,
428+
"percent_statements_covered_display": "0",
429+
"percent_branches_covered": 0.0,
430+
"percent_branches_covered_display": "0",
389431
},
390432
},
391433
},
@@ -410,6 +452,10 @@ def test_branch_regions_coverage(self) -> None:
410452
"num_statements": 7,
411453
"percent_covered": 100.0,
412454
"percent_covered_display": "100",
455+
"percent_statements_covered": 100.0,
456+
"percent_statements_covered_display": "100",
457+
"percent_branches_covered": 100.0,
458+
"percent_branches_covered_display": "100",
413459
},
414460
},
415461
"D.e": {
@@ -429,6 +475,10 @@ def test_branch_regions_coverage(self) -> None:
429475
"num_statements": 3,
430476
"percent_covered": 0.0,
431477
"percent_covered_display": "0",
478+
"percent_statements_covered": 0.0,
479+
"percent_statements_covered_display": "0",
480+
"percent_branches_covered": 0.0,
481+
"percent_branches_covered_display": "0",
432482
},
433483
},
434484
"D.f": {
@@ -448,6 +498,10 @@ def test_branch_regions_coverage(self) -> None:
448498
"num_statements": 1,
449499
"percent_covered": 0.0,
450500
"percent_covered_display": "0",
501+
"percent_statements_covered": 0.0,
502+
"percent_statements_covered_display": "0",
503+
"percent_branches_covered": 100.0,
504+
"percent_branches_covered_display": "100",
451505
},
452506
},
453507
"c": {
@@ -467,6 +521,10 @@ def test_branch_regions_coverage(self) -> None:
467521
"num_statements": 1,
468522
"percent_covered": 0.0,
469523
"percent_covered_display": "0",
524+
"percent_statements_covered": 0.0,
525+
"percent_statements_covered_display": "0",
526+
"percent_branches_covered": 100.0,
527+
"percent_branches_covered_display": "100",
470528
},
471529
},
472530
},
@@ -483,6 +541,10 @@ def test_branch_regions_coverage(self) -> None:
483541
"num_statements": 12,
484542
"percent_covered": 50.0,
485543
"percent_covered_display": "50",
544+
"percent_statements_covered": 58.333333333333336,
545+
"percent_statements_covered_display": "58",
546+
"percent_branches_covered": 0.0,
547+
"percent_branches_covered_display": "0",
486548
},
487549
},
488550
},
@@ -502,6 +564,10 @@ def test_branch_regions_coverage(self) -> None:
502564
"num_statements": 12,
503565
"percent_covered": 50.0,
504566
"percent_covered_display": "50",
567+
"percent_statements_covered": 58.333333333333336,
568+
"percent_statements_covered_display": "58",
569+
"percent_branches_covered": 0.0,
570+
"percent_branches_covered_display": "0",
505571
},
506572
}
507573
self._assert_expected_json_report_with_regions(cov, expected_result)
@@ -540,6 +606,8 @@ def run_context_test(self, relative_files: bool) -> None:
540606
"num_statements": 8,
541607
"percent_covered": 62.5,
542608
"percent_covered_display": "62.50",
609+
"percent_statements_covered": 62.5,
610+
"percent_statements_covered_display": "62.50",
543611
},
544612
}
545613
expected_result = {
@@ -558,6 +626,8 @@ def run_context_test(self, relative_files: bool) -> None:
558626
"num_statements": 8,
559627
"percent_covered": 62.5,
560628
"percent_covered_display": "62.50",
629+
"percent_statements_covered": 62.5,
630+
"percent_statements_covered_display": "62.50",
561631
},
562632
}
563633
# With regions, a lot of data is duplicated.

0 commit comments

Comments
 (0)