added doc and text coverage
This commit is contained in:
parent
b424c2d831
commit
624f92b8f9
@ -40,7 +40,55 @@ 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: |
|
||||||
|
|||||||
108
scripts/run_coverage.py
Normal file
108
scripts/run_coverage.py
Normal 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()
|
||||||
81
scripts/update_coverage_badges.py
Normal file
81
scripts/update_coverage_badges.py
Normal 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 = ""
|
||||||
|
docs_coverage_badge = ""
|
||||||
|
|
||||||
|
# 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()
|
||||||
Loading…
x
Reference in New Issue
Block a user