282 lines
7.5 KiB
Python
282 lines
7.5 KiB
Python
|
|
#!/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())
|