ATLAS Offline Software
Loading...
Searching...
No Matches
Tools/WorkflowTestRunner/python/Test.py
Go to the documentation of this file.
1# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2import subprocess
3from enum import Enum
4from logging import Logger
5from os import environ
6from pathlib import Path
7from uuid import uuid4
8
9from .Helpers import get_release_setup, list_changed_packages
10from .Inputs import references_CVMFS_path, references_override_url
11from .References import references_map
12
13
15 """Test setup."""
16
17 def __init__(self, logger: Logger) -> None:
18 self.logger = logger
19 self.validation_run_path = Path.cwd()
20 self.reference_run_path = Path("/tmp")
21 self.diff_rules_path = None
22 self.unique_ID = str(uuid4())
24 self.validation_only = False
25 self.run_only = False
26 self.checks_only = False
29 self.release_ID = "main" # The following is not flexible enough, can probably be hardcoded: environ["AtlasVersion"][0:4]
30 self.parallel_execution = False
32 self.custom_threads = None
34 self.allow_tolerance = False
35
36 def setup_release(self, reference=None, validation=None) -> None:
37 if reference and validation:
38 self.release_reference = reference
39 self.release_validation = validation
40 self.logger.info(f"WARNING: You have specified a dedicated release as reference {reference} and as validation {validation} release.")
41 self.logger.info("Your local setup area will not be considered!!!")
42 self.logger.info("this option is mainly designed for comparing release versions!!")
43 elif reference:
44 self.release_reference = reference
45 self.release_validation = ''
46 self.logger.info(f"You have specified a dedicated release as reference {reference}.")
47 else:
48 self.release_reference = get_release_setup(self.logger, self.disable_release_setup)
50 try:
51 list_changed_packages(self.logger, self.disable_release_setup)
52 except Exception:
53 self.logger.warning("Cannot list changed packages...\n")
54
55
56class WorkflowRun(Enum):
57 Run2 = "Run2"
58 Run3 = "Run3"
59 Run4 = "Run4"
60
61 def __str__(self):
62 return self.name
63
64
65class WorkflowType(Enum):
66 Generation = "Generation"
67 FullSim = "FullSim"
68 AF3 = "AF3"
69 HitsMerge = "HitsMerge"
70 HitsFilter = "HitsFilter"
71 MCOverlay = "MCOverlay"
72 DataOverlay = "DataOverlay"
73 DataOverlayChain = "DataOverlayChain"
74 DataOverlayReco = "DataOverlayReco"
75 MCReco = "MCReco"
76 MCPileUpReco = "MCPileUpReco"
77 DataReco = "DataReco"
78 PileUpPresampling = "PileUpPresampling"
79 MinbiasPreprocessing = "MinbiasPreprocessing"
80 Derivation = "Derivation"
81
82 def __str__(self):
83 return self.name
84
85
87 """Workflow check base class."""
88
89 def __init__(self, setup: TestSetup) -> None:
90 self.setup = setup
91 self.logger = setup.logger
92
93 def reference_file(self, test: "WorkflowTest", file_name: str) -> Path | None:
94 reference_path: Path = test.reference_path
95 reference_file = reference_path / file_name
96
97 # Read references from CVMFS
98 if self.setup.validation_only:
99 # Resolve the subfolder first. Results are stored like: main_folder/branch/test/version/.
100 reference_revision = references_map[f"{test.ID}"]
101 cvmfs_path = Path(references_CVMFS_path)
102 rel_path = Path(self.setup.release_ID) / test.ID / reference_revision
103 reference_path = cvmfs_path / rel_path
104 reference_file = reference_path / file_name
105
106 if not reference_path.exists():
107 self.logger.error(f"CVMFS reference location {reference_path} does not exist!")
108 return None
109
110 if references_override_url is not None:
111 import requests
112
113 url = references_override_url
114 if not url.endswith("/"): url += "/"
115 url += str(rel_path / file_name)
116 self.logger.info("Checking for reference override at %s", url)
117 if requests.head(url).ok: # file exists at url
118 reference_file = Path.cwd() / f"reference_{file_name}"
119 self.logger.info("Downloading reference from %s to %s", url, reference_file)
120 r = requests.get(url, stream=True)
121 with reference_file.open('wb') as f:
122 for chunk in r.iter_content(chunk_size=1024):
123 if chunk: # filter out keep-alive new chunks
124 f.write(chunk)
125 else:
126 self.logger.info("No reference override found")
127
128 return reference_file
129
130
132 """Workflow test base class."""
133
134 def __init__(self, ID: str, run: WorkflowRun, type: WorkflowType, steps: list[str], setup: TestSetup) -> None:
135 if not hasattr(self, "ID"):
136 self.ID = ID
137
138 if not hasattr(self, "tag"):
139 self.tag = ID
140
141 if not hasattr(self, "steps"):
142 self.steps = steps
143
144 if not self.command:
145 raise NotImplementedError("Command needs to be defined")
146
147 if not hasattr(self, "output_checks"):
149
150 if not hasattr(self, "digest_checks"):
152
153 if not hasattr(self, "skip_performance_checks"):
155
156 if not hasattr(self, "diff_tolerances"):
157 self.diff_tolerances = None
158
159 self.run = run
160 self.type = type
161 self.setup = setup
162 self.logger = setup.logger
163 self.validation_path: Path = self.setup.validation_run_path / f"run_{self.ID}"
164 self.reference_path: Path = self.setup.reference_run_path / f"run_{self.ID}"
165
166 def run_reference(self) -> None:
167 self.logger.info(f"Running reference in rel {self.setup.release_reference}")
168 self.logger.info(f"\"{self.command}\"")
169
170 self.reference_path.mkdir(parents=True, exist_ok=True)
171
172 cmd = (f"cd {self.reference_path};"
173 f"source $AtlasSetup/scripts/asetup.sh {self.setup.release_reference} >& /dev/null;")
174 cmd += f"TRF_NOECHO=1 {self.command} > {self.ID}.log 2>&1"
175
176 subprocess.call(cmd, shell=True)
177
178 self.logger.info(f"Finished clean in rel {self.setup.release_reference}")
179 self.logger.info(f"\"{self.command}\"")
180
181 def run_validation(self) -> None:
182 self.logger.info(f"Running validation in rel {self.setup.release_validation}")
183 self.logger.info(f"\"{self.command}\"")
184
185 self.validation_path.mkdir(parents=True, exist_ok=True)
186
187 cmd = f"cd {self.validation_path};"
188 if self.setup.disable_release_setup or not self.setup.release_validation:
189 pass
190 elif "WorkDir_DIR" in environ:
191 cmake_build_dir = environ["WorkDir_DIR"]
192 cmd += (f"source $AtlasSetup/scripts/asetup.sh {self.setup.release_validation} >& /dev/null;"
193 f"source {cmake_build_dir}/setup.sh;")
194 else:
195 cmd += f"source $AtlasSetup/scripts/asetup.sh {self.setup.release_validation} >& /dev/null;"
196 cmd += f"TRF_NOECHO=1 {self.command} > {self.ID}.log 2>&1"
197
198 subprocess.call(cmd, shell=True)
199
200 self.logger.info(f"Finished validation in rel {self.setup.release_validation}")
201 self.logger.info(f"\"{self.command}\"")
202
203 def run_checks(self, performance_checks: list[WorkflowCheck]) -> bool:
204 self.logger.info("-----------------------------------------------------")
205 self.logger.info(f"----------- Post-processing of {self.ID} Test -----------")
206 result = True
207
208 # digest checks
209 for check in self.digest_checks:
210 result = check.run(self) and result
211
212 # output checks
213 if not self.setup.disable_output_checks:
214 for check in self.output_checks:
215 result = check.run(self) and result
216
217 if self.setup.validation_only or self.skip_performance_checks:
218 return result # Performance checks against static references not possible
219
220 # performance checks
221 for check in performance_checks:
222 result = check.run(self) and result
223
224 return result
None setup_release(self, reference=None, validation=None)
Path|None reference_file(self, "WorkflowTest" test, str file_name)
bool run_checks(self, list[WorkflowCheck] performance_checks)
None __init__(self, str ID, WorkflowRun run, WorkflowType type, list[str] steps, TestSetup setup)