Skip to content

Commit

Permalink
Refactored code that parses Maven surefire test result files
Browse files Browse the repository at this point in the history
  • Loading branch information
dwinchell committed Oct 27, 2021
1 parent c630d15 commit 7b453db
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 282 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
`maven-profiles` | No | `[]` | List of maven profiles to use.
"""# pylint: disable=line-too-long

import glob
import os

from ploigos_step_runner.exceptions import StepRunnerException
from ploigos_step_runner.utils.maven import \
get_plugin_configuration_absolute_path_values
from ploigos_step_runner.utils.xml import \
aggregate_xml_element_attribute_values
from ploigos_step_runner.utils.xml import get_xml_element


class MavenTestReportingMixin:
Expand All @@ -38,6 +38,7 @@ class MavenTestReportingMixin:
SUREFIRE_PLUGIN_DEFAULT_REPORTS_DIR = 'target/surefire-reports'
FAILSAFE_PLUGIN_DEFAULT_REPORTS_DIR = 'target/failsafe-reports'
TESTSUITE_EVIDENCE_ATTRIBUTES = ["time", "tests", "errors", "skipped", "failures"]
TESTSUITE_EVIDENCE_ELEMENT = "testsuite"

def _attempt_get_test_report_directory(
self,
Expand Down Expand Up @@ -141,13 +142,9 @@ def _gather_evidence_from_test_report_directory_testsuite_elements(
test_report_evidence_attributes = MavenTestReportingMixin.TESTSUITE_EVIDENCE_ATTRIBUTES
test_report_evidence_element = 'testsuite'

not_found_attribs = []
report_results = aggregate_xml_element_attribute_values(
xml_file_paths=test_report_dir,
xml_element=test_report_evidence_element,
attribs=test_report_evidence_attributes
)
report_results = MavenTestReportingMixin._collect_report_results(test_report_dir)

not_found_attribs = []
for attribute in test_report_evidence_attributes:
if attribute in report_results:
step_result.add_evidence(
Expand All @@ -161,6 +158,55 @@ def _gather_evidence_from_test_report_directory_testsuite_elements(
f" attributes ({not_found_attribs}) on xml element" \
f" ({test_report_evidence_element}) in test report" \
f" directory ({test_report_dir})."

else:
step_result.message += f"\nWARNING: test report directory ({test_report_dir})" \
" does not exist to gather evidence from"

@staticmethod
def _collect_report_results(
test_report_dir
):
report_results = {}

# Iterate over each file that contains test results
xml_files = glob.glob(test_report_dir + '/*.xml', recursive=False)
for file in xml_files:
try:

# Iterate over the XML attributes that are evidence
element = get_xml_element(file, MavenTestReportingMixin.TESTSUITE_EVIDENCE_ELEMENT)
for attrib in element.attrib:
if attrib in MavenTestReportingMixin.TESTSUITE_EVIDENCE_ATTRIBUTES: # Is this attribute evidence?

# Parse each attribute as a number
attrib_value = 0
try:
attrib_value = MavenTestReportingMixin._to_number(element.attrib[attrib])
except ValueError:
print(
f"WARNING: While parsing test results, expected the value of {attrib} " \
f"in {file} to be a number. It was '{attrib_value}'. Ignoring."
)

# Add up the totals across all files
if attrib in report_results:
report_results[attrib] += attrib_value
else:
report_results[attrib] = attrib_value

# If we cannot parse a file for some reason, warn but continue processing.
except ValueError:
print(
f"WARNING: could not parse test results" \
f" in file ({file}). Ignoring."
)
continue

return report_results

@staticmethod
def _to_number(string):
if string.isnumeric():
return int(string)
return float(string)
110 changes: 0 additions & 110 deletions src/ploigos_step_runner/utils/xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,113 +154,3 @@ def get_xml_element_text_by_path(
result = xml_elements.text

return result

def aggregate_xml_element_attribute_values(xml_file_paths, xml_element, attribs): # pylint: disable=too-many-branches
"""Function to parse a XML file looking for a specific element and obtaining its attributes.
If element is not found in file the file is ignored.
If attribute is not found in element then any other attributes that are in the file are still
aggregated.
Parameters
----------
xml_file_paths : str, [str]
Path(s) to XML file(s) or directory(s) containing XML file(s)
xml_element: str
XML element being searched for
attribs: list
List of attributes being searched for in the XML element
Returns
--------
report_results: dict
A dictionary of attribute values.
"""
report_results = {}

if not isinstance(xml_file_paths, list):
xml_file_paths = [xml_file_paths]

# collect all the xml file paths
xml_files = []
for xml_file_path in xml_file_paths:
if os.path.isdir(xml_file_path):
xml_files += glob.glob(xml_file_path + '/*.xml', recursive=False)
elif os.path.isfile(xml_file_path):
xml_files += [xml_file_path]

#Iterate over XML files
for file in xml_files:
try:
#Add attributes to dictionary
element = get_xml_element(file, xml_element)
for attrib in attribs:
if attrib in element.attrib:
# aggregate attribute accross multiple files
attribute_value = element.attrib[attrib]

if not attrib in report_results:
report_results[attrib] = []
report_results[attrib].append(attribute_value)
else:
# ignore if a file has the element but missing an attribute
print(
f"WARNING: Did not find attribute ({attrib}) on element ({element})" \
f" in file ({file}). Ignoring."
)
continue
except ValueError:
# ignore if a file has the element but missing an attribute
print(
f"WARNING: Did not find element ({xml_element})" \
f" in file ({file}). Ignoring."
)
continue

#Loop to check if all values aggregated are integers, floats, strings, or a mix
#of all three.
for attrib in attribs:
only_ints = True
only_floats = True

#Iteration to determine what pattern all values follow.
if attrib in report_results:
for value in report_results[attrib]:
# check if int
try:
int(value)
except ValueError:
only_ints = False

# check if float
try:
float(value)
except ValueError:
only_floats = False


if only_ints or only_floats:
aggregated_value = 0
else:
aggregated_value = []

#Iteration to aggregate values based on determined pattern.
for value in report_results[attrib]:
#If a value is numeric and can be a float then it is an integer
if only_ints:
aggregated_value = int(aggregated_value) + int(value)
#If a value is not numeric but can be a float then it is a float
elif only_floats:
aggregated_value = float(aggregated_value) + float(value)
else:
aggregated_value.append(value)
else:
aggregated_value = None

#Replace list of values with aggregated value that is either an int,
#float, or string
if aggregated_value is not None:
report_results[attrib] = aggregated_value

return report_results
52 changes: 15 additions & 37 deletions tests/step_implementers/shared/test_maven_test_reporting_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,37 +161,26 @@ def test_plugin_not_found(self, get_plugin_configuration_absolute_path_values_mo
require_phase_execution_config=False
)

@patch("ploigos_step_runner.step_implementers.shared.maven_test_reporting_mixin.aggregate_xml_element_attribute_values")
class TestMavenTestReportingMixin__gather_evidence_from_test_report_directory_testsuite_elements(
BaseStepImplementerTestCase
):
def test_found_all_attributes(self, aggregate_xml_element_attribute_values_mock):
def test_found_all_attributes(self):
with TempDirectory() as test_dir:
# setup test
actual_step_result = StepResult(
step_name='mock-maven-test-step',
sub_step_name='mock-maven-test-sub-step',
sub_step_implementer_name='MockMavenTestReportingMixinStepImplementer'
)
test_report_dir = os.path.join(
test_dir.path,
'mock-test-results'
)

# setup mocks
os.mkdir(test_report_dir)
aggregate_xml_element_attribute_values_mock.return_value = {
"time": 1.42,
"tests": 42,
"errors": 3,
"skipped": 2,
"failures": 1
}
test_result = b'<testsuite time="1.42" tests="42" errors="3" skipped="2" failures="1" />'
test_dir.write('test_result.xml', test_result)

# run test
MavenTestReportingMixin._gather_evidence_from_test_report_directory_testsuite_elements(
step_result=actual_step_result,
test_report_dir=test_report_dir
test_report_dir=test_dir.path
)

# verify results
Expand All @@ -207,30 +196,23 @@ def test_found_all_attributes(self, aggregate_xml_element_attribute_values_mock)
expected_step_result.add_evidence(name='failures', value=1)
self.assertEqual(actual_step_result, expected_step_result)

def test_found_dir_found_no_attributes(self, aggregate_xml_element_attribute_values_mock):
def test_found_dir_found_some_attributes(self):
with TempDirectory() as test_dir:
# setup test
actual_step_result = StepResult(
step_name='mock-maven-test-step',
sub_step_name='mock-maven-test-sub-step',
sub_step_implementer_name='MockMavenTestReportingMixinStepImplementer'
)
test_report_dir = os.path.join(
test_dir.path,
'mock-test-results'
)

# setup mocks
os.mkdir(test_report_dir)
aggregate_xml_element_attribute_values_mock.return_value = {
"time": 1.42,
"tests": 42
}
test_result = b'<testsuite time="1.42" tests="42" />'
test_dir.write('test_result.xml', test_result)

# run test
MavenTestReportingMixin._gather_evidence_from_test_report_directory_testsuite_elements(
step_result=actual_step_result,
test_report_dir=test_report_dir
test_report_dir=test_dir.path
)

# verify results
Expand All @@ -246,30 +228,26 @@ def test_found_dir_found_no_attributes(self, aggregate_xml_element_attribute_val
expected_step_result.message += "\nWARNING: could not find expected evidence" \
f" attributes ({not_found_attribs}) on xml element" \
f" ({test_report_evidence_element}) in test report" \
f" directory ({test_report_dir})."
f" directory ({test_dir.path})."
self.assertEqual(actual_step_result, expected_step_result)

def test_found_dir_found_some_attributes(self, aggregate_xml_element_attribute_values_mock):
def test_found_dir_found_no_attributes(self):
with TempDirectory() as test_dir:
# setup test
actual_step_result = StepResult(
step_name='mock-maven-test-step',
sub_step_name='mock-maven-test-sub-step',
sub_step_implementer_name='MockMavenTestReportingMixinStepImplementer'
)
test_report_dir = os.path.join(
test_dir.path,
'mock-test-results'
)

# setup mocks
os.mkdir(test_report_dir)
aggregate_xml_element_attribute_values_mock.return_value = {}
test_result = b'<testsuite />'
test_dir.write('test_result.xml', test_result)

# run test
MavenTestReportingMixin._gather_evidence_from_test_report_directory_testsuite_elements(
step_result=actual_step_result,
test_report_dir=test_report_dir
test_report_dir=test_dir.path
)

# verify results
Expand All @@ -283,10 +261,10 @@ def test_found_dir_found_some_attributes(self, aggregate_xml_element_attribute_v
expected_step_result.message += "\nWARNING: could not find expected evidence" \
f" attributes ({not_found_attribs}) on xml element" \
f" ({test_report_evidence_element}) in test report" \
f" directory ({test_report_dir})."
f" directory ({test_dir.path})."
self.assertEqual(actual_step_result, expected_step_result)

def test_test_report_dir_does_not_exist(self, aggregate_xml_element_attribute_values_mock):
def test_test_report_dir_does_not_exist(self):
with TempDirectory() as test_dir:
# setup test
actual_step_result = StepResult(
Expand Down
4 changes: 2 additions & 2 deletions tests/utils/files/multiple_xml/sample2.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuite xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report.xsd" name="org.acme.rest.json.RunCucumberTest" time="4.485" tests="3" errors="0" skipped="0" failures="0" quality="medium-rare">
<testsuite xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report.xsd" name="org.acme.rest.json.RunCucumberTest" time="4" tests="3" errors="0" skipped="0" failures="0" quality="medium-rare">
<properties>
<property name="java.runtime.name" value="OpenJDK Runtime Environment"/>
<property name="sun.boot.library.path" value="/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el8_4.x86_64/jre/lib/amd64"/>
Expand Down Expand Up @@ -65,4 +65,4 @@
<testcase name="Get fruits" classname="Able to interact with fruits" time="1.954"/>
<testcase name="Create fruit" classname="Able to interact with fruits" time="0.666"/>
<testcase name="Get legumes" classname="Able to interact with legumes" time="1.095"/>
</testsuite>
</testsuite>
Loading

0 comments on commit 7b453db

Please sign in to comment.