Files
chipsec-chipsec/chipsec/testcase.py
William Leara 79f81bbb7e fix for flake8 rules related to whitespace
This commit fixes whitespace errors from checkers W291,W292,W293,W391.  They were found by running "flake8 ." from the root directory.  These are errors defined by "pycodestyle", and have no functional impact to the source.  This is simply fixing whitespace as defined by CHIPSEC's .flake8 configuration.

background:
https://www.flake8rules.com/rules/W291.html
https://www.flake8rules.com/rules/W292.html
https://www.flake8rules.com/rules/W293.html
https://www.flake8rules.com/rules/W391.html

tool versions: flake8 v7.3.0, python v3.13.11

Signed-off-by: William Leara <william.leara@dell.com>
2026-03-10 14:20:23 -07:00

367 lines
14 KiB
Python

# CHIPSEC: Platform Security Assessment Framework
# Copyright (c) 2018-2022, Intel Corporation
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; Version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# Contact information:
# chipsec@intel.com
#
import json
import time
import os
from collections import OrderedDict
import xml.etree.ElementTree as ET
import xml.dom.minidom
from chipsec.library.logger import logger
from typing import Dict, List, Optional
class ExitCode:
OK = 0
WARNING = 2
DEPRECATED = 4
FAIL = 8
ERROR = 16
EXCEPTION = 32
INFORMATION = 64
NOTAPPLICABLE = 128
ARCHIVED = 256
help_epilog = """\
Exit Code
---------
CHIPSEC returns an integer exit code:
- Exit code is 0: all modules ran successfully and passed
- Exit code is not 0: each bit means the following:
- Bit 1: WARNING at least one module had a warning
- Bit 2: DEPRECATED at least one module uses deprecated API
- Bit 3: FAIL at least one module failed
- Bit 4: ERROR at least one module wasn't able to run
- Bit 5: EXCEPTION at least one module threw an unexpected exception
- Bit 6: INFORMATION at least one module contained information
- Bit 7: NOT APPLICABLE at least one module was not applicable for the platform
"""
class TestCase:
def __init__(self, name: str) -> None:
self.name = name
self.result = ''
self.result_code = 0
self.output = ''
self.argv = ''
self.desc = ''
self.startTime = 0
self.endTime = 0
self.time = None
def get_fields(self) -> Dict[str, str]:
try:
result_code = f'0x{self.result_code:016X}'
except ValueError:
result_code = f'0x{self.result_code.value:016X}'
return {'name': self.name, 'output': self.output, 'result': self.result, 'code': result_code}
def start_module(self) -> None:
"""Displays a banner for the module name provided."""
text = f'\n[*] Running module: {self.name}'
logger().log_heading(text)
self.startTime = time.time()
self.desc = self.name
def end_module(self, result: str, result_code: int, arg: str) -> None:
self.result = result
self.result_code = result_code
self.argv = arg
self.endTime = time.time()
self.time = self.endTime - self.startTime
class ChipsecResults:
def __init__(self):
self.test_cases = []
self.properties = None
self.summary = False
self.exceptions = []
self.time = None
def add_properties(self, properties: Dict[str, str]) -> None:
self.properties = properties
def add_testcase(self, test: TestCase) -> None:
self.test_cases.append(test)
def get_current_testcase(self) -> Optional[TestCase]:
if len(self.test_cases) == 0 or self.summary:
return None
return self.test_cases[len(self.test_cases) - 1]
def add_exception(self, name):
self.exceptions.append(str(name))
def get_return_code(self):
pass
def order_summary(self):
return {}
def print_summary(self):
pass
def set_time(self, pTime: Optional[float] = None) -> None:
"""Sets the time"""
if pTime is not None:
self.time = pTime
else:
curr_testcase = self.get_current_testcase()
if curr_testcase:
self.time = curr_testcase.endTime - self.test_cases[0].startTime
else:
self.time = self.test_cases[0].time
def get_results(self) -> Dict[str, int]:
results = {}
for test in self.test_cases:
results[test.name] = {'result': test.result}
return results
def xml_summary(self) -> str:
summary = self.order_summary()
xml_element = ET.Element("Summary")
for value in summary.keys():
temp = {}
if value == 'total':
temp['name'] = value
temp['total'] = f'{summary[value]:d}'
m_element = ET.SubElement(xml_element, 'result', temp)
else:
temp['name'] = value
temp['total'] = f'{len(summary[value]):d}'
m_element = ET.SubElement(xml_element, 'result', temp)
for mod in summary[value]:
n_element = ET.SubElement(m_element, 'module')
n_element.text = mod
return ET.tostring(xml_element, "unicode", None)
def json_summary(self) -> str:
summary = self.order_summary()
js = json.dumps(summary, sort_keys=False, indent=2, separators=(',', ': '))
return js
def json_full(self) -> str:
summary = self.get_results()
js = json.dumps(summary, sort_keys=False, indent=2, separators=(',', ': '))
return js
def xml_full(self, name: str, runtime: Optional[float] = None) -> str:
xml_element = ET.Element('testsuites')
summary = self.order_summary()
summary_dict = {}
for value in summary.keys():
if value == 'total':
summary_dict[value] = f'{summary[value]:d}'
else:
summary_dict[value.replace(' ', '')] = f'{len(summary[value]):d}'
summary_dict["name"] = os.path.basename(os.path.splitext(name)[0])
if runtime is not None:
summary_dict["time"] = f'{runtime:5f}'
ts_element = ET.SubElement(xml_element, "testsuite", summary_dict)
# add properties
pr_element = ET.SubElement(ts_element, "properties")
prop_dict = {}
if self.properties is not None:
for value in self.properties:
prop_dict["name"] = value
prop_dict["value"] = self.properties[value]
ET.SubElement(pr_element, "property", prop_dict)
# add test cases
for test in self.test_cases:
ttime = test.time if test.time is not None else 0.0
tc_element = ET.SubElement(ts_element, "testcase", {'classname': test.name, 'name': test.desc, 'time': f'{ttime:5f}'})
ET.SubElement(tc_element, "pass", {"type": test.result})
out_element = ET.SubElement(tc_element, "system-out")
out_element.text = test.output
return xml.dom.minidom.parseString(ET.tostring(xml_element, None, None)).toprettyxml()
def markdown_full(self, name: str) -> str:
passed = []
failed = []
error = []
warning = []
information = []
notapplicable = []
deprecated = []
archived = []
destination = {'Passed': passed,
'Failed': failed,
'Error': error,
'Warning': warning,
'Information': information,
'NotApplicable': notapplicable,
'Deprecated': deprecated,
'Archived': archived
}
for test in self.test_cases:
# Test case as header level 4
out_string = f'#### {test.name.replace("chipsec.modules.", ""):s}\n'
for line in test.output.splitlines(True):
# Format output as code
out_string += f' {line:s}'
destination[test.result].append(out_string)
ret_string = ''
for result in destination:
# Category as header level 1
ret_string += f'\n# {result:s}:{len(destination[result]):d}\n'
ret_string += ''.join(destination[result])
return ret_string
class LegacyResults(ChipsecResults):
def print_summary(self, runtime: Optional[float] = None) -> None:
summary = self.order_summary()
filler = '*' * 27
logger().log(f'\n[CHIPSEC] {filler} SUMMARY {filler}')
print_dictionary = {'failed to run': logger().log_error,
'passed': logger().log_passed,
'information': logger().log_information,
'archived': logger().log_information,
'failed': logger().log_failed,
'not applicable': logger().log_not_applicable}
if runtime is not None:
logger().log(f'[CHIPSEC] Time elapsed {runtime:.3f}')
for result in summary.keys():
if result == 'total':
logger().log(f'[CHIPSEC] Modules {result:16}{summary[result]:d}')
elif result == 'warnings':
logger().log(f'[CHIPSEC] Modules with {result:11}{len(summary[result]):d}:')
for mod in summary[result]:
logger().log_warning(mod)
elif result == 'exceptions':
if len(summary[result]) > 0:
logger().log(f'[CHIPSEC] Modules with {result:11}{len(summary[result]):d}:')
for mod in summary[result]:
logger().log_error(mod)
elif result == 'archived':
continue
else:
logger().log(f'[CHIPSEC] Modules {result:16}{len(summary[result]):d}:')
for mod in summary[result]:
print_dictionary[result](mod)
logger().log('[CHIPSEC] *****************************************************************')
def order_summary(self) -> Dict[str, List[TestCase]]:
self.summary = True
ret = OrderedDict()
ret['failed to run'] = []
ret['passed'] = []
ret['information'] = []
ret['failed'] = []
ret['warnings'] = []
ret['not applicable'] = []
ret['archived'] = []
destination = {'Passed': 'passed',
'Failed': 'failed',
'Error': 'failed to run',
'Warning': 'warnings',
'Information': 'information',
'NotApplicable': 'not applicable',
'Archived': 'archived'
}
executed = 0
for test in self.test_cases:
executed += 1
fields = test.get_fields()
ret[destination[fields['result']]].append(fields['name'])
ret['total'] = executed
ret['exceptions'] = self.exceptions
return ret
def get_return_code(self) -> int:
summary = self.order_summary()
destination = {
'failed to run': ExitCode.ERROR,
'exceptions': ExitCode.EXCEPTION,
'failed': ExitCode.FAIL,
'warnings': ExitCode.WARNING,
'not applicable': ExitCode.NOTAPPLICABLE,
'information': ExitCode.INFORMATION,
'archived': ExitCode.ARCHIVED
}
for result in destination.keys():
if len(summary[result]) != 0:
return destination[result]
return ExitCode.OK
class ReturnCodeResults(ChipsecResults):
def print_summary(self, runtime: Optional[float] = None) -> None:
summary = self.order_summary()
filler = '*' * 27
logger().log(f'\n[CHIPSEC] {filler} SUMMARY {filler}')
if runtime is not None:
logger().log(f'[CHIPSEC] Time elapsed {runtime:.3f}')
for result in summary.keys():
if result == 'total':
logger().log(f'[CHIPSEC] Modules {result:16}{summary[result]:d}')
elif result == 'exceptions':
if len(summary[result]) > 0:
logger().log(f'[CHIPSEC] Modules with {result:11}{len(summary[result]):d}:')
for mod in summary[result]:
logger().log_error(mod)
elif result == 'archived':
continue
else:
logger().log(f'[CHIPSEC] Modules {result:16}{len(summary[result]):d}:')
for mod in summary[result]:
logger().log(f" {mod}")
logger().log('[CHIPSEC] *****************************************************************')
def order_summary(self):
if self.time is None:
self.set_time()
self.summary = True
ret = OrderedDict()
passed = []
failed = []
archived = []
executed = 0
for test in self.test_cases:
executed += 1
fields = test.get_fields()
if fields['result'] == 'Passed':
passed.append(fields['name'])
elif fields['result'] == 'Failed':
failed.append(fields['name'])
elif fields['result'] == 'Archived':
archived.append(fields['name'])
ret['total'] = executed
ret['failed'] = failed
ret['passed'] = passed
ret['archived'] = archived
ret['exceptions'] = self.exceptions
return ret
def get_return_code(self):
summary = self.order_summary()
if len(self.test_cases) == 1:
return self.test_cases[0].result_code
elif len(summary['failed']) != 0:
return ExitCode.FAIL
else:
return ExitCode.OK