ATLAS Offline Software
Loading...
Searching...
No Matches
Checks.py
Go to the documentation of this file.
1# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2import logging
3import subprocess
4from pathlib import Path
5
6from .Helpers import warnings_count
7from .Inputs import references_CVMFS_path
8from .Test import TestSetup, WorkflowCheck, WorkflowTest, WorkflowType
9
10
12 """Was the q test successful? To check simply count the number of lines containing the string "successful run"."""
13
14 def run(self, test: WorkflowTest) -> bool:
15 result = True
16 printed_trf = False
17 for step in test.steps:
18 self.logger.info("-----------------------------------------------------")
19 log = test.validation_path / f"log.{step}"
20 counter = 0
21 warnings = []
22 errors = []
23 if log.exists():
24 with log.open() as file:
25 for line in file:
26 if ("ERROR" in line and "| ERROR |" not in line) or ("FATAL" in line and "| FATAL |" not in line):
27 errors.append(line[9:].strip())
28 elif "WARNING" in line and "| WARNING |" not in line:
29 warnings.append(line[9:].strip())
30 elif '"successful run"' in line or step == "DQHistogramMerge" and "Writing file: myHIST.root" in line:
31 counter += 1
32
33 if warnings:
34 self.logger.info(f"{step} validation test step WARNINGS")
35 warnings = list(dict.fromkeys(warnings))
36 for w in warnings:
37 self.logger.info(f" {w}")
38 self.logger.info("-----------------------------------------------------")
39
40 if errors:
41 self.logger.info(f"{step} validation test step ERRORS")
42 errors = list(dict.fromkeys(errors))
43 for e in errors:
44 self.logger.info(f" {e}")
45 self.logger.info("-----------------------------------------------------")
46
47 if counter and not errors:
48 self.logger.info(f"{step} validation test step successful")
49
50 if step == "DQHistogramMerge":
51 self.logger.info(f"Full {step} step log:")
52 with log.open() as file:
53 for line in file:
54 self.logger.print(f" {line.strip()}")
55 self.logger.info("-----------------------------------------------------")
56 else:
57 result = False
58 if log.exists():
59 printed_trf = True # if one step fails, next steps are expected so don't be too verbose
60 self.logger.error(f"{step} validation test step failed")
61 self.logger.error(f"Full {step} step log:")
62 with log.open() as file:
63 for line in file:
64 self.logger.print(f" {line.strip()}")
65 self.logger.info("-----------------------------------------------------")
66 else:
67 self.logger.error(f"{step} validation test step did not run")
68 if not printed_trf:
69 printed_trf = True
70 self.logger.error("Full transform log:")
71 with (test.validation_path / f"{test.ID}.log").open() as file:
72 for line in file:
73 self.logger.print(f" {line.strip()}")
74 self.logger.info("-----------------------------------------------------")
75
76 if self.setup.validation_only:
77 continue # Skip checking reference test because in this mode the clean tests have not been run
78
79 log = test.reference_path / f"log.{step}"
80 counter = 0
81 with log.open() as file:
82 for line in file:
83 if '"successful run"' in line or (step == "DQHistogramMerge"
84 and "Writing file: myHIST.root" in line): # DQ merge hack
85 counter += 1
86
87 if counter:
88 self.logger.info(f"{step} reference test step successful")
89 else:
90 self.logger.error(f"{step} reference test step failed")
91 result = False
92
93 if result:
94 self.logger.info(f"All {test.ID} athena steps completed successfully\n")
95 else :
96 self.logger.error(f"One or more {test.ID} Athena steps failed. Please investigate the cause.\n")
97
98 return result
99
100
102 """Run Frozen Tier0 Policy Check."""
103
104 def __init__(self, setup: TestSetup, input_format: str, max_events: int) -> None:
105 super().__init__(setup)
106 self.format = input_format
107 self.max_events = str(max_events)
108 self.detailed_comparison = setup.detailed_comparison
109 self.allow_tolerance = setup.allow_tolerance
110
111 def run(self, test: WorkflowTest) -> bool:
112 self.logger.info("---------------------------------------------------------------------------------------")
113 self.logger.info(f"Running {test.ID} Frozen Tier0 Policy Check on {self.format} for {self.max_events} events")
114
115 diff_rules_path: Path = self.setup.diff_rules_path
116 diff_rules_exclusion_filename: str = f"{test.ID}_{self.format}_diff-exclusion-list.txt"
117 diff_rules_interest_filename: str = f"{test.ID}_{self.format}_diff-interest-list.txt"
118 diff_rules_file = None
119
120 file_name = f"my{self.format}.pool.root"
121 if test.type == WorkflowType.Derivation:
122 file_name = f"{self.format}.myOutput.pool.root"
123 reference_file = self.reference_file(test, file_name)
124 if reference_file is None:
125 self.logger.error(f"Reference file {file_name} not found")
126 return False
127
128 if self.setup.validation_only:
129 cvmfs_path = Path(references_CVMFS_path)
130 diff_rules_path = cvmfs_path / self.setup.release_ID / test.ID
131
132 self.logger.info(f"Reading the reference file from location {reference_file}")
133
134 # try to get the exclusion list or the list of branches of interest
135 branches_of_interest = False
136 if self.setup.diff_rules_path is None:
137 diff_rules_exclusion_local_path = test.validation_path / diff_rules_exclusion_filename
138 diff_rules_interest_local_path = test.validation_path / diff_rules_interest_filename
139 subprocess.Popen(["/bin/bash", "-c", f"cd {test.validation_path}; get_files -remove -data {diff_rules_exclusion_filename}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
140 subprocess.Popen(["/bin/bash", "-c", f"cd {test.validation_path}; get_files -remove -data {diff_rules_interest_filename}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
141 if not diff_rules_exclusion_local_path.exists() and not diff_rules_interest_local_path.exists():
142 self.logger.info(f"Neither '{diff_rules_exclusion_local_path}' nor '{diff_rules_interest_local_path}' files exist in the release.")
143 elif diff_rules_exclusion_local_path.exists():
144 diff_rules_file = diff_rules_exclusion_local_path
145 elif diff_rules_interest_local_path.exists():
146 diff_rules_file = diff_rules_interest_local_path
147 branches_of_interest = True
148
149 if diff_rules_file is None and diff_rules_path is not None:
150 diff_rules_file = diff_rules_path / diff_rules_exclusion_filename
151 if not diff_rules_file.exists():
152 diff_rules_file = diff_rules_path / diff_rules_interest_filename
153 if diff_rules_file.exists():
154 branches_of_interest = True
155
156 if diff_rules_file is not None and diff_rules_file.exists():
157 self.logger.info(f"Reading the diff rules file from location {diff_rules_file}")
158 diff_root_list = []
159 with diff_rules_file.open() as f:
160 for line in f:
161 stripped_line = line.rstrip()
162 if stripped_line and stripped_line[0] != '#':
163 diff_root_list.append(rf"'{stripped_line}'")
164 else:
165 self.logger.info("No diff rules file exists, using the default list")
166 diff_root_list = [r"'index_ref'"]
167
168 if self.allow_tolerance and test.diff_tolerances is not None:
169 self.logger.info(f"Reading the tolerances from location {test.diff_tolerances}")
170 else:
171 self.logger.info("Default diff-root tolerances will be used")
172
173 validation_file = test.validation_path / file_name
174 log_file = test.validation_path / f"diff-root-{test.ID}.{self.format}.log"
175 diff_root_list = " ".join(diff_root_list)
176 diff_root_mode = "--branches-of-interest" if branches_of_interest else "--ignore-leaves"
177 tolerances_mode = f"--tolerances @{test.diff_tolerances}" if self.allow_tolerance and test.diff_tolerances is not None else ""
178
179 comparison_mode = "detailed" if self.detailed_comparison else "semi-detailed"
180 comparison_command = f"acmd.py diff-root {reference_file} {validation_file} --order-trees --nan-equal --exact-branches --mode {comparison_mode} --error-mode resilient {diff_root_mode} {diff_root_list} --entries {self.max_events} {tolerances_mode} > {log_file} 2>&1"
181 output, error = subprocess.Popen(["/bin/bash", "-c", comparison_command], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
182 output, error = output.decode("utf-8"), error.decode("utf-8")
183
184 # We want to catch/print both container additions/subtractions as well as
185 # changes in these containers. `allGood_return_code` is meant to catch
186 # other issues found in the diff (not expected, but just to be safe)
187 passed_frozen_tier0_test = True
188 all_good = False
189 with log_file.open() as file:
190 for line in file:
191 if "WARNING" in line: # Catches container addition/subtractions
192 self.logger.error(line.strip())
193 passed_frozen_tier0_test = False
194 if "leaves differ" in line: # Catches changes in branches
195 self.logger.error(line.strip())
196 passed_frozen_tier0_test = False
197 if "ERROR" in line: # Catches other issues (including unmatched branches)
198 self.logger.error(line.strip())
199 passed_frozen_tier0_test = False
200 if "INFO all good." in line:
201 all_good = True
202
203 result = passed_frozen_tier0_test and all_good
204 if result:
205 self.logger.info("Passed!\n")
206 else:
207 # print CI helper directly to avoid logger decorations
208 if self.setup.disable_release_setup:
209 self.logger.print(f"ATLAS-CI-ADD-LABEL: {test.run.value}-{test.type.value}-output-changed")
210 self.logger.print("")
211
212 if "DAOD" in self.format:
213 self.logger.error(f"Your change breaks the frozen derivation policy in test {test.ID}.")
214 self.logger.error("Please make sure you explain the reason for the change and ask relevant experts for approval.")
215 else:
216 self.logger.error(f"Your change affects standard outputs in test {test.ID}.")
217 self.logger.error("Please make sure you explain the reason for the change and ask relevant experts for approval.")
218
219 # copy the artifacts
220 if self.setup.disable_release_setup:
221 comparison_command = f"CopyCIArtifact.sh {validation_file}"
222 output, error = subprocess.Popen(["/bin/bash", "-c", comparison_command], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
223 output, error = output.decode("utf-8"), error.decode("utf-8")
224
225 if error or not output:
226 self.logger.error(f"Tried copying '{validation_file}' to the CI artifacts area but it failed.")
227 self.logger.error(f" {error.strip()}")
228 else:
229 self.logger.error(output)
230
231 with log_file.open() as file:
232 for line in file:
233 self.logger.info(f" {line.strip()}")
234 self.logger.info("-----------------------------------------------------\n")
235
236 return result
237
238
240 """Run metadata check."""
241
242 def __init__(self, setup: TestSetup, input_format: str) -> None:
243 super().__init__(setup)
244 self.format = input_format
245
246 def run(self, test: WorkflowTest) -> bool:
247 self.logger.info("---------------------------------------------------------------------------------------")
248 self.logger.info(f"Running {test.ID} metadata check on {self.format}")
249
250 file_name = f"my{self.format}.pool.root"
251 if test.type == WorkflowType.Derivation:
252 file_name = f"{self.format}.myOutput.pool.root"
253
254 reference_file = self.reference_file(test, file_name)
255 if reference_file is None:
256 self.logger.error(f"Reference file {file_name} not found")
257 return False
258
259 self.logger.info(f"Reading the reference file from location {reference_file}")
260
261 exclusion_list = " ".join([
262 "file_guid", "file_size",
263 "/TagInfo/AtlasRelease", "FileMetaData/productionRelease",
264 ] + ([
265 "auto_flush",
266 "StreamDAOD_PHYS/eventTypes", "StreamDAOD_PHYSLITE/eventTypes",
267 ] if test.type is WorkflowType.Derivation else []))
268
269 validation_file = test.validation_path / file_name
270 log_file = test.validation_path / f"meta-diff-{test.ID}.{self.format}.log"
271
272 comparison_command = f"meta-diff --ordered -m full -x diff {reference_file} {validation_file} --drop {exclusion_list} --ignoreTrigger > {log_file} 2>&1"
273 output, error = subprocess.Popen(["/bin/bash", "-c", comparison_command], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
274 output, error = output.decode("utf-8"), error.decode("utf-8")
275
276 result = True
277 with log_file.open() as file:
278 for line in file:
279 if line.strip():
280 result = False
281
282 if result:
283 self.logger.info("Passed!\n")
284 else:
285 # print CI helper directly to avoid logger decorations
286 if self.setup.disable_release_setup:
287 self.logger.print(f"ATLAS-CI-ADD-LABEL: {test.run.value}-{test.type.value}-output-changed")
288 self.logger.print("")
289
290 self.logger.error(f"Your change breaks the frozen tier0 policy in test {test.ID}.")
291 self.logger.error("Please make sure this has been discussed in the correct meeting (RIG or Simulation) meeting and approved by the relevant experts.")
292
293 # copy the artifacts
294 if self.setup.disable_release_setup:
295 comparison_command = f"CopyCIArtifact.sh {validation_file}"
296 output, error = subprocess.Popen(["/bin/bash", "-c", comparison_command], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
297 output, error = output.decode("utf-8"), error.decode("utf-8")
298
299 if error or not output:
300 self.logger.error(f"Tried copying '{validation_file}' to the CI artifacts area but it failed.")
301 self.logger.error(f" {error.strip()}")
302 else:
303 self.logger.error(output)
304
305 with log_file.open() as file:
306 for line in file:
307 self.logger.info(f" {line.strip()}")
308 self.logger.info("-----------------------------------------------------\n")
309
310 return result
311
313 """Run AOD Content Check."""
314
315 def run(self, test: WorkflowTest) -> bool:
316 self.logger.info("---------------------------------------------------------------------------------------")
317 self.logger.info(f"Running {test.ID} AOD content check")
318
319 file_name = "myAOD.pool.root"
320 output_name = f"{test.ID}_AOD_content.txt"
321
322 validation_file = test.validation_path / file_name
323 validation_output = test.validation_path / output_name
324 validation_command = f"acmd.py chk-file {validation_file} | awk '/---/{{flag=1;next}}/===/{{flag=0}}flag' | awk '{{print $10}}' | LC_ALL=C sort | uniq > {validation_output}"
325
326 output_val, error_val = subprocess.Popen(["/bin/bash", "-c", validation_command], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
327 output_val, error_val = output_val.decode("utf-8"), error_val.decode("utf-8")
328 if error_val:
329 self.logger.error(f"Something went wrong with retrieving the content for test {test.ID}:")
330 self.logger.error(error_val)
331
332 # Read references
333 if self.setup.validation_only:
334 # try to get the reference
335 reference_path = test.validation_path
336 reference_output_name = f"{test.ID}_AOD_content.ref"
337 reference_output = reference_path / reference_output_name
338 subprocess.Popen(["/bin/bash", "-c", f"cd {reference_path}; get_files -remove -data {reference_output_name}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
339 if not reference_output.exists():
340 self.logger.info(f"No reference file '{reference_output_name}' to compare the content with.")
341 return True
342 else:
343 reference_path = test.reference_path
344 reference_output = reference_path / output_name
345 reference_file = reference_path / file_name
346
347 reference_command = f"acmd.py chk-file {reference_file} | awk '/---/{{flag=1;next}}/===/{{flag=0}}flag' | awk '{{print $10}}' | LC_ALL=C sort | uniq > {reference_output}"
348 subprocess.Popen(["/bin/bash", "-c", reference_command], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
349
350 # Remove HLT containers in some cases
351 extra_diff_args = ""
352 if test.type == WorkflowType.MCReco or test.type == WorkflowType.MCPileUpReco:
353 extra_diff_args = "-I '^HLT' -I '^LVL1' -I '^L1'"
354
355 # Compute the diff
356 diff_output, diff_error = subprocess.Popen(["/bin/bash", "-c", f"diff {extra_diff_args} {reference_output} {validation_output}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
357 diff_output, diff_error = diff_output.decode("utf-8"), diff_error.decode("utf-8")
358
359 result = False
360 if not diff_output and not diff_error:
361 self.logger.info("Passed!\n")
362 result = True
363 else:
364 # print CI helper directly to avoid logger decorations
365 if self.setup.disable_release_setup:
366 self.logger.print(f"ATLAS-CI-ADD-LABEL: {test.run.value}-{test.type.value}-output-changed")
367 self.logger.print("")
368
369 self.logger.error(f"Your change modifies the output in test {test.ID}.")
370 self.logger.error("Please make sure this has been discussed in the correct meeting (RIG or Simulation) meeting and approved by the relevant experts.")
371 if self.setup.validation_only:
372 self.logger.error(f"The output '{output_name}' (>) differs from the reference '{reference_output_name}' (<):")
373 else:
374 self.logger.error(f"The output '{validation_output}' (>) differs from the reference '{reference_output}' (<):")
375 if diff_output:
376 self.logger.print("")
377 self.logger.print(diff_output)
378 if diff_error:
379 self.logger.print(diff_error)
380 self.logger.info("-----------------------------------------------------\n")
381
382 return result
383
384
386 """Run AOD Digest Check."""
387
388 def __init__(self, setup: TestSetup, max_events: int = -1) -> None:
389 super().__init__(setup)
390 self.max_events = str(max_events)
391
392 def run(self, test: WorkflowTest) -> bool:
393 self.logger.info("---------------------------------------------------------------------------------------")
394 self.logger.info(f"Running {test.ID} AOD digest")
395
396 file_name = "myAOD.pool.root"
397 output_name = f"{test.ID}_AOD_digest.txt"
398
399 validation_file = test.validation_path / file_name
400 validation_output = test.validation_path / output_name
401 validation_log_file = test.validation_path / f"AODdigest-{test.ID}.log"
402 validation_command = f"xAODDigest.py {validation_file} {validation_output} > {validation_log_file} 2>&1"
403
404 output_val, error_val = subprocess.Popen(["/bin/bash", "-c", validation_command], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
405 output_val, error_val = output_val.decode("utf-8"), error_val.decode("utf-8")
406 if error_val:
407 self.logger.error(f"Something went wrong with the digest calculation for test {test.ID}:")
408 self.logger.error(error_val)
409
410 # Read references
411 if self.setup.validation_only:
412 # try to get the reference
413 reference_path = test.validation_path
414 reference_output_name = f"{test.ID}_AOD_digest.ref"
415 reference_output = reference_path / reference_output_name
416 subprocess.Popen(["/bin/bash", "-c", f"cd {reference_path}; get_files -remove -data {reference_output_name}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
417 if not reference_output.exists():
418 self.logger.info(f"No reference file '{reference_output_name}' to compare the digest with. Printing the full digest:")
419 with validation_output.open() as f:
420 for line in f:
421 self.logger.print(f" {line.strip()}")
422 return True
423 else:
424 reference_path = test.reference_path
425 reference_output = reference_path / output_name
426 reference_file = reference_path / file_name
427 reference_log_file = test.reference_path / f"AODdigest-{test.ID}.log"
428
429 reference_command = f"xAODDigest.py {reference_file} {reference_output} > {reference_log_file} 2>&1"
430 subprocess.Popen(["/bin/bash", "-c", reference_command], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
431
432 # Compute the diff
433 diff_output, diff_error = subprocess.Popen(["/bin/bash", "-c", f"diff {reference_output} {validation_output}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
434 diff_output, diff_error = diff_output.decode("utf-8"), diff_error.decode("utf-8")
435
436 result = False
437 if not diff_output and not diff_error:
438 self.logger.info("Passed!\n")
439 result = True
440 else:
441 # print CI helper directly to avoid logger decorations
442 if self.setup.disable_release_setup:
443 self.logger.print(f"ATLAS-CI-ADD-LABEL: {test.run.value}-{test.type.value}-output-changed")
444 self.logger.print("")
445
446 self.logger.error(f"Your change breaks the digest in test {test.ID}.")
447 self.logger.error("Please make sure this has been discussed in the correct meeting (RIG or Simulation) meeting and approved by the relevant experts.")
448 if self.setup.validation_only:
449 self.logger.error(f"The output '{output_name}' (>) differs from the reference '{reference_output_name}' (<):")
450 else:
451 self.logger.error(f"The output '{validation_output}' (>) differs from the reference '{reference_output}' (<):")
452 if diff_output:
453 with reference_output.open() as file:
454 self.logger.print(file.readline())
455 self.logger.print(diff_output)
456 if diff_error:
457 self.logger.print(diff_error)
458 self.logger.info("-----------------------------------------------------\n")
459
460 return result
461
462
464 """Run A Very Simple Check."""
465
466 def __init__(self, setup: TestSetup, name: str, quantity: str, unit: str, field: int, threshold: float):
467 super().__init__(setup)
468 self.name = name
469 self.quantity = quantity
470 self.unit = unit
471 self.field = field
472 self.threshold = threshold
473
474 def run(self, test: WorkflowTest) -> bool:
475 self.logger.info("-----------------------------------------------------")
476 self.logger.info(f"Running {test.ID} {self.name} Check")
477
478 result = True
479 for step in test.steps:
480 log_name = f"log.{step}"
481 reference_log = test.reference_path / log_name
482 validation_log = test.validation_path / log_name
483
484 reference_value = 0
485 with reference_log.open() as file:
486 found = False
487 for line in file:
488 if self.quantity in line:
489 reference_value = float(line.split()[self.field])
490 found = True
491 break
492 if not found:
493 self.logger.error(f"No data available in {reference_log}. Job failed.")
494 return False
495
496 validation_value = 0
497 with validation_log.open() as file:
498 found = False
499 for line in file:
500 if self.quantity in line:
501 validation_value = float(line.split()[self.field])
502 found = True
503 break
504 if not found:
505 self.logger.error(f"No data available in {validation_log}. Job failed.")
506 return False
507
508 if reference_value != 0:
509 factor = validation_value / reference_value
510
511 # Error if the factor increases (very bad things)
512 # Warning if the factor decreases (should be an understood feature)
513 if factor > 1. + self.threshold:
514 self.logger.error(f"{self.quantity} in the {step} step with(out) your change is {validation_value} ({reference_value}) {self.unit}")
515 self.logger.error(f"Your change changes {self.quantity} by a factor {factor}")
516 self.logger.error("Is this an expected outcome of your change(s)?")
517 result = False
518 self.logger.error(f"{step}: {self.name}")
519 self.logger.error(f"ref {reference_value} {self.unit}")
520 self.logger.error(f"val {validation_value} {self.unit}")
521 if factor < 1. - self.threshold:
522 self.logger.warning(f"{self.quantity} in the {step} step with(out) your change is {validation_value} ({reference_value}) {self.unit}")
523 self.logger.warning(f"Your change changes {self.quantity} by a factor {factor}")
524 self.logger.warning("Is this an expected outcome of your change(s)?")
525 result = True
526 self.logger.warning(f"{step}: {self.name}")
527 self.logger.warning(f"ref {reference_value} {self.unit}")
528 self.logger.warning(f"val {validation_value} {self.unit}")
529
530 if result:
531 self.logger.info("Passed!\n")
532 else :
533 self.logger.error("Failed!\n")
534
535 return result
536
537
539 """Run WARNINGS check."""
540
541 def run(self, test: WorkflowTest):
542 self.logger.info("-----------------------------------------------------")
543 self.logger.info(f"Running {test.ID} WARNINGS Check\n")
544
545 result = True
546 for step in test.steps:
547 log_name = f"log.{step}"
548 reference_log = test.reference_path / log_name
549 validation_log = test.validation_path / log_name
550 warnings_reference = warnings_count(reference_log)
551 warnings_validation = warnings_count (validation_log)
552
553 wr=[]
554 for w in warnings_reference:
555 wr.append(w[9:])
556 wv=[]
557 for w in warnings_validation:
558 wv.append(w[9:])
559
560 wn = list(set(wv)-set(wr))
561 wo = list(set(wr)-set(wv))
562
563
564 if len(warnings_validation) > len(warnings_reference):
565 self.logger.error(f"Validation log file {validation_log} has {len(warnings_validation) - len(warnings_reference)} more warning(s) than the reference log file {reference_log}")
566 self.logger.error("Please remove the new warning message(s):")
567 for w in wn:
568 self.logger.error(w)
569 result = False
570 elif len(warnings_validation) < len(warnings_reference):
571 self.logger.info(f"Validation log file {validation_log} has {len(warnings_reference) - len(warnings_validation)} less warnings than the reference log file {reference_log}")
572 self.logger.info("The reduction of unnecessary WARNINGs is much appreciated. Is it expected?")
573 self.logger.info("The following warning messages have been removed:")
574 for w in wo:
575 self.logger.info(w)
576 result = True
577 else :
578 self.logger.info(f"Validation log file {validation_log} has the same number of warnings as the reference log file {reference_log}")
579 result = True
580
581 if result:
582 self.logger.info("Passed!\n")
583 else :
584 self.logger.error("Failed!\n")
585
586 return result
587
588
590 """Run FPE check."""
591
592 # Ignore FPEs for these tests:
593 ignoreTestRuns = []
594 ignoreTestTypes = [WorkflowType.FullSim, WorkflowType.AF3]
595 ignoreTestIDs = ['x686']
596
597 def run(self, test: WorkflowTest):
598 self.logger.info("-----------------------------------------------------")
599 self.logger.info(f"Running {test.ID} FPE Check")
600
601 result = True
602 for step in test.steps:
603 log = test.validation_path / f"log.{step}"
604 fpes = {}
605 stack_traces = {}
606 with log.open() as file:
607 last_stack_trace = None
608 for line in file:
609 if "WARNING FPE" in line:
610 last_stack_trace = None
611 fpe = None
612 for part in reversed(line.split()):
613 if "[" in part:
614 fpe = part.strip().replace("[", "").replace("]", "")
615 break
616 if fpe:
617 if fpe in fpes:
618 fpes[fpe] += 1
619 else:
620 fpes[fpe] = 1
621 last_stack_trace = []
622 stack_traces[fpe] = last_stack_trace
623 elif "FPE stacktrace" in line and last_stack_trace is not None:
624 line = next(file)
625 last_stack_trace.append(line.strip()[9:])
626
627 if fpes.keys():
628 msgLvl = logging.WARNING if test.run in self.ignoreTestRuns or test.type in self.ignoreTestTypes or test.ID in self.ignoreTestIDs else logging.ERROR
629 result = False
630 self.logger.log(msgLvl, f" {step} validation test step FPEs")
631 for fpe, count in sorted(fpes.items(), key=lambda item: item[1]):
632 self.logger.log(msgLvl, f"{count:>5} {fpe}")
633 for fpe in fpes:
634 self.logger.log(msgLvl, "-----------------------------------------------------")
635 self.logger.log(msgLvl, f" first stack trace for algorithm {fpe}:")
636 for line in stack_traces[fpe]:
637 self.logger.log(msgLvl, line)
638 self.logger.log(msgLvl, "-----------------------------------------------------")
639
640 if result:
641 self.logger.info("Passed!\n")
642 elif test.run in self.ignoreTestRuns or test.type in self.ignoreTestTypes or test.ID in self.ignoreTestIDs:
643 self.logger.warning("Failed!")
644 self.logger.warning("Check disabled due to irreproducibilities!\n")
645 result = True
646 else:
647 self.logger.error("Failed!\n")
648
649 return result
void print(char *figname, TCanvas *c1)
bool run(self, WorkflowTest test)
Definition Checks.py:315
None __init__(self, TestSetup setup, int max_events=-1)
Definition Checks.py:388
bool run(self, WorkflowTest test)
Definition Checks.py:392
run(self, WorkflowTest test)
Definition Checks.py:597
bool run(self, WorkflowTest test)
Definition Checks.py:14
bool run(self, WorkflowTest test)
Definition Checks.py:111
None __init__(self, TestSetup setup, str input_format, int max_events)
Definition Checks.py:104
bool run(self, WorkflowTest test)
Definition Checks.py:246
None __init__(self, TestSetup setup, str input_format)
Definition Checks.py:242
__init__(self, TestSetup setup, str name, str quantity, str unit, int field, float threshold)
Definition Checks.py:466
bool run(self, WorkflowTest test)
Definition Checks.py:474
run(self, WorkflowTest test)
Definition Checks.py:541
Path|None reference_file(self, "WorkflowTest" test, str file_name)
STL class.
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:312