coverage improvmeents
Some checks failed
Python CI / test (push) Failing after 41s

This commit is contained in:
Duncan Tourolle 2025-06-07 18:01:31 +02:00
parent dd2c98d4e0
commit 98cbe296a5
9 changed files with 275 additions and 80 deletions

View File

@ -48,7 +48,7 @@ jobs:
continue-on-error: true continue-on-error: true
run: | run: |
# Run tests with coverage # Run tests with coverage
python -m pytest tests/ -v --cov=pyWebLayout --cov-report=term-missing --cov-report=json --cov-report=html python -m pytest tests/ -v --cov=pyWebLayout --cov-report=term-missing --cov-report=json --cov-report=html --cov-report=xml
- name: Check documentation coverage - name: Check documentation coverage
id: docs id: docs
@ -102,6 +102,7 @@ jobs:
with open('coverage-summary.txt', 'w') as f: with open('coverage-summary.txt', 'w') as f:
f.write(f'{total_coverage}%') f.write(f'{total_coverage}%')
print(f'Test Coverage: {total_coverage}%') print(f'Test Coverage: {total_coverage}%')
print(f'Lines Covered: {coverage_data[\'totals\'][\'covered_lines\']}/{coverage_data[\'totals\'][\'num_statements\']}')
else: else:
print('No coverage data found') print('No coverage data found')
" "
@ -137,6 +138,7 @@ jobs:
coverage-docs.svg coverage-docs.svg
htmlcov/ htmlcov/
coverage.json coverage.json
coverage.xml
coverage-summary.txt coverage-summary.txt
- name: Safety check - prevent infinite loops - name: Safety check - prevent infinite loops

View File

@ -1,5 +1,5 @@
<svg width="140" height="20" viewBox="0 0 140 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"> <svg width="140" height="20" viewBox="0 0 140 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<title>interrogate: 93.2%</title> <title>interrogate: 90.0%</title>
<g transform="matrix(1,0,0,1,22,0)"> <g transform="matrix(1,0,0,1,22,0)">
<g id="backgrounds" transform="matrix(1.32789,0,0,1,-22.3892,0)"> <g id="backgrounds" transform="matrix(1.32789,0,0,1,-22.3892,0)">
<rect x="0" y="0" width="71" height="20" style="fill:rgb(85,85,85);"/> <rect x="0" y="0" width="71" height="20" style="fill:rgb(85,85,85);"/>
@ -12,8 +12,8 @@
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
<text x="590" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="610">interrogate</text> <text x="590" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="610">interrogate</text>
<text x="590" y="140" transform="scale(.1)" textLength="610">interrogate</text> <text x="590" y="140" transform="scale(.1)" textLength="610">interrogate</text>
<text x="1160" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="370" data-interrogate="result">93.2%</text> <text x="1160" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="370" data-interrogate="result">90.0%</text>
<text x="1160" y="140" transform="scale(.1)" textLength="370" data-interrogate="result">93.2%</text> <text x="1160" y="140" transform="scale(.1)" textLength="370" data-interrogate="result">90.0%</text>
</g> </g>
<g id="logo-shadow" serif:id="logo shadow" transform="matrix(0.854876,0,0,0.854876,-6.73514,1.732)"> <g id="logo-shadow" serif:id="logo shadow" transform="matrix(0.854876,0,0,0.854876,-6.73514,1.732)">
<g transform="matrix(0.299012,0,0,0.299012,9.70229,-6.68582)"> <g transform="matrix(0.299012,0,0,0.299012,9.70229,-6.68582)">

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

1
coverage-summary.txt Normal file
View File

@ -0,0 +1 @@
33.9%

1
coverage.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
<?xml version="1.0" ?> <?xml version="1.0" ?>
<coverage version="7.8.2" timestamp="1749309218680" lines-valid="3794" lines-covered="1466" line-rate="0.3864" branches-valid="1364" branches-covered="191" branch-rate="0.14" complexity="0"> <coverage version="7.8.2" timestamp="1749312034554" lines-valid="3794" lines-covered="1519" line-rate="0.4004" branches-valid="1364" branches-covered="227" branch-rate="0.1664" complexity="0">
<!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.8.2 --> <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.8.2 -->
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd --> <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
<sources> <sources>
@ -652,7 +652,7 @@
</class> </class>
</classes> </classes>
</package> </package>
<package name="abstract" line-rate="0.7742" branch-rate="0.3663" complexity="0"> <package name="abstract" line-rate="0.8326" branch-rate="0.5756" complexity="0">
<classes> <classes>
<class name="__init__.py" filename="abstract/__init__.py" complexity="0" line-rate="1" branch-rate="1"> <class name="__init__.py" filename="abstract/__init__.py" complexity="0" line-rate="1" branch-rate="1">
<methods/> <methods/>
@ -1432,7 +1432,7 @@
<line number="315" hits="1"/> <line number="315" hits="1"/>
</lines> </lines>
</class> </class>
<class name="inline.py" filename="abstract/inline.py" complexity="0" line-rate="0.6467" branch-rate="0.1818"> <class name="inline.py" filename="abstract/inline.py" complexity="0" line-rate="1" branch-rate="1">
<methods/> <methods/>
<lines> <lines>
<line number="1" hits="1"/> <line number="1" hits="1"/>
@ -1450,38 +1450,38 @@
<line number="30" hits="1"/> <line number="30" hits="1"/>
<line number="32" hits="1"/> <line number="32" hits="1"/>
<line number="33" hits="1"/> <line number="33" hits="1"/>
<line number="56" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="57,63"/> <line number="56" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="57" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="58,60"/> <line number="57" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="58" hits="0"/> <line number="58" hits="1"/>
<line number="60" hits="0"/> <line number="60" hits="1"/>
<line number="63" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="64,67"/> <line number="63" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="64" hits="0"/> <line number="64" hits="1"/>
<line number="67" hits="0"/> <line number="67" hits="1"/>
<line number="68" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="70,71"/> <line number="68" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="70" hits="0"/> <line number="70" hits="1"/>
<line number="71" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="73,81"/> <line number="71" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="73" hits="0"/> <line number="73" hits="1"/>
<line number="75" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="76,81"/> <line number="75" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="76" hits="0"/> <line number="76" hits="1"/>
<line number="77" hits="0"/> <line number="77" hits="1"/>
<line number="78" hits="0"/> <line number="78" hits="1"/>
<line number="81" hits="0"/> <line number="81" hits="1"/>
<line number="84" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="85,88"/> <line number="84" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="85" hits="0"/> <line number="85" hits="1"/>
<line number="88" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="90,113"/> <line number="88" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="90" hits="0"/> <line number="90" hits="1"/>
<line number="91" hits="0"/> <line number="91" hits="1"/>
<line number="92" hits="0"/> <line number="92" hits="1"/>
<line number="94" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="96,111"/> <line number="94" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="96" hits="0"/> <line number="96" hits="1"/>
<line number="97" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="99,104"/> <line number="97" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="99" hits="0"/> <line number="99" hits="1"/>
<line number="104" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="105,108"/> <line number="104" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="105" hits="0"/> <line number="105" hits="1"/>
<line number="108" hits="0"/> <line number="108" hits="1"/>
<line number="111" hits="0"/> <line number="111" hits="1"/>
<line number="113" hits="0"/> <line number="113" hits="1"/>
<line number="115" hits="0"/> <line number="115" hits="1"/>
<line number="117" hits="1"/> <line number="117" hits="1"/>
<line number="118" hits="1"/> <line number="118" hits="1"/>
<line number="120" hits="1"/> <line number="120" hits="1"/>
@ -1533,17 +1533,17 @@
<line number="248" hits="1"/> <line number="248" hits="1"/>
<line number="250" hits="1"/> <line number="250" hits="1"/>
<line number="251" hits="1"/> <line number="251" hits="1"/>
<line number="268" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="269,275"/> <line number="268" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="269" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="270,272"/> <line number="269" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="270" hits="0"/> <line number="270" hits="1"/>
<line number="272" hits="0"/> <line number="272" hits="1"/>
<line number="275" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="276,279"/> <line number="275" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="276" hits="0"/> <line number="276" hits="1"/>
<line number="279" hits="0"/> <line number="279" hits="1"/>
<line number="282" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="283,285"/> <line number="282" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="283" hits="0"/> <line number="283" hits="1"/>
<line number="285" hits="0"/> <line number="285" hits="1"/>
<line number="287" hits="0"/> <line number="287" hits="1"/>
<line number="289" hits="1"/> <line number="289" hits="1"/>
<line number="290" hits="1"/> <line number="290" hits="1"/>
<line number="292" hits="1"/> <line number="292" hits="1"/>
@ -1573,18 +1573,18 @@
<line number="352" hits="1"/> <line number="352" hits="1"/>
<line number="354" hits="1"/> <line number="354" hits="1"/>
<line number="355" hits="1"/> <line number="355" hits="1"/>
<line number="357" hits="0"/> <line number="357" hits="1"/>
<line number="359" hits="1"/> <line number="359" hits="1"/>
<line number="360" hits="1"/> <line number="360" hits="1"/>
<line number="371" hits="0"/> <line number="371" hits="1"/>
<line number="374" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="375,376"/> <line number="374" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="375" hits="0"/> <line number="375" hits="1"/>
<line number="376" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="377,378"/> <line number="376" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="377" hits="0"/> <line number="377" hits="1"/>
<line number="378" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="380,383"/> <line number="378" hits="1" branch="true" condition-coverage="100% (2/2)"/>
<line number="380" hits="0"/> <line number="380" hits="1"/>
<line number="383" hits="0"/> <line number="383" hits="1"/>
<line number="385" hits="0"/> <line number="385" hits="1"/>
</lines> </lines>
</class> </class>
</classes> </classes>

61
fix_coverage_paths.py Normal file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""
Fix coverage paths for Coverage Gutters extension.
This script modifies the coverage.xml file to use relative paths instead of absolute paths.
"""
import xml.etree.ElementTree as ET
import os
def fix_coverage_paths():
"""Fix the paths in coverage.xml to be relative to the workspace root."""
# Read the coverage.xml file
tree = ET.parse('coverage.xml')
root = tree.getroot()
# Get the current working directory
current_dir = os.getcwd()
print(f"Current directory: {current_dir}")
# Find and update the source element
sources = root.find('sources')
if sources is not None:
for source in sources.findall('source'):
old_path = source.text
print(f"Old source path: {old_path}")
# Convert absolute path to relative path
if old_path.startswith(current_dir):
# Remove the current directory part and the extra 'pyWebLayout' part
relative_path = old_path.replace(current_dir + '/pyWebLayout', './pyWebLayout')
# Or just use current directory
relative_path = '.'
source.text = relative_path
print(f"New source path: {relative_path}")
# Update all filename attributes in class elements to be relative
for package in root.findall('.//package'):
for cls in package.findall('classes/class'):
filename = cls.get('filename')
if filename:
# Ensure filename is relative to project root
if not filename.startswith('./') and not filename.startswith('pyWebLayout/'):
# Add pyWebLayout/ prefix if it's just a bare filename
if '/' not in filename:
# This is a top-level file
new_filename = f"pyWebLayout/{filename}"
else:
# This already has a path, just prefix with pyWebLayout/
new_filename = f"pyWebLayout/{filename}"
cls.set('filename', new_filename)
print(f"Updated filename: {filename} -> {new_filename}")
# Save the modified XML
tree.write('coverage.xml', encoding='utf-8', xml_declaration=True)
print("Coverage paths fixed successfully!")
if __name__ == "__main__":
fix_coverage_paths()

View File

@ -2,6 +2,7 @@
""" """
Simple coverage runner for Coverage Gutters extension. Simple coverage runner for Coverage Gutters extension.
Generates coverage.xml file needed by the VSCode Coverage Gutters extension. Generates coverage.xml file needed by the VSCode Coverage Gutters extension.
Uses the same approach as CI for consistency.
""" """
import subprocess import subprocess
@ -12,32 +13,46 @@ import os
def main(): def main():
"""Run coverage for Coverage Gutters.""" """Run coverage for Coverage Gutters."""
print("Generating coverage for Coverage Gutters...") print("Generating coverage for Coverage Gutters...")
print("Using the same pytest approach as CI...")
try: try:
# Run tests with coverage and generate XML report # Run tests with coverage and generate all report formats (same as CI)
cmd = [ cmd = [
sys.executable, "-m", "pytest", sys.executable, "-m", "pytest",
"tests/", "tests/",
"-v",
"--cov=pyWebLayout", "--cov=pyWebLayout",
"--cov-report=xml", "--cov-report=term-missing",
"--cov-report=term" "--cov-report=json",
"--cov-report=html",
"--cov-report=xml"
] ]
print(f"Running: {' '.join(cmd)}")
result = subprocess.run(cmd, check=True) result = subprocess.run(cmd, check=True)
# Check if coverage.xml was created # Check if coverage.xml was created
if os.path.exists("coverage.xml"): if os.path.exists("coverage.xml"):
print("✓ coverage.xml generated successfully!") print("✓ coverage.xml generated successfully!")
print("Coverage Gutters should now be able to display coverage data.") print("✓ coverage.json generated for CI compatibility")
print("✓ HTML coverage report generated in htmlcov/")
print("\nCoverage Gutters should now be able to display coverage data.")
print("\nTo use Coverage Gutters in VSCode:") print("\nTo use Coverage Gutters in VSCode:")
print("1. Open Command Palette (Ctrl+Shift+P)") print("1. Open Command Palette (Ctrl+Shift+P)")
print("2. Run 'Coverage Gutters: Display Coverage'") print("2. Run 'Coverage Gutters: Remove Coverage' (to clear cache)")
print("3. Or use the Coverage Gutters buttons in the status bar") print("3. Run 'Coverage Gutters: Display Coverage'")
print("4. Or use the Coverage Gutters buttons in the status bar")
# Show file info
size = os.path.getsize("coverage.xml")
print(f"\nGenerated coverage.xml: {size} bytes")
else: else:
print("✗ coverage.xml was not generated") print("✗ coverage.xml was not generated")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
print(f"Error running tests: {e}") print(f"Error running tests: {e}")
print("This may indicate test failures or missing dependencies.")
sys.exit(1) sys.exit(1)
except FileNotFoundError: except FileNotFoundError:
print("pytest not found. Please install it with: pip install pytest pytest-cov") print("pytest not found. Please install it with: pip install pytest pytest-cov")

View File

@ -58,7 +58,7 @@ def main():
# Generate test coverage badge # Generate test coverage badge
print("\n3. Generating test coverage badge...") print("\n3. Generating test coverage badge...")
run_command("coverage-badge -o coverage.svg", "Generating test coverage badge") run_command("coverage-badge -o coverage.svg -f", "Generating test coverage badge")
# Check documentation coverage # Check documentation coverage
print("\n4. Checking documentation coverage...") print("\n4. Checking documentation coverage...")
@ -67,25 +67,37 @@ def main():
# Generate coverage summary # Generate coverage summary
print("\n5. Generating coverage summary...") print("\n5. Generating coverage summary...")
summary_script = """
import json # Write a temporary script to avoid shell quoting issues
summary_script_content = '''import json
import os import os
if os.path.exists('coverage.json'): if os.path.exists("coverage.json"):
with open('coverage.json', 'r') as f: with open("coverage.json", "r") as f:
coverage_data = json.load(f) coverage_data = json.load(f)
total_coverage = round(coverage_data['totals']['percent_covered'], 1) total_coverage = round(coverage_data["totals"]["percent_covered"], 1)
covered_lines = coverage_data["totals"]["covered_lines"]
total_lines = coverage_data["totals"]["num_statements"]
with open('coverage-summary.txt', 'w') as f: with open("coverage-summary.txt", "w") as f:
f.write(f'{total_coverage}%') f.write(f"{total_coverage}%")
print(f'Test Coverage: {total_coverage}%') print(f"Test Coverage: {total_coverage}%")
print(f'Lines Covered: {coverage_data["totals"]["covered_lines"]}/{coverage_data["totals"]["num_statements"]}') print(f"Lines Covered: {covered_lines}/{total_lines}")
else: else:
print('No coverage data found') print("No coverage data found")
""" '''
run_command(f'python -c "{summary_script}"', "Generating coverage summary")
# Write and execute temporary script
with open('temp_coverage_summary.py', 'w') as f:
f.write(summary_script_content)
success = run_command("python temp_coverage_summary.py", "Generating coverage summary")
# Clean up temporary script
if os.path.exists('temp_coverage_summary.py'):
os.remove('temp_coverage_summary.py')
# List generated files # List generated files
print("\n6. Generated files:") print("\n6. Generated files:")

103
update_coverage_gutters.py Normal file
View File

@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""
Update coverage gutters configuration and fix coverage paths.
This script ensures Coverage Gutters can properly display coverage information.
"""
import os
import json
def main():
"""Main function to fix coverage gutters configuration."""
print("=== Coverage Gutters Fix ===")
print(f"Current working directory: {os.getcwd()}")
# 1. Check if coverage.xml exists
if os.path.exists('coverage.xml'):
print("✓ coverage.xml exists")
# Check file size and basic content
size = os.path.getsize('coverage.xml')
print(f"✓ coverage.xml size: {size} bytes")
# Read first few lines to verify it's valid XML
try:
with open('coverage.xml', 'r') as f:
first_line = f.readline().strip()
if first_line.startswith('<?xml'):
print("✓ coverage.xml appears to be valid XML")
else:
print("✗ coverage.xml does not start with XML declaration")
except Exception as e:
print(f"✗ Error reading coverage.xml: {e}")
else:
print("✗ coverage.xml does not exist")
print("Running coverage to generate coverage.xml...")
os.system("python -m coverage run --source=pyWebLayout -m unittest tests.test_abstract_inline")
os.system("python -m coverage xml")
# 2. Check VSCode settings
vscode_settings_path = '.vscode/settings.json'
if os.path.exists(vscode_settings_path):
print("✓ VSCode settings.json exists")
with open(vscode_settings_path, 'r') as f:
try:
settings = json.load(f)
gutters_config = {k: v for k, v in settings.items() if 'coverage-gutters' in k}
if gutters_config:
print("✓ Coverage Gutters settings found:")
for key, value in gutters_config.items():
print(f" {key}: {value}")
else:
print("✗ No Coverage Gutters settings found")
except json.JSONDecodeError as e:
print(f"✗ Error parsing VSCode settings: {e}")
else:
print("✗ VSCode settings.json not found")
# 3. Check if inline.py file exists
inline_file = 'pyWebLayout/abstract/inline.py'
if os.path.exists(inline_file):
print(f"{inline_file} exists")
# Check file size
size = os.path.getsize(inline_file)
print(f"{inline_file} size: {size} bytes")
else:
print(f"{inline_file} does not exist")
# 4. Run a fresh coverage collection specifically for the inline module
print("\n=== Running Fresh Coverage ===")
try:
print("Running tests with coverage...")
os.system("python -m coverage erase") # Clear old coverage data
os.system("python -m coverage run --source=pyWebLayout -m unittest tests.test_abstract_inline -v")
os.system("python -m coverage xml -o coverage.xml")
os.system("python -m coverage report --include='pyWebLayout/abstract/inline.py'")
print("✓ Fresh coverage data generated")
except Exception as e:
print(f"✗ Error generating coverage: {e}")
# 5. Instructions for manual verification
print("\n=== Manual Verification Steps ===")
print("1. In VSCode, open the Command Palette (Ctrl+Shift+P)")
print("2. Run 'Coverage Gutters: Display Coverage'")
print("3. If coverage doesn't appear, try:")
print(" - 'Coverage Gutters: Remove Coverage'")
print(" - 'Coverage Gutters: Display Coverage' again")
print("4. Check that coverage.xml contains data for pyWebLayout/abstract/inline.py")
print("5. The file should show 100% coverage (all lines covered)")
print("\n=== Troubleshooting ===")
print("If coverage still doesn't show:")
print("1. Restart VSCode")
print("2. Check Coverage Gutters extension is enabled")
print("3. Try running: python run_coverage_gutters.py")
print("4. Check VSCode Output panel for Coverage Gutters logs")
if __name__ == "__main__":
main()