Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions tools/tests/metadata_parser/metdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import itertools
from paths import PRECICE_TESTS_DIR, PRECICE_TUTORIAL_DIR

# Import TutorialSource from systemtests.sources (used for external tutorial sources).
from systemtests.sources import TutorialSource


@dataclass
class BuildArgument:
Expand Down Expand Up @@ -279,13 +282,15 @@ def from_cases_tuple(cls, cases: Tuple[Case], tutorial: Tutorial):
class ReferenceResult:
path: Path
case_combination: CaseCombination
base_dir: Optional[Path] = None

def __repr__(self) -> str:
return f"{self.path.as_posix()}"

def __post_init__(self):
# built full path
self.path = PRECICE_TUTORIAL_DIR / self.path
base = self.base_dir if self.base_dir is not None else PRECICE_TUTORIAL_DIR
self.path = Path(base) / self.path


@dataclass
Expand All @@ -299,6 +304,7 @@ class Tutorial:
url: str
participants: List[str]
cases: List[Case]
source: "TutorialSource" = field(default_factory=TutorialSource.local)
case_combinations: List[CaseCombination] = field(init=False)

def __post_init__(self):
Expand Down Expand Up @@ -355,29 +361,36 @@ def get_case_by_string(self, case_name: str) -> Optional[Case]:
return None

@classmethod
def from_yaml(cls, path, available_components):
def from_yaml(cls, path, available_components, base_dir=None, source=None):
"""
Creates a Tutorial instance from a YAML file.

Args:
path: The path to the YAML file.
path: The path to the metadata.yaml file.
available_components: The Components instance containing available components.
base_dir: Optional base directory for resolving tutorial path (for external sources).
Defaults to PRECICE_TUTORIAL_DIR.
source: Optional TutorialSource (for external tutorials).

Returns:
An instance of Tutorial.
"""
with open(path, 'r') as f:
data = yaml.safe_load(f)
name = data['name']
path = PRECICE_TUTORIAL_DIR / data['path']
base = base_dir if base_dir is not None else PRECICE_TUTORIAL_DIR
tutorial_path = Path(base) / data['path']
url = data['url']
participants = data.get('participants', [])
cases_raw = data.get('cases', {})
cases = []
for case_name in cases_raw.keys():
cases.append(Case.from_dict(
case_name, cases_raw[case_name], available_components))
return cls(name, path, url, participants, cases)
tut = cls(name, tutorial_path, url, participants, cases)
if source is not None:
tut.source = source
return tut


class Tutorials(list):
Expand Down Expand Up @@ -440,4 +453,4 @@ def from_path(cls, path):
for yaml_path in yaml_files:
tut = Tutorial.from_yaml(yaml_path, available_components)
tutorials.append(tut)
return cls(tutorials)
return cls(tutorials)
65 changes: 47 additions & 18 deletions tools/tests/systemtests/Systemtest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import subprocess
from .sources import resolve_tutorial_root, PRECICE_EXTERNAL_CACHE_DIR
from typing import List, Dict, Optional
from jinja2 import Environment, FileSystemLoader
from dataclasses import dataclass, field
Expand Down Expand Up @@ -299,28 +300,56 @@ def __copy_tutorial_into_directory(self, run_directory: Path):
"""
current_time_string = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
self.run_directory = run_directory
pr_requested = self.params_to_use.get("TUTORIALS_PR")
if pr_requested:
logging.debug(f"Fetching the PR {pr_requested} HEAD reference")
self._fetch_pr(PRECICE_TUTORIAL_DIR, pr_requested)
current_ref = self._get_git_ref(PRECICE_TUTORIAL_DIR)
ref_requested = self.params_to_use.get("TUTORIALS_REF")
if ref_requested:
logging.debug(f"Checking out tutorials {ref_requested} before copying")
self._fetch_ref(PRECICE_TUTORIAL_DIR, ref_requested)
self._checkout_ref_in_subfolder(PRECICE_TUTORIAL_DIR, self.tutorial.path, ref_requested)

self.tutorial_folder = slugify(f'{self.tutorial.path.name}_{self.case_combination.cases}_{current_time_string}')

# Only apply PR/ref overrides for LOCAL tutorials
if self.tutorial.source.type == "local":
pr_requested = self.params_to_use.get("TUTORIALS_PR")
if pr_requested:
logging.debug(f"Fetching the PR {pr_requested} HEAD reference")
self._fetch_pr(PRECICE_TUTORIAL_DIR, pr_requested)

current_ref = self._get_git_ref(PRECICE_TUTORIAL_DIR)

ref_requested = self.params_to_use.get("TUTORIALS_REF")
if ref_requested:
logging.debug(f"Checking out tutorials {ref_requested} before copying")
self._fetch_ref(PRECICE_TUTORIAL_DIR, ref_requested)
self._checkout_ref_in_subfolder(
PRECICE_TUTORIAL_DIR,
self.tutorial.path,
ref_requested,
)

# Create run directory name
self.tutorial_folder = slugify(
f"{self.tutorial.path.name}_{self.case_combination.cases}_{current_time_string}"
)

destination = run_directory / self.tutorial_folder
src = self.tutorial.path

# Resolve the actual tutorial root depending on source type
src = resolve_tutorial_root(
self.tutorial.path,
self.tutorial.source,
PRECICE_EXTERNAL_CACHE_DIR,
)

self.system_test_dir = destination
shutil.copytree(src, destination)

if ref_requested:
with open(destination / "tutorials_ref", 'w') as file:
file.write(ref_requested)
self._checkout_ref_in_subfolder(PRECICE_TUTORIAL_DIR, self.tutorial.path, current_ref)

# Restore original ref if needed (local tutorials only)
if self.tutorial.source.type == "local":
ref_requested = self.params_to_use.get("TUTORIALS_REF")
if ref_requested:
with open(destination / "tutorials_ref", "w") as file:
file.write(ref_requested)

current_ref = self._get_git_ref(PRECICE_TUTORIAL_DIR)
self._checkout_ref_in_subfolder(
PRECICE_TUTORIAL_DIR,
self.tutorial.path,
current_ref,
)
def __copy_tools(self, run_directory: Path):
destination = run_directory / "tools"
src = PRECICE_TOOLS_DIR
Expand Down
46 changes: 43 additions & 3 deletions tools/tests/systemtests/TestSuite.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional, List, Dict
from metadata_parser.metdata import Tutorials, Tutorial, Case, CaseCombination, ReferenceResult
from metadata_parser.metdata import (
Tutorials,
Tutorial,
Case,
CaseCombination,
ReferenceResult,
Components,
)
from paths import PRECICE_TESTS_DIR
from systemtests.sources import (
TutorialSource,
resolve_tutorial_root,
PRECICE_EXTERNAL_CACHE_DIR,
)

import yaml

Expand Down Expand Up @@ -42,6 +56,7 @@ def from_yaml(cls, path, parsed_tutorials: Tutorials):
An instance of TestSuites.
"""
testsuites = []
available_components = Components.from_yaml(PRECICE_TESTS_DIR / "components.yaml")
with open(path, 'r') as f:
data = yaml.safe_load(f)
test_suites_raw = data['test_suites']
Expand All @@ -50,7 +65,28 @@ def from_yaml(cls, path, parsed_tutorials: Tutorials):
reference_results_of_tutorial = {}
# iterate over tutorials:
for tutorial_case in test_suites_raw[test_suite_name]['tutorials']:
source = TutorialSource.from_dict(tutorial_case.get('source'))
tutorial = parsed_tutorials.get_by_path(tutorial_case['path'])
if not tutorial and source.type != "local":
# External tutorial: fetch and load metadata
tutorial_root = resolve_tutorial_root(
Path(tutorial_case['path']),
source,
PRECICE_EXTERNAL_CACHE_DIR,
)
metadata_path = tutorial_root / "metadata.yaml"
if not metadata_path.exists():
raise FileNotFoundError(
f"No metadata.yaml found for external tutorial "
f"{tutorial_case['path']} at {tutorial_root}"
)
tutorial = Tutorial.from_yaml(
metadata_path,
available_components,
base_dir=tutorial_root.parent,
source=source,
)
parsed_tutorials.tutorials.append(tutorial)
if not tutorial:
raise Exception(f"No tutorial with path {tutorial_case['path']} found.")
# initialize the datastructure for the new Testsuite
Expand All @@ -63,8 +99,12 @@ def from_yaml(cls, path, parsed_tutorials: Tutorials):
tutorial_case['case_combination'], tutorial)
if case_combination_requested in all_case_combinations:
case_combinations_of_tutorial[tutorial].append(case_combination_requested)
ref_base = tutorial.path.parent if source.type != "local" else None
reference_results_of_tutorial[tutorial].append(ReferenceResult(
tutorial_case['reference_result'], case_combination_requested))
Path(tutorial_case['reference_result']),
case_combination_requested,
base_dir=ref_base,
))
else:
raise Exception(
f"Could not find the following cases {tutorial_case['case-combination']} in the current metadata of tutorial {tutorial.name}")
Expand Down Expand Up @@ -106,4 +146,4 @@ def __repr__(self) -> str:
return_str = ""
for tests_suite in self.testsuites:
return_str += f"{tests_suite}\n\n"
return return_str
return return_str
Loading