Skip to content

Commit

Permalink
Add missing Junit fields
Browse files Browse the repository at this point in the history
- 'software' section
- 'hardware' section
- 'timestamp' for testcases
- 'hostname' for testsuite
- 'id' for testsuite
- 'package' for testsuite
  • Loading branch information
denisbrykovpartner committed Sep 26, 2023
1 parent b4851c5 commit 0359367
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 23 deletions.
4 changes: 2 additions & 2 deletions src/dltlyse/core/analyser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022. BMW Car IT GmbH. All rights reserved.
# Copyright (C) 2022-23. BMW Car IT GmbH. All rights reserved.
"""DLT file analyser"""

from contextlib import contextmanager
Expand Down Expand Up @@ -428,7 +428,7 @@ def run_analyse(self, traces, xunit, no_sort, is_live, testsuite_name="dltlyse")
# for the method (e.g. avoid to access attribute with dots, function
# inlining, loop unrolling, ...). The inner most loop is called over
# 10 million times when the input file is large. Any small/tiny change
# could causes performance pentlty.
# could causes performance penalty.

filters = self.get_filters()
# add filter for lifecycle start message in case it is missing
Expand Down
48 changes: 36 additions & 12 deletions src/dltlyse/core/report.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Copyright (C) 2022. BMW Car IT GmbH. All rights reserved.
# Copyright (C) 2022-23. BMW Car IT GmbH. All rights reserved.
"""Reporting for dltlyse"""
from collections import Counter
import datetime as dt
import logging
import socket
import xml.etree.ElementTree as etree
from collections import Counter


ATTACHMENT_TEMPLATE = "[[ATTACHMENT|{filename}]]"

Expand Down Expand Up @@ -67,6 +70,7 @@ def __init__(
message="",
metadata=None,
attach=None,
timestamp=None,
):
self.classname = classname
self.testname = testname
Expand All @@ -79,6 +83,7 @@ def __init__(
self.attach = attach

self.metadata = Metadata(metadata)
self.timestamp = str(dt.datetime.now(dt.timezone.utc)) if timestamp is None else str(timestamp)

def __repr__(self):
return repr(self.__dict__)
Expand All @@ -99,7 +104,9 @@ def render_xml(self):
self.state = "error"

# Prepare test case root
root = etree.Element("testcase", classname="dltlyse." + self.classname, name=self.testname, time="0")
root = etree.Element(
"testcase", classname="dltlyse." + self.classname, name=self.testname, time="0", timestamp=self.timestamp
)

# Set attachment
root.text = "".join(ATTACHMENT_TEMPLATE.format(filename=filename) for filename in self.attach)
Expand All @@ -123,10 +130,17 @@ def render_xml(self):
class XUnitReport(object):
"""Template class producing report in xUnit format"""

def __init__(self, outfile="", testsuite_name="dltlyse"):
def __init__(
self, outfile="", testsuite_name="dltlyse", hardware=None, software=None, hostname=None, id_=None, package=None
):
self.results = []
self.outfile = outfile
self.testsuite_name = testsuite_name
self.hardware = hardware
self.software = software
self.hostname = socket.gethostname() if hostname is None else hostname
self.id = id_
self.package = package

def add_results(self, results):
"""Adds a result to the report"""
Expand All @@ -145,15 +159,25 @@ def _generate_summary(self):
def render_xml(self):
"""Return a xml element to present report"""
summary = self._generate_summary()
root_attributes = {
"name": self.testsuite_name,
"tests": summary["number_of_tests"],
"errors": summary["number_of_errors"],
"failures": summary["number_of_failures"],
"skip": summary["number_of_skipped"],
"hostname": self.hostname,
}
if self.id:
root_attributes["id"] = str(self.id)
if self.package:
root_attributes["package"] = str(self.package)

root = etree.Element(
"testsuite",
name=self.testsuite_name,
tests=summary["number_of_tests"],
errors=summary["number_of_errors"],
failures=summary["number_of_failures"],
skip=summary["number_of_skipped"],
)
root = etree.Element("testsuite", **root_attributes)

if self.hardware and isinstance(self.hardware, dict):
etree.SubElement(root, "hardware", self.hardware)
if self.software and isinstance(self.software, dict):
etree.SubElement(root, "software", self.software)

result_elements = []
for result in self.results:
Expand Down
64 changes: 55 additions & 9 deletions tests/unittests/test_plugin_report.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Copyright (C) 2022. BMW Car IT GmbH. All rights reserved.
# Copyright (C) 2022-23. BMW Car IT GmbH. All rights reserved.
"""Test plugin_metadata decorator and xunit report functions"""
import xml.etree.ElementTree as etree
import datetime as dt
import inspect
import socket
from unittest.mock import patch, mock_open
import xml.etree.ElementTree as etree

from dltlyse.core.plugin_base import Plugin, plugin_metadata
from dltlyse.core.report import logger, Metadata, Result, XUnitReport
Expand Down Expand Up @@ -79,22 +81,25 @@ def generate_test_result(attach=None, extra=""):
"""Prepare test result data and xml string"""
attach = attach or []

timestamp = dt.datetime.now()

result = Result(
classname="TestPlugin",
testname="TestPlugin-shot-description",
state="success",
stdout="TestPlugin-stdoutput",
message="TestPlugin-success-message",
attach=attach,
timestamp=timestamp,
)

xml_str = (
'<testcase classname="dltlyse.TestPlugin" name="TestPlugin-shot-description" time="0">'
'<testcase classname="dltlyse.TestPlugin" name="TestPlugin-shot-description" time="0" timestamp="{}">'
"{}"
"<system-out>TestPlugin-stdoutput</system-out>"
"{}"
"</testcase>"
).format("".join("[[ATTACHMENT|{}]]".format(filename) for filename in attach), extra)
).format(timestamp, "".join("[[ATTACHMENT|{}]]".format(filename) for filename in attach), extra)

return result, xml_str

Expand Down Expand Up @@ -199,14 +204,16 @@ def test_metadata_render_recursive():


def test_result_equal():
result = Result()
other = Result(metadata={"key": "should-not-have-effect"})
timestamp = str(dt.datetime.now())
result = Result(timestamp=timestamp)
other = Result(metadata={"key": "should-not-have-effect"}, timestamp=timestamp)

assert result == other


def test_result_render_xml_error_state(): # pylint: disable=invalid-name
"""Test the warning message when the test state is undefined."""

result = Result(classname="noclass", state="nostate")

with patch.object(logger, "warning") as logger_mock:
Expand All @@ -220,23 +227,26 @@ def test_result_render_xml_fail():
"""Tests that result is rendered when the state is error."""
state = "error"
state_type = "error"
timestamp = str(dt.datetime.now())

result = Result(
classname="TestPlugin",
testname="TestPlugin-shot-description",
state=state,
stdout="TestPlugin-stdoutput",
message="TestPlugin-{}-message".format(state),
timestamp=timestamp,
)

assert equal_xml_tree(
result.render_xml(),
(
'<testcase classname="dltlyse.TestPlugin" name="TestPlugin-shot-description" time="0">'
'<testcase classname="dltlyse.TestPlugin" name="TestPlugin-shot-description" \
time="0" timestamp="{timestamp}">'
'<{state} message="TestPlugin-{state}-message" type="{state_type}"/>'
"<system-out>TestPlugin-stdoutput</system-out>"
"</testcase>"
).format(state=state, state_type=state_type),
).format(timestamp=timestamp, state=state, state_type=state_type),
)


Expand Down Expand Up @@ -291,7 +301,8 @@ def test_xunit_report_render_xml():
with patch("dltlyse.core.report.Result.render_xml", return_value=etree.Element("testcase")):
assert equal_xml_tree(
xunit_report.render_xml(),
'<testsuite errors="0" failures="0" name="dltlyse" skip="0" tests="1"><testcase/></testsuite>',
f'<testsuite errors="0" failures="0" name="dltlyse" skip="0" tests="1" \
hostname="{socket.gethostname()}"><testcase/></testsuite>',
)


Expand All @@ -318,3 +329,38 @@ def test_xunit_report_render():

write_xml = "".join(args[0].decode() for args, _ in mocked_file().write.call_args_list)
assert write_xml == "<?xml version='1.0' encoding='UTF-8'?>\n<testsuite />"


def test_xunit_report_check_software_hardware():
"""Tests that xunit report contains software and hardware sections."""
xunit_report = XUnitReport(
software={"OS": "Windows NT 3.5 Daytona"}, hardware={"CPU": "Intel 486DX2-66", "RAM": "8Mb"}
)
xunit_report.outfile = "mocked-file"

with patch("dltlyse.core.report.open", mock_open()) as mocked_file:
xunit_report.render()

write_xml = "".join(args[0].decode() for args, _ in mocked_file().write.call_args_list)
print(write_xml)
check_params = [
'<hardware CPU="Intel 486DX2-66" RAM="8Mb" />',
'<software OS="Windows NT 3.5 Daytona" />',
]
for param in check_params:
assert param in write_xml


def test_xunit_report_check_testsuite_params():
"""Tests that xunit report contains additional testsuite params."""
xunit_report = XUnitReport(package="package.gz", id_="some_strange_id", hostname="test1")
xunit_report.outfile = "mocked-file"

with patch("dltlyse.core.report.open", mock_open()) as mocked_file:
xunit_report.render()

write_xml = "".join(args[0].decode() for args, _ in mocked_file().write.call_args_list)
print(write_xml)
check_params = ['package="package.gz"', 'id="some_strange_id"', 'hostname="test1"']
for param in check_params:
assert param in write_xml

0 comments on commit 0359367

Please sign in to comment.