Files

282 lines
7.5 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Local test runner script for QwenPaw project.
Usage:
python scripts/run_tests.py [OPTIONS]
Options:
-u, --unit [DIR] Run unit tests (optionally specify subdirectory)
-i, --integrated Run integrated tests
-a, --all Run all tests (default)
-c, --coverage Generate coverage report
-p, --parallel Run tests in parallel
-h, --help Show this help message
Examples:
python scripts/run_tests.py # Run all tests
python scripts/run_tests.py -u # Run all unit tests
python scripts/run_tests.py -u providers # Run unit tests in providers
python scripts/run_tests.py -i # Run integrated tests
python scripts/run_tests.py -a -c # Run all tests with coverage
python scripts/run_tests.py -p # Run tests in parallel
"""
import argparse
import subprocess
import sys
from pathlib import Path
from typing import Optional
class Colors:
"""ANSI color codes for terminal output."""
RED = "\033[0;31m"
GREEN = "\033[0;32m"
YELLOW = "\033[1;33m"
BLUE = "\033[0;34m"
NC = "\033[0m" # No Color
def print_info(message: str) -> None:
"""Print info message."""
print(f"{Colors.BLUE} {message}{Colors.NC}")
def print_success(message: str) -> None:
"""Print success message."""
print(f"{Colors.GREEN}{message}{Colors.NC}")
def print_error(message: str) -> None:
"""Print error message."""
print(f"{Colors.RED}{message}{Colors.NC}")
def print_warning(message: str) -> None:
"""Print warning message."""
print(f"{Colors.YELLOW}{message}{Colors.NC}")
def check_pytest() -> bool:
"""Check if pytest is installed."""
try:
subprocess.run(
["pytest", "--version"],
capture_output=True,
check=True,
)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
return False
def run_unit_tests(
project_root: Path,
subdir: Optional[str] = None,
coverage: bool = False,
parallel: bool = False,
) -> int:
"""Run unit tests."""
if subdir:
# Run specific subdirectory
test_path = project_root / "tests" / "unit" / subdir
if not test_path.is_dir():
print_error(f"Unit test directory not found: {test_path}")
return 1
print_info(f"Running unit tests in: {subdir}")
return_code = run_pytest(test_path, coverage, parallel)
if return_code == 0:
print_success(f"Unit tests in {subdir} completed")
return return_code
else:
# Run all unit test subdirectories
print_info("Running all unit tests...")
unit_dir = project_root / "tests" / "unit"
if not unit_dir.is_dir():
print_warning("Unit test directory not found: tests/unit")
return 0
subdirs = [d for d in unit_dir.iterdir() if d.is_dir()]
if not subdirs:
print_warning("No unit test subdirectories found")
return 0
overall_return_code = 0
for test_dir in subdirs:
dirname = test_dir.name
print_info(f"Running unit tests in: {dirname}")
return_code = run_pytest(test_dir, coverage, parallel)
if return_code == 0:
print_success(f"Unit tests in {dirname} completed")
else:
overall_return_code = return_code
print()
return overall_return_code
def run_integrated_tests(
project_root: Path,
coverage: bool = False,
parallel: bool = False,
) -> int:
"""Run integrated tests."""
print_info("Running integrated tests...")
integrated_dir = project_root / "tests" / "integrated"
if not integrated_dir.is_dir():
print_warning("Integrated test directory not found: tests/integrated")
return 0
# Check if there are any Python test files
test_files = list(integrated_dir.glob("*.py"))
if not test_files:
print_warning("No integrated test files found in tests/integrated")
return 0
return_code = run_pytest(integrated_dir, coverage, parallel)
if return_code == 0:
print_success("Integrated tests completed")
return return_code
def run_pytest(
test_path: Path,
coverage: bool = False,
parallel: bool = False,
) -> int:
"""Run pytest with specified options."""
cmd = ["pytest", "-v", str(test_path)]
if coverage:
cmd.extend(
[
"--cov=src/pineagents",
"--cov-report=html",
"--cov-report=term-missing",
],
)
if parallel:
cmd.extend(["-n", "auto"])
try:
result = subprocess.run(cmd, cwd=test_path.parents[2], check=True)
return result.returncode
except subprocess.CalledProcessError as e:
return e.returncode
def main() -> int:
"""Main entry point."""
parser = argparse.ArgumentParser(
description="QwenPaw test runner",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
parser.add_argument(
"-u",
"--unit",
nargs="?",
const="",
metavar="DIR",
help="Run unit tests (optionally specify subdirectory)",
)
parser.add_argument(
"-i",
"--integrated",
action="store_true",
help="Run integrated tests",
)
parser.add_argument(
"-a",
"--all",
action="store_true",
help="Run all tests (default)",
)
parser.add_argument(
"-c",
"--coverage",
action="store_true",
help="Generate coverage report",
)
parser.add_argument(
"-p",
"--parallel",
action="store_true",
help="Run tests in parallel (requires pytest-xdist)",
)
args = parser.parse_args()
# Get project root
script_path = Path(__file__).resolve()
project_root = script_path.parents[1]
# Check if pytest is installed
if not check_pytest():
print_error(
"pytest is not installed. Please install dev dependencies:",
)
print(' pip install -e ".[dev,test,full]"')
return 1
# Determine what to run
run_all = args.all or (args.unit is None and not args.integrated)
print()
print_info("QwenPaw Test Runner")
print("===================")
print()
return_code = 0
if run_all:
print_info("Running all tests...")
print()
unit_code = run_unit_tests(
project_root,
coverage=args.coverage,
parallel=args.parallel,
)
print()
integrated_code = run_integrated_tests(
project_root,
coverage=args.coverage,
parallel=args.parallel,
)
return_code = unit_code or integrated_code
elif args.unit is not None:
return_code = run_unit_tests(
project_root,
subdir=args.unit if args.unit else None,
coverage=args.coverage,
parallel=args.parallel,
)
elif args.integrated:
return_code = run_integrated_tests(
project_root,
coverage=args.coverage,
parallel=args.parallel,
)
print()
if args.coverage:
print_success(
"Test run completed! Coverage report generated in htmlcov/index.html",
)
else:
print_success("Test run completed!")
print()
return return_code
if __name__ == "__main__":
sys.exit(main())