From f50ba1647b124085c27d6b5676bb80129e573b1a Mon Sep 17 00:00:00 2001 From: Villiam Riegler <74962511+villiamriegler@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:34:28 +0100 Subject: [PATCH 1/3] docs: Add report template (#2) - Adds the report template as is provided on canvas to the repository. --- report.md | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 report.md diff --git a/report.md b/report.md new file mode 100644 index 000000000000..6730fc5a313e --- /dev/null +++ b/report.md @@ -0,0 +1,103 @@ +# Report for assignment 3 + +This is a template for your report. You are free to modify it as needed. +It is not required to use markdown for your report either, but the report +has to be delivered in a standard, cross-platform format. + +## Project + +Name: + +URL: + +One or two sentences describing it + +## Onboarding experience + +Did it build and run as documented? + +See the assignment for details; if everything works out of the box, +there is no need to write much here. If the first project(s) you picked +ended up being unsuitable, you can describe the "onboarding experience" +for each project, along with reason(s) why you changed to a different one. + + +## Complexity + +1. What are your results for five complex functions? + * Did all methods (tools vs. manual count) get the same result? + * Are the results clear? +2. Are the functions just complex, or also long? +3. What is the purpose of the functions? +4. Are exceptions taken into account in the given measurements? +5. Is the documentation clear w.r.t. all the possible outcomes? + +## Refactoring + +Plan for refactoring complex code: + +Estimated impact of refactoring (lower CC, but other drawbacks?). + +Carried out refactoring (optional, P+): + +git diff ... + +## Coverage + +### Tools + +Document your experience in using a "new"/different coverage tool. + +How well was the tool documented? Was it possible/easy/difficult to +integrate it with your build environment? + +### Your own coverage tool + +Show a patch (or link to a branch) that shows the instrumented code to +gather coverage measurements. + +The patch is probably too long to be copied here, so please add +the git command that is used to obtain the patch instead: + +git diff ... + +What kinds of constructs does your tool support, and how accurate is +its output? + +### Evaluation + +1. How detailed is your coverage measurement? + +2. What are the limitations of your own tool? + +3. Are the results of your tool consistent with existing coverage tools? + +## Coverage improvement + +Show the comments that describe the requirements for the coverage. + +Report of old coverage: [link] + +Report of new coverage: [link] + +Test cases added: + +git diff ... + +Number of test cases added: two per team member (P) or at least four (P+). + +## Self-assessment: Way of working + +Current state according to the Essence standard: ... + +Was the self-assessment unanimous? Any doubts about certain items? + +How have you improved so far? + +Where is potential for improvement? + +## Overall experience + +What are your main take-aways from this project? What did you learn? + +Is there something special you want to mention here? From ee7caeea38d1ad17e8e8d7dc348a8bbc7a1650a1 Mon Sep 17 00:00:00 2001 From: Alrik Jonek Holmquist <49986286+Alrikk@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:26:36 +0100 Subject: [PATCH 2/3] chore: fix linting (#4) --- report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/report.md b/report.md index 6730fc5a313e..b3f2aecac618 100644 --- a/report.md +++ b/report.md @@ -15,7 +15,7 @@ One or two sentences describing it ## Onboarding experience Did it build and run as documented? - + See the assignment for details; if everything works out of the box, there is no need to write much here. If the first project(s) you picked ended up being unsuitable, you can describe the "onboarding experience" From bd1ef9fcb336d84dddfbf1f3734dda67f1d17364 Mon Sep 17 00:00:00 2001 From: Lynx-Zhang <1321671150@qq.com> Date: Tue, 17 Feb 2026 18:51:39 +0100 Subject: [PATCH 3/3] Added DIY branch coverage tool --- BRANCH_COVERAGE_GUIDE.md | 61 +++++++++++++++++++++++ mypy/branch_coverage.py | 101 +++++++++++++++++++++++++++++++++++++++ mypy/test/conftest.py | 42 ++++++++++++++++ 3 files changed, 204 insertions(+) create mode 100644 BRANCH_COVERAGE_GUIDE.md create mode 100644 mypy/branch_coverage.py create mode 100644 mypy/test/conftest.py diff --git a/BRANCH_COVERAGE_GUIDE.md b/BRANCH_COVERAGE_GUIDE.md new file mode 100644 index 000000000000..e90c6058fabf --- /dev/null +++ b/BRANCH_COVERAGE_GUIDE.md @@ -0,0 +1,61 @@ +## Add Branch Coverage + +### Step 1: Register in `mypy/branch_coverage.py` + +```python +BRANCH_COVERAGE = { + 'check_return_stmt': set(), + 'your_function_name': set(), # Add your function +} + +BRANCH_DESCRIPTIONS = { + 'your_function_name': { + 1: 'Function entry', + 2: 'if condition_x - TRUE', + 3: 'if condition_x - FALSE', + 4: 'elif condition_y - TRUE', + 5: 'else branch', + } +} +``` + +### Step 2: Instrument Your Function + +```python +def your_function_name(self, param): + from mypy.branch_coverage import record_branch + record_branch('your_function_name', 1) # Function entry + + if condition_x: + record_branch('your_function_name', 2) # TRUE + # code... + elif condition_y: + record_branch('your_function_name', 3) # FALSE from if + record_branch('your_function_name', 4) # TRUE for elif + # code... + else: + record_branch('your_function_name', 3) # FALSE from if + record_branch('your_function_name', 5) # else + # code... +``` + +**Important:** Import `record_branch` inside the function to avoid circular imports. + +## Run Tests + +**CRITICAL**: Must use `-n0` to disable parallel execution, or coverage data will not be collected! + +```bash +# Activate virtual environment first +source venv/bin/activate + +# Run all tests +pytest mypy/test/testcheck.py -n0 + +# Run specific test file +pytest mypy/test/testcheck.py::TypeCheckSuite::::check-basic.test::testInvalidReturn -n0 +``` + +## View Reports + +Reports are automatically saved in the project root directory:`branch_coverage_report.txt` diff --git a/mypy/branch_coverage.py b/mypy/branch_coverage.py new file mode 100644 index 000000000000..deb897ea0935 --- /dev/null +++ b/mypy/branch_coverage.py @@ -0,0 +1,101 @@ +"""Branch Coverage Tracking Module""" + +import json +from pathlib import Path + +BRANCH_COVERAGE = { + 'check_return_stmt': set() +} + +BRANCH_DESCRIPTIONS = { + 'check_return_stmt': { + 1: 'Function entry', + 2: 'defn is not None - TRUE', + 3: 'defn is not None - FALSE', + 4: 'defn.is_generator - TRUE', + 5: 'defn.is_generator - FALSE (else/elif)', + 6: 'defn.is_coroutine - TRUE', + 7: 'defn.is_coroutine - FALSE (else)', + 8: 'isinstance(return_type, UninhabitedType) - TRUE', + 9: 'isinstance(return_type, UninhabitedType) - FALSE', + 10: 'not is_lambda and not return_type.ambiguous - TRUE', + 11: 'not is_lambda and not return_type.ambiguous - FALSE', + 12: 's.expr - TRUE (has return value)', + 13: 's.expr - FALSE (empty return)', + 14: 'isinstance(s.expr, (CallExpr, ...)) or isinstance(s.expr, AwaitExpr) - TRUE', + 15: 'isinstance(s.expr, (CallExpr, ...)) or isinstance(s.expr, AwaitExpr) - FALSE', + 16: 'isinstance(typ, Instance) and typ.type.fullname in NOT_IMPLEMENTED - TRUE', + 17: 'isinstance(typ, Instance) and typ.type.fullname in NOT_IMPLEMENTED - FALSE', + 18: 'defn.is_async_generator - TRUE', + 19: 'defn.is_async_generator - FALSE', + 20: 'isinstance(typ, AnyType) - TRUE', + 21: 'isinstance(typ, AnyType) - FALSE', + 22: 'warn_return_any conditions - TRUE (all conditions met)', + 23: 'warn_return_any conditions - FALSE (at least one condition not met)', + 24: 'declared_none_return - TRUE', + 25: 'declared_none_return - FALSE', + 26: 'is_lambda or isinstance(typ, NoneType) - TRUE', + 27: 'is_lambda or isinstance(typ, NoneType) - FALSE', + 28: 'defn.is_generator and not defn.is_coroutine and isinstance(return_type, AnyType) - TRUE', + 29: 'defn.is_generator and not defn.is_coroutine and isinstance(return_type, AnyType) - FALSE', + 30: 'isinstance(return_type, (NoneType, AnyType)) - TRUE', + 31: 'isinstance(return_type, (NoneType, AnyType)) - FALSE', + 32: 'self.in_checked_function() - TRUE', + 33: 'self.in_checked_function() - FALSE', + } +} + + +def record_branch(function_name, branch_id): + + if function_name in BRANCH_COVERAGE: + BRANCH_COVERAGE[function_name].add(branch_id) + + +def get_coverage_report(): + + report = [] + report.append("=" * 80) + report.append("BRANCH COVERAGE REPORT") + report.append("=" * 80) + + for func_name, covered_branches in BRANCH_COVERAGE.items(): + report.append(f"\n{'=' * 80}") + report.append(f"Function: {func_name}") + report.append(f"{'=' * 80}") + + descriptions = BRANCH_DESCRIPTIONS.get(func_name, {}) + total_branches = len(descriptions) + covered_count = len(covered_branches) + + report.append(f"Coverage: {covered_count}/{total_branches} branches ({covered_count/total_branches*100:.1f}%)") + report.append("") + + + for branch_id in sorted(descriptions.keys()): + status = "COVERED" if branch_id in covered_branches else "NOT COVERED" + desc = descriptions[branch_id] + report.append(f" Branch {branch_id:2d}: {status:15s} | {desc}") + + + uncovered = set(descriptions.keys()) - covered_branches + if uncovered: + report.append("\n" + "=" * 80) + report.append("UNCOVERED BRANCHES:") + report.append("=" * 80) + for branch_id in sorted(uncovered): + report.append(f" Branch {branch_id:2d}: {descriptions[branch_id]}") + + report.append("\n" + "=" * 80) + return "\n".join(report) + + +def save_coverage_report(filename="branch_coverage_report.txt"): + + report = get_coverage_report() + output_path = Path.cwd() / filename + with open(output_path, 'w', encoding='utf-8') as f: + f.write(report) + print(f"\nCoverage report saved to: {output_path}") + + diff --git a/mypy/test/conftest.py b/mypy/test/conftest.py new file mode 100644 index 000000000000..569bb3c4f983 --- /dev/null +++ b/mypy/test/conftest.py @@ -0,0 +1,42 @@ +""" +Pytest configuration for branch coverage collection +""" + +import pytest + + +def pytest_sessionfinish(session, exitstatus): + """ + Hook that runs after all tests complete + """ + try: + from mypy.branch_coverage import ( + save_coverage_report, + get_coverage_report, + BRANCH_COVERAGE + ) + + total_covered = sum(len(branches) for branches in BRANCH_COVERAGE.values()) + + if total_covered > 0: + print("\n" + "=" * 80) + print("BRANCH COVERAGE COLLECTION COMPLETED") + print("=" * 80) + print(f"Total branches covered: {total_covered}") + + save_coverage_report() + + print("\n" + get_coverage_report()) + + print("\n" + "=" * 80) + print("Coverage reports saved!") + print("=" * 80) + else: + print("\nWarning: No branch coverage data collected") + + except ImportError: + print("\nBranch coverage module not found - skipping coverage report") + except Exception as e: + print(f"\nError saving coverage report: {e}") + import traceback + traceback.print_exc()