def create_xml_output_file(path_to_file, error_code, error_msg): """ Produces a template xml file at path to file that contains the error code and error message as text. :param path_to_file: The full path to the file to be generated :param error_code: The error code returned by the test runner failure :param error_msg: The error message returned by the test runner failure :return: None """ if not path_to_file: raise InvalidUseError("XML creation requires path to XML output file") try: error_code = int(error_code) except TypeError: raise InvalidUseError("The error code produced by the test runner should be an integer") if not isinstance(error_msg, str): raise InvalidUseError("The error message produced by the test runner should be of type basestring") current_time = datetime.now().strftime("%Y-%m-%dT%X") # Create the attributes required for the HTMLReporter testsuite_attributes = {"tests": "0", "time": "0.0", "failures": "0", "disabled": "0", "errors": "1", "timestamp": current_time} xml_root = Element("testsuites", testsuite_attributes) properties = SubElement(xml_root, "properties") error_code_property = SubElement(properties, "property", name="ErrorCode", value=str(error_code)) error_msg_property = SubElement(properties, "property", name="ErrorMsg", value=error_msg) testsuite = SubElement(xml_root, "testsuite", attrib=testsuite_attributes, name=os.path.basename(path_to_file)) testcase = SubElement(testsuite, "testcase", name="AzRunTests", time="0") error = SubElement(testcase, "error", message="Failed with error code {}: {}".format(error_code, error_msg)) xml_tree = ElementTree(xml_root) with open(path_to_file, "w") as xml_file: xml_tree.write(xml_file, encoding="UTF-8", xml_declaration=True)
def create_xml_output_filename(path_to_file, output_dir=""): if not path_to_file: raise InvalidUseError("XML output file requires path to test module") filename = os.path.basename(path_to_file) base_filename, ext = os.path.splitext(filename) return os.path.abspath( os.path.join(output_dir, 'test_results_' + base_filename + '.xml'))
def generate_standalone_report(args, extra): """ Generates an HTML report using an already existing directory of XML files. This allows for XML files generated from other frameworks/runs to be included in the report. Since this is standalone, we do not have a list of ScanResults like we do during a normal run, so we have to create a new list from the files in the directory. The only parameters from args we care about are 'dir' and 'output_path'. :param args: the parsed command line arguments :param extra: the unknown parsed command line arguments (these are ignored) :return: None """ # Check to make sure given XML directory has files, if not then check the last modified directory in the given # directory. This will be common since the output_path from the scanner is one level above the actual result # directory. xml_dir = args.dir def has_xml_files(xml_dir): for f in os.listdir(xml_dir): if f.endswith('.xml'): return True return False def get_xml_files(xml_dir): files = [] for f in os.listdir(xml_dir): if f.endswith('.xml'): files.append(os.path.join(xml_dir, f)) return files if not has_xml_files(xml_dir): xml_dir = max([ os.path.join(xml_dir, d) for d in os.listdir(xml_dir) if os.path.isdir(os.path.join(xml_dir, d)) ], key=os.path.getmtime) if not has_xml_files(xml_dir): raise InvalidUseError( "Directory must be a valid results directory: {}".format( os.path.abspath(args.dir))) # Report directory defaults to XML directory if different path is not given output_dir = args.output_path or xml_dir # Get ScanResult list from the XML files scan_results = [] xml_files = get_xml_files(xml_dir) for f in xml_files: scan_result = get_scan_result_from_module(f) if scan_result: scan_results.append(scan_result) # Generate the reports create_html_report(scan_results, output_dir) create_html_failure_report(scan_results, output_dir)
def clean_timestamp(timestamp): """Cleans timestamp by replacing colons, spaces, and periods with underscores for file creation. :param timestamp: the timestamp to clean :return: cleaned timestamp :rtype: str """ if timestamp is None: return "" if not isinstance(timestamp, str): raise InvalidUseError("Timestamp must be a string") timestamp = timestamp.replace(':', '_') timestamp = timestamp.replace('.', '_') timestamp = timestamp.replace(' ', '_') return timestamp