blob: e46dca4ff9c8412bdc1b937cc256fe24e706537a [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
manasarm5dedfaf2018-02-07 14:54:54 +05302
George Keishing9fbdf792016-10-18 06:16:09 -05003r"""
manasarm5dedfaf2018-02-07 14:54:54 +05304Use robot framework API to extract test result data from output.xml generated
5by robot tests. For more information on the Robot Framework API, see
6http://robot-framework.readthedocs.io/en/3.0/autodoc/robot.result.html
George Keishing9fbdf792016-10-18 06:16:09 -05007"""
manasarm5dedfaf2018-02-07 14:54:54 +05308
George Keishinge635ddc2022-12-08 07:38:02 -06009import csv
Patrick Williams20f38712022-12-08 06:18:26 -060010import datetime
Patrick Williams20f38712022-12-08 06:18:26 -060011import os
Steven Sombar3e82e3b2019-03-21 10:33:52 -050012import stat
Patrick Williams20f38712022-12-08 06:18:26 -060013import sys
14from xml.etree import ElementTree
15
Patrick Williams20f38712022-12-08 06:18:26 -060016from robot.api import ExecutionResult
17from robot.result.visitor import ResultVisitor
manasarm5dedfaf2018-02-07 14:54:54 +053018
19# Remove the python library path to restore with local project path later.
George Keishingddfd7992023-02-16 03:39:47 -060020SAVE_PATH_0 = sys.path[0]
manasarm5dedfaf2018-02-07 14:54:54 +053021del sys.path[0]
George Keishingc2a6f092019-02-20 12:26:54 -060022sys.path.append(os.path.join(os.path.dirname(__file__), "../../lib"))
manasarm5dedfaf2018-02-07 14:54:54 +053023
Patrick Williams20f38712022-12-08 06:18:26 -060024from gen_arg import * # NOQA
George Keishing09679892022-12-08 08:21:52 -060025from gen_print import * # NOQA
26from gen_valid import * # NOQA
George Keishing37c58c82022-12-08 07:42:54 -060027
manasarm5dedfaf2018-02-07 14:54:54 +053028# Restore sys.path[0].
George Keishingddfd7992023-02-16 03:39:47 -060029sys.path.insert(0, SAVE_PATH_0)
manasarm5dedfaf2018-02-07 14:54:54 +053030
Steven Sombara5e32f22019-03-01 13:33:15 -060031
32this_program = sys.argv[0]
Patrick Williams20f38712022-12-08 06:18:26 -060033info = " For more information: " + this_program + " -h"
Steven Sombara5e32f22019-03-01 13:33:15 -060034if len(sys.argv) == 1:
George Keishingbfa859a2020-04-02 00:38:24 -050035 print(info)
Steven Sombara5e32f22019-03-01 13:33:15 -060036 sys.exit(1)
37
38
manasarm5dedfaf2018-02-07 14:54:54 +053039parser = argparse.ArgumentParser(
Steven Sombara5e32f22019-03-01 13:33:15 -060040 usage=info,
Patrick Williams20f38712022-12-08 06:18:26 -060041 description=(
42 "%(prog)s uses a robot framework API to extract test result data"
43 " from output.xml generated by robot tests. For more information on"
44 " the Robot Framework API, see "
45 " http://robot-framework.readthedocs.io/en/3.0/autodoc/robot.result.html"
46 ),
manasarm5dedfaf2018-02-07 14:54:54 +053047 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
Patrick Williams20f38712022-12-08 06:18:26 -060048 prefix_chars="-+",
49)
manasarm5dedfaf2018-02-07 14:54:54 +053050
51parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060052 "--source",
53 "-s",
54 help=(
55 "The output.xml robot test result file path. This parameter is "
56 " required."
57 ),
58)
manasarm5dedfaf2018-02-07 14:54:54 +053059
60parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060061 "--dest",
62 "-d",
63 help=(
64 "The directory path where the generated .csv files will go. This "
65 " parameter is required."
66 ),
67)
manasarm5dedfaf2018-02-07 14:54:54 +053068
manasarme4f79c92018-02-22 13:02:46 +053069parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060070 "--version_id",
71 help=(
72 "Driver version of openbmc firmware which was used during test, "
73 ' e.g. "v2.1-215-g6e7eacb". This parameter is required.'
74 ),
75)
manasarme4f79c92018-02-22 13:02:46 +053076
77parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060078 "--platform",
79 help=(
80 "OpenBMC platform which was used during test, e.g."
81 ' "Witherspoon". This parameter is required.'
82 ),
83)
manasarme4f79c92018-02-22 13:02:46 +053084
George Keishing19533d52018-04-09 03:08:03 -050085parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060086 "--level",
87 help=(
88 "OpenBMC release level which was used during test, e.g."
89 ' "Master", "OBMC920". This parameter is required.'
90 ),
91)
Steven Sombara5e32f22019-03-01 13:33:15 -060092
93parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060094 "--test_phase",
95 help=(
96 'Name of testing phase, e.g. "DVT", "SVT", etc. This'
97 " parameter is optional."
98 ),
99 default="FVT",
100)
Steven Sombara5e32f22019-03-01 13:33:15 -0600101
102parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -0600103 "--subsystem",
104 help=(
105 'Name of the subsystem, e.g. "OPENBMC" etc. This parameter is'
106 " optional."
107 ),
108 default="OPENBMC",
109)
Vijaye3a98372019-10-30 02:04:22 -0500110
111parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -0600112 "--processor",
Steven Sombara5e32f22019-03-01 13:33:15 -0600113 help='Name of processor, e.g. "P9". This parameter is optional.',
Patrick Williams20f38712022-12-08 06:18:26 -0600114 default="OPENPOWER",
115)
Steven Sombara5e32f22019-03-01 13:33:15 -0600116
George Keishing19533d52018-04-09 03:08:03 -0500117
manasarm5dedfaf2018-02-07 14:54:54 +0530118# Populate stock_list with options we want.
119stock_list = [("test_mode", 0), ("quiet", 0), ("debug", 0)]
120
121
Patrick Williams20f38712022-12-08 06:18:26 -0600122def exit_function(signal_number=0, frame=None):
manasarm5dedfaf2018-02-07 14:54:54 +0530123 r"""
124 Execute whenever the program ends normally or with the signals that we
125 catch (i.e. TERM, INT).
126 """
127
128 dprint_executing()
129
130 dprint_var(signal_number)
131
132 qprint_pgm_footer()
133
134
Patrick Williams20f38712022-12-08 06:18:26 -0600135def signal_handler(signal_number, frame):
manasarm5dedfaf2018-02-07 14:54:54 +0530136 r"""
137 Handle signals. Without a function to catch a SIGTERM or SIGINT, the
138 program would terminate immediately with return code 143 and without
139 calling the exit_function.
140 """
141
142 # Our convention is to set up exit_function with atexit.register() so
143 # there is no need to explicitly call exit_function from here.
144
145 dprint_executing()
146
147 # Calling exit prevents us from returning to the code that was running
148 # when the signal was received.
George Keishingddfd7992023-02-16 03:39:47 -0600149 sys.exit(0)
manasarm5dedfaf2018-02-07 14:54:54 +0530150
151
152def validate_parms():
manasarm5dedfaf2018-02-07 14:54:54 +0530153 r"""
154 Validate program parameters, etc. Return True or False (i.e. pass/fail)
155 accordingly.
156 """
157
158 if not valid_file_path(source):
159 return False
160
161 if not valid_dir_path(dest):
162 return False
163
164 gen_post_validation(exit_function, signal_handler)
165
166 return True
George Keishing9fbdf792016-10-18 06:16:09 -0500167
168
Patrick Williams20f38712022-12-08 06:18:26 -0600169def parse_output_xml(
170 xml_file_path,
171 csv_dir_path,
172 version_id,
173 platform,
174 level,
175 test_phase,
176 processor,
177):
manasarm5dedfaf2018-02-07 14:54:54 +0530178 r"""
179 Parse the robot-generated output.xml file and extract various test
180 output data. Put the extracted information into a csv file in the "dest"
181 folder.
182
183 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500184 xml_file_path The path to a Robot-generated output.xml
185 file.
186 csv_dir_path The path to the directory that is to
187 contain the .csv files generated by
188 this function.
189 version_id Version of the openbmc firmware
190 (e.g. "v2.1-215-g6e7eacb").
191 platform Platform of the openbmc system.
192 level Release level of the OpenBMC system
George Keishing360db632018-09-06 12:01:28 -0500193 (e.g. "Master").
manasarm5dedfaf2018-02-07 14:54:54 +0530194 """
195
Peter D Phan8e13ade2021-09-16 06:15:55 -0500196 # Initialize tallies
197 total_critical_tc = 0
198 total_critical_passed = 0
199 total_critical_failed = 0
200 total_non_critical_tc = 0
201 total_non_critical_passed = 0
202 total_non_critical_failed = 0
203
George Keishing74777bd2017-02-07 01:43:38 -0600204 result = ExecutionResult(xml_file_path)
Patrick Williams20f38712022-12-08 06:18:26 -0600205 result.configure(
206 stat_config={
207 "suite_stat_level": 2,
208 "tag_stat_combine": "tagANDanother",
209 }
210 )
George Keishing9fbdf792016-10-18 06:16:09 -0500211
212 stats = result.statistics
George Keishingf8a9ebe2018-08-06 12:49:11 -0500213 print("--------------------------------------")
Peter D Phan8e13ade2021-09-16 06:15:55 -0500214 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600215 total_critical_tc = (
216 stats.total.critical.passed + stats.total.critical.failed
217 )
Peter D Phan8e13ade2021-09-16 06:15:55 -0500218 total_critical_passed = stats.total.critical.passed
219 total_critical_failed = stats.total.critical.failed
220 except AttributeError:
221 pass
222
223 try:
224 total_non_critical_tc = stats.total.passed + stats.total.failed
225 total_non_critical_passed = stats.total.passed
226 total_non_critical_failed = stats.total.failed
227 except AttributeError:
228 pass
229
Patrick Williams20f38712022-12-08 06:18:26 -0600230 print(
231 "Total Test Count:\t %d" % (total_non_critical_tc + total_critical_tc)
232 )
Peter D Phan8e13ade2021-09-16 06:15:55 -0500233
234 print("Total Critical Test Failed:\t %d" % total_critical_failed)
235 print("Total Critical Test Passed:\t %d" % total_critical_passed)
236 print("Total Non-Critical Test Failed:\t %d" % total_non_critical_failed)
237 print("Total Non-Critical Test Passed:\t %d" % total_non_critical_passed)
George Keishingf8a9ebe2018-08-06 12:49:11 -0500238 print("Test Start Time:\t %s" % result.suite.starttime)
239 print("Test End Time:\t\t %s" % result.suite.endtime)
240 print("--------------------------------------")
George Keishing9fbdf792016-10-18 06:16:09 -0500241
George Keishingddfd7992023-02-16 03:39:47 -0600242 # Use ResultVisitor object and save off the test data info.
George Keishing9fbdf792016-10-18 06:16:09 -0500243 class TestResult(ResultVisitor):
George Keishingddfd7992023-02-16 03:39:47 -0600244 r"""
245 Class methods to save off the test data information.
246 """
247
George Keishing9fbdf792016-10-18 06:16:09 -0500248 def __init__(self):
George Keishingddfd7992023-02-16 03:39:47 -0600249 self.test_data = []
George Keishing9fbdf792016-10-18 06:16:09 -0500250
251 def visit_test(self, test):
George Keishingddfd7992023-02-16 03:39:47 -0600252 self.test_data += [test]
George Keishing9fbdf792016-10-18 06:16:09 -0500253
George Keishingddfd7992023-02-16 03:39:47 -0600254 collect_data_obj = TestResult()
255 result.visit(collect_data_obj)
George Keishing9fbdf792016-10-18 06:16:09 -0500256
257 # Write the result statistics attributes to CSV file
258 l_csvlist = []
George Keishing74777bd2017-02-07 01:43:38 -0600259
260 # Default Test data
Steven Sombara5e32f22019-03-01 13:33:15 -0600261 l_test_type = test_phase
George Keishing19533d52018-04-09 03:08:03 -0500262
Patrick Williams20f38712022-12-08 06:18:26 -0600263 l_pse_rel = "Master"
George Keishing19533d52018-04-09 03:08:03 -0500264 if level:
265 l_pse_rel = level
266
Patrick Williams20f38712022-12-08 06:18:26 -0600267 l_env = "HW"
Steven Sombara5e32f22019-03-01 13:33:15 -0600268 l_proc = processor
George Keishing74777bd2017-02-07 01:43:38 -0600269 l_platform_type = ""
270 l_func_area = ""
271
Gunnar Mills096cd562018-03-26 10:19:12 -0500272 # System data from XML meta data
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500273 # l_system_info = get_system_details(xml_file_path)
manasarme4f79c92018-02-22 13:02:46 +0530274
275 # First let us try to collect information from keyboard input
276 # If keyboard input cannot give both information, then find from xml file.
277 if version_id and platform:
278 l_driver = version_id
279 l_platform_type = platform
George Keishingf8a9ebe2018-08-06 12:49:11 -0500280 print("BMC Version_id:%s" % version_id)
281 print("BMC Platform:%s" % platform)
George Keishing74777bd2017-02-07 01:43:38 -0600282 else:
manasarme4f79c92018-02-22 13:02:46 +0530283 # System data from XML meta data
284 l_system_info = get_system_details(xml_file_path)
285 l_driver = l_system_info[0]
286 l_platform_type = l_system_info[1]
287
288 # Driver version id and platform are mandatorily required for CSV file
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500289 # generation. If any one is not avaulable, exit CSV file generation
290 # process.
manasarme4f79c92018-02-22 13:02:46 +0530291 if l_driver and l_platform_type:
George Keishingf8a9ebe2018-08-06 12:49:11 -0500292 print("Driver and system info set.")
manasarme4f79c92018-02-22 13:02:46 +0530293 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600294 print(
295 "Both driver and system info need to be set. CSV"
296 " file is not generated."
297 )
George Keishing74777bd2017-02-07 01:43:38 -0600298 sys.exit()
George Keishing9fbdf792016-10-18 06:16:09 -0500299
300 # Default header
Patrick Williams20f38712022-12-08 06:18:26 -0600301 l_header = [
302 "test_start",
303 "test_end",
304 "subsys",
305 "test_type",
306 "test_result",
307 "test_name",
308 "pse_rel",
309 "driver",
310 "env",
311 "proc",
312 "platform_type",
313 "test_func_area",
314 ]
George Keishing9fbdf792016-10-18 06:16:09 -0500315
316 l_csvlist.append(l_header)
317
318 # Generate CSV file onto the path with current time stamp
George Keishing74777bd2017-02-07 01:43:38 -0600319 l_base_dir = csv_dir_path
George Keishing937fe3c2019-10-29 11:20:34 -0500320 l_timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%d-%H-%M-%S")
George Keishing74777bd2017-02-07 01:43:38 -0600321 # Example: 2017-02-20-08-47-22_Witherspoon.csv
322 l_csvfile = l_base_dir + l_timestamp + "_" + l_platform_type + ".csv"
George Keishing9fbdf792016-10-18 06:16:09 -0500323
George Keishingf8a9ebe2018-08-06 12:49:11 -0500324 print("Writing data into csv file:%s" % l_csvfile)
George Keishing9fbdf792016-10-18 06:16:09 -0500325
George Keishingddfd7992023-02-16 03:39:47 -0600326 for testcase in collect_data_obj.test_data:
George Keishing74777bd2017-02-07 01:43:38 -0600327 # Functional Area: Suite Name
328 # Test Name: Test Case Name
Patrick Williams20f38712022-12-08 06:18:26 -0600329 l_func_area = str(testcase.parent).split(" ", 1)[1]
George Keishing74777bd2017-02-07 01:43:38 -0600330 l_test_name = str(testcase)
George Keishing9fbdf792016-10-18 06:16:09 -0500331
332 # Test Result pass=0 fail=1
Patrick Williams20f38712022-12-08 06:18:26 -0600333 if testcase.status == "PASS":
George Keishing9fbdf792016-10-18 06:16:09 -0500334 l_test_result = 0
George Keishingc6f90c52023-02-16 03:09:25 -0600335 elif testcase.status == "SKIP":
336 # Skipped test result should not be mark pass or fail.
337 continue
George Keishing9fbdf792016-10-18 06:16:09 -0500338 else:
339 l_test_result = 1
340
George Keishing74777bd2017-02-07 01:43:38 -0600341 # Format datetime from robot output.xml to "%Y-%m-%d-%H-%M-%S"
342 l_stime = xml_to_csv_time(testcase.starttime)
343 l_etime = xml_to_csv_time(testcase.endtime)
George Keishing9fbdf792016-10-18 06:16:09 -0500344 # Data Sequence: test_start,test_end,subsys,test_type,
George Keishing74777bd2017-02-07 01:43:38 -0600345 # test_result,test_name,pse_rel,driver,
George Keishing360db632018-09-06 12:01:28 -0500346 # env,proc,platform_type,test_func_area,
Patrick Williams20f38712022-12-08 06:18:26 -0600347 l_data = [
348 l_stime,
349 l_etime,
350 subsystem,
351 l_test_type,
352 l_test_result,
353 l_test_name,
354 l_pse_rel,
355 l_driver,
356 l_env,
357 l_proc,
358 l_platform_type,
359 l_func_area,
360 ]
George Keishing9fbdf792016-10-18 06:16:09 -0500361 l_csvlist.append(l_data)
362
George Keishinga96e27c2016-12-04 23:05:04 -0600363 # Open the file and write to the CSV file
George Keishingddfd7992023-02-16 03:39:47 -0600364 with open(l_csvfile, "w", encoding="utf8") as l_file:
365 l_writer = csv.writer(l_file, lineterminator="\n")
366 l_writer.writerows(l_csvlist)
367 l_file.close()
368
Steven Sombar3e82e3b2019-03-21 10:33:52 -0500369 # Set file permissions 666.
Patrick Williams20f38712022-12-08 06:18:26 -0600370 perm = (
371 stat.S_IRUSR
372 + stat.S_IWUSR
373 + stat.S_IRGRP
374 + stat.S_IWGRP
375 + stat.S_IROTH
376 + stat.S_IWOTH
377 )
Steven Sombar3e82e3b2019-03-21 10:33:52 -0500378 os.chmod(l_csvfile, perm)
George Keishing9fbdf792016-10-18 06:16:09 -0500379
380
George Keishing74777bd2017-02-07 01:43:38 -0600381def xml_to_csv_time(xml_datetime):
382 r"""
383 Convert the time from %Y%m%d %H:%M:%S.%f format to %Y-%m-%d-%H-%M-%S format
384 and return it.
385
manasarm5dedfaf2018-02-07 14:54:54 +0530386 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500387 datetime The date in the following format: %Y%m%d
388 %H:%M:%S.%f (This is the format
389 typically found in an XML file.)
George Keishing74777bd2017-02-07 01:43:38 -0600390
391 The date returned will be in the following format: %Y-%m-%d-%H-%M-%S
392 """
manasarm5dedfaf2018-02-07 14:54:54 +0530393
George Keishing74777bd2017-02-07 01:43:38 -0600394 # 20170206 05:05:19.342
George Keishing937fe3c2019-10-29 11:20:34 -0500395 l_str = datetime.datetime.strptime(xml_datetime, "%Y%m%d %H:%M:%S.%f")
George Keishing74777bd2017-02-07 01:43:38 -0600396 # 2017-02-06-05-05-19
397 l_str = l_str.strftime("%Y-%m-%d-%H-%M-%S")
398 return str(l_str)
399
400
401def get_system_details(xml_file_path):
402 r"""
403 Get the system data from output.xml generated by robot and return it.
Gunnar Mills28e403b2017-10-25 16:16:38 -0500404 The list returned will be in the following order: [driver,platform]
George Keishing74777bd2017-02-07 01:43:38 -0600405
manasarm5dedfaf2018-02-07 14:54:54 +0530406 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500407 xml_file_path The relative or absolute path to the
408 output.xml file.
George Keishing74777bd2017-02-07 01:43:38 -0600409 """
manasarm5dedfaf2018-02-07 14:54:54 +0530410
manasarme4f79c92018-02-22 13:02:46 +0530411 bmc_version_id = ""
George Keishing74777bd2017-02-07 01:43:38 -0600412 bmc_platform = ""
George Keishingddfd7992023-02-16 03:39:47 -0600413 with open(xml_file_path, "rt", encoding="utf-8") as output:
George Keishing74777bd2017-02-07 01:43:38 -0600414 tree = ElementTree.parse(output)
415
Patrick Williams20f38712022-12-08 06:18:26 -0600416 for node in tree.iter("msg"):
George Keishing74777bd2017-02-07 01:43:38 -0600417 # /etc/os-release output is logged in the XML as msg
George Keishing360db632018-09-06 12:01:28 -0500418 # Example: ${output} = VERSION_ID="v1.99.2-71-gbc49f79"
Patrick Williams20f38712022-12-08 06:18:26 -0600419 if "${output} = VERSION_ID=" in node.text:
George Keishing360db632018-09-06 12:01:28 -0500420 # Get BMC version (e.g. v1.99.1-96-g2a46570)
manasarme4f79c92018-02-22 13:02:46 +0530421 bmc_version_id = str(node.text.split("VERSION_ID=")[1])[1:-1]
George Keishing74777bd2017-02-07 01:43:38 -0600422
423 # Platform is logged in the XML as msg.
424 # Example: ${bmc_model} = Witherspoon BMC
Patrick Williams20f38712022-12-08 06:18:26 -0600425 if "${bmc_model} = " in node.text:
George Keishing74777bd2017-02-07 01:43:38 -0600426 bmc_platform = node.text.split(" = ")[1]
427
manasarme4f79c92018-02-22 13:02:46 +0530428 print_vars(bmc_version_id, bmc_platform)
429 return [str(bmc_version_id), str(bmc_platform)]
George Keishing74777bd2017-02-07 01:43:38 -0600430
431
manasarm5dedfaf2018-02-07 14:54:54 +0530432def main():
George Keishingddfd7992023-02-16 03:39:47 -0600433 r"""
434 Main caller.
435 """
436
manasarm5dedfaf2018-02-07 14:54:54 +0530437 if not gen_get_options(parser, stock_list):
438 return False
George Keishing9fbdf792016-10-18 06:16:09 -0500439
manasarm5dedfaf2018-02-07 14:54:54 +0530440 if not validate_parms():
441 return False
George Keishing9fbdf792016-10-18 06:16:09 -0500442
manasarm5dedfaf2018-02-07 14:54:54 +0530443 qprint_pgm_header()
George Keishing9fbdf792016-10-18 06:16:09 -0500444
Patrick Williams20f38712022-12-08 06:18:26 -0600445 parse_output_xml(
446 source, dest, version_id, platform, level, test_phase, processor
447 )
George Keishing9fbdf792016-10-18 06:16:09 -0500448
manasarm5dedfaf2018-02-07 14:54:54 +0530449 return True
450
451
452# Main
453
454if not main():
George Keishingddfd7992023-02-16 03:39:47 -0600455 sys.exit(1)