1111import sys
1212import traceback
1313from typing import TYPE_CHECKING , Any , Dict , Generator , Literal , TypedDict
14+ from packaging .version import Version
1415
1516import pytest
1617
@@ -61,6 +62,7 @@ def __init__(self, message):
6162collected_tests_so_far = []
6263TEST_RUN_PIPE = os .getenv ("TEST_RUN_PIPE" )
6364SYMLINK_PATH = None
65+ INCLUDE_BRANCHES = False
6466
6567
6668def pytest_load_initial_conftests (early_config , parser , args ): # noqa: ARG001
@@ -70,6 +72,9 @@ def pytest_load_initial_conftests(early_config, parser, args): # noqa: ARG001
7072 raise VSCodePytestError (
7173 "\n \n ERROR: pytest-cov is not installed, please install this before running pytest with coverage as pytest-cov is required. \n "
7274 )
75+ if "--cov-branch" in args :
76+ global INCLUDE_BRANCHES
77+ INCLUDE_BRANCHES = True
7378
7479 global TEST_RUN_PIPE
7580 TEST_RUN_PIPE = os .getenv ("TEST_RUN_PIPE" )
@@ -363,6 +368,8 @@ def check_skipped_condition(item):
363368class FileCoverageInfo (TypedDict ):
364369 lines_covered : list [int ]
365370 lines_missed : list [int ]
371+ executed_branches : int
372+ total_branches : int
366373
367374
368375def pytest_sessionfinish (session , exitstatus ):
@@ -436,6 +443,15 @@ def pytest_sessionfinish(session, exitstatus):
436443 # load the report and build the json result to return
437444 import coverage
438445
446+ coverage_version = Version (coverage .__version__ )
447+ global INCLUDE_BRANCHES
448+ # only include branches if coverage version is 7.7.0 or greater (as this was when the api saves)
449+ if coverage_version < Version ("7.7.0" ) and INCLUDE_BRANCHES :
450+ print (
451+ "Plugin warning[vscode-pytest]: Branch coverage not supported in this coverage versions < 7.7.0. Please upgrade coverage package if you would like to see branch coverage."
452+ )
453+ INCLUDE_BRANCHES = False
454+
439455 try :
440456 from coverage .exceptions import NoSource
441457 except ImportError :
@@ -448,9 +464,8 @@ def pytest_sessionfinish(session, exitstatus):
448464 file_coverage_map : dict [str , FileCoverageInfo ] = {}
449465
450466 # remove files omitted per coverage report config if any
451- omit_files = cov .config .report_omit
452- if omit_files :
453- print ("Plugin info[vscode-pytest]: Omit files/rules: " , omit_files )
467+ omit_files : list [str ] | None = cov .config .report_omit
468+ if omit_files is not None :
454469 for pattern in omit_files :
455470 for file in list (file_set ):
456471 if pathlib .Path (file ).match (pattern ):
@@ -459,6 +474,14 @@ def pytest_sessionfinish(session, exitstatus):
459474 for file in file_set :
460475 try :
461476 analysis = cov .analysis2 (file )
477+ taken_file_branches = 0
478+ total_file_branches = - 1
479+
480+ if INCLUDE_BRANCHES :
481+ branch_stats : dict [int , tuple [int , int ]] = cov .branch_stats (file )
482+ total_file_branches = sum ([total_exits for total_exits , _ in branch_stats .values ()])
483+ taken_file_branches = sum ([taken_exits for _ , taken_exits in branch_stats .values ()])
484+
462485 except NoSource :
463486 # as per issue 24308 this best way to handle this edge case
464487 continue
@@ -473,6 +496,8 @@ def pytest_sessionfinish(session, exitstatus):
473496 file_info : FileCoverageInfo = {
474497 "lines_covered" : list (lines_covered ), # list of int
475498 "lines_missed" : list (lines_missed ), # list of int
499+ "executed_branches" : taken_file_branches ,
500+ "total_branches" : total_file_branches ,
476501 }
477502 # convert relative path to absolute path
478503 if not pathlib .Path (file ).is_absolute ():
0 commit comments