Download tests/scripts/check_pass_rate.py from techprotrade/annator-command-center: direct link, hf CLI and curl.
- Browser
- Download file 10.8 kB
-
https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/scripts/check_pass_rate.py
- Command line
-
hf download hf://spaces/techprotrade/annator-command-center/tests/scripts/check_pass_rate.py
-
curl -L -o check_pass_rate.py https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/scripts/check_pass_rate.py
10.8 kB
| #!/usr/bin/env python | |
| """ | |
| Pass Rate Validation Script for Atom Test Suite | |
| This script validates test suite pass rate against a minimum threshold (default 90%). | |
| It parses pytest JSON output, categorizes failures, and returns appropriate exit codes. | |
| Usage: | |
| python check_pass_rate.py --json-file pytest_report.json --minimum 90 | |
| python check_pass_rate.py --help | |
| Exit Codes: | |
| 0: Pass rate meets or exceeds minimum threshold | |
| 1: Pass rate below minimum threshold | |
| 2: Error in execution | |
| """ | |
| import argparse | |
| import json | |
| import sys | |
| from datetime import datetime | |
| from pathlib import Path | |
| def parse_pytest_json(json_file: str): | |
| """ | |
| Parse pytest JSON report file and extract test results. | |
| Args: | |
| json_file: Path to pytest JSON report file | |
| Returns: | |
| Dictionary with passed, failed, error, skipped counts | |
| """ | |
| try: | |
| with open(json_file, 'r') as f: | |
| data = json.load(f) | |
| except FileNotFoundError: | |
| print(f"ERROR: JSON report file not found: {json_file}") | |
| sys.exit(2) | |
| except json.JSONDecodeError as e: | |
| print(f"ERROR: Invalid JSON in report file: {e}") | |
| sys.exit(2) | |
| # Initialize counters | |
| results = { | |
| "passed": 0, | |
| "failed": 0, | |
| "error": 0, | |
| "skipped": 0, | |
| "total": 0 | |
| } | |
| # Parse pytest-json-report format | |
| if "summary" in data: | |
| # pytest-json-report format | |
| summary = data["summary"] | |
| results["passed"] = summary.get("passed", 0) | |
| results["failed"] = summary.get("failed", 0) | |
| results["skipped"] = summary.get("skipped", 0) | |
| results["total"] = summary.get("total", 0) | |
| # Errors are counted as failed in this format | |
| results["error"] = summary.get("error", 0) | |
| elif "tests" in data: | |
| # Alternative format: iterate through test list | |
| for test in data["tests"]: | |
| outcome = test.get("outcome", "unknown") | |
| if outcome == "passed": | |
| results["passed"] += 1 | |
| elif outcome == "failed": | |
| results["failed"] += 1 | |
| elif outcome == "skipped": | |
| results["skipped"] += 1 | |
| elif outcome == "error": | |
| results["error"] += 1 | |
| results["total"] += 1 | |
| else: | |
| print("ERROR: Unknown JSON report format") | |
| sys.exit(2) | |
| return results | |
| def calculate_pass_rate(results): | |
| """ | |
| Calculate pass rate percentage. | |
| Args: | |
| results: Dictionary with test counts | |
| Returns: | |
| Pass rate percentage (0-100) | |
| """ | |
| total_run = results["passed"] + results["failed"] | |
| if total_run == 0: | |
| return 100.0 # No tests run, consider as passing | |
| return (results["passed"] / total_run) * 100 | |
| def categorize_failures(json_file: str): | |
| """ | |
| Categorize test failures by type (assertion, error, timeout, skip). | |
| Args: | |
| json_file: Path to pytest JSON report file | |
| Returns: | |
| Dictionary mapping failure type to count | |
| """ | |
| categories = { | |
| "assertion": 0, | |
| "error": 0, | |
| "timeout": 0, | |
| "skip": 0 | |
| } | |
| try: | |
| with open(json_file, 'r') as f: | |
| data = json.load(f) | |
| except (FileNotFoundError, json.JSONDecodeError): | |
| return categories | |
| # Parse test failures | |
| tests = data.get("tests", []) | |
| for test in tests: | |
| outcome = test.get("outcome", "") | |
| if outcome == "failed": | |
| # Check if it's an assertion error or exception | |
| call = test.get("call", {}) | |
| longrepr = call.get("longrepr", "") | |
| if "AssertionError" in longrepr or "assert" in longrepr: | |
| categories["assertion"] += 1 | |
| elif "TimeoutError" in longrepr or "timeout" in longrepr.lower(): | |
| categories["timeout"] += 1 | |
| else: | |
| categories["error"] += 1 | |
| elif outcome == "skipped": | |
| categories["skip"] += 1 | |
| return categories | |
| def print_summary( | |
| pass_rate, | |
| results, | |
| categories, | |
| minimum, | |
| verbose=False | |
| ): | |
| """ | |
| Print formatted summary of test results. | |
| Args: | |
| pass_rate: Calculated pass rate percentage | |
| results: Test result counts | |
| categories: Failure type counts | |
| minimum: Minimum pass rate threshold | |
| verbose: Enable verbose output | |
| """ | |
| print("\n" + "="*70) | |
| print("TEST PASS RATE VALIDATION") | |
| print("="*70) | |
| # Test results | |
| print(f"\nTest Results:") | |
| print(f" Total Tests: {results['total']}") | |
| print(f" Passed: {results['passed']}") | |
| if results["failed"] > 0: | |
| print(f" Failed: {results['failed']}") | |
| if results["error"] > 0: | |
| print(f" Errors: {results['error']}") | |
| if results["skipped"] > 0: | |
| print(f" Skipped: {results['skipped']}") | |
| # Pass rate | |
| print(f"\nPass Rate: {pass_rate:.2f}%") | |
| print(f"Threshold: {minimum:.2f}%") | |
| # Failure breakdown | |
| if sum(categories.values()) > 0 or verbose: | |
| print(f"\nFailure Categories:") | |
| print(f" Assertion Errors: {categories['assertion']}") | |
| print(f" Exceptions: {categories['error']}") | |
| print(f" Timeouts: {categories['timeout']}") | |
| print(f" Skipped: {categories['skip']}") | |
| # Status | |
| print("\n" + "="*70) | |
| if pass_rate >= minimum: | |
| print("STATUS: PASS ✓") | |
| print("="*70 + "\n") | |
| else: | |
| print(f"STATUS: FAIL ✗ (Pass rate {pass_rate:.2f}% below {minimum:.2f}%)") | |
| print("="*70 + "\n") | |
| def update_health_json( | |
| results, | |
| pass_rate, | |
| categories, | |
| phase="090", | |
| plan="02", | |
| duration_seconds=0 | |
| ): | |
| """ | |
| Update test_health.json with current test results. | |
| Args: | |
| results: Test result counts | |
| pass_rate: Calculated pass rate percentage | |
| categories: Failure type counts | |
| phase: Current phase number | |
| plan: Current plan number | |
| duration_seconds: Test execution duration | |
| """ | |
| health_file = Path(__file__).parent.parent / "coverage_reports" / "metrics" / "test_health.json" | |
| # Load existing health data or create new structure | |
| if health_file.exists(): | |
| try: | |
| with open(health_file, 'r') as f: | |
| health_data = json.load(f) | |
| except (json.JSONDecodeError, IOError): | |
| health_data = {} | |
| else: | |
| health_data = {} | |
| # Ensure structure exists | |
| if "pass_rate_history" not in health_data: | |
| health_data["pass_rate_history"] = [] | |
| if "failure_categories" not in health_data: | |
| health_data["failure_categories"] = { | |
| "assertion": 0, | |
| "error": 0, | |
| "timeout": 0, | |
| "skip": 0 | |
| } | |
| if "baseline" not in health_data: | |
| health_data["baseline"] = {} | |
| if "metadata" not in health_data: | |
| health_data["metadata"] = {} | |
| # Add current run to history | |
| current_entry = { | |
| "date": datetime.now().strftime("%Y-%m-%d"), | |
| "timestamp": datetime.now().isoformat(), | |
| "phase": phase, | |
| "plan": plan, | |
| "total_tests": results["total"], | |
| "passed": results["passed"], | |
| "failed": results["failed"], | |
| "skipped": results["skipped"], | |
| "pass_rate": round(pass_rate, 2), | |
| "duration_seconds": duration_seconds | |
| } | |
| health_data["pass_rate_history"].append(current_entry) | |
| # Update failure categories (cumulative) | |
| for key in health_data["failure_categories"]: | |
| health_data["failure_categories"][key] += categories.get(key, 0) | |
| # Set baseline if not exists | |
| if not health_data["baseline"]: | |
| health_data["baseline"] = { | |
| "date": current_entry["date"], | |
| "pass_rate": round(pass_rate, 2), | |
| "total_tests": results["total"] | |
| } | |
| # Update metadata | |
| health_data["metadata"]["format_version"] = 1 | |
| health_data["metadata"]["minimum_pass_rate"] = 90.0 | |
| health_data["metadata"]["last_updated"] = datetime.now().isoformat() | |
| # Write back to file | |
| health_file.parent.mkdir(parents=True, exist_ok=True) | |
| with open(health_file, 'w') as f: | |
| json.dump(health_data, f, indent=2) | |
| def main(): | |
| """Main entry point for pass rate validation.""" | |
| parser = argparse.ArgumentParser( | |
| description="Validate test pass rate against minimum threshold", | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| epilog=""" | |
| Examples: | |
| python check_pass_rate.py --json-file pytest_report.json | |
| python check_pass_rate.py --json-file pytest_report.json --minimum 95 | |
| python check_pass_rate.py --json-file pytest_report.json --verbose --update-health | |
| Exit Codes: | |
| 0: Pass rate meets or exceeds minimum threshold | |
| 1: Pass rate below minimum threshold | |
| 2: Error in execution | |
| """ | |
| ) | |
| parser.add_argument( | |
| "--json-file", | |
| type=str, | |
| default="pytest_report.json", | |
| help="Path to pytest JSON report file (default: pytest_report.json)" | |
| ) | |
| parser.add_argument( | |
| "--minimum", | |
| type=float, | |
| default=90.0, | |
| help="Minimum pass rate threshold (default: 90.0)" | |
| ) | |
| parser.add_argument( | |
| "--verbose", | |
| action="store_true", | |
| help="Enable verbose output" | |
| ) | |
| parser.add_argument( | |
| "--update-health", | |
| action="store_true", | |
| help="Update test_health.json with current results" | |
| ) | |
| parser.add_argument( | |
| "--phase", | |
| type=str, | |
| default="090", | |
| help="Current phase number for health tracking (default: 090)" | |
| ) | |
| parser.add_argument( | |
| "--plan", | |
| type=str, | |
| default="02", | |
| help="Current plan number for health tracking (default: 02)" | |
| ) | |
| parser.add_argument( | |
| "--duration", | |
| type=int, | |
| default=0, | |
| help="Test execution duration in seconds (default: 0)" | |
| ) | |
| args = parser.parse_args() | |
| # Parse test results | |
| results = parse_pytest_json(args.json_file) | |
| # Calculate pass rate | |
| pass_rate = calculate_pass_rate(results) | |
| # Categorize failures | |
| categories = categorize_failures(args.json_file) | |
| # Print summary | |
| print_summary(pass_rate, results, categories, args.minimum, args.verbose) | |
| # Update health JSON if requested | |
| if args.update_health: | |
| update_health_json( | |
| results, | |
| pass_rate, | |
| categories, | |
| phase=args.phase, | |
| plan=args.plan, | |
| duration_seconds=args.duration | |
| ) | |
| if args.verbose: | |
| print(f"Updated test_health.json with current results\n") | |
| # Return exit code | |
| if pass_rate >= args.minimum: | |
| sys.exit(0) | |
| else: | |
| sys.exit(1) | |
| if __name__ == "__main__": | |
| main() | |