FB_xUnitXmlPublisher Function Block

Implements I_TestResultLogger

Publishes test results into an xUnit compatible Xml file


Variables

NameTypeDefaultDescription
TestResultsI_TestResults
AccessModeSysFile.ACCESS_MODESysFile.AM_WRITE_PLUS
FileFB_FileControl
XmlFB_XMLControl
BufferInitialisedBOOLFALSE
BufferARRAY [0..(GVL_Param_TcUnit.XUnitBufferSize - 1)] OF BYTE
WritingTestSuiteResultNumberUINT(1..GVL_Param_TcUnit.MaxNumberOfTestSuites)
PublishTriggerR_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

NameTypeDescription
bInitRetainsBOOLTRUE: the retain variables are initialized (reset warm / reset cold)
bInCopyCodeBOOLTRUE: the instance will be copied to the copy code afterward (online change)
iTestResultsI_TestResultsInterface 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