1313from typing import TYPE_CHECKING , Any , Dict , Generator , Literal , TypedDict
1414
1515import pytest
16+ from packaging .version import Version
1617
1718if TYPE_CHECKING :
1819 from pluggy import Result
@@ -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,18 @@ 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 (
483+ [total_exits for total_exits , _ in branch_stats .values ()]
484+ )
485+ taken_file_branches = sum (
486+ [taken_exits for _ , taken_exits in branch_stats .values ()]
487+ )
488+
462489 except NoSource :
463490 # as per issue 24308 this best way to handle this edge case
464491 continue
@@ -473,6 +500,8 @@ def pytest_sessionfinish(session, exitstatus):
473500 file_info : FileCoverageInfo = {
474501 "lines_covered" : list (lines_covered ), # list of int
475502 "lines_missed" : list (lines_missed ), # list of int
503+ "executed_branches" : taken_file_branches ,
504+ "total_branches" : total_file_branches ,
476505 }
477506 # convert relative path to absolute path
478507 if not pathlib .Path (file ).is_absolute ():
0 commit comments