From aabc54031a673e2f789620a993a90197a36f0e95 Mon Sep 17 00:00:00 2001 From: Christoph Blessing <33834216+cblessing24@users.noreply.github.com> Date: Thu, 18 Aug 2022 12:58:29 +0200 Subject: [PATCH] feat: include branches taken and missed in JSON report. #1425 Added more branches to the code whose coverage is checked. Add branch details to json report The json report now includes for each branch which branches have been executed, missed and what the percentage of covered branches was. Add exiting branch arc to json report test Update branch details format Executed and missing branch arcs are stored in the fields named 'executed_branches' and 'missing_branches' respectively. Both fields contain a list of two element lists. The first element represents the source line number and the second one the target line number. Exit branches have their target line number set to 0. Fix linting errors --- coverage/jsonreport.py | 13 +++++ tests/test_json.py | 115 +++++++++++++++++++++++++---------------- 2 files changed, 83 insertions(+), 45 deletions(-) diff --git a/coverage/jsonreport.py b/coverage/jsonreport.py index 43edc4520..7ca468e32 100644 --- a/coverage/jsonreport.py +++ b/coverage/jsonreport.py @@ -102,4 +102,17 @@ def report_one_file(self, coverage_data, analysis): 'covered_branches': nums.n_executed_branches, 'missing_branches': nums.n_missing_branches, }) + reported_file['executed_branches'] = list( + _convert_branch_arcs(analysis.executed_branch_arcs()) + ) + reported_file['missing_branches'] = list( + _convert_branch_arcs(analysis.missing_branch_arcs()) + ) return reported_file + + +def _convert_branch_arcs(branch_arcs): + """Convert branch arcs to a list of two-element tuples.""" + for source, targets in branch_arcs.items(): + for target in targets: + yield source, target if target != -1 else 0 diff --git a/tests/test_json.py b/tests/test_json.py index 63713af8c..7205c284c 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -21,6 +21,12 @@ def _assert_expected_json_report(self, cov, expected_result): a = {'b': 1} if a.get('a'): b = 1 + elif a.get('b'): + b = 2 + else: + b = 3 + if not a: + b = 4 """) a = self.start_import_stop(cov, "a") output_path = os.path.join(self.temp_dir, "a.json") @@ -43,34 +49,44 @@ def test_branch_coverage(self): }, 'files': { 'a.py': { - 'executed_lines': [1, 2], - 'missing_lines': [3], + 'executed_lines': [1, 2, 4, 5, 8], + 'missing_lines': [3, 7, 9], 'excluded_lines': [], + 'executed_branches': [ + [2, 4], + [4, 5], + [8, 0], + ], + 'missing_branches': [ + [2, 3], + [4, 7], + [8, 9], + ], 'summary': { - 'missing_lines': 1, - 'covered_lines': 2, - 'num_statements': 3, - 'num_branches': 2, + 'missing_lines': 3, + 'covered_lines': 5, + 'num_statements': 8, + 'num_branches': 6, 'excluded_lines': 0, - 'num_partial_branches': 1, - 'covered_branches': 1, - 'missing_branches': 1, - 'percent_covered': 60.0, - 'percent_covered_display': '60', + 'num_partial_branches': 3, + 'covered_branches': 3, + 'missing_branches': 3, + 'percent_covered': 57.142857142857146, + 'percent_covered_display': '57', }, }, }, 'totals': { - 'missing_lines': 1, - 'covered_lines': 2, - 'num_statements': 3, - 'num_branches': 2, + 'missing_lines': 3, + 'covered_lines': 5, + 'num_statements': 8, + 'num_branches': 6, 'excluded_lines': 0, - 'num_partial_branches': 1, - 'percent_covered': 60.0, - 'percent_covered_display': '60', - 'covered_branches': 1, - 'missing_branches': 1, + 'num_partial_branches': 3, + 'percent_covered': 57.142857142857146, + 'percent_covered_display': '57', + 'covered_branches': 3, + 'missing_branches': 3, }, } self._assert_expected_json_report(cov, expected_result) @@ -85,26 +101,26 @@ def test_simple_line_coverage(self): }, 'files': { 'a.py': { - 'executed_lines': [1, 2], - 'missing_lines': [3], + 'executed_lines': [1, 2, 4, 5, 8], + 'missing_lines': [3, 7, 9], 'excluded_lines': [], 'summary': { 'excluded_lines': 0, - 'missing_lines': 1, - 'covered_lines': 2, - 'num_statements': 3, - 'percent_covered': 66.66666666666667, - 'percent_covered_display': '67', + 'missing_lines': 3, + 'covered_lines': 5, + 'num_statements': 8, + 'percent_covered': 62.5, + 'percent_covered_display': '62', }, }, }, 'totals': { 'excluded_lines': 0, - 'missing_lines': 1, - 'covered_lines': 2, - 'num_statements': 3, - 'percent_covered': 66.66666666666667, - 'percent_covered_display': '67', + 'missing_lines': 3, + 'covered_lines': 5, + 'num_statements': 8, + 'percent_covered': 62.5, + 'percent_covered_display': '62', }, } self._assert_expected_json_report(cov, expected_result) @@ -130,8 +146,8 @@ def run_context_test(self, relative_files): }, 'files': { 'a.py': { - 'executed_lines': [1, 2], - 'missing_lines': [3], + 'executed_lines': [1, 2, 4, 5, 8], + 'missing_lines': [3, 7, 9], 'excluded_lines': [], "contexts": { "1": [ @@ -139,25 +155,34 @@ def run_context_test(self, relative_files): ], "2": [ "cool_test" - ] + ], + "4": [ + "cool_test" + ], + "5": [ + "cool_test" + ], + "8": [ + "cool_test" + ], }, 'summary': { 'excluded_lines': 0, - 'missing_lines': 1, - 'covered_lines': 2, - 'num_statements': 3, - 'percent_covered': 66.66666666666667, - 'percent_covered_display': '66.67', + 'missing_lines': 3, + 'covered_lines': 5, + 'num_statements': 8, + 'percent_covered': 62.5, + 'percent_covered_display': '62.50', }, }, }, 'totals': { 'excluded_lines': 0, - 'missing_lines': 1, - 'covered_lines': 2, - 'num_statements': 3, - 'percent_covered': 66.66666666666667, - 'percent_covered_display': '66.67', + 'missing_lines': 3, + 'covered_lines': 5, + 'num_statements': 8, + 'percent_covered': 62.5, + 'percent_covered_display': '62.50', }, } self._assert_expected_json_report(cov, expected_result)