FB_xUnitXmlPublisher  Function Block

Implements I_TestResultLogger

Publishes test results into an xUnit compatible Xml file


Variables

Name Type Default Description
TestResults I_TestResults
AccessMode SysFile.ACCESS_MODE SysFile.AM_WRITE_PLUS
File FB_FileControl
Xml FB_XMLControl
BufferInitialised BOOL FALSE
Buffer ARRAY [0..(GVL_Param_TcUnit.XUnitBufferSize - 1)] OF BYTE
WritingTestSuiteResultNumber UINT(1..GVL_Param_TcUnit.MaxNumberOfTestSuites)
PublishTrigger R_TRIG

Methods

DeleteOpenWriteClose PRIVATE

Deletes the former file (if it exists). Opens the file, writes the buffer and closes it.

Implementation
DeleteOpenWriteClose := SysDir.CmpErrors.Errors.ERR_OK;
IF Initialised() THEN
    DeleteOpenWriteClose := MAX(DeleteOpenWriteClose, File.Delete(Filename := GVL_Param_TcUnit.xUnitFilePath));
    DeleteOpenWriteClose := MAX(DeleteOpenWriteClose, File.Open(Filename := GVL_Param_TcUnit.xUnitFilePath, FileAccessMode := AccessMode));
    DeleteOpenWriteClose := MAX(DeleteOpenWriteClose, File.Write(BufferPointer := ADR(Buffer), Xml.Length)); 
    DeleteOpenWriteClose := MAX(DeleteOpenWriteClose, File.Close());
ELSE
    DeleteOpenWriteClose := SysDir.CmpErrors.Errors.ERR_NOBUFFER;
END_IF;
FB_Init : BOOL

FB_Init is always available implicitly AND it is used primarily FOR initialization. The return value is not evaluated. For a specific influence, you can also declare the

Parameters

Name Type Description
bInitRetains BOOL TRUE: the retain variables are initialized (reset warm / reset cold)
bInCopyCode BOOL TRUE: the instance will be copied to the copy code afterward (online change)
iTestResults I_TestResults Interface dependency injection
Implementation
// Set buffer and flag
Xml.SetBuffer(PointerToBuffer := ADR(Buffer), SizeOfBuffer := SIZEOF(Buffer));

// Set buffer initialised
BufferInitialised := TRUE;

// Set testresult interface
THIS^.TestResults := iTestResults;
Initialised PRIVATE
Implementation
Initialised := THIS^.BufferInitialised;
LogTestSuiteResults PUBLIC

This method is responsible for the entire generation of the output file. The output of the xml writer is NOT beautified. When new data is available, feel free to add it to the report

Implementation
UnitTestResults REF= TestResults.GetTestSuiteResults();

// Only publish once if "GVL_Param_TcUnit.xUnitEnablePublish" is enabled and all test results are stored.
PublishTrigger(CLK := (TestResults.GetAreTestResultsAvailable() AND GVL_Param_TcUnit.xUnitEnablePublish));
IF PublishTrigger.Q THEN

    // <?xml version="1.0" encoding="UTF-8"?>
    Xml.WriteDocumentHeader(Header := '<?xml version="1.0" encoding="UTF-8"?>');

    // <testsuites>
    Xml.NewTag('testsuites');
    Xml.NewParameter('disabled', '');
    Xml.NewParameter('failures', UINT_TO_STRING(UnitTestResults.NumberOfFailedTestCases));
    Xml.NewParameter('tests', UINT_TO_STRING(UnitTestResults.NumberOfSuccessfulTestCases));
    Xml.NewParameter('time', LREAL_TO_STRING(UnitTestResults.Duration));

    FOR CurrentSuiteNumber := 1 TO UnitTestResults.NumberOfTestSuites BY 1 DO
        // <testsuite>
        Xml.NewTag('testsuite');
        Xml.NewParameter('id', UINT_TO_STRING(UnitTestResults.TestSuiteResults[CurrentSuiteNumber].Identity));
        Xml.NewParameter('name', UnitTestResults.TestSuiteResults[CurrentSuiteNumber].name);
        Xml.NewParameter('tests', UINT_TO_STRING(UnitTestResults.TestSuiteResults[CurrentSuiteNumber].NumberOfTests));
        Xml.NewParameter('failures', UINT_TO_STRING(UnitTestResults.TestSuiteResults[CurrentSuiteNumber].NumberOfFailedTests));
        Xml.NewParameter('time', LREAL_TO_STRING(UnitTestResults.TestSuiteResults[CurrentSuiteNumber].Duration));

        FOR CurrentTestCount := 1 TO UnitTestResults.TestSuiteResults[CurrentSuiteNumber].NumberOfTests BY 1 DO
            // <testcase>
            Xml.NewTag('testcase');
            Xml.NewParameter('name', UnitTestResults.TestSuiteResults[CurrentSuiteNumber].TestCaseResults[CurrentTestCount].TestName);
            Xml.NewParameter('classname', UnitTestResults.TestSuiteResults[CurrentSuiteNumber].TestCaseResults[CurrentTestCount].TestClassName);
            Xml.NewParameter('time', LREAL_TO_STRING(UnitTestResults.TestSuiteResults[CurrentSuiteNumber].TestCaseResults[CurrentTestCount].Duration));

            IF UnitTestResults.TestSuiteResults[CurrentSuiteNumber].TestCaseResults[CurrentTestCount].TestIsFailed THEN
                Xml.NewParameter('status', TEST_STATUS_FAIL);
            ELSIF UnitTestResults.TestSuiteResults[CurrentSuiteNumber].TestCaseResults[CurrentTestCount].TestIsSkipped THEN
                Xml.NewParameter('status', TEST_STATUS_SKIP);
            ELSE
                Xml.NewParameter('status', TEST_STATUS_PASS);
            END_IF

            // Determine testcase fail or success
            IF UnitTestResults.TestSuiteResults[CurrentSuiteNumber].TestCaseResults[CurrentTestCount].FailureType <> E_AssertionType.Type_UNDEFINED THEN
                (* In case of fail 
                    <failure message="Values differ" type="BYTE" />
                *)
                Xml.NewTag('failure');
                Xml.NewParameter('message', UnitTestResults.TestSuiteResults[CurrentSuiteNumber].TestCaseResults[CurrentTestCount].FailureMessage);
                Xml.NewParameter('type', F_AssertionTypeToString(UnitTestResults.TestSuiteResults[CurrentSuiteNumber].TestCaseResults[CurrentTestCount].FailureType));       
                // Close failure tag
                Xml.CloseTag();
            ELSE
            // In case of success
            Xml.NewTagData('');

            END_IF
            // Close testcase tag
            Xml.CloseTag();

        END_FOR
        // Close testsuite tag
        Xml.CloseTag();

    END_FOR
    // Close testsuites
    Xml.CloseTag();

    // Delete, open, save and close the file
    DeleteOpenWriteClose();

    // Clear the internal buffer
    Xml.ClearBuffer();

    // Inform user
    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                        msgFmtStr := '%s',
                                        strArg := '| ==========TEST RESULTS EXPORTED===========');

    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                        msgFmtStr :=  '| Location: %s ',
                                        strArg := GVL_Param_TcUnit.xUnitFilePath);

    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                        MsgFmtStr := '%s',
                                        StrArg := '| ======================================');
END_IF

Used by

Declaration source
// Publishes test results into an xUnit compatible Xml file
FUNCTION_BLOCK FB_xUnitXmlPublisher IMPLEMENTS I_TestResultLogger
VAR
    // Dependency injection via FB_Init
    TestResults : I_TestResults;

    // File access mode
    AccessMode : SysFile.ACCESS_MODE := SysFile.AM_WRITE_PLUS;

    File : FB_FileControl;
    Xml : FB_XMLControl;
    BufferInitialised : BOOL := FALSE;
    Buffer : ARRAY [0..(GVL_Param_TcUnit.XUnitBufferSize - 1)] OF BYTE;
    WritingTestSuiteResultNumber : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestSuites);
    PublishTrigger : R_TRIG;
END_VAR