blob: 2b4df12e6b37b2cd4f6bb08f2ae93912d5b3f43c [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 Keishing9fbdf792016-10-18 06:16:09 -05009import csv
Patrick Williams57318182022-12-08 06:18:26 -060010import datetime
11import getopt
12import os
manasarm5dedfaf2018-02-07 14:54:54 +053013import re
Steven Sombar3e82e3b2019-03-21 10:33:52 -050014import stat
Patrick Williams57318182022-12-08 06:18:26 -060015import sys
George Keishing74777bd2017-02-07 01:43:38 -060016from xml.etree import ElementTree
manasarm5dedfaf2018-02-07 14:54:54 +053017
Patrick Williams57318182022-12-08 06:18:26 -060018import robot.errors
Sridevi Ramesh47375aa2022-12-08 05:05:31 -060019from gen_arg import *
20from gen_print import *
21from gen_valid import *
Patrick Williams57318182022-12-08 06:18:26 -060022from robot.api import ExecutionResult
23from robot.result.visitor import ResultVisitor
Sridevi Ramesh47375aa2022-12-08 05:05:31 -060024
manasarm5dedfaf2018-02-07 14:54:54 +053025# Remove the python library path to restore with local project path later.
26save_path_0 = sys.path[0]
27del sys.path[0]
George Keishingc2a6f092019-02-20 12:26:54 -060028sys.path.append(os.path.join(os.path.dirname(__file__), "../../lib"))
manasarm5dedfaf2018-02-07 14:54:54 +053029
manasarm5dedfaf2018-02-07 14:54:54 +053030# Restore sys.path[0].
31sys.path.insert(0, save_path_0)
32
Steven Sombara5e32f22019-03-01 13:33:15 -060033
34this_program = sys.argv[0]
Patrick Williams57318182022-12-08 06:18:26 -060035info = " For more information: " + this_program + " -h"
Steven Sombara5e32f22019-03-01 13:33:15 -060036if len(sys.argv) == 1:
George Keishingbfa859a2020-04-02 00:38:24 -050037 print(info)
Steven Sombara5e32f22019-03-01 13:33:15 -060038 sys.exit(1)
39
40
manasarm5dedfaf2018-02-07 14:54:54 +053041parser = argparse.ArgumentParser(
Steven Sombara5e32f22019-03-01 13:33:15 -060042 usage=info,
Patrick Williams57318182022-12-08 06:18:26 -060043 description=(
44 "%(prog)s uses a robot framework API to extract test result data"
45 " from output.xml generated by robot tests. For more information on"
46 " the Robot Framework API, see "
47 " http://robot-framework.readthedocs.io/en/3.0/autodoc/robot.result.html"
48 ),
manasarm5dedfaf2018-02-07 14:54:54 +053049 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
Patrick Williams57318182022-12-08 06:18:26 -060050 prefix_chars="-+",
51)
manasarm5dedfaf2018-02-07 14:54:54 +053052
53parser.add_argument(
Patrick Williams57318182022-12-08 06:18:26 -060054 "--source",
55 "-s",
56 help=(
57 "The output.xml robot test result file path. This parameter is "
58 " required."
59 ),
60)
manasarm5dedfaf2018-02-07 14:54:54 +053061
62parser.add_argument(
Patrick Williams57318182022-12-08 06:18:26 -060063 "--dest",
64 "-d",
65 help=(
66 "The directory path where the generated .csv files will go. This "
67 " parameter is required."
68 ),
69)
manasarm5dedfaf2018-02-07 14:54:54 +053070
manasarme4f79c92018-02-22 13:02:46 +053071parser.add_argument(
Patrick Williams57318182022-12-08 06:18:26 -060072 "--version_id",
73 help=(
74 "Driver version of openbmc firmware which was used during test, "
75 ' e.g. "v2.1-215-g6e7eacb". This parameter is required.'
76 ),
77)
manasarme4f79c92018-02-22 13:02:46 +053078
79parser.add_argument(
Patrick Williams57318182022-12-08 06:18:26 -060080 "--platform",
81 help=(
82 "OpenBMC platform which was used during test, e.g."
83 ' "Witherspoon". This parameter is required.'
84 ),
85)
manasarme4f79c92018-02-22 13:02:46 +053086
George Keishing19533d52018-04-09 03:08:03 -050087parser.add_argument(
Patrick Williams57318182022-12-08 06:18:26 -060088 "--level",
89 help=(
90 "OpenBMC release level which was used during test, e.g."
91 ' "Master", "OBMC920". This parameter is required.'
92 ),
93)
Steven Sombara5e32f22019-03-01 13:33:15 -060094
95parser.add_argument(
Patrick Williams57318182022-12-08 06:18:26 -060096 "--test_phase",
97 help=(
98 'Name of testing phase, e.g. "DVT", "SVT", etc. This'
99 " parameter is optional."
100 ),
101 default="FVT",
102)
Steven Sombara5e32f22019-03-01 13:33:15 -0600103
104parser.add_argument(
Patrick Williams57318182022-12-08 06:18:26 -0600105 "--subsystem",
106 help=(
107 'Name of the subsystem, e.g. "OPENBMC" etc. This parameter is'
108 " optional."
109 ),
110 default="OPENBMC",
111)
Vijaye3a98372019-10-30 02:04:22 -0500112
113parser.add_argument(
Patrick Williams57318182022-12-08 06:18:26 -0600114 "--processor",
Steven Sombara5e32f22019-03-01 13:33:15 -0600115 help='Name of processor, e.g. "P9". This parameter is optional.',
Patrick Williams57318182022-12-08 06:18:26 -0600116 default="OPENPOWER",
117)
Steven Sombara5e32f22019-03-01 13:33:15 -0600118
George Keishing19533d52018-04-09 03:08:03 -0500119
manasarm5dedfaf2018-02-07 14:54:54 +0530120# Populate stock_list with options we want.
121stock_list = [("test_mode", 0), ("quiet", 0), ("debug", 0)]
122
123
Patrick Williams57318182022-12-08 06:18:26 -0600124def exit_function(signal_number=0, frame=None):
manasarm5dedfaf2018-02-07 14:54:54 +0530125 r"""
126 Execute whenever the program ends normally or with the signals that we
127 catch (i.e. TERM, INT).
128 """
129
130 dprint_executing()
131
132 dprint_var(signal_number)
133
134 qprint_pgm_footer()
135
136
Patrick Williams57318182022-12-08 06:18:26 -0600137def signal_handler(signal_number, frame):
manasarm5dedfaf2018-02-07 14:54:54 +0530138 r"""
139 Handle signals. Without a function to catch a SIGTERM or SIGINT, the
140 program would terminate immediately with return code 143 and without
141 calling the exit_function.
142 """
143
144 # Our convention is to set up exit_function with atexit.register() so
145 # there is no need to explicitly call exit_function from here.
146
147 dprint_executing()
148
149 # Calling exit prevents us from returning to the code that was running
150 # when the signal was received.
151 exit(0)
152
153
154def validate_parms():
manasarm5dedfaf2018-02-07 14:54:54 +0530155 r"""
156 Validate program parameters, etc. Return True or False (i.e. pass/fail)
157 accordingly.
158 """
159
160 if not valid_file_path(source):
161 return False
162
163 if not valid_dir_path(dest):
164 return False
165
166 gen_post_validation(exit_function, signal_handler)
167
168 return True
George Keishing9fbdf792016-10-18 06:16:09 -0500169
170
Patrick Williams57318182022-12-08 06:18:26 -0600171def parse_output_xml(
172 xml_file_path,
173 csv_dir_path,
174 version_id,
175 platform,
176 level,
177 test_phase,
178 processor,
179):
manasarm5dedfaf2018-02-07 14:54:54 +0530180 r"""
181 Parse the robot-generated output.xml file and extract various test
182 output data. Put the extracted information into a csv file in the "dest"
183 folder.
184
185 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500186 xml_file_path The path to a Robot-generated output.xml
187 file.
188 csv_dir_path The path to the directory that is to
189 contain the .csv files generated by
190 this function.
191 version_id Version of the openbmc firmware
192 (e.g. "v2.1-215-g6e7eacb").
193 platform Platform of the openbmc system.
194 level Release level of the OpenBMC system
George Keishing360db632018-09-06 12:01:28 -0500195 (e.g. "Master").
manasarm5dedfaf2018-02-07 14:54:54 +0530196 """
197
Peter D Phan8e13ade2021-09-16 06:15:55 -0500198 # Initialize tallies
199 total_critical_tc = 0
200 total_critical_passed = 0
201 total_critical_failed = 0
202 total_non_critical_tc = 0
203 total_non_critical_passed = 0
204 total_non_critical_failed = 0
205
George Keishing74777bd2017-02-07 01:43:38 -0600206 result = ExecutionResult(xml_file_path)
Patrick Williams57318182022-12-08 06:18:26 -0600207 result.configure(
208 stat_config={
209 "suite_stat_level": 2,
210 "tag_stat_combine": "tagANDanother",
211 }
212 )
George Keishing9fbdf792016-10-18 06:16:09 -0500213
214 stats = result.statistics
George Keishingf8a9ebe2018-08-06 12:49:11 -0500215 print("--------------------------------------")
Peter D Phan8e13ade2021-09-16 06:15:55 -0500216 try:
Patrick Williams57318182022-12-08 06:18:26 -0600217 total_critical_tc = (
218 stats.total.critical.passed + stats.total.critical.failed
219 )
Peter D Phan8e13ade2021-09-16 06:15:55 -0500220 total_critical_passed = stats.total.critical.passed
221 total_critical_failed = stats.total.critical.failed
222 except AttributeError:
223 pass
224
225 try:
226 total_non_critical_tc = stats.total.passed + stats.total.failed
227 total_non_critical_passed = stats.total.passed
228 total_non_critical_failed = stats.total.failed
229 except AttributeError:
230 pass
231
Patrick Williams57318182022-12-08 06:18:26 -0600232 print(
233 "Total Test Count:\t %d" % (total_non_critical_tc + total_critical_tc)
234 )
Peter D Phan8e13ade2021-09-16 06:15:55 -0500235
236 print("Total Critical Test Failed:\t %d" % total_critical_failed)
237 print("Total Critical Test Passed:\t %d" % total_critical_passed)
238 print("Total Non-Critical Test Failed:\t %d" % total_non_critical_failed)
239 print("Total Non-Critical Test Passed:\t %d" % total_non_critical_passed)
George Keishingf8a9ebe2018-08-06 12:49:11 -0500240 print("Test Start Time:\t %s" % result.suite.starttime)
241 print("Test End Time:\t\t %s" % result.suite.endtime)
242 print("--------------------------------------")
George Keishing9fbdf792016-10-18 06:16:09 -0500243
244 # Use ResultVisitor object and save off the test data info
245 class TestResult(ResultVisitor):
246 def __init__(self):
247 self.testData = []
248
249 def visit_test(self, test):
250 self.testData += [test]
251
252 collectDataObj = TestResult()
253 result.visit(collectDataObj)
254
255 # Write the result statistics attributes to CSV file
256 l_csvlist = []
George Keishing74777bd2017-02-07 01:43:38 -0600257
258 # Default Test data
Steven Sombara5e32f22019-03-01 13:33:15 -0600259 l_test_type = test_phase
George Keishing19533d52018-04-09 03:08:03 -0500260
Patrick Williams57318182022-12-08 06:18:26 -0600261 l_pse_rel = "Master"
George Keishing19533d52018-04-09 03:08:03 -0500262 if level:
263 l_pse_rel = level
264
Patrick Williams57318182022-12-08 06:18:26 -0600265 l_env = "HW"
Steven Sombara5e32f22019-03-01 13:33:15 -0600266 l_proc = processor
George Keishing74777bd2017-02-07 01:43:38 -0600267 l_platform_type = ""
268 l_func_area = ""
269
Gunnar Mills096cd562018-03-26 10:19:12 -0500270 # System data from XML meta data
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500271 # l_system_info = get_system_details(xml_file_path)
manasarme4f79c92018-02-22 13:02:46 +0530272
273 # First let us try to collect information from keyboard input
274 # If keyboard input cannot give both information, then find from xml file.
275 if version_id and platform:
276 l_driver = version_id
277 l_platform_type = platform
George Keishingf8a9ebe2018-08-06 12:49:11 -0500278 print("BMC Version_id:%s" % version_id)
279 print("BMC Platform:%s" % platform)
George Keishing74777bd2017-02-07 01:43:38 -0600280 else:
manasarme4f79c92018-02-22 13:02:46 +0530281 # System data from XML meta data
282 l_system_info = get_system_details(xml_file_path)
283 l_driver = l_system_info[0]
284 l_platform_type = l_system_info[1]
285
286 # Driver version id and platform are mandatorily required for CSV file
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500287 # generation. If any one is not avaulable, exit CSV file generation
288 # process.
manasarme4f79c92018-02-22 13:02:46 +0530289 if l_driver and l_platform_type:
George Keishingf8a9ebe2018-08-06 12:49:11 -0500290 print("Driver and system info set.")
manasarme4f79c92018-02-22 13:02:46 +0530291 else:
Patrick Williams57318182022-12-08 06:18:26 -0600292 print(
293 "Both driver and system info need to be set. CSV"
294 " file is not generated."
295 )
George Keishing74777bd2017-02-07 01:43:38 -0600296 sys.exit()
George Keishing9fbdf792016-10-18 06:16:09 -0500297
298 # Default header
Patrick Williams57318182022-12-08 06:18:26 -0600299 l_header = [
300 "test_start",
301 "test_end",
302 "subsys",
303 "test_type",
304 "test_result",
305 "test_name",
306 "pse_rel",
307 "driver",
308 "env",
309 "proc",
310 "platform_type",
311 "test_func_area",
312 ]
George Keishing9fbdf792016-10-18 06:16:09 -0500313
314 l_csvlist.append(l_header)
315
316 # Generate CSV file onto the path with current time stamp
George Keishing74777bd2017-02-07 01:43:38 -0600317 l_base_dir = csv_dir_path
George Keishing937fe3c2019-10-29 11:20:34 -0500318 l_timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%d-%H-%M-%S")
George Keishing74777bd2017-02-07 01:43:38 -0600319 # Example: 2017-02-20-08-47-22_Witherspoon.csv
320 l_csvfile = l_base_dir + l_timestamp + "_" + l_platform_type + ".csv"
George Keishing9fbdf792016-10-18 06:16:09 -0500321
George Keishingf8a9ebe2018-08-06 12:49:11 -0500322 print("Writing data into csv file:%s" % l_csvfile)
George Keishing9fbdf792016-10-18 06:16:09 -0500323
324 for testcase in collectDataObj.testData:
George Keishing74777bd2017-02-07 01:43:38 -0600325 # Functional Area: Suite Name
326 # Test Name: Test Case Name
Patrick Williams57318182022-12-08 06:18:26 -0600327 l_func_area = str(testcase.parent).split(" ", 1)[1]
George Keishing74777bd2017-02-07 01:43:38 -0600328 l_test_name = str(testcase)
George Keishing9fbdf792016-10-18 06:16:09 -0500329
330 # Test Result pass=0 fail=1
Patrick Williams57318182022-12-08 06:18:26 -0600331 if testcase.status == "PASS":
George Keishing9fbdf792016-10-18 06:16:09 -0500332 l_test_result = 0
333 else:
334 l_test_result = 1
335
George Keishing74777bd2017-02-07 01:43:38 -0600336 # Format datetime from robot output.xml to "%Y-%m-%d-%H-%M-%S"
337 l_stime = xml_to_csv_time(testcase.starttime)
338 l_etime = xml_to_csv_time(testcase.endtime)
George Keishing9fbdf792016-10-18 06:16:09 -0500339 # Data Sequence: test_start,test_end,subsys,test_type,
George Keishing74777bd2017-02-07 01:43:38 -0600340 # test_result,test_name,pse_rel,driver,
George Keishing360db632018-09-06 12:01:28 -0500341 # env,proc,platform_type,test_func_area,
Patrick Williams57318182022-12-08 06:18:26 -0600342 l_data = [
343 l_stime,
344 l_etime,
345 subsystem,
346 l_test_type,
347 l_test_result,
348 l_test_name,
349 l_pse_rel,
350 l_driver,
351 l_env,
352 l_proc,
353 l_platform_type,
354 l_func_area,
355 ]
George Keishing9fbdf792016-10-18 06:16:09 -0500356 l_csvlist.append(l_data)
357
George Keishinga96e27c2016-12-04 23:05:04 -0600358 # Open the file and write to the CSV file
359 l_file = open(l_csvfile, "w")
Patrick Williams57318182022-12-08 06:18:26 -0600360 l_writer = csv.writer(l_file, lineterminator="\n")
George Keishinga96e27c2016-12-04 23:05:04 -0600361 l_writer.writerows(l_csvlist)
362 l_file.close()
Steven Sombar3e82e3b2019-03-21 10:33:52 -0500363 # Set file permissions 666.
Patrick Williams57318182022-12-08 06:18:26 -0600364 perm = (
365 stat.S_IRUSR
366 + stat.S_IWUSR
367 + stat.S_IRGRP
368 + stat.S_IWGRP
369 + stat.S_IROTH
370 + stat.S_IWOTH
371 )
Steven Sombar3e82e3b2019-03-21 10:33:52 -0500372 os.chmod(l_csvfile, perm)
George Keishing9fbdf792016-10-18 06:16:09 -0500373
374
George Keishing74777bd2017-02-07 01:43:38 -0600375def xml_to_csv_time(xml_datetime):
376 r"""
377 Convert the time from %Y%m%d %H:%M:%S.%f format to %Y-%m-%d-%H-%M-%S format
378 and return it.
379
manasarm5dedfaf2018-02-07 14:54:54 +0530380 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500381 datetime The date in the following format: %Y%m%d
382 %H:%M:%S.%f (This is the format
383 typically found in an XML file.)
George Keishing74777bd2017-02-07 01:43:38 -0600384
385 The date returned will be in the following format: %Y-%m-%d-%H-%M-%S
386 """
manasarm5dedfaf2018-02-07 14:54:54 +0530387
George Keishing74777bd2017-02-07 01:43:38 -0600388 # 20170206 05:05:19.342
George Keishing937fe3c2019-10-29 11:20:34 -0500389 l_str = datetime.datetime.strptime(xml_datetime, "%Y%m%d %H:%M:%S.%f")
George Keishing74777bd2017-02-07 01:43:38 -0600390 # 2017-02-06-05-05-19
391 l_str = l_str.strftime("%Y-%m-%d-%H-%M-%S")
392 return str(l_str)
393
394
395def get_system_details(xml_file_path):
396 r"""
397 Get the system data from output.xml generated by robot and return it.
Gunnar Mills28e403b2017-10-25 16:16:38 -0500398 The list returned will be in the following order: [driver,platform]
George Keishing74777bd2017-02-07 01:43:38 -0600399
manasarm5dedfaf2018-02-07 14:54:54 +0530400 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500401 xml_file_path The relative or absolute path to the
402 output.xml file.
George Keishing74777bd2017-02-07 01:43:38 -0600403 """
manasarm5dedfaf2018-02-07 14:54:54 +0530404
manasarme4f79c92018-02-22 13:02:46 +0530405 bmc_version_id = ""
George Keishing74777bd2017-02-07 01:43:38 -0600406 bmc_platform = ""
Patrick Williams57318182022-12-08 06:18:26 -0600407 with open(xml_file_path, "rt") as output:
George Keishing74777bd2017-02-07 01:43:38 -0600408 tree = ElementTree.parse(output)
409
Patrick Williams57318182022-12-08 06:18:26 -0600410 for node in tree.iter("msg"):
George Keishing74777bd2017-02-07 01:43:38 -0600411 # /etc/os-release output is logged in the XML as msg
George Keishing360db632018-09-06 12:01:28 -0500412 # Example: ${output} = VERSION_ID="v1.99.2-71-gbc49f79"
Patrick Williams57318182022-12-08 06:18:26 -0600413 if "${output} = VERSION_ID=" in node.text:
George Keishing360db632018-09-06 12:01:28 -0500414 # Get BMC version (e.g. v1.99.1-96-g2a46570)
manasarme4f79c92018-02-22 13:02:46 +0530415 bmc_version_id = str(node.text.split("VERSION_ID=")[1])[1:-1]
George Keishing74777bd2017-02-07 01:43:38 -0600416
417 # Platform is logged in the XML as msg.
418 # Example: ${bmc_model} = Witherspoon BMC
Patrick Williams57318182022-12-08 06:18:26 -0600419 if "${bmc_model} = " in node.text:
George Keishing74777bd2017-02-07 01:43:38 -0600420 bmc_platform = node.text.split(" = ")[1]
421
manasarme4f79c92018-02-22 13:02:46 +0530422 print_vars(bmc_version_id, bmc_platform)
423 return [str(bmc_version_id), str(bmc_platform)]
George Keishing74777bd2017-02-07 01:43:38 -0600424
425
manasarm5dedfaf2018-02-07 14:54:54 +0530426def main():
manasarm5dedfaf2018-02-07 14:54:54 +0530427 if not gen_get_options(parser, stock_list):
428 return False
George Keishing9fbdf792016-10-18 06:16:09 -0500429
manasarm5dedfaf2018-02-07 14:54:54 +0530430 if not validate_parms():
431 return False
George Keishing9fbdf792016-10-18 06:16:09 -0500432
manasarm5dedfaf2018-02-07 14:54:54 +0530433 qprint_pgm_header()
George Keishing9fbdf792016-10-18 06:16:09 -0500434
Patrick Williams57318182022-12-08 06:18:26 -0600435 parse_output_xml(
436 source, dest, version_id, platform, level, test_phase, processor
437 )
George Keishing9fbdf792016-10-18 06:16:09 -0500438
manasarm5dedfaf2018-02-07 14:54:54 +0530439 return True
440
441
442# Main
443
444if not main():
445 exit(1)