Skip to content

Commit

Permalink
Enhanced testcases for JUnit dialects.
Browse files Browse the repository at this point in the history
  • Loading branch information
Paebbels committed Oct 8, 2024
1 parent e029be0 commit 3bae460
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 43 deletions.
6 changes: 3 additions & 3 deletions pyEDAA/Reports/CLI/Unittesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def _open(self, task: str) -> ju_TestsuiteSummary:

if dataFormat == "junit":
if dialect == "ant":
from pyEDAA.Reports.Unittesting.JUnit.AntJUnit import Document
from pyEDAA.Reports.Unittesting.JUnit.AntJUnit4 import Document
docClass = Document
elif dialect == "any":
from pyEDAA.Reports.Unittesting.JUnit import Document
Expand Down Expand Up @@ -148,7 +148,7 @@ def _merge(self, testsuiteSummary: MergedTestsuiteSummary, task: str) -> None:
self.WriteError(f"Syntax error: '{task}'")

def _mergeAntJUnit(self, testsuiteSummary: MergedTestsuiteSummary, foundFiles: Tuple[Path, ...]) -> None:
from pyEDAA.Reports.Unittesting.JUnit.AntJUnit import Document
from pyEDAA.Reports.Unittesting.JUnit.AntJUnit4 import Document

self.WriteNormal(f"Reading {len(foundFiles)} Ant-JUnit unit test summary files ...")

Expand Down Expand Up @@ -390,7 +390,7 @@ def _output(self, testsuiteSummary: TestsuiteSummary, task: str):
self.WriteError(f"Syntax error: '{task}'")

def _outputAntJUnit(self, testsuiteSummary: TestsuiteSummary, file: Path):
from pyEDAA.Reports.Unittesting.JUnit.AntJUnit import Document, UnittestException
from pyEDAA.Reports.Unittesting.JUnit.AntJUnit4 import Document, UnittestException

self.WriteNormal(f"Writing merged unit test summaries to file ...")
self.WriteVerbose(f" Common Data Model -> OUT (JUnit): {file}")
Expand Down
50 changes: 17 additions & 33 deletions pyEDAA/Reports/Unittesting/JUnit/CTestJUnit.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from typing import Optional as Nullable, Generator, Tuple, Union, TypeVar, Type, ClassVar

from lxml.etree import ElementTree, Element, SubElement, tostring, _Element
from pyTooling.Common import firstValue
from pyTooling.Decorators import export

from pyEDAA.Reports.Unittesting import UnittestException, TestsuiteKind
Expand Down Expand Up @@ -299,8 +300,8 @@ def Convert(self) -> None:
# failures = rootElement.getAttribute("failures")
# assertions = rootElement.getAttribute("assertions")

for rootNode in rootElement.iterchildren(tag="testsuite"): # type: _Element
self._ConvertTestsuite(self, rootNode)
ts = Testsuite(self._name, startTime=self._startTime, duration=self._duration, parent=self)
self._ConvertTestsuiteChildren(rootElement, ts)

self.Aggregate()
endConversation = perf_counter_ns()
Expand Down Expand Up @@ -337,52 +338,33 @@ def Generate(self, overwrite: bool = False) -> None:
if not overwrite and self._xmlDocument is not None:
raise UnittestException(f"Internal XML document is populated with data.")

rootElement = Element("testsuites")
if self.TestsuiteCount != 1:
ex = UnittestException(f"The CTest JUnit format requires exactly one test suite.")
ex.add_note(f"Found {self.TestsuiteCount} test suites.")
raise ex

testsuite = firstValue(self._testsuites)

rootElement = Element("testsuite")
rootElement.attrib["name"] = self._name
if self._startTime is not None:
rootElement.attrib["timestamp"] = f"{self._startTime.isoformat()}"
if self._duration is not None:
rootElement.attrib["time"] = f"{self._duration.total_seconds():.6f}"
rootElement.attrib["tests"] = str(self._tests)
rootElement.attrib["failures"] = str(self._failed)
rootElement.attrib["errors"] = str(self._errored)
# rootElement.attrib["errors"] = str(self._errored)
rootElement.attrib["skipped"] = str(self._skipped)
rootElement.attrib["disabled"] = "0" # TODO: find a value
# if self._assertionCount is not None:
# rootElement.attrib["assertions"] = f"{self._assertionCount}"
rootElement.attrib["hostname"] = str(testsuite._hostname) # TODO: find a value

self._xmlDocument = ElementTree(rootElement)

for testsuite in self._testsuites.values():
self._GenerateTestsuite(testsuite, rootElement)

def _GenerateTestsuite(self, testsuite: Testsuite, parentElement: _Element) -> None:
"""
Generate the internal XML data structure for a test suite.
This method generates the XML element (``<testsuite>``) and recursively calls other generated methods.
:param testsuite: The test suite to convert to an XML data structures.
:param parentElement: The parent XML data structure element, this data structure part will be added to.
:return:
"""
testsuiteElement = SubElement(parentElement, "testsuite")
testsuiteElement.attrib["name"] = testsuite._name
if testsuite._startTime is not None:
testsuiteElement.attrib["timestamp"] = f"{testsuite._startTime.isoformat()}"
if testsuite._duration is not None:
testsuiteElement.attrib["time"] = f"{testsuite._duration.total_seconds():.6f}"
testsuiteElement.attrib["tests"] = str(testsuite._tests)
testsuiteElement.attrib["failures"] = str(testsuite._failed)
testsuiteElement.attrib["errors"] = str(testsuite._errored)
testsuiteElement.attrib["skipped"] = str(testsuite._skipped)
# if testsuite._assertionCount is not None:
# testsuiteElement.attrib["assertions"] = f"{testsuite._assertionCount}"
if testsuite._hostname is not None:
testsuiteElement.attrib["hostname"] = testsuite._hostname

for testclass in testsuite._testclasses.values():
for tc in testclass._testcases.values():
self._GenerateTestcase(tc, testsuiteElement)
self._GenerateTestcase(tc, rootElement)

def _GenerateTestcase(self, testcase: Testcase, parentElement: _Element) -> None:
"""
Expand All @@ -403,6 +385,8 @@ def _GenerateTestcase(self, testcase: Testcase, parentElement: _Element) -> None
if testcase._assertionCount is not None:
testcaseElement.attrib["assertions"] = f"{testcase._assertionCount}"

testcaseElement.attrib["status"] = "run" # TODO: find a value

if testcase._status is TestcaseStatus.Passed:
pass
elif testcase._status is TestcaseStatus.Failed:
Expand Down
14 changes: 11 additions & 3 deletions pyEDAA/Reports/Unittesting/JUnit/GoogleTestJUnit.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ def Generate(self, overwrite: bool = False) -> None:
rootElement.attrib["tests"] = str(self._tests)
rootElement.attrib["failures"] = str(self._failed)
rootElement.attrib["errors"] = str(self._errored)
rootElement.attrib["skipped"] = str(self._skipped)
# rootElement.attrib["skipped"] = str(self._skipped)
rootElement.attrib["disabled"] = "0" # TODO: find a value
# if self._assertionCount is not None:
# rootElement.attrib["assertions"] = f"{self._assertionCount}"

Expand Down Expand Up @@ -375,10 +376,11 @@ def _GenerateTestsuite(self, testsuite: Testsuite, parentElement: _Element) -> N
testsuiteElement.attrib["failures"] = str(testsuite._failed)
testsuiteElement.attrib["errors"] = str(testsuite._errored)
testsuiteElement.attrib["skipped"] = str(testsuite._skipped)
testsuiteElement.attrib["disabled"] = "0" # TODO: find a value
# if testsuite._assertionCount is not None:
# testsuiteElement.attrib["assertions"] = f"{testsuite._assertionCount}"
if testsuite._hostname is not None:
testsuiteElement.attrib["hostname"] = testsuite._hostname
# if testsuite._hostname is not None:
# testsuiteElement.attrib["hostname"] = testsuite._hostname

for testclass in testsuite._testclasses.values():
for tc in testclass._testcases.values():
Expand All @@ -403,6 +405,12 @@ def _GenerateTestcase(self, testcase: Testcase, parentElement: _Element) -> None
if testcase._assertionCount is not None:
testcaseElement.attrib["assertions"] = f"{testcase._assertionCount}"

testcaseElement.attrib["timestamp"] = f"{testcase._parent._parent._startTime.isoformat()}" # TODO: find a value
testcaseElement.attrib["file"] = "" # TODO: find a value
testcaseElement.attrib["line"] = "0" # TODO: find a value
testcaseElement.attrib["status"] = "run" # TODO: find a value
testcaseElement.attrib["result"] = "completed" # TODO: find a value

if testcase._status is TestcaseStatus.Passed:
pass
elif testcase._status is TestcaseStatus.Failed:
Expand Down
191 changes: 187 additions & 4 deletions tests/unit/Unittesting/Examples/pyEDAAReports.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
from pathlib import Path
from unittest import TestCase

from pyTooling.Common import zipdicts

# FIXME: change to generic JUnit
from pyEDAA.Reports.Unittesting.JUnit.AntJUnit import Document as JUnit4Document
from pyEDAA.Reports.Unittesting.JUnit.AntJUnit4 import Document as JUnit4Document
from pyEDAA.Reports.Unittesting.JUnit.CTestJUnit import Document as CTestDocument
from pyEDAA.Reports.Unittesting.JUnit.GoogleTestJUnit import Document as GTestDocument
from pyEDAA.Reports.Unittesting.JUnit.PyTestJUnit import Document as PyTestDocument
Expand Down Expand Up @@ -63,6 +65,50 @@ def test_gtest(self):
print(f"Statistics:")
print(f" Times: parsing by lxml: {doc.AnalysisDuration.total_seconds():.3f}s convert: {doc.ModelConversionDuration.total_seconds():.3f}s")

def test_ReadWrite(self):
print()

junitExampleFile = Path("tests/data/JUnit/pyEDAA.Reports/Cpp-GoogleTest/gtest.xml")
doc = GTestDocument(junitExampleFile, analyzeAndConvert=True)

junitOutputFile = Path("tests/output/JUnit/pyEDAA.Reports/Cpp-GoogleTest/gtest.xml")
junitOutputFile.parent.mkdir(parents=True, exist_ok=True)
doc.Write(junitOutputFile, regenerate=True, overwrite=True)

sameDoc = GTestDocument(junitOutputFile, analyzeAndConvert=True)

self.assertEqual(doc.TestsuiteCount, sameDoc.TestsuiteCount)
self.assertEqual(doc.TestcaseCount, sameDoc.TestcaseCount)
self.assertEqual(doc.Errored, sameDoc.Errored)
self.assertEqual(doc.Skipped, sameDoc.Skipped)
self.assertEqual(doc.Failed, sameDoc.Failed)
self.assertEqual(doc.Passed, sameDoc.Passed)
self.assertEqual(doc.Tests, sameDoc.Tests)

for tsName, ts, sameTS in zipdicts(doc._testsuites, sameDoc._testsuites):
self.assertEqual(ts.Name, sameTS.Name)
self.assertEqual(ts.Duration, sameTS.Duration)
self.assertEqual(ts.TestcaseCount, sameTS.TestcaseCount)
self.assertEqual(ts.AssertionCount, sameTS.AssertionCount)
self.assertEqual(ts.Errored, sameTS.Errored)
self.assertEqual(ts.Skipped, sameTS.Skipped)
self.assertEqual(ts.Failed, sameTS.Failed)
self.assertEqual(ts.Passed, sameTS.Passed)
self.assertEqual(ts.Tests, sameTS.Tests)

for tclsName, tcls, sameTCls in zipdicts(ts._testclasses, sameTS._testclasses):
self.assertEqual(tcls.Name, sameTCls.Name)
self.assertEqual(tcls.Classname, sameTCls.Classname)
self.assertEqual(tcls.TestcaseCount, sameTCls.TestcaseCount)
self.assertEqual(tcls.AssertionCount, sameTCls.AssertionCount)

for tcName, tc, sameTC in zipdicts(tcls._testcases, sameTCls._testcases):
self.assertEqual(tc.Name, sameTC.Name)
self.assertEqual(tc.Classname, sameTC.Classname)
self.assertEqual(tc.Status, sameTC.Status)
self.assertEqual(tc.Duration, sameTC.Duration)
self.assertEqual(tc.AssertionCount, sameTC.AssertionCount)


class CppGoogleTestCTest(TestCase):
def test_ctest(self):
Expand All @@ -82,6 +128,50 @@ def test_ctest(self):
print(f"Statistics:")
print(f" Times: parsing by lxml: {doc.AnalysisDuration.total_seconds():.3f}s convert: {doc.ModelConversionDuration.total_seconds():.3f}s")

def test_ReadWrite(self):
print()

junitExampleFile = Path("tests/data/JUnit/pyEDAA.Reports/Cpp-GoogleTest/ctest.xml")
doc = CTestDocument(junitExampleFile, analyzeAndConvert=True)

junitOutputFile = Path("tests/output/JUnit/pyEDAA.Reports/Cpp-GoogleTest/ctest.xml")
junitOutputFile.parent.mkdir(parents=True, exist_ok=True)
doc.Write(junitOutputFile, regenerate=True, overwrite=True)

sameDoc = CTestDocument(junitOutputFile, analyzeAndConvert=True)

self.assertEqual(doc.TestsuiteCount, sameDoc.TestsuiteCount)
self.assertEqual(doc.TestcaseCount, sameDoc.TestcaseCount)
self.assertEqual(doc.Errored, sameDoc.Errored)
self.assertEqual(doc.Skipped, sameDoc.Skipped)
self.assertEqual(doc.Failed, sameDoc.Failed)
self.assertEqual(doc.Passed, sameDoc.Passed)
self.assertEqual(doc.Tests, sameDoc.Tests)

for tsName, ts, sameTS in zipdicts(doc._testsuites, sameDoc._testsuites):
self.assertEqual(ts.Name, sameTS.Name)
self.assertEqual(ts.Duration, sameTS.Duration)
self.assertEqual(ts.TestcaseCount, sameTS.TestcaseCount)
self.assertEqual(ts.AssertionCount, sameTS.AssertionCount)
self.assertEqual(ts.Errored, sameTS.Errored)
self.assertEqual(ts.Skipped, sameTS.Skipped)
self.assertEqual(ts.Failed, sameTS.Failed)
self.assertEqual(ts.Passed, sameTS.Passed)
self.assertEqual(ts.Tests, sameTS.Tests)

for tclsName, tcls, sameTCls in zipdicts(ts._testclasses, sameTS._testclasses):
self.assertEqual(tcls.Name, sameTCls.Name)
self.assertEqual(tcls.Classname, sameTCls.Classname)
self.assertEqual(tcls.TestcaseCount, sameTCls.TestcaseCount)
self.assertEqual(tcls.AssertionCount, sameTCls.AssertionCount)

for tcName, tc, sameTC in zipdicts(tcls._testcases, sameTCls._testcases):
self.assertEqual(tc.Name, sameTC.Name)
self.assertEqual(tc.Classname, sameTC.Classname)
self.assertEqual(tc.Status, sameTC.Status)
self.assertEqual(tc.Duration, sameTC.Duration)
self.assertEqual(tc.AssertionCount, sameTC.AssertionCount)


class JavaAntJUnit4(TestCase):
def test_JUnit4(self):
Expand All @@ -90,8 +180,8 @@ def test_JUnit4(self):
junitExampleFile = Path("tests/data/JUnit/pyEDAA.Reports/Java-Ant-JUnit4/TEST-my.AllTests.xml")
doc = JUnit4Document(junitExampleFile, analyzeAndConvert=True)

# self.assertEqual(1, doc.TestsuiteCount)
# self.assertEqual(3, doc.TestcaseCount)
self.assertEqual(1, doc.TestsuiteCount)
self.assertEqual(2, doc.TestcaseCount)

print(f"JUnit file:")
print(f" Testsuites: {doc.TestsuiteCount}")
Expand All @@ -101,16 +191,65 @@ def test_JUnit4(self):
print(f"Statistics:")
print(f" Times: parsing by lxml: {doc.AnalysisDuration.total_seconds():.3f}s convert: {doc.ModelConversionDuration.total_seconds():.3f}s")

def test_ReadWrite(self):
print()

junitExampleFile = Path("tests/data/JUnit/pyEDAA.Reports/Java-Ant-JUnit4/TEST-my.AllTests.xml")
doc = JUnit4Document(junitExampleFile, analyzeAndConvert=True)

junitOutputFile = Path("tests/output/JUnit/pyEDAA.Reports/Java-Ant-JUnit4/TEST-my.AllTests.xml")
junitOutputFile.parent.mkdir(parents=True, exist_ok=True)
doc.Write(junitOutputFile, regenerate=True, overwrite=True)

sameDoc = JUnit4Document(junitOutputFile, analyzeAndConvert=True)

self.assertEqual(doc.TestsuiteCount, sameDoc.TestsuiteCount)
self.assertEqual(doc.TestcaseCount, sameDoc.TestcaseCount)
self.assertEqual(doc.Errored, sameDoc.Errored)
self.assertEqual(doc.Skipped, sameDoc.Skipped)
self.assertEqual(doc.Failed, sameDoc.Failed)
self.assertEqual(doc.Passed, sameDoc.Passed)
self.assertEqual(doc.Tests, sameDoc.Tests)

for tsName, ts, sameTS in zipdicts(doc._testsuites, sameDoc._testsuites):
self.assertEqual(ts.Name, sameTS.Name)
self.assertEqual(ts.Duration, sameTS.Duration)
self.assertEqual(ts.TestcaseCount, sameTS.TestcaseCount)
self.assertEqual(ts.AssertionCount, sameTS.AssertionCount)
self.assertEqual(ts.Errored, sameTS.Errored)
self.assertEqual(ts.Skipped, sameTS.Skipped)
self.assertEqual(ts.Failed, sameTS.Failed)
self.assertEqual(ts.Passed, sameTS.Passed)
self.assertEqual(ts.Tests, sameTS.Tests)

for tclsName, tcls, sameTCls in zipdicts(ts._testclasses, sameTS._testclasses):
self.assertEqual(tcls.Name, sameTCls.Name)
self.assertEqual(tcls.Classname, sameTCls.Classname)
self.assertEqual(tcls.TestcaseCount, sameTCls.TestcaseCount)
self.assertEqual(tcls.AssertionCount, sameTCls.AssertionCount)

for tcName, tc, sameTC in zipdicts(tcls._testcases, sameTCls._testcases):
self.assertEqual(tc.Name, sameTC.Name)
self.assertEqual(tc.Classname, sameTC.Classname)
self.assertEqual(tc.Status, sameTC.Status)
self.assertEqual(tc.Duration, sameTC.Duration)
self.assertEqual(tc.AssertionCount, sameTC.AssertionCount)


class PythonPyTest(TestCase):
def test_PyTest(self):
def test_Read(self):
print()

junitExampleFile = Path("tests/data/JUnit/pyEDAA.Reports/Python-pytest/TestReportSummary.xml")
doc = PyTestDocument(junitExampleFile, analyzeAndConvert=True)

self.assertEqual(1, doc.TestsuiteCount)
self.assertEqual(8, doc.TestcaseCount)
self.assertEqual(0, doc.Errored)
self.assertEqual(0, doc.Skipped)
self.assertEqual(0, doc.Failed)
# self.assertEqual(8, doc.Passed)
self.assertEqual(8, doc.Tests)

print(f"JUnit file:")
print(f" Testsuites: {doc.TestsuiteCount}")
Expand All @@ -119,3 +258,47 @@ def test_PyTest(self):
print()
print(f"Statistics:")
print(f" Times: parsing by lxml: {doc.AnalysisDuration.total_seconds():.3f}s convert: {doc.ModelConversionDuration.total_seconds():.3f}s")

def test_ReadWrite(self):
print()

junitExampleFile = Path("tests/data/JUnit/pyEDAA.Reports/Python-pytest/TestReportSummary.xml")
doc = PyTestDocument(junitExampleFile, analyzeAndConvert=True)

junitOutputFile = Path("tests/output/JUnit/pyEDAA.Reports/Python-pytest/TestReportSummary.xml")
junitOutputFile.parent.mkdir(parents=True, exist_ok=True)
doc.Write(junitOutputFile, regenerate=True, overwrite=True)

sameDoc = PyTestDocument(junitOutputFile, analyzeAndConvert=True)

self.assertEqual(doc.TestsuiteCount, sameDoc.TestsuiteCount)
self.assertEqual(doc.TestcaseCount, sameDoc.TestcaseCount)
self.assertEqual(doc.Errored, sameDoc.Errored)
self.assertEqual(doc.Skipped, sameDoc.Skipped)
self.assertEqual(doc.Failed, sameDoc.Failed)
self.assertEqual(doc.Passed, sameDoc.Passed)
self.assertEqual(doc.Tests, sameDoc.Tests)

for tsName, ts, sameTS in zipdicts(doc._testsuites, sameDoc._testsuites):
self.assertEqual(ts.Name, sameTS.Name)
self.assertEqual(ts.Duration, sameTS.Duration)
self.assertEqual(ts.TestcaseCount, sameTS.TestcaseCount)
self.assertEqual(ts.AssertionCount, sameTS.AssertionCount)
self.assertEqual(ts.Errored, sameTS.Errored)
self.assertEqual(ts.Skipped, sameTS.Skipped)
self.assertEqual(ts.Failed, sameTS.Failed)
self.assertEqual(ts.Passed, sameTS.Passed)
self.assertEqual(ts.Tests, sameTS.Tests)

for tclsName, tcls, sameTCls in zipdicts(ts._testclasses, sameTS._testclasses):
self.assertEqual(tcls.Name, sameTCls.Name)
self.assertEqual(tcls.Classname, sameTCls.Classname)
self.assertEqual(tcls.TestcaseCount, sameTCls.TestcaseCount)
self.assertEqual(tcls.AssertionCount, sameTCls.AssertionCount)

for tcName, tc, sameTC in zipdicts(tcls._testcases, sameTCls._testcases):
self.assertEqual(tc.Name, sameTC.Name)
self.assertEqual(tc.Classname, sameTC.Classname)
self.assertEqual(tc.Status, sameTC.Status)
self.assertEqual(tc.Duration, sameTC.Duration)
self.assertEqual(tc.AssertionCount, sameTC.AssertionCount)

0 comments on commit 3bae460

Please sign in to comment.