# 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}")