2025-05-16 15:18:02 +08:00

100 lines
4.5 KiB
Python

# ddms_compliance_suite/test_loader/loader.py
from abc import ABC, abstractmethod
from pathlib import Path
from typing import List, Union, Dict, Any
import yaml # PyYAML
import json
from ..models.test_models import TestSuite # Ensure correct path
# from ..models.test_models import TestCase # If TestCase can be loaded standalone
class LoadError(Exception):
"""Custom exception for errors during test case loading."""
pass
class BaseTestCaseLoader(ABC):
@abstractmethod
def load_suites_from_file(self, file_path: Union[str, Path]) -> List[TestSuite]:
"""Loads a list of TestSuite objects from a single file."""
pass
def load_suites_from_directory(self, directory_path: Union[str, Path], recursive: bool = False, pattern: str = "*.yaml") -> List[TestSuite]:
"""Loads TestSuite objects from all matching files in a directory."""
suites: List[TestSuite] = []
p = Path(directory_path)
if not p.is_dir():
raise LoadError(f"Directory not found: {directory_path}")
file_paths = list(p.rglob(pattern)) if recursive else list(p.glob(pattern))
for file_path in file_paths:
if file_path.is_file():
try:
# Ensure that the loader instance calls its own method
# This part might need adjustment if called directly on BaseTestCaseLoader
# For now, assuming it's called from a concrete instance like YAMLTestCaseLoader
suites.extend(self.load_suites_from_file(file_path))
except LoadError as e:
# Consider using a logger for warnings/errors
print(f"Warning: Could not load test suites from {file_path}: {e}")
except Exception as e:
print(f"Warning: An unexpected error occurred loading {file_path}: {e}")
return suites
class YAMLTestCaseLoader(BaseTestCaseLoader):
def load_suites_from_file(self, file_path: Union[str, Path]) -> List[TestSuite]:
file_p = Path(file_path)
if not file_p.exists() or not file_p.is_file():
raise LoadError(f"Test case file not found or is not a file: {file_path}")
try:
with open(file_p, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
except yaml.YAMLError as e:
raise LoadError(f"Error parsing YAML file {file_p}: {e}")
except Exception as e:
raise LoadError(f"An unexpected error occurred while reading {file_p}: {e}")
if data is None: # Handle empty YAML file
return []
if not isinstance(data, dict) or "test_suites" not in data:
raise LoadError(f"Invalid format in {file_p}: Missing 'test_suites' top-level key or not a dictionary.")
suite_data_list = data.get("test_suites") # Use .get for safer access
if not isinstance(suite_data_list, list):
raise LoadError(f"Invalid format in {file_p}: 'test_suites' must be a list.")
loaded_suites: List[TestSuite] = []
for i, suite_data in enumerate(suite_data_list):
if not isinstance(suite_data, dict):
print(f"Warning: Suite data #{i+1} in {file_p} is not a dictionary, skipping.")
continue
try:
suite = TestSuite.model_validate(suite_data) # For Pydantic v2+
# For Pydantic v1, use: suite = TestSuite.parse_obj(suite_data)
loaded_suites.append(suite)
except Exception as e: # Catch Pydantic validation errors and others
# It's often better to log this and continue, or collect all errors
raise LoadError(f"Error validating test suite #{i+1} (ID: {suite_data.get('id', 'N/A')}) in {file_p}: {e}")
return loaded_suites
# Example Usage (conceptual):
# if __name__ == '__main__':
# yaml_loader = YAMLTestCaseLoader()
# try:
# # suites_from_single_file = yaml_loader.load_suites_from_file('path/to/your/single_test_suite_file.yaml')
# # for suite in suites_from_single_file:
# # print(f"Loaded Suite: {suite.name}")
# all_suites_in_dir = yaml_loader.load_suites_from_directory('path/to/test_suites_directory', recursive=True, pattern="*.test_suite.yaml")
# for suite in all_suites_in_dir:
# print(f"Loaded Suite from Dir: {suite.name}")
# for tc in suite.test_cases:
# print(f" - TestCase: {tc.name}")
# except LoadError as e:
# print(f"Loading Error: {e}")