Skip to content

Task: Upgrade the logging module to support xUnit format output #147

@Ulrond

Description

@Ulrond

Subject

Goal

  • Extend logging to support output using an xUnitGenerator
  • Have provided an example of implementation that may work just as a guide
  • The idea being that there's no change to logging and all existing tests work the same, just now there's xUnit support for both the raftUnitTest.py & testControl.py
  • This is an important module and all rack integration into Jenkins requires this logging.

Notes (Optional)

# logModule.py (modified)

from lxml import etree
import time

class XUnitGenerator:
    def __init__(self, suite_name):
        self.root = etree.Element("testsuite", name=suite_name)
        self.testsuite = None

    def start_test_suite(self, test_name):
        self.testsuite = etree.SubElement(self.root, "testsuite", name=test_name)

    def end_test_suite(self):
        self.testsuite = None

    def start_test_case(self, test_name):
        self.testcase = etree.SubElement(self.testsuite, "testcase", name=test_name)
        self.start_time = time.time()

    def end_test_case(self, test_name):
        end_time = time.time()
        elapsed_time = end_time - self.start_time
        self.testcase.set("time", str(elapsed_time))
        self.testcase = None

    def add_step(self, step_message):
        if self.testcase is not None:
            system_out = etree.SubElement(self.testcase, "system-out")
            system_out.text = step_message

    def add_failure(self, message, details=None):
        failure = etree.SubElement(self.testcase, "failure", message=message)
        if details:
            failure.text = details

    def add_error(self, message, details=None):
        error = etree.SubElement(self.testcase, "error", message=message)
        if details:
            error.text = details

    def add_skipped(self, message):
        skipped = etree.SubElement(self.testcase, "skipped", message=message)


    def to_xml_string(self):
        return etree.tostring(self.root, encoding="UTF-8", pretty_print=True).decode()

    def write_to_file(self, filename):
        with open(filename, "wb") as f:
            f.write(etree.tostring(self.root, encoding="UTF-8", pretty_print=True))


xunit_generator = XUnitGenerator("MyTestSuite")
xunit_enabled = False

# Original logging functions (replace with your actual implementations)

def stepStartMessage(*args, **kwargs):
    print(f"Original stepStartMessage: {args} {kwargs}")  # Replace with your actual logging

def testStartMessage(*args, **kwargs):
    print(f"Original testStartMessage: {args} {kwargs}") # Replace with your actual logging

def testSummaryMessage(*args, **kwargs):
    print(f"Original testSummaryMessage: {args} {kwargs}") # Replace with your actual logging

def stepResultMessage(*args, **kwargs):
    print(f"Original stepResultMessage: {args} {kwargs}") # Replace with your actual logging

def stepStart(*args, **kwargs):
    print(f"Original stepStart: {args} {kwargs}") # Replace with your actual logging

def step(*args, **kwargs):
    print(f"Original step: {args} {kwargs}") # Replace with your actual logging

def stepResult(*args, **kwargs):
    print(f"Original stepResult: {args} {kwargs}") # Replace with your actual logging



# Modified logging functions to include xUnit reporting:

def _log_and_report(log_function, *args, **kwargs):
    log_function(*args, **kwargs)  # Call the original logging function

    if xunit_enabled:  # Only if xUnit is enabled
        message = ""
        details = None
        if args:
            message = str(args[0])
            if len(args) > 1:
              details = str(args[1])

        if log_function.__name__ == "testStartMessage":
            test_name = message
            xunit_generator.start_test_case(test_name)
        elif log_function.__name__ == "testSummaryMessage":
            test_name = message
            xunit_generator.end_test_case(test_name)
        elif log_function.__name__ == "stepResultMessage":
            if "passed" in message.lower():
              xunit_generator.add_step(message)
            elif "failed" in message.lower():
              xunit_generator.add_failure(message, details)
        elif log_function.__name__ == "stepStartMessage":
            xunit_generator.add_step(message)
        elif log_function.__name__ == "stepStart":
            xunit_generator.add_step(message)
        elif log_function.__name__ == "step":
            xunit_generator.add_step(message)
        elif log_function.__name__ == "stepResult":
            if "passed" in message.lower():
              xunit_generator.add_step(message)
            elif "failed" in message.lower():
              xunit_generator.add_failure(message, details)


# Override the original functions:
stepStartMessage = lambda *args, **kwargs: _log_and_report(stepStartMessage, *args, **kwargs)
testStartMessage = lambda *args, **kwargs: _log_and_report(testStartMessage, *args, **kwargs)
testSummaryMessage = lambda *args, **kwargs: _log_and_report(testSummaryMessage, *args, **kwargs)
stepResultMessage = lambda *args, **kwargs: _log_and_report(stepResultMessage, *args, **kwargs)
stepStart = lambda *args, **kwargs: _log_and_report(stepStart, *args, **kwargs)
step = lambda *args, **kwargs: _log_and_report(step, *args, **kwargs)
stepResult = lambda *args, **kwargs: _log_and_report(stepResult, *args, **kwargs)


def enable_xunit_reporting():
    global xunit_enabled
    xunit_enabled = True

def disable_xunit_reporting():
    global xunit_enabled
    xunit_enabled = False

def generate_xunit_report(filename="test_report.xml"):
    xunit_generator.write_to_file(filename)

# Example Usage (in your tests):
# import logModule

# logModule.enable_xunit_reporting()

# logModule.testStartMessage("My Test")
# logModule.stepStartMessage("Step 1")
# logModule.stepResultMessage("Step 1 passed")
# logModule.testSummaryMessage("My Test")

# logModule.generate_xunit_report()
# logModule.disable_xunit_reporting()

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions