Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Test Runner for Skill Seeker | |
| Runs all test suites and generates a comprehensive test report | |
| """ | |
| import sys | |
| import unittest | |
| import os | |
| from io import StringIO | |
| from pathlib import Path | |
| class ColoredTextTestResult(unittest.TextTestResult): | |
| """Custom test result class with colored output""" | |
| # ANSI color codes | |
| GREEN = '\033[92m' | |
| RED = '\033[91m' | |
| YELLOW = '\033[93m' | |
| BLUE = '\033[94m' | |
| RESET = '\033[0m' | |
| BOLD = '\033[1m' | |
| def __init__(self, *args, **kwargs): | |
| super().__init__(*args, **kwargs) | |
| self.test_results = [] | |
| def addSuccess(self, test): | |
| super().addSuccess(test) | |
| self.test_results.append(('PASS', test)) | |
| if self.showAll: | |
| self.stream.write(f"{self.GREEN}β PASS{self.RESET}\n") | |
| elif self.dots: | |
| self.stream.write(f"{self.GREEN}.{self.RESET}") | |
| self.stream.flush() | |
| def addError(self, test, err): | |
| super().addError(test, err) | |
| self.test_results.append(('ERROR', test)) | |
| if self.showAll: | |
| self.stream.write(f"{self.RED}β ERROR{self.RESET}\n") | |
| elif self.dots: | |
| self.stream.write(f"{self.RED}E{self.RESET}") | |
| self.stream.flush() | |
| def addFailure(self, test, err): | |
| super().addFailure(test, err) | |
| self.test_results.append(('FAIL', test)) | |
| if self.showAll: | |
| self.stream.write(f"{self.RED}β FAIL{self.RESET}\n") | |
| elif self.dots: | |
| self.stream.write(f"{self.RED}F{self.RESET}") | |
| self.stream.flush() | |
| def addSkip(self, test, reason): | |
| super().addSkip(test, reason) | |
| self.test_results.append(('SKIP', test)) | |
| if self.showAll: | |
| self.stream.write(f"{self.YELLOW}β SKIP{self.RESET}\n") | |
| elif self.dots: | |
| self.stream.write(f"{self.YELLOW}s{self.RESET}") | |
| self.stream.flush() | |
| class ColoredTextTestRunner(unittest.TextTestRunner): | |
| """Custom test runner with colored output""" | |
| resultclass = ColoredTextTestResult | |
| def discover_tests(test_dir='tests'): | |
| """Discover all test files in the tests directory""" | |
| loader = unittest.TestLoader() | |
| start_dir = test_dir | |
| pattern = 'test_*.py' | |
| suite = loader.discover(start_dir, pattern=pattern) | |
| return suite | |
| def run_specific_suite(suite_name): | |
| """Run a specific test suite""" | |
| loader = unittest.TestLoader() | |
| suite_map = { | |
| 'config': 'tests.test_config_validation', | |
| 'features': 'tests.test_scraper_features', | |
| 'integration': 'tests.test_integration' | |
| } | |
| if suite_name not in suite_map: | |
| print(f"Unknown test suite: {suite_name}") | |
| print(f"Available suites: {', '.join(suite_map.keys())}") | |
| return None | |
| module_name = suite_map[suite_name] | |
| try: | |
| suite = loader.loadTestsFromName(module_name) | |
| return suite | |
| except Exception as e: | |
| print(f"Error loading test suite '{suite_name}': {e}") | |
| return None | |
| def print_summary(result): | |
| """Print a detailed test summary""" | |
| total = result.testsRun | |
| passed = total - len(result.failures) - len(result.errors) - len(result.skipped) | |
| failed = len(result.failures) | |
| errors = len(result.errors) | |
| skipped = len(result.skipped) | |
| print("\n" + "="*70) | |
| print("TEST SUMMARY") | |
| print("="*70) | |
| # Overall stats | |
| print(f"\n{ColoredTextTestResult.BOLD}Total Tests:{ColoredTextTestResult.RESET} {total}") | |
| print(f"{ColoredTextTestResult.GREEN}β Passed:{ColoredTextTestResult.RESET} {passed}") | |
| if failed > 0: | |
| print(f"{ColoredTextTestResult.RED}β Failed:{ColoredTextTestResult.RESET} {failed}") | |
| if errors > 0: | |
| print(f"{ColoredTextTestResult.RED}β Errors:{ColoredTextTestResult.RESET} {errors}") | |
| if skipped > 0: | |
| print(f"{ColoredTextTestResult.YELLOW}β Skipped:{ColoredTextTestResult.RESET} {skipped}") | |
| # Success rate | |
| if total > 0: | |
| success_rate = (passed / total) * 100 | |
| color = ColoredTextTestResult.GREEN if success_rate == 100 else \ | |
| ColoredTextTestResult.YELLOW if success_rate >= 80 else \ | |
| ColoredTextTestResult.RED | |
| print(f"\n{color}Success Rate: {success_rate:.1f}%{ColoredTextTestResult.RESET}") | |
| # Category breakdown | |
| if hasattr(result, 'test_results'): | |
| print(f"\n{ColoredTextTestResult.BOLD}Test Breakdown by Category:{ColoredTextTestResult.RESET}") | |
| categories = {} | |
| for status, test in result.test_results: | |
| test_name = str(test) | |
| # Extract test class name | |
| if '.' in test_name: | |
| class_name = test_name.split('.')[0].split()[-1] | |
| if class_name not in categories: | |
| categories[class_name] = {'PASS': 0, 'FAIL': 0, 'ERROR': 0, 'SKIP': 0} | |
| categories[class_name][status] += 1 | |
| for category, stats in sorted(categories.items()): | |
| total_cat = sum(stats.values()) | |
| passed_cat = stats['PASS'] | |
| print(f" {category}: {passed_cat}/{total_cat} passed") | |
| print("\n" + "="*70) | |
| # Return status | |
| return failed == 0 and errors == 0 | |
| def main(): | |
| """Main test runner""" | |
| import argparse | |
| parser = argparse.ArgumentParser( | |
| description='Run tests for Skill Seeker', | |
| formatter_class=argparse.RawDescriptionHelpFormatter | |
| ) | |
| parser.add_argument('--suite', '-s', type=str, | |
| help='Run specific test suite (config, features, integration)') | |
| parser.add_argument('--verbose', '-v', action='store_true', | |
| help='Verbose output (show each test)') | |
| parser.add_argument('--quiet', '-q', action='store_true', | |
| help='Quiet output (minimal output)') | |
| parser.add_argument('--failfast', '-f', action='store_true', | |
| help='Stop on first failure') | |
| parser.add_argument('--list', '-l', action='store_true', | |
| help='List all available tests') | |
| args = parser.parse_args() | |
| # Set verbosity | |
| verbosity = 1 | |
| if args.verbose: | |
| verbosity = 2 | |
| elif args.quiet: | |
| verbosity = 0 | |
| print(f"\n{ColoredTextTestResult.BOLD}{'='*70}{ColoredTextTestResult.RESET}") | |
| print(f"{ColoredTextTestResult.BOLD}SKILL SEEKER TEST SUITE{ColoredTextTestResult.RESET}") | |
| print(f"{ColoredTextTestResult.BOLD}{'='*70}{ColoredTextTestResult.RESET}\n") | |
| # Discover or load specific suite | |
| if args.suite: | |
| print(f"Running test suite: {ColoredTextTestResult.BLUE}{args.suite}{ColoredTextTestResult.RESET}\n") | |
| suite = run_specific_suite(args.suite) | |
| if suite is None: | |
| return 1 | |
| else: | |
| print(f"Running {ColoredTextTestResult.BLUE}all tests{ColoredTextTestResult.RESET}\n") | |
| suite = discover_tests() | |
| # List tests | |
| if args.list: | |
| print("\nAvailable tests:\n") | |
| for test_group in suite: | |
| for test in test_group: | |
| print(f" - {test}") | |
| print() | |
| return 0 | |
| # Run tests | |
| runner = ColoredTextTestRunner( | |
| verbosity=verbosity, | |
| failfast=args.failfast | |
| ) | |
| result = runner.run(suite) | |
| # Print summary | |
| success = print_summary(result) | |
| # Return appropriate exit code | |
| return 0 if success else 1 | |
| if __name__ == '__main__': | |
| sys.exit(main()) | |