added doc and text coverage

This commit is contained in:
Duncan Tourolle 2025-06-07 15:39:18 +02:00
parent b424c2d831
commit 624f92b8f9
3 changed files with 239 additions and 2 deletions

View File

@ -40,9 +40,57 @@ jobs:
- name: Run tests with pytest - name: Run tests with pytest
run: | run: |
# Run tests with coverage # Run tests with coverage
python -m pytest tests/ -v --cov=pyWebLayout --cov-report=term-missing python -m pytest tests/ -v --cov=pyWebLayout --cov-report=term-missing --cov-report=json --cov-report=html
- name: Generate test coverage badge
run: |
# Install coverage-badge for generating badges
pip install coverage-badge
# Generate coverage badge from coverage data
coverage-badge -o coverage.svg
- name: Check documentation coverage
run: |
# Install interrogate for documentation coverage
pip install interrogate
# Generate documentation coverage report and badge
interrogate -v --ignore-init-method --ignore-init-module --ignore-magic --ignore-private --ignore-property-decorators --ignore-semiprivate --fail-under=80 --generate-badge coverage-docs.svg pyWebLayout/
- name: Generate coverage reports
run: |
# Generate coverage summary for README
python -c "
import json
import os
# Read coverage data
if os.path.exists('coverage.json'):
with open('coverage.json', 'r') as f:
coverage_data = json.load(f)
total_coverage = round(coverage_data['totals']['percent_covered'], 1)
# Create coverage summary file
with open('coverage-summary.txt', 'w') as f:
f.write(f'{total_coverage}%')
print(f'Test Coverage: {total_coverage}%')
else:
print('No coverage data found')
"
- name: Upload coverage artifacts
uses: actions/upload-artifact@v3
with:
name: coverage-reports
path: |
coverage.svg
coverage-docs.svg
htmlcov/
coverage.json
coverage-summary.txt
- name: Test package installation - name: Test package installation
run: | run: |
# Test that the package can be imported # Test that the package can be imported
python -c "import pyWebLayout; print('Package imported successfully')" python -c "import pyWebLayout; print('Package imported successfully')"

108
scripts/run_coverage.py Normal file
View File

@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""
Local coverage runner script.
Runs test and documentation coverage locally and generates badges.
"""
import subprocess
import sys
import os
def run_command(cmd, description):
"""Run a command and handle errors."""
print(f"\n{'='*50}")
print(f"Running: {description}")
print(f"Command: {cmd}")
print(f"{'='*50}")
try:
result = subprocess.run(cmd, shell=True, check=True, capture_output=True, text=True)
print(result.stdout)
if result.stderr:
print("STDERR:", result.stderr)
return True
except subprocess.CalledProcessError as e:
print(f"Error running {description}:")
print(f"Return code: {e.returncode}")
print(f"STDOUT: {e.stdout}")
print(f"STDERR: {e.stderr}")
return False
def main():
"""Run full coverage analysis locally."""
print("Local Coverage Analysis for pyWebLayout")
print("=" * 60)
# Change to project root if running from scripts directory
if os.path.basename(os.getcwd()) == "scripts":
os.chdir("..")
# Install required packages
print("\n1. Installing required packages...")
packages = [
"pytest pytest-cov",
"coverage-badge",
"interrogate"
]
for package in packages:
if not run_command(f"pip install {package}", f"Installing {package}"):
print(f"Failed to install {package}, continuing...")
# Run tests with coverage
print("\n2. Running tests with coverage...")
test_cmd = "python -m pytest tests/ -v --cov=pyWebLayout --cov-report=term-missing --cov-report=json --cov-report=html"
run_command(test_cmd, "Running tests with coverage")
# Generate test coverage badge
print("\n3. Generating test coverage badge...")
run_command("coverage-badge -o coverage.svg", "Generating test coverage badge")
# Check documentation coverage
print("\n4. Checking documentation coverage...")
docs_cmd = "interrogate -v --ignore-init-method --ignore-init-module --ignore-magic --ignore-private --ignore-property-decorators --ignore-semiprivate --fail-under=80 --generate-badge coverage-docs.svg pyWebLayout/"
run_command(docs_cmd, "Checking documentation coverage")
# Generate coverage summary
print("\n5. Generating coverage summary...")
summary_script = """
import json
import os
if os.path.exists('coverage.json'):
with open('coverage.json', 'r') as f:
coverage_data = json.load(f)
total_coverage = round(coverage_data['totals']['percent_covered'], 1)
with open('coverage-summary.txt', 'w') as f:
f.write(f'{total_coverage}%')
print(f'Test Coverage: {total_coverage}%')
print(f'Lines Covered: {coverage_data["totals"]["covered_lines"]}/{coverage_data["totals"]["num_statements"]}')
else:
print('No coverage data found')
"""
run_command(f'python -c "{summary_script}"', "Generating coverage summary")
# List generated files
print("\n6. Generated files:")
files = ["coverage.svg", "coverage-docs.svg", "coverage-summary.txt", "htmlcov/", "coverage.json"]
for file in files:
if os.path.exists(file):
print(f"{file}")
else:
print(f"{file} (not found)")
print("\n" + "="*60)
print("Coverage analysis complete!")
print("To update your README with badges, run:")
print(" python scripts/update_coverage_badges.py")
print("\nTo view detailed HTML coverage report:")
print(" open htmlcov/index.html")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""
Script to update README.md with coverage badges from CI artifacts.
This script should be run after CI completes to update the badges in your README.
"""
import os
import re
import sys
def update_readme_badges():
"""Update README.md with coverage badges."""
readme_path = "README.md"
if not os.path.exists(readme_path):
print("README.md not found!")
return False
# Read current README
with open(readme_path, 'r') as f:
content = f.read()
# Coverage badges to add/update
test_coverage_badge = "![Test Coverage](./coverage.svg)"
docs_coverage_badge = "![Documentation Coverage](./coverage-docs.svg)"
# Check if badges already exist and update them, otherwise add them at the top
if "![Test Coverage]" in content:
content = re.sub(r'!\[Test Coverage\]\([^)]+\)', test_coverage_badge, content)
else:
# Add after the first line (title)
lines = content.split('\n')
if len(lines) > 0:
lines.insert(1, f"\n{test_coverage_badge}")
content = '\n'.join(lines)
if "![Documentation Coverage]" in content:
content = re.sub(r'!\[Documentation Coverage\]\([^)]+\)', docs_coverage_badge, content)
else:
# Add after test coverage badge
lines = content.split('\n')
for i, line in enumerate(lines):
if "![Test Coverage]" in line:
lines.insert(i + 1, docs_coverage_badge)
break
content = '\n'.join(lines)
# Write updated README
with open(readme_path, 'w') as f:
f.write(content)
print("README.md updated with coverage badges!")
return True
def show_coverage_summary():
"""Display coverage summary if available."""
if os.path.exists("coverage-summary.txt"):
with open("coverage-summary.txt", 'r') as f:
test_coverage = f.read().strip()
print(f"Current Test Coverage: {test_coverage}")
# Try to get documentation coverage from interrogate output
if os.path.exists("coverage.json"):
import json
try:
with open("coverage.json", 'r') as f:
coverage_data = json.load(f)
print(f"Detailed Coverage: {coverage_data['totals']['percent_covered']:.1f}%")
print(f"Lines Covered: {coverage_data['totals']['covered_lines']}/{coverage_data['totals']['num_statements']}")
except (KeyError, json.JSONDecodeError):
print("Could not parse coverage data")
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "--summary":
show_coverage_summary()
else:
update_readme_badges()
show_coverage_summary()