ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
Trigger
TrigValidation
TrigValTools
bin
runTrigART.py
Go to the documentation of this file.
1
#!/usr/bin/env python
2
#
3
# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
4
#
5
6
import
sys
7
import
os
8
import
logging
9
import
argparse
10
import
shutil
11
import
subprocess
12
import
json
13
from
TrigValTools.TrigARTUtils
import
package_prefix, find_scripts, remember_cwd
14
15
16
def
minimal_pattern
(package):
17
dict = {
'TriggerTest'
:
'test_trig_data_v1Dev_build|test_trig_mc_v1DevHI_build'
,
18
'TrigP1Test'
:
'test_trigP1_v1Dev_decodeBS_build'
,
19
'TrigAnalysisTest'
:
'test_trigAna_RDOtoRDOTrig_v1Dev_Run4_build'
}
20
if
package ==
'ALL'
:
21
return
'({})'
.format(
'|'
.join([v
for
v
in
dict.values()
if
v]))
22
elif
package
in
dict
and
dict[package]
is
not
None
:
23
return
dict[package]
24
else
:
25
logging.error(
"Minimal set of tests for %s is not defined."
, package)
26
exit(1)
27
28
29
def
get_parser
():
30
packages = [
'TriggerTest'
,
'TrigAnalysisTest'
,
'TrigP1Test'
,
'ALL'
]
31
parser = argparse.ArgumentParser(usage=
'%(prog)s [options] [PackageName]'
)
32
parser.add_argument(
'package'
,
33
metavar=
'PackageName'
,
34
default=
'ALL'
,
35
nargs=
'?'
,
36
help=
'Name of the package from which to run ART tests. Options are: %(choices)s.'
37
' If no name is provided, %(default)s is used.'
,
38
choices=packages)
39
parser.add_argument(
'-m'
,
'--minimal'
,
40
action=
'store_true'
,
41
help=
'Run a small pre-defined set of tests for basic verification'
)
42
parser.add_argument(
'-n'
,
'--testName'
,
43
metavar=
'pattern'
,
44
help=
'Run only tests with a given pattern in the name'
)
45
parser.add_argument(
'-r'
,
'--rerun-failed'
,
46
action=
'store_true'
,
47
help=
'Run tests that failed previously'
)
48
parser.add_argument(
'-s'
,
'--summary'
,
49
action=
'store_true'
,
50
help=
'Print summary of previously run tests'
)
51
parser.add_argument(
'-t'
,
'--artType'
,
52
metavar=
'type'
,
53
default=
'build'
,
54
choices=[
'build'
,
'grid'
],
55
help=
'Run tests with the given art-type: build (default) or grid'
)
56
parser.add_argument(
'-j'
,
'--maxJobs'
,
57
metavar=
'N'
,
58
type=int,
59
default=1,
60
help=
'Run up to N tests in parallel (actual number may be limited by ART based on available CPUs and memory)'
)
61
parser.add_argument(
'-v'
,
'--verbose'
,
62
action=
'store_true'
,
63
help=
'Increase output verbosity'
)
64
parser.add_argument(
'-d'
,
'--dryRun'
,
65
action=
'store_true'
,
66
help=
'List tests which would be executed, but don\'t execute them'
)
67
68
return
parser
69
70
71
def
get_patterns
(args):
72
patterns = [
'^test_'
,
'(.sh|.py)$'
, package_prefix(args.package)]
73
if
args.testName:
74
patterns.append(args.testName)
75
if
args.artType ==
'grid'
:
76
patterns.append(
'_grid.'
)
77
else
:
78
patterns.append(
'_build.'
)
79
if
args.minimal:
80
patterns.append(
minimal_pattern
(args.package))
81
82
logging.debug(
"Searching for scripts with the following patterns: %s"
, patterns)
83
return
patterns
84
85
86
def
analyse_result_python
(test_results):
87
'''Analyses test results based on the final exit code, which determines
88
the status for ART tests based on TrigValSteering python framework'''
89
result_string =
''
90
for
step
in
test_results[
'result'
]:
91
result_string +=
"%s: %d, "
% (step[
'name'
], step[
'result'
])
92
result_string +=
"exit: %d"
% test_results[
'exit_code'
]
93
is_success = (test_results[
'exit_code'
] == 0)
94
return
result_string, is_success
95
96
97
def
analyse_result_shell
(test_results):
98
'''Analyses test results based on individual hard-coded step names, as
99
the final status cannot be unambiguously determined in the old shell-based
100
ART tests not using the TrigValSteering framework'''
101
result_string =
''
102
steps_to_ignore = [
'RootComp'
,
'MessageCount'
]
103
is_success =
True
104
for
step
in
test_results[
'result'
]:
105
result_string +=
"%s: %d, "
% (step[
'name'
], step[
'result'
])
106
if
step[
'name'
]
not
in
steps_to_ignore
and
step[
'result'
] != 0:
107
is_success =
False
108
result_string +=
"exit: %d"
% test_results[
'exit_code'
]
109
return
result_string, is_success
110
111
112
def
analyse_results
(all_test_results):
113
'''Prints a summary table of all results and returns two lists. One includes names of failed tests,
114
the other names of tests in which only the RootComp step failed. If only RootComp fails, the test is
115
not added to the first list, as we currently do not enforce updating RootComp references on every change.'''
116
failed_tests = []
117
table = {}
# test name : results
118
for
test_name
in
all_test_results.keys():
119
if
test_name.endswith(
'.py'
):
120
result_string, is_success =
analyse_result_python
(all_test_results[test_name])
121
else
:
122
result_string, is_success =
analyse_result_shell
(all_test_results[test_name])
123
table[test_name] = result_string
124
if
not
is_success:
125
failed_tests.append(test_name)
126
max_len_col1 = len(
max
(table.keys(), key=len))
127
max_len_col2 = len(
max
(table.values(), key=len))
128
logging.info(
'-'
*(max_len_col1+max_len_col2+7))
129
for
k, v
in
table.items():
130
logging.info(
'| {col1:<{width1}} | {col2:<{width2}} |'
.format(
131
col1=k, width1=max_len_col1,
132
col2=v, width2=max_len_col2))
133
logging.info(
'-'
*(max_len_col1+max_len_col2+7))
134
return
failed_tests
135
136
137
def
print_summary
(all_test_results, failed_tests):
138
if
len(failed_tests) > 0:
139
logging.info(
140
"%d tests succeeded out of %d executed"
,
141
len(all_test_results)-len(failed_tests),
142
len(all_test_results))
143
logging.error(
"=================================================="
)
144
logging.error(
"The following %d tests failed:"
, len(failed_tests))
145
for
test_name
in
failed_tests:
146
logging.error(
" %s"
, test_name)
147
logging.error(
"=================================================="
)
148
else
:
149
logging.info(
"=================================================="
)
150
logging.info(
"All %d executed tests succeeded"
, len(all_test_results))
151
logging.info(
"=================================================="
)
152
153
154
def
print_failed_ref_comp
(all_test_results, failed_tests):
155
if
len(failed_tests) == 0:
156
return
157
158
step_name =
'CountRefComp'
159
for
test_name
in
failed_tests:
160
athena_failed =
False
161
refcomp_failed =
False
162
test_results = all_test_results[test_name]
163
for
step
in
test_results[
'result'
]:
164
if
step[
'name'
]
in
[
'athena'
,
'athenaEF'
,
'Reco_tf'
,
'CheckLog'
]
and
step[
'result'
] != 0:
165
athena_failed =
True
166
if
step[
'name'
] == step_name
and
step[
'result'
] != 0:
167
refcomp_failed =
True
168
169
# Print only if athena didn't fail, as a failed job will always give wrong counts
170
if
refcomp_failed
and
not
athena_failed:
171
log_path =
'results/runTrigART/{:s}/{:s}.log'
.format(os.path.splitext(test_name)[0], step_name)
172
if
not
os.path.isfile(log_path):
173
continue
174
logging.info(
"=================================================="
)
175
logging.info(
'Printing output of failed CountRefComp for %s'
, test_name)
176
subprocess.call(
'cat '
+ log_path, shell=
True
)
177
logging.info(
"=================================================="
)
178
179
180
def
prep_dirs
(topdir, scripts):
181
""" Creates test result structure if missing, if present clears the area only for the tests to be run"""
182
183
shutil.rmtree(topdir+
'/test'
, ignore_errors=
True
)
184
os.makedirs(topdir+
'/test'
, exist_ok=
True
)
185
186
# clear results dir
187
for
script
in
scripts:
188
toerase = topdir+
'/results/runTrigART/'
+os.path.splitext(os.path.basename(script))[0]
189
shutil.rmtree(toerase, ignore_errors=
True
)
190
191
192
def
main
():
193
args =
get_parser
().parse_args()
194
logging.basicConfig(stream=sys.stdout,
195
format=
'%(levelname)-8s %(message)s'
,
196
level=logging.DEBUG
if
args.verbose
else
logging.INFO)
197
198
topdir =
'runTrigART'
199
statusfile =
'results/{:s}-status.json'
.format(topdir)
200
201
# Load previous results
202
if
args.rerun_failed
or
args.summary:
203
status_data = json.load(open(os.path.join(topdir,statusfile)))
204
all_test_results = status_data[topdir]
205
failed_tests =
analyse_results
(all_test_results)
206
if
args.summary:
207
print_summary
(all_test_results, failed_tests)
208
return
0
209
210
if
args.rerun_failed:
211
scripts = find_scripts(
get_patterns
(args)+[
'|'
.join(failed_tests)])
212
else
:
213
scripts = find_scripts(
get_patterns
(args))
214
215
logging.info(
"The following %d tests will be executed: "
, len(scripts))
216
for
filename
in
scripts:
217
logging.info(
" %s"
, os.path.basename(filename))
218
219
if
len(scripts) > 5*args.maxJobs:
220
if
args.maxJobs == 1:
221
logging.warning(
"You are running %d tests in sequence. This may take "
222
"a long time, consider using -j N option."
, len(scripts))
223
else
:
224
logging.warning(
"You are running %d tests with %d parallel jobs. "
225
"This may take a long time."
, len(scripts), args.maxJobs)
226
227
if
args.dryRun:
228
return
0
229
230
success =
True
231
with
remember_cwd():
232
prep_dirs
(topdir, scripts)
233
os.chdir(topdir)
234
for
script_path
in
scripts:
235
target =
'test/'
+ os.path.basename(script_path)
236
os.symlink(script_path, target)
237
238
# Set up and run ART
239
commands = [
240
'export ATLAS_LOCAL_ROOT_BASE="${ATLAS_LOCAL_ROOT_BASE:-/cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase}"'
,
241
'source "${ATLAS_LOCAL_ROOT_BASE}"/user/atlasLocalSetup.sh --quiet'
,
242
'lsetup -q art'
]
243
art_cmd =
'art.py run --run-all-tests --max-jobs={:d} {:s} . results'
.format(args.maxJobs,
'-v'
if
args.verbose
else
'-q'
)
244
commands.append(art_cmd)
245
cmd =
' && '
.join(commands)
246
logging.info(
"Executing ART command: %s"
, art_cmd)
247
subprocess.call(cmd, shell=
True
)
248
logging.info(
"ART finished, analysing the results\n"
)
249
250
# Read the result summary from JSON
251
if
not
os.path.isfile(statusfile):
252
logging.error(
"ART status.json file is missing - likely the ART runner failed!"
)
253
exit(1)
254
with
open(statusfile,
'r'
)
as
f:
255
status_data = json.load(f)
256
all_test_results = status_data[topdir]
257
if
len(all_test_results) != len(scripts):
258
logging.warning(
"Selected %d tests but ART executed only %d. Please check why some tests did not run!"
)
259
failed_tests =
analyse_results
(all_test_results)
260
print_summary
(all_test_results, failed_tests)
261
print_failed_ref_comp
(all_test_results, failed_tests)
262
if
len(failed_tests) > 0:
263
success =
False
264
265
if
not
success:
266
exit(1)
267
else
:
268
exit(0)
269
270
271
if
"__main__"
in
__name__:
272
sys.exit(
main
())
max
#define max(a, b)
Definition
cfImp.cxx:41
runTrigART.analyse_result_python
analyse_result_python(test_results)
Definition
runTrigART.py:86
runTrigART.analyse_results
analyse_results(all_test_results)
Definition
runTrigART.py:112
runTrigART.print_summary
print_summary(all_test_results, failed_tests)
Definition
runTrigART.py:137
runTrigART.get_patterns
get_patterns(args)
Definition
runTrigART.py:71
runTrigART.main
main()
Definition
runTrigART.py:192
runTrigART.minimal_pattern
minimal_pattern(package)
Definition
runTrigART.py:16
runTrigART.print_failed_ref_comp
print_failed_ref_comp(all_test_results, failed_tests)
Definition
runTrigART.py:154
runTrigART.analyse_result_shell
analyse_result_shell(test_results)
Definition
runTrigART.py:97
runTrigART.get_parser
get_parser()
Definition
runTrigART.py:29
runTrigART.prep_dirs
prep_dirs(topdir, scripts)
Definition
runTrigART.py:180
Generated on
for ATLAS Offline Software by
1.17.0