Skip to content

TcUnit 1.3.2

TwinCAT unit testing framework. Documentation and examples are available at www.tcunit.org

Interfaces

Function Blocks

User Datatypes

Global Variablelists

Dependencies

  • SysDir
    * – System
  • SysFile
    * – System
  • Tc2_Standard
    * – Beckhoff Automation GmbH
  • Tc2_System
    * – Beckhoff Automation GmbH
  • Tc2_Utilities
    * – Beckhoff Automation GmbH

I_AssertMessageFormatter

Methods

LogAssertFailure

Inputs

Source Code
METHOD LogAssertFailure
VAR_INPUT
    Expected : T_MaxString;
    Actual : T_MaxString;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR

Expected T_MaxString

Actual T_MaxString

Message T_MaxString

TestInstancePath T_MaxString

I_TestResultLogger

Methods

LogTestSuiteResults PUBLIC

Source Code
METHOD PUBLIC LogTestSuiteResults

I_TestResults

Methods

GetAreTestResultsAvailable BOOL

Source Code
METHOD GetAreTestResultsAvailable : BOOL

GetTestSuiteResults REFERENCE TO ST_TestSuiteResults

Source Code
METHOD GetTestSuiteResults : REFERENCE TO ST_TestSuiteResults;

FB_AdsAssertMessageFormatter Implements: I_AssertMessageFormatter

This

function block is responsible for printing the results of the assertions using the built-in ADSLOGSTR functionality provided by the Tc2_System library. This sends the result using ADS, which is consumed by the error list of Visual Studio.

Methods

Source Code
(*
    This function block is responsible for printing the results of the assertions using the built-in
    ADSLOGSTR functionality provided by the Tc2_System library. This sends the result using ADS, which
    is consumed by the error list of Visual Studio.
*)
FUNCTION_BLOCK FB_AdsAssertMessageFormatter IMPLEMENTS I_AssertMessageFormatter

LogAssertFailure PUBLIC

Inputs

Source Code
METHOD PUBLIC LogAssertFailure
VAR_INPUT
    Expected : T_MaxString;
    Actual : T_MaxString;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR
    AdjustAssertFailureMessageToMax253CharLength : FB_AdjustAssertFailureMessageToMax253CharLength;
    TestInstancePathCleaned : T_MaxString;
    TestInstancePathFinal : T_MaxString;
    ReturnValue : DINT;
    TestInstancePathProcessed : T_MaxString;
    MessageProcessed : T_MaxString;
END_VAR
TestInstancePathCleaned := F_RemoveInstancePathAndProjectNameFromTestInstancePath(TestInstancePath);
TestInstancePathFinal := CONCAT(STR1 := 'FAILED TEST $'',
                                STR2 := TestInstancePathCleaned);
TestInstancePathFinal := CONCAT(STR1 := TestInstancePathFinal,
                                STR2 := '$'');

TestInstancePathFinal := CONCAT(STR1 := TestInstancePathFinal,
                                STR2 := ', EXP: ');
TestInstancePathFinal := CONCAT(STR1 := TestInstancePathFinal,
                                STR2 := Expected);
TestInstancePathFinal := CONCAT(STR1 := TestInstancePathFinal,
                                STR2 := ', ACT: ');
TestInstancePathFinal := CONCAT(STR1 := TestInstancePathFinal,
                                STR2 := Actual);
IF LEN(STR := Message) > 0 THEN
    TestInstancePathFinal := CONCAT(STR1 := TestInstancePathFinal,
                                    STR2 := ', MSG: %s');
END_IF

AdjustAssertFailureMessageToMax253CharLength(TestInstancePath := TestInstancePathFinal,
                                             TestMessage := Message,
                                             TestInstancePathProcessed => TestInstancePathProcessed,
                                             TestMessageProcessed => MessageProcessed);

GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                    MsgFmtStr := TestInstancePathProcessed,
                                    StrArg := MessageProcessed);

Expected T_MaxString

Actual T_MaxString

Message T_MaxString

TestInstancePath T_MaxString

FB_AdsLogStringMessageFifoQueue

Methods

Source Code
(* This function block is responsible for making sure that the ADSLOGSTR-messages to the ADS-router are transmitted
   cyclically and not in a burst. The reason this is necessary is because that if too many messages are sent at the
   same time some get lost and are never printed to the error list output
*)
FUNCTION_BLOCK FB_AdsLogStringMessageFifoQueue
VAR
    ArrayBuffer : ARRAY[0..((GVL_Param_TcUnit.AdsLogMessageFifoRingBufferSize * (SIZEOF(ST_ADSLogStringMessage) + MEM_RING_BUFFER_INTERNAL_USE_PER_DATA_RECORD)) - 1)] OF BYTE;
    MemRingBuffer : FB_MemRingBuffer;
    TimerBetweenMessages : TON := (IN := TRUE, PT := TIME_BETWEEN_MESSAGES);
END_VAR
VAR CONSTANT
    MEM_RING_BUFFER_INTERNAL_USE_PER_DATA_RECORD : USINT := 4;
    TIME_BETWEEN_MESSAGES : TIME := T#10MS;
END_VAR
VAR_TEMP
    MessageToBeSent : ST_AdsLogStringMessage;
    ErrorGet : BOOL;
    ReturnValue : DINT;
END_VAR
TimerBetweenMessages();
IF GetLogCount() > 0 THEN
    // Timer has elapsed, print message
    IF TimerBetweenMessages.Q THEN
        GetAndRemoveLogFromQueue(AdsLogStringMessage => MessageToBeSent,
                                 Error => ErrorGet);
        IF NOT ErrorGet THEN
            ReturnValue := ADSLOGSTR(msgCtrlMask := MessageToBeSent.MsgCtrlMask,
                                     msgFmtStr := MessageToBeSent.MsgFmtStr,
                                     strArg := MessageToBeSent.StrArg);
        END_IF
        TimerBetweenMessages(IN := FALSE); // Reset timer
        TimerBetweenMessages.IN := TRUE;
    END_IF
END_IF

GetAndRemoveLogFromQueue PRIVATE

Outputs

Source Code
(* Reads and removes the oldest message *)
METHOD PRIVATE GetAndRemoveLogFromQueue
VAR_OUTPUT
    AdsLogStringMessage : ST_ADSLogStringMessage;
    Error : BOOL; // Buffer empty
END_VAR
MemRingBuffer.A_RemoveHead(pRead := ADR(AdsLogStringMessage),
                           cbRead := SIZEOF(ST_ADSLogStringMessage),
                           pBuffer := ADR(ArrayBuffer),
                           cbBuffer := SIZEOF(ArrayBuffer));
Error := NOT MemRingBuffer.bOk;

AdsLogStringMessage ST_ADSLogStringMessage

Error BOOL

Buffer empty

GetLogCount PRIVATE

Source Code
METHOD PRIVATE GetLogCount : UDINT
GetLogCount := MemRingBuffer.nCount;

WriteLog INTERNAL

Inputs

Outputs

  • Error

    Buffer overflow

Source Code
// Writes a new data set into the ring buffer
METHOD INTERNAL WriteLog
VAR_INPUT
    MsgCtrlMask : DWORD;
    MsgFmtStr : T_MaxString;
    StrArg : T_MaxString;
END_VAR
VAR_OUTPUT
    Error : BOOL; // Buffer overflow
END_VAR
VAR
    AdsLogStringMessage : ST_AdsLogStringMessage;
END_VAR
// Only log message types of ERROR if log extended results is not enabled
IF MsgCtrlMask = ADSLOG_MSGTYPE_ERROR OR GVL_Param_TcUnit.LogExtendedResults THEN
    AdsLogStringMessage.MsgCtrlMask := MsgCtrlMask;
    AdsLogStringMessage.MsgFmtStr := MsgFmtStr;
    AdsLogStringMessage.StrArg := StrArg;

    MemRingBuffer.A_AddTail(pWrite := ADR(AdsLogStringMessage),
                            cbWrite := SIZEOF(ST_AdsLogStringMessage),
                            pBuffer := ADR(ArrayBuffer),
                            cbBuffer := SIZEOF(ArrayBuffer));

    Error := NOT MemRingBuffer.bOk;
END_IF

MsgCtrlMask DWORD

MsgFmtStr T_MaxString

StrArg T_MaxString

Error BOOL

Buffer overflow

FB_AdsTestResultLogger

This

function block reports the results from the tests using the built-in ADSLOGSTR functionality provided by the Tc2_System library. This sends the result using ADS, which is consumed by the "Error List" of Visual Studio (which can print Errors, Warnings and Messages).

Methods

Source Code
(*
    This function block reports the results from the tests using the built-in ADSLOGSTR functionality
    provided by the Tc2_System library. This sends the result using ADS, which is consumed by the "Error List"
    of Visual Studio (which can print Errors, Warnings and Messages).
*)
FUNCTION_BLOCK FB_AdsTestResultLogger IMPLEMENTS I_TestResultLogger
VAR
    TestResults : I_TestResults;

    PrintingTestSuiteResultNumber : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestSuites);
    PrintingTestSuiteTrigger : R_TRIG;

    // This flag is set once the final end result has printed
    PrintedFinalTestResults : BOOL;

    // This flag is set once the test suites result have been printed
    PrintedTestSuitesResults : BOOL;
END_VAR

FB_init BOOL

Inputs

  • bInitRetains

    if TRUE, the retain variables are initialized (warm start / cold start)

  • bInCopyCode

    if TRUE, the instance afterwards gets moved into the copy code (online change)

  • iTestResults
Source Code
METHOD FB_init : BOOL
VAR_INPUT
	bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
	bInCopyCode : BOOL;  // if TRUE, the instance afterwards gets moved into the copy code (online change)
    iTestResults : I_TestResults;
END_VAR
TestResults := iTestResults;

bInitRetains BOOL

if TRUE, the retain variables are initialized (warm start / cold start)

bInCopyCode BOOL

if TRUE, the instance afterwards gets moved into the copy code (online change)

iTestResults I_TestResults

LogTestSuiteResults PUBLIC

Source Code
METHOD PUBLIC LogTestSuiteResults
VAR
    TcUnitTestResults : REFERENCE TO ST_TestSuiteResults;
    StringToPrint : T_MaxString; 
    TestsInTestSuiteCounter : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
    MaxNumberOfTestsToPrint : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
VAR CONSTANT
    TEST_STATUS_SKIP : STRING := 'SKIP';
    TEST_STATUS_PASS : STRING := 'PASS';
    TEST_STATUS_FAIL : STRING := 'FAIL';
END_VAR
TcUnitTestResults REF= TestResults.GetTestSuiteResults();

IF PrintingTestSuiteResultNumber <= GVL_TcUnit.NumberOfInitializedTestSuites AND NOT PrintedTestSuitesResults THEN
    PrintingTestSuiteTrigger(CLK := GVL_TcUnit.TestSuiteAddresses[PrintingTestSuiteResultNumber]^.AreAllTestsFinished());
    IF PrintingTestSuiteTrigger.Q THEN
        StringToPrint := CONCAT(STR1 := '| Test suite ID=',
                                STR2 := UINT_TO_STRING(TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].Identity));
        StringToPrint := CONCAT(STR1 := StringToPrint,
                                STR2 :=  ' $'%s$'');

        // Print test suite name and ID
        GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                            MsgFmtStr := StringToPrint,
                                            StrArg := TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].Name);

        // Print number of tests/number of failed tests in test suite
        StringToPrint := CONCAT(STR1 := '| ID=',
                                             STR2 := UINT_TO_STRING(TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].Identity));
        StringToPrint := CONCAT(STR1 := StringToPrint,
                                             STR2 := ' number of tests=');
        StringToPrint := CONCAT(STR1 := StringToPrint,
                                             STR2 := UINT_TO_STRING(TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].NumberOfTests));
        StringToPrint := CONCAT(STR1 := StringToPrint,
                                             STR2 := ', number of failed tests=');
        StringToPrint := CONCAT(STR1 := StringToPrint,
                                             STR2 := UINT_TO_STRING(TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].NumberOfFailedTests));
        StringToPrint := CONCAT(STR1 := StringToPrint,
                                             STR2 := ', duration=');
        StringToPrint := CONCAT(STR1 := StringToPrint,
                                             STR2 := LREAL_TO_STRING(TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].Duration));

        GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                            MsgFmtStr := StringToPrint,
                                            StrArg := '');

        // Print error if there are too many tests in the test suite
        IF (TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].NumberOfTests > GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite) THEN
            MaxNumberOfTestsToPrint := GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite;
        ELSE
            MaxNumberOfTestsToPrint := TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].NumberOfTests;
        END_IF

        // Iterate and print all tests in test suite
        FOR TestsInTestSuiteCounter := 1 TO MaxNumberOfTestsToPrint BY 1 DO
            // Print test name
            GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                                MsgFmtStr := '| Test name=%s',
                                                StrArg := TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].TestName);
            // Print test class name
            GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                                MsgFmtStr := '| Test class name=%s',
                                                StrArg := TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].TestClassName);

            // Print test result/status (SUCCESS, FAILED, SKIPPED) + number of assertions made
            IF TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].TestIsFailed THEN
                StringToPrint := CONCAT(STR1 := '| Test status=', STR2 := TEST_STATUS_FAIL);
            ELSIF TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].TestIsSkipped THEN
                StringToPrint := CONCAT(STR1 := '| Test status=', STR2 := TEST_STATUS_SKIP);
            ELSE
                StringToPrint := CONCAT(STR1 := '| Test status=', STR2 := TEST_STATUS_PASS);
            END_IF

            StringToPrint := CONCAT(STR1 := StringToPrint, STR2 := ', number of asserts=');
            StringToPrint := CONCAT(STR1 := StringToPrint, STR2 :=
                                                 UINT_TO_STRING(TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].NumberOfAsserts));
            StringToPrint := CONCAT(STR1 := StringToPrint, STR2 := ', duration=');
            StringToPrint := CONCAT(STR1 := StringToPrint, STR2 :=
                                                 LREAL_TO_STRING(TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].Duration));												 
            GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                                MsgFmtStr := StringToPrint,
                                                StrArg := '');

            // Print assertion/failure message (if existing)
            IF (LEN(STR := TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].FailureMessage) > 0) THEN
                GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                                    MsgFmtStr := '| Test assert message=%s',
                                                    StrArg := TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].FailureMessage);
            END_IF

            // Print assertion/failure type (if existing)
            IF TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].FailureType <> E_AssertionType.Type_UNDEFINED THEN
                GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_HINT,
                                                    MsgFmtStr := '| Test assert type=%s',
                                                    StrArg := F_AssertionTypeToString(AssertionType :=
                                                        TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].FailureType));
            END_IF
        END_FOR

        // Print error message if there are too many tests in the test suite
        IF (TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].NumberOfTests > GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite) THEN
            GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                                MsgFmtStr := 'Tests failed because the number of tests (%s',
                                                StrArg := CONCAT(STR1 := TO_STRING(TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].NumberOfTests),
                                                                              STR2 := CONCAT(STR1 := ') in the test suite $'',
                                                                                                          STR2 := CONCAT(STR1 := TcUnitTestResults.TestSuiteResults[PrintingTestSuiteResultNumber].Name,
                                                                                                                                      STR2 := CONCAT(STR1 := '$' are more than is defined in parameter $'GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite$' (',
                                                                                                                                                                  STR2 := CONCAT(STR1 := UINT_TO_STRING(GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite),
                                                                                                                                                                                              STR2 := '). Please increase this parameter.'))))));
        END_IF

        IF PrintingTestSuiteResultNumber = GVL_TcUnit.NumberOfInitializedTestSuites THEN
            PrintedTestSuitesResults := TRUE;
        ELSE
            PrintingTestSuiteResultNumber := PrintingTestSuiteResultNumber + 1;
        END_IF
        PrintingTestSuiteTrigger(CLK := FALSE); // Reset trigger
    END_IF
END_IF

// Log end results once all test suites have finished running
IF (GVL_TcUnit.NumberOfInitializedTestSuites = 0 OR PrintingTestSuiteResultNumber = GVL_TcUnit.NumberOfInitializedTestSuites)
    AND NOT PrintedFinalTestResults AND TestResults.GetAreTestResultsAvailable() THEN
    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                        MsgFmtStr := '%s',
                                        StrArg := '| ==========TESTS FINISHED RUNNING==========');
    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                        MsgFmtStr := '| Test suites: %s',
                                        StrArg := UINT_TO_STRING(TcUnitTestResults.NumberOfTestSuites));
    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                        MsgFmtStr := '| Tests: %s',
                                        StrArg := UINT_TO_STRING(TcUnitTestResults.NumberOfTestCases));
    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                        MsgFmtStr := '| Successful tests: %s',
                                        StrArg := UINT_TO_STRING(TcUnitTestResults.NumberOfSuccessfulTestCases));
    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                        MsgFmtStr := '| Failed tests: %s',
                                        StrArg := UINT_TO_STRING(TcUnitTestResults.NumberOfFailedTestCases));
    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                        MsgFmtStr := '| Duration: %s',
                                        StrArg := LREAL_TO_STRING(TcUnitTestResults.Duration));                                        
    GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                        MsgFmtStr := '%s',
                                        StrArg := '| ======================================');
    PrintedFinalTestResults := TRUE;
END_IF

FB_FileControl

Methods

Source Code
// This functionblock can open, close, read, write and delete files on the local filesystem
FUNCTION_BLOCK FB_FileControl
VAR
    FileAccessMode : SysFile.ACCESS_MODE := SysFile.AM_APPEND_PLUS; // Append_Plus creates the file if it doesn't exist yet. 
    FileHandle : SysFile.SysTypes.RTS_IEC_HANDLE;
END_VAR

Close PUBLIC

Source Code
// Closes the currently opened file.
METHOD PUBLIC Close : SysFile.SysTypes.RTS_IEC_RESULT;
IF FileHandle <> SysFile.SysTypes.RTS_INVALID_HANDLE THEN
    Close := SysFile.SysFileClose(hFile := FileHandle);
ELSE
    Close := SysDir.CmpErrors.Errors.ERR_INVALID_HANDLE;
END_IF

Delete PUBLIC

Inputs

Source Code
// Deletes a file specified by name, if it exists.
METHOD PUBLIC Delete : SysFile.SysTypes.RTS_IEC_RESULT;
VAR_INPUT
    // File name can contain an absolute or relative path to the file. Path entries must be separated with a forward slash (/)
    FileName : T_MaxString;
END_VAR
Delete := SysFile.SysFileDelete(szFileName := FileName);

FileName T_MaxString

Open PUBLIC

Inputs

  • FileName

    File name can contain an absolute or relative path to the file. Path entries must be separated with a Slash (/)

  • FileAccessMode
Source Code
// Opens a file
METHOD PUBLIC Open : SysFile.SysTypes.RTS_IEC_RESULT;
VAR_INPUT
    FileName : T_MaxString := 'filepath/output.xml';  // File name can contain an absolute or relative path to the file. Path entries must be separated with a Slash (/)
    FileAccessMode : SysFile.ACCESS_MODE := SysFile.ACCESS_MODE.AM_APPEND_PLUS;
END_VAR
FileHandle := SysFile.SysFileOpen(szFile := Filename,
                                  am := FileAccessMode,
                                  pResult := ADR(Open));

FileName T_MaxString

File name can contain an absolute or relative path to the file. Path entries must be separated with a Slash (/)

FileAccessMode SysFile.ACCESS_MODE

Read PUBLIC

Inputs

Outputs

Source Code
// Reads a file from disk into the buffer
METHOD PUBLIC Read : SysFile.SysTypes.RTS_IEC_RESULT;
VAR_INPUT
    BufferPointer : POINTER TO BYTE; // Call with ADR();
    Size : UDINT; // Call with SIZEOF(); 
END_VAR
VAR_OUTPUT
    FileSize : SysFile.SysTypes.RTS_IEC_SIZE;
END_VAR
IF FileHandle <> SysFile.SysTypes.RTS_INVALID_HANDLE THEN
    FileSize := SysFile.SysFileRead(hFile := FileHandle, 
                                    pbyBuffer := BufferPointer,
                                    ulSize := Size,
                                    pResult := ADR(Read));
ELSE
    Read := SysDir.CmpErrors.Errors.ERR_INVALID_HANDLE;
END_IF

BufferPointer POINTER TO BYTE

Call with ADR();

Size UDINT

Call with SIZEOF();

FileSize SysFile.SysTypes.RTS_IEC_SIZE

Write PUBLIC

Inputs

Source Code
// Writes the contents of the buffer into a file.
METHOD PUBLIC Write : SysFile.SysTypes.RTS_IEC_RESULT;
VAR_INPUT
    BufferPointer : POINTER TO BYTE; // Call with ADR();
    Size : UDINT; // Call with SIZEOF();
END_VAR
IF FileHandle <> SysFile.SysTypes.RTS_INVALID_HANDLE THEN
    SysFile.SysFileWrite(hFile := FileHandle,
                         pbyBuffer := BufferPointer,
                         ulSize := Size,
                         pResult := ADR(Write));
ELSE
    Write := SysDir.CmpErrors.Errors.ERR_INVALID_HANDLE;
END_IF

BufferPointer POINTER TO BYTE

Call with ADR();

Size UDINT

Call with SIZEOF();

FB_StreamBuffer

Methods

Properties

Source Code
// This functionblock acts as a stream buffer for use with FB_XmlControl
FUNCTION_BLOCK FB_StreamBuffer 
VAR
    _PointerToStringBuffer : POINTER TO BYTE;
    _BufferSize : UDINT;
    _Length : UDINT;
END_VAR

Clear PUBLIC

Source Code
// Clears the buffer and sets the length to 0
METHOD PUBLIC Clear
VAR
	Count : UDINT;
END_VAR
IF (_PointerToStringBuffer = 0) OR (_BufferSize = 0) THEN
    RETURN;
END_IF

FOR Count := 0 TO (_BufferSize - 1) DO
    _PointerToStringBuffer[Count] := 0;
END_FOR

_Length := 0;

Copy PUBLIC

Inputs

Outputs

Source Code
// Copies a string from the character buffer
METHOD PUBLIC Copy : T_MaxString
VAR_INPUT
    StartPos : UDINT;
    EndPos : UDINT;
END_VAR
VAR_OUTPUT
    CopyLen : UDINT;
    XmlError : E_XmlError;
END_VAR
VAR
    Loop : UDINT;
    PointerToByteToCopy : POINTER TO BYTE;
    PointerToBuffer : POINTER TO BYTE;
    CurPos : UDINT;
END_VAR
Loop := 0;
PointerToByteToCopy := ADR(Copy);
PointerToBuffer := _PointerToStringBuffer + StartPos - 1;

WHILE(Loop < SIZEOF(Copy)) AND (StartPos - 1 + Loop < _Length) AND (StartPos + Loop < EndPos) DO
    PointerToByteToCopy^ := PointerToBuffer^;
    Loop := Loop + 1;
    PointerToByteToCopy := ADR(Copy) + Loop;
    PointerToBuffer := _PointerToStringBuffer + StartPos + Loop -1;
END_WHILE;

IF Loop = SIZEOF(Copy) THEN
    XmlError := E_XmlError.ErrorStringLen;
ELSIF StartPos - 1 + Loop = _Length THEN
    XmlError := E_XmlError.ErrorMaxBufferLen;
ELSE
    XmlError := E_XmlError.OK;
END_IF;

PointerToByteToCopy^ := 0;
CopyLen :=  Loop;

StartPos UDINT

EndPos UDINT

CopyLen UDINT

XmlError E_XmlError

CutOff PUBLIC

Inputs

Outputs

Source Code
METHOD PUBLIC CutOff : T_MaxString
VAR_INPUT
    StartPos : UDINT;
END_VAR
VAR_OUTPUT
    CutLen : UDINT; 
    XmlError : E_XmlError;
END_VAR
VAR
    Loop : UDINT;
    PointerToByteToCut : POINTER TO BYTE;
    PointerToByteBuffer : POINTER TO BYTE;
END_VAR
Loop := 0;
PointerToByteToCut := ADR(CutOff);
PointerToByteBuffer := _PointerToStringBuffer + StartPos - 1;

WHILE PointerToByteBuffer^ <> 0 AND(Loop < SIZEOF(CutOff)) AND StartPos -1  + Loop < _Length DO
    PointerToByteToCut^ := PointerToByteBuffer^;
    Loop := Loop + 1;
    PointerToByteToCut := ADR(CutOff) + Loop;
    PointerToByteBuffer := _PointerToStringBuffer + StartPos - 1 + Loop;
END_WHILE;

IF PointerToByteBuffer^ = 0 THEN
    XmlError := E_XmlError.OK;
ELSIF Loop = SIZEOF(CutOff) THEN
    XmlError := E_XmlError.ErrorStringLen;
ELSIF StartPos - 1 + Loop = _Length THEN
    XmlError := E_XmlError.ErrorMaxBufferLen;
END_IF;

PointerToByteToCut^ := 0;
_Length := StartPos -1;

PointerToByteBuffer := _PointerToStringBuffer + StartPos - 1;
PointerToByteBuffer^ := 0;

CutLen := Loop;

StartPos UDINT

CutLen UDINT

XmlError E_XmlError

Find PUBLIC

Inputs

Source Code
(* 
    Find a searchstring in the buffer and returns its position.
    It's possible to add a preffered startposition within buffer
*)
METHOD PUBLIC Find : UDINT
VAR_INPUT
    SearchString : STRING;
    StartPos : UDINT;
END_VAR
VAR
    Loop : UDINT;
    Search : UDINT;
    PointerToBuffer : POINTER TO BYTE;
    PointerToSearch : POINTER TO BYTE;
END_VAR
Loop := 0;
Search := 0;

PointerToBuffer := _PointerToStringBuffer + StartPos;
PointerToSearch := ADR(SearchString);

WHILE(PointerToSearch^ <> 0 ) AND Loop + StartPos < _Length DO
    IF PointerToBuffer^ <> PointerToSearch^ THEN
        Loop := Loop + 1;
        PointerToBuffer := _PointerToStringBuffer + StartPos + Loop;
        PointerToSearch := ADR(SearchString);
        Search := 0;
    ELSE
        Search := Search + 1;
        PointerToBuffer := _PointerToStringBuffer + StartPos + Loop + Search;
        PointerToSearch := ADR(SearchString ) + Search;
    END_IF;
END_WHILE;
Find := Loop + 1 + StartPos;

SearchString STRING

StartPos UDINT

FindBack PUBLIC

Inputs

Source Code
METHOD PUBLIC FindBack : UDINT
VAR_INPUT
    SearchString : T_MaxString;
END_VAR
VAR
    Loop : UDINT;
    Search : UDINT;
    PointerToBuffer : POINTER TO BYTE;
    PointerToSearch : POINTER TO BYTE;
END_VAR
Loop := 0;
Search := 0;

PointerToBuffer := _PointerToStringBuffer + _Length;
PointerToSearch := ADR(SearchString);

WHILE(PointerToSearch^ <> 0) AND Loop < _Length DO
    IF PointerToBuffer^ <> PointerToSearch^ THEN
        Loop := Loop + 1;
        PointerToBuffer := _PointerToStringBuffer + _Length - Loop;
        PointerToSearch := ADR(SearchString);
        Search := 0;
    ELSE
        Search := Search + 1;
        PointerToBuffer := _PointerToStringBuffer + _Length - Loop + Search;
        PointerToSearch := ADR(SearchString ) + Search;
    END_IF;
END_WHILE;
FindBack := _Length - Loop + 1;

SearchString T_MaxString

SetBuffer PUBLIC

Inputs

Source Code
METHOD PUBLIC SetBuffer : BOOL;
VAR_INPUT
    PointerToBufferAddress : POINTER TO BYTE; // Set buffer address (ADR ...)
    SizeOfBuffer : UDINT; // Set buffer size (SIZEOF ...)
END_VAR
IF (PointerToBufferAddress = 0) OR (SizeOfBuffer = 0) THEN
    SetBuffer := FALSE;
    RETURN;
END_IF;

_BufferSize := SizeOfBuffer;
_PointerToStringBuffer := PointerToBufferAddress;

SetBuffer := TRUE;

PointerToBufferAddress POINTER TO BYTE

Set buffer address (ADR ...)

SizeOfBuffer UDINT

Set buffer size (SIZEOF ...)

Append T_MaxString Set

Source Code
ByteIn := ADR(Append); 
ByteBuffer := _PointerToStringBuffer + _Length; // set start address

WHILE ByteIn^ <> 0 AND (_Length < _BufferSize ) DO
    ByteBuffer^ := ByteIn^; //Copy the 1st byte
    _Length := _Length + 1; // set new buffer length
    ByteIn := ByteIn + 1; // calculate new start address 
    ByteBuffer := ByteBuffer + 1; // calculate new start address 
END_WHILE;

ByteBuffer := _PointerToStringBuffer + _Length; // String End
ByteBuffer^ := 0; // null terminated string

BufferSize UDINT Get

Source Code
BufferSize := _BufferSize;

Length UDINT Get/Set

Source Code
Length := _Length;
_Length := Length;

FB_xUnitXmlPublisher

Methods

Source Code
// 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

DeleteOpenWriteClose PRIVATE

Deletes

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

Source Code
(*
    Deletes the former file (if it exists).
    Opens the file, writes the buffer and closes it.
*)
METHOD PRIVATE DeleteOpenWriteClose : SysFile.SysTypes.RTS_IEC_RESULT;
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 methods explicitly and provide additional code there with the standard initialization code. You can evaluate the return value.

Inputs

Source Code
(*
    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
    methods explicitly and provide additional code there with the standard initialization
    code. You can evaluate the return value.
*)
METHOD FB_Init: BOOL
VAR_INPUT
    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
END_VAR
// Set buffer and flag
Xml.SetBuffer(PointerToBuffer := ADR(Buffer), SizeOfBuffer := SIZEOF(Buffer));

// Set buffer initialised
BufferInitialised := TRUE;

// Set testresult interface
THIS^.TestResults := iTestResults;

iTestResults I_TestResults

Interface dependency injection

Initialised PRIVATE

Source Code
METHOD PRIVATE Initialised : BOOL
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
Source Code
(*
    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
*)
METHOD PUBLIC LogTestSuiteResults
VAR
    UnitTestResults : REFERENCE TO ST_TestSuiteResults;
    CurrentSuiteNumber : UINT;
    CurrentTestCount : UINT;
END_VAR
VAR CONSTANT
    TEST_STATUS_SKIP : STRING := 'SKIP';
    TEST_STATUS_PASS : STRING := 'PASS';
    TEST_STATUS_FAIL : STRING := 'FAIL';
END_VAR
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

FB_TestResults

Methods

Source Code
// This function block holds results of the complete test run, i.e. results for all test suites
FUNCTION_BLOCK FB_TestResults IMPLEMENTS I_TestResults
VAR
    // Test results
    TestSuiteResults : ST_TestSuiteResults;

    // Misc variables
    StoringTestSuiteResultNumber : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestSuites);
    StoringTestSuiteTrigger : R_TRIG;
    StoredTestSuiteResults : BOOL;
    StoredGeneralTestResults : BOOL;
    NumberOfTestsToAnalyse : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
VAR_TEMP
    TestSuiteName : T_MaxString;
    TestName : T_MaxString;

    TestsInTestSuiteCounter : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);

    TestToBeStored : FB_Test;
    GeneralTestResultsTestSuitesCounter : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestSuites);
END_VAR
// The body of the function block stores the test results
IF StoringTestSuiteResultNumber <= GVL_TcUnit.NumberOfInitializedTestSuites AND NOT StoredTestSuiteResults THEN
	StoringTestSuiteTrigger(CLK := GVL_TcUnit.TestSuiteAddresses[StoringTestSuiteResultNumber]^.AreAllTestsFinished());

    IF StoringTestSuiteTrigger.Q THEN
        // Remove everything except the program name + "." + test suite name
        TestSuiteName := F_RemoveInstancePathAndProjectNameFromTestInstancePath(TestInstancePath := GVL_TcUnit.TestSuiteAddresses[StoringTestSuiteResultNumber]^.GetInstancePath());

        // Store test suite name and ID
        TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].Name := TestSuiteName;
        TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].Identity := StoringTestSuiteResultNumber - 1;

        // Store number of tests in test suite
        TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].NumberOfTests :=
            GVL_TcUnit.TestSuiteAddresses[StoringTestSuiteResultNumber]^.GetNumberOfTests();

        // Store number of failed tests in test suite
        TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].NumberOfFailedTests :=
            GVL_TcUnit.TestSuiteAddresses[StoringTestSuiteResultNumber]^.GetNumberOfFailedTests();

        // Store the duration
        TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].Duration :=
            GVL_TcUnit.TestSuiteAddresses[StoringTestSuiteResultNumber]^.GetDuration();

        // Limit the test analyse to the max array limit of 'Tests[]' in FB_TestSuite
        NumberOfTestsToAnalyse := GVL_TcUnit.TestSuiteAddresses[StoringTestSuiteResultNumber]^.GetNumberOfTestsToAnalyse();

        // Iterate and print all tests in test suite
        FOR TestsInTestSuiteCounter := 1 TO NumberOfTestsToAnalyse BY 1 DO
            TestToBeStored := GVL_TcUnit.TestSuiteAddresses[StoringTestSuiteResultNumber]^.GetTestByPosition(TestsInTestSuiteCounter);
            // Store test name
            TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].TestName := TestToBeStored.GetName();

            // Store test class name
            TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].TestClassName :=
                F_RemoveInstancePathAndProjectNameFromTestInstancePath(GVL_TcUnit.TestSuiteAddresses[StoringTestSuiteResultNumber]^.GetInstancePath());

            // Store whether the test has failed
            TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].TestIsFailed :=
                TestToBeStored.IsFailed();

            // Store whether the test was skipped
            TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].TestIsSkipped :=
                TestToBeStored.IsSkipped();

            // Store the (first failed) assertion message
            TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].FailureMessage :=
                TestToBeStored.GetAssertionMessage();

            // Store the (first failed) assertion type
            TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].FailureType :=
                TestToBeStored.GetAssertionType();

            // Store number of assertions made
            TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].NumberOfAsserts :=
                TestToBeStored.GetNumberOfAssertions();

            // Store the duration of the test
            TestSuiteResults.TestSuiteResults[StoringTestSuiteResultNumber].TestCaseResults[TestsInTestSuiteCounter].Duration :=
                TestToBeStored.GetDuration();
        END_FOR

        IF StoringTestSuiteResultNumber = GVL_TcUnit.NumberOfInitializedTestSuites THEN
            StoredTestSuiteResults := TRUE;
        ELSE
            StoringTestSuiteResultNumber := StoringTestSuiteResultNumber + 1;
        END_IF
        StoringTestSuiteTrigger(CLK := FALSE); // Reset trigger
    END_IF
END_IF

(* If all test suites have finished running, store the general test results.
   Take care OF the special CASE where no tests have NOT yet been defined *)
IF GVL_TcUnit.NumberOfInitializedTestSuites = 0 AND (NOT StoredGeneralTestResults) THEN
    StoredGeneralTestResults := TRUE;
    StoredTestSuiteResults := TRUE;
ELSIF StoringTestSuiteResultNumber = GVL_TcUnit.NumberOfInitializedTestSuites AND_THEN
    GVL_TcUnit.TestSuiteAddresses[StoringTestSuiteResultNumber]^.AreAllTestsFinished() AND StoredTestSuiteResults AND (NOT StoredGeneralTestResults) THEN
    TestSuiteResults.NumberOfTestSuites := GVL_TcUnit.NumberOfInitializedTestSuites;
	GVL_TcUnit.Duration := LWORD_TO_LREAL(F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter) - GVL_TcUnit.StartedAt) * GVL_TcUnit.HundredNanosecondToSecond; // Seconds
    TestSuiteResults.Duration := GVL_TcUnit.Duration;
    FOR GeneralTestResultsTestSuitesCounter := 1 TO GVL_TcUnit.NumberOfInitializedTestSuites BY 1 DO
        TestSuiteResults.NumberOfTestCases := TestSuiteResults.NumberOfTestCases + TestSuiteResults.TestSuiteResults[GeneralTestResultsTestSuitesCounter].NumberOfTests;
        TestSuiteResults.NumberOfSuccessfulTestCases := TestSuiteResults.NumberOfSuccessfulTestCases +
                                                        (TestSuiteResults.TestSuiteResults[GeneralTestResultsTestSuitesCounter].NumberOfTests -
                                                         TestSuiteResults.TestSuiteResults[GeneralTestResultsTestSuitesCounter].NumberOfFailedTests);
        TestSuiteResults.NumberOfFailedTestCases := TestSuiteResults.NumberOfFailedTestCases + TestSuiteResults.TestSuiteResults[GeneralTestResultsTestSuitesCounter].NumberOfFailedTests;
    END_FOR
    StoredGeneralTestResults := TRUE;
END_IF

GetAreTestResultsAvailable PUBLIC

Source Code
// Returns whether the storing of the test results is finished
METHOD PUBLIC GetAreTestResultsAvailable : BOOL
GetAreTestResultsAvailable := (StoredTestSuiteResults AND StoredGeneralTestResults);

GetTestSuiteResults PUBLIC

Source Code
METHOD PUBLIC GetTestSuiteResults : REFERENCE TO ST_TestSuiteResults;
GetTestSuiteResults REF=TestSuiteResults;

FB_AdjustAssertFailureMessageToMax253CharLength

This

function block is responsible for making sure that the asserted test instance path and test message are not loo long. The total printed message can not be more than 253 characters long.

Inputs

Outputs

Source Code
(*
    This function block is responsible for making sure that the asserted test instance path and test message are not
    loo long. The total printed message can not be more than 253 characters long.
*)
FUNCTION_BLOCK FB_AdjustAssertFailureMessageToMax253CharLength
VAR_INPUT
    TestInstancePath : T_MaxString;
    TestMessage : T_MaxString;
END_VAR
VAR_OUTPUT
    TestInstancePathProcessed : T_MaxString;
    TestMessageProcessed : T_MaxString;
END_VAR
VAR_TEMP
    TestInstancePathTemporary : T_MaxString;
END_VAR
VAR CONSTANT
    MESSAGE_FORMATTED_STRING_MAX_NUMBER_OF_CHARACTERS : INT := 253; // This is actually 254, but if StrArg-argument is used (which it is in TcUnit) it is 253.
    TEST_NAME_TOO_LONG : STRING := '...TestName too long';
    TEST_MESSAGE_TOO_LONG : STRING := '...TestMsg too long';
END_VAR
// Check if any of the two strings are too long (or the combination of them)

// All OK
IF (LEN(STR := TestInstancePath) + LEN(STR := TestMessage)) <= MESSAGE_FORMATTED_STRING_MAX_NUMBER_OF_CHARACTERS THEN
    TestInstancePathProcessed := TestInstancePath;
    TestMessageProcessed := TestMessage;
// If test instance path is longer than 253 chars, shorten it down to 253 characters and additionally with the length of the '...TestName too long'. Add the text '...TestName too long' to the test instance path. Leave no characters for the message.
ELSIF LEN(STR := TestInstancePath) > MESSAGE_FORMATTED_STRING_MAX_NUMBER_OF_CHARACTERS THEN
    TestInstancePathTemporary := LEFT(STR := TestInstancePath,
                                      SIZE := (MESSAGE_FORMATTED_STRING_MAX_NUMBER_OF_CHARACTERS - LEN(STR := TEST_NAME_TOO_LONG)));
    TestInstancePathProcessed := CONCAT(STR1 := TestInstancePathTemporary,
                                        STR2 := TEST_NAME_TOO_LONG);
    TestMessageProcessed := '';
// If test message is too long (so we cant fit the text "...TestMsg too long" to the end of it) , shorten it (so that we can fit the text)
ELSIF (MESSAGE_FORMATTED_STRING_MAX_NUMBER_OF_CHARACTERS - LEN(STR := TEST_MESSAGE_TOO_LONG)) >= LEN(STR := TestInstancePath) THEN
    TestInstancePathProcessed := TestInstancePath;
    TestMessageProcessed := LEFT(STR := TestMessage,
                                 SIZE := (MESSAGE_FORMATTED_STRING_MAX_NUMBER_OF_CHARACTERS - LEN(STR := TEST_MESSAGE_TOO_LONG) - LEN(STR := TestInstancePathProcessed)));
    TestMessageProcessed := CONCAT(STR1 := TestMessageProcessed,
                                   STR2 := TEST_MESSAGE_TOO_LONG);
// If test instance path is too long (length is between 233 and 253 characters long), shorten it and add the text '...TestName too long'. Leave no characters for the message
ELSE
    TestInstancePathTemporary := LEFT(STR := TestInstancePath,
                                      SIZE := (MESSAGE_FORMATTED_STRING_MAX_NUMBER_OF_CHARACTERS - LEN(STR := TEST_NAME_TOO_LONG)));
    TestInstancePathProcessed := CONCAT(STR1 := TestInstancePathTemporary,
                                        STR2 := TEST_NAME_TOO_LONG);
    TestMessageProcessed := '';
END_IF

TestInstancePath T_MaxString

TestMessage T_MaxString

TestInstancePathProcessed T_MaxString

TestMessageProcessed T_MaxString

FB_AssertResultStatic

This

function block is responsible for keeping track of which asserts that have been made. The reason we need to keep track of these is because if the user does the same assert twice (because of running a test suite over several PLC-cycles) we want to know it so we don't print several times (if the assert fails). An instance of an assert is keyed/identified with the following parameters as key: - Value of expected - Value of actual - Message (string) - Test instance path (string)

Methods

Source Code
(*
    This function block is responsible for keeping track of which asserts that have been made. The reason we need to
    keep track of these is because if the user does the same assert twice (because of running a test suite over several
    PLC-cycles) we want to know it so we don't print several times (if the assert fails).
    An instance of an assert is keyed/identified with the following parameters as key:
    - Value of expected
    - Value of actual
    - Message (string)
    - Test instance path (string)
*)
FUNCTION_BLOCK FB_AssertResultStatic
VAR
    // The total number of instances of each of the "AssertResults"
    AssertResults : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite] OF ST_AssertResult;

    // The total number of unique asserts
    TotalAsserts : UINT := 0;

    // Function block to get the current task cycle
    GetCurrentTaskIndex : GETCURTASKINDEX;

    // The total number of instances of each of the "AssertResults"
    AssertResultInstances : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite] OF ST_AssertResultInstances;

    // The last PLC cycle count
    CycleCount : UDINT;

    // Only run first cycle
    FirstCycleExecuted : BOOL;
END_VAR

AddAssertResult PRIVATE

Inputs

Source Code
METHOD PRIVATE AddAssertResult
VAR_INPUT
    ExpectedSize : UDINT;
    ExpectedTypeClass : IBaseLibrary.TypeClass;
    ExpectedValue : POINTER TO BYTE;
    ActualSize : UDINT;
    ActualTypeClass : IBaseLibrary.TypeClass;
    ActualValue : POINTER TO BYTE;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR_INST
	AssertResultOverflow : BOOL := FALSE;
END_VAR
VAR
    sErrorString : T_MaxString;
END_VAR
IF TotalAsserts < GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite THEN
    TotalAsserts := TotalAsserts + 1;
    AssertResults[TotalAsserts].Expected := F_AnyToUnionValue(AnySize := ExpectedSize, AnyTypeClass := ExpectedTypeClass, AnyValue := ExpectedValue);
    AssertResults[TotalAsserts].Actual := F_AnyToUnionValue(AnySize := ActualSize, AnyTypeClass := ActualTypeClass, AnyValue := ActualValue);
    AssertResults[TotalAsserts].Message := Message;
    AssertResults[TotalAsserts].TestInstancePath := TestInstancePath;
ELSE
    IF NOT AssertResultOverflow THEN
        sErrorString := CONCAT(STR1 := F_GetTestSuiteNameFromTestInstancePath(TestInstancePath := TestInstancePath),
                               STR2 := '. Max number of assertions exceeded. Check parameter MaxNumberOfAssertsForEachTestSuite.');
		GVL_TcUnit.AdsMessageQueue.WriteLog(msgCtrlMask := ADSLOG_MSGTYPE_ERROR,
											msgFmtStr := sErrorString,
											strArg := '');
		AssertResultOverflow := TRUE;
	END_IF
END_IF

ExpectedSize UDINT

ExpectedTypeClass IBaseLibrary.TypeClass

ExpectedValue POINTER TO BYTE

ActualSize UDINT

ActualTypeClass IBaseLibrary.TypeClass

ActualValue POINTER TO BYTE

Message T_MaxString

TestInstancePath T_MaxString

CopyDetectionCountAndResetDetectionCountInThisCycle PRIVATE

Source Code
METHOD PRIVATE CopyDetectionCountAndResetDetectionCountInThisCycle
VAR
    IteratorCounter : UINT;
END_VAR
FOR IteratorCounter := 1 TO GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite BY 1 DO
    AssertResultInstances[IteratorCounter].DetectionCount := AssertResultInstances[IteratorCounter].DetectionCountThisCycle;
    AssertResultInstances[IteratorCounter].DetectionCountThisCycle := 0;
END_FOR

CreateAssertResultInstance PRIVATE

Inputs

Source Code
METHOD PRIVATE CreateAssertResultInstance
VAR_INPUT
    ExpectedSize : UDINT;
    ExpectedTypeClass : IBaseLibrary.TypeClass;
    ExpectedValue : POINTER TO BYTE;
    ActualSize : UDINT;
    ActualTypeClass : IBaseLibrary.TypeClass;
    ActualValue : POINTER TO BYTE;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR
    IteratorCounter : UINT;
END_VAR
FOR IteratorCounter := 1 TO GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite BY 1 DO
    IF AssertResultInstances[IteratorCounter].DetectionCount = 0 AND
        AssertResultInstances[IteratorCounter].DetectionCountThisCycle = 0 THEN // Find first free spot
        AssertResultInstances[IteratorCounter].AssertResult.Expected := F_AnyToUnionValue(AnySize := ExpectedSize, AnyTypeClass := ExpectedTypeClass, AnyValue := ExpectedValue);
        AssertResultInstances[IteratorCounter].AssertResult.Actual := F_AnyToUnionValue(AnySize := ActualSize, AnyTypeClass := ActualTypeClass, AnyValue := ActualValue);
        AssertResultInstances[IteratorCounter].AssertResult.Message := Message;
        AssertResultInstances[IteratorCounter].AssertResult.TestInstancePath := TestInstancePath;
        AssertResultInstances[IteratorCounter].DetectionCountThisCycle := 1;
        EXIT;
    END_IF
END_FOR

ExpectedSize UDINT

ExpectedTypeClass IBaseLibrary.TypeClass

ExpectedValue POINTER TO BYTE

ActualSize UDINT

ActualTypeClass IBaseLibrary.TypeClass

ActualValue POINTER TO BYTE

Message T_MaxString

TestInstancePath T_MaxString

GetDetectionCount PRIVATE

Inputs

Source Code
METHOD PRIVATE GetDetectionCount : UINT
VAR_INPUT
    ExpectedSize : UDINT;
    ExpectedTypeClass : IBaseLibrary.TypeClass;
    ExpectedValue : POINTER TO BYTE;
    ActualSize : UDINT;
    ActualTypeClass : IBaseLibrary.TypeClass;
    ActualValue : POINTER TO BYTE;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR
    IteratorCounter : UINT;
END_VAR
FOR IteratorCounter := 1 TO GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite BY 1 DO
    IF F_IsAnyEqualToUnionValue(ExpectedOrActual := AssertResultInstances[IteratorCounter].AssertResult.Expected,
                                ExpectedOrActualSize := ExpectedSize,
                                ExpectedOrActualTypeClass := ExpectedTypeClass,
                                ExpectedOrActualValue := ExpectedValue) AND
       F_IsAnyEqualToUnionValue(ExpectedOrActual := AssertResultInstances[IteratorCounter].AssertResult.Actual,
                                ExpectedOrActualSize := ActualSize,
                                ExpectedOrActualTypeClass := ActualTypeClass,
                                ExpectedOrActualValue := ActualValue) AND
        AssertResultInstances[IteratorCounter].AssertResult.Message = Message AND
        AssertResultInstances[IteratorCounter].AssertResult.TestInstancePath = TestInstancePath THEN
        GetDetectionCount := AssertResultInstances[IteratorCounter].DetectionCount;
    END_IF
END_FOR

ExpectedSize UDINT

ExpectedTypeClass IBaseLibrary.TypeClass

ExpectedValue POINTER TO BYTE

ActualSize UDINT

ActualTypeClass IBaseLibrary.TypeClass

ActualValue POINTER TO BYTE

Message T_MaxString

TestInstancePath T_MaxString

GetDetectionCountThisCycle PRIVATE

Inputs

Source Code
METHOD PRIVATE GetDetectionCountThisCycle : UINT
VAR_INPUT
    ExpectedSize : UDINT;
    ExpectedTypeClass : IBaseLibrary.TypeClass;
    ExpectedValue : POINTER TO BYTE;
    ActualSize : UDINT;
    ActualTypeClass : IBaseLibrary.TypeClass;
    ActualValue : POINTER TO BYTE;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR
    IteratorCounter : UINT;
END_VAR
FOR IteratorCounter := 1 TO GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite BY 1 DO
    IF F_IsAnyEqualToUnionValue(ExpectedOrActual := AssertResultInstances[IteratorCounter].AssertResult.Expected,
                                ExpectedOrActualSize := ExpectedSize,
                                ExpectedOrActualTypeClass := ExpectedTypeClass,
                                ExpectedOrActualValue := ExpectedValue) AND
       F_IsAnyEqualToUnionValue(ExpectedOrActual := AssertResultInstances[IteratorCounter].AssertResult.Actual,
                                ExpectedOrActualSize := ActualSize,
                                ExpectedOrActualTypeClass := ActualTypeClass,
                                ExpectedOrActualValue := ActualValue) AND
        AssertResultInstances[IteratorCounter].AssertResult.Message = Message AND
        AssertResultInstances[IteratorCounter].AssertResult.Message = TestInstancePath THEN
        GetDetectionCountThisCycle := AssertResultInstances[IteratorCounter].DetectionCountThisCycle;
    END_IF
END_FOR

ExpectedSize UDINT

ExpectedTypeClass IBaseLibrary.TypeClass

ExpectedValue POINTER TO BYTE

ActualSize UDINT

ActualTypeClass IBaseLibrary.TypeClass

ActualValue POINTER TO BYTE

Message T_MaxString

TestInstancePath T_MaxString

GetNumberOfAssertsForTest INTERNAL

Inputs

Source Code
METHOD INTERNAL GetNumberOfAssertsForTest : UINT
VAR_INPUT
    CompleteTestInstancePath : T_MaxString;
END_VAR
VAR
    Counter : UINT;
    NumberOfAsserts : UINT := 0;
END_VAR
IF TotalAsserts > 0 THEN
    FOR Counter := 1 TO TotalAsserts BY 1 DO
        IF AssertResults[Counter].TestInstancePath = CompleteTestInstancePath THEN
            NumberOfAsserts := NumberOfAsserts + 1;
        END_IF
    END_FOR
END_IF

GetNumberOfAssertsForTest := NumberOfAsserts;

CompleteTestInstancePath T_MaxString

ReportResult INTERNAL

This

method is called in every assert and returns whether this particular assert has already been called. The reason one would like to know whether this assert has already been reported or not is to not report it several times to any logging service. Because a test-suite can consist of several tests, and certain tests can require the test to run over several cycles it means that certain asserts could be called several times and thus we need to keep track of which asserts we've already reported. The user of the framework should not need to care for any of this and he/she should be able to call the asserts in any way they find suitable.

To know what assert this is we need to check for the total combination of:
- Test message
- Test instance path
- Expected value
- Actual value
Theoretically we can have a situation where a test has three different asserts, each and one with the same test
message/test instance path/actual value/expected value but called within the same or different cycles. In order for
us to handle all situations we need a simple algorithm that works according to:
- Keep track of how many instances the combination of test message/test instance path/expected value/actual value
  we have. So for example, if we have called Assert(Exp := 5, Act := 5, 'Hello there', 'PRG.InstanceTestSuite.Test')
  two times in one cycle, we have two instances of that combination. This is done according to:
- Iterate all existing reports.
  - If we have a new PLC-cycle, set the current detection-count to zero.
  - If new report does not match in any of the above fields, create it (together with current PLC-cycle).
    Also store the information that we have one instance of this combination and +1 on the detection-count.
  - If new report matches in all of the above, +1 in the detection-count. If this detection-count is larger than
    the stored detection-count for this combination, create a new report and add +1 to the storage of
    the detection-count.

Inputs

Outputs

Source Code
(*
    This method is called in every assert and returns whether this particular assert has already been called.
    The reason one would like to know whether this assert has already been reported or not is to not report it several
    times to any logging service. Because a test-suite can consist of several tests, and certain tests can require the
    test to run over several cycles it means that certain asserts could be called several times and thus we need to
    keep track of which asserts we've already reported. The user of the framework should not need to care for any of
    this and he/she should be able to call the asserts in any way they find suitable.

    To know what assert this is we need to check for the total combination of:
    - Test message
    - Test instance path
    - Expected value
    - Actual value
    Theoretically we can have a situation where a test has three different asserts, each and one with the same test
    message/test instance path/actual value/expected value but called within the same or different cycles. In order for
    us to handle all situations we need a simple algorithm that works according to:
    - Keep track of how many instances the combination of test message/test instance path/expected value/actual value
      we have. So for example, if we have called Assert(Exp := 5, Act := 5, 'Hello there', 'PRG.InstanceTestSuite.Test')
      two times in one cycle, we have two instances of that combination. This is done according to:
    - Iterate all existing reports.
      - If we have a new PLC-cycle, set the current detection-count to zero.
      - If new report does not match in any of the above fields, create it (together with current PLC-cycle).
        Also store the information that we have one instance of this combination and +1 on the detection-count.
      - If new report matches in all of the above, +1 in the detection-count. If this detection-count is larger than
        the stored detection-count for this combination, create a new report and add +1 to the storage of
        the detection-count.
*)
METHOD INTERNAL ReportResult
VAR_INPUT
    ExpectedSize : UDINT;
    ExpectedTypeClass : IBaseLibrary.TypeClass;
    ExpectedValue : POINTER TO BYTE;
    ActualSize : UDINT;
    ActualTypeClass : IBaseLibrary.TypeClass;
    ActualValue : POINTER TO BYTE;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR_OUTPUT
    AlreadyReported : BOOL := FALSE;
END_VAR
VAR
    LocationIndex : UINT;
    DataTypesNotEquals : BOOL;
    DataSizeNotEquals : BOOL;
    DataContentNotEquals : BOOL;
    CurrentCycleCount : UDINT;
    IteratorCounter : UINT;
    DetectionCountTemp : UINT := 0;
    FoundOne : BOOL;
    AdditionalIdenticalAssert : BOOL;
END_VAR
IF NOT FirstCycleExecuted THEN
    GetCurrentTaskIndex();
    FirstCycleExecuted := TRUE;
END_IF

CurrentCycleCount := TwinCAT_SystemInfoVarList._TaskInfo[GetCurrentTaskIndex.index].CycleCount;
(* Is current cycle the same as the last call to this method?
   If not, reset the detection count *)
IF CurrentCycleCount <> CycleCount THEN
    CopyDetectionCountAndResetDetectionCountInThisCycle();
END_IF

FOR IteratorCounter := 1 TO TotalAsserts BY 1 DO
      IF F_IsAnyEqualToUnionValue(ExpectedOrActual := AssertResultInstances[IteratorCounter].AssertResult.Expected,
                                  ExpectedOrActualSize := ExpectedSize,
                                  ExpectedOrActualTypeClass := ExpectedTypeClass,
                                  ExpectedOrActualValue := ExpectedValue) AND
         F_IsAnyEqualToUnionValue(ExpectedOrActual := AssertResultInstances[IteratorCounter].AssertResult.Actual,
                                  ExpectedOrActualSize := ActualSize,
                                  ExpectedOrActualTypeClass := ActualTypeClass,
                                  ExpectedOrActualValue := ActualValue) AND
        AssertResultInstances[IteratorCounter].AssertResult.Message = Message AND
        AssertResultInstances[IteratorCounter].AssertResult.TestInstancePath = TestInstancePath THEN
            AssertResultInstances[IteratorCounter].DetectionCountThisCycle :=
                AssertResultInstances[IteratorCounter].DetectionCountThisCycle + 1;
            FoundOne := TRUE;
        IF AssertResultInstances[IteratorCounter].DetectionCountThisCycle >
            AssertResultInstances[IteratorCounter].DetectionCount THEN // This assert is new
            AdditionalIdenticalAssert := TRUE;
        END_IF
        EXIT;
    END_IF
END_FOR

// If not found anything, create the first
IF NOT FoundOne THEN
    // No existing match found, create a new entry
    AddAssertResult(ExpectedSize := ExpectedSize,
                    ExpectedTypeClass := ExpectedTypeClass,
                    ExpectedValue := ExpectedValue,
                    ActualSize := ActualSize,
                    ActualTypeClass := ActualTypeClass,
                    ActualValue := ActualValue,
                    Message := Message, TestInstancePath := TestInstancePath);

    CreateAssertResultInstance(ExpectedSize := ExpectedSize,
                               ExpectedTypeClass := ExpectedTypeClass,
                               ExpectedValue := ExpectedValue,
                               ActualSize := ActualSize,
                               ActualTypeClass := ActualTypeClass,
                               ActualValue := ActualValue,
                               Message := Message, TestInstancePath := TestInstancePath);
// An additional instance of this assert needs to be created
ELSIF AdditionalIdenticalAssert THEN
    AddAssertResult(ExpectedSize := ExpectedSize,
                    ExpectedTypeClass := ExpectedTypeClass,
                    ExpectedValue := ExpectedValue,
                    ActualSize := ActualSize,
                    ActualTypeClass := ActualTypeClass,
                    ActualValue := ActualValue,
                    Message := Message, TestInstancePath := TestInstancePath);
// In all other cases, this assert has already been reported, we don't need to do anything
ELSE
    AlreadyReported := TRUE;
END_IF

// Update the cycle count
CycleCount := TwinCAT_SystemInfoVarList._TaskInfo[GetCurrentTaskIndex.index].CycleCount;

ExpectedSize UDINT

ExpectedTypeClass IBaseLibrary.TypeClass

ExpectedValue POINTER TO BYTE

ActualSize UDINT

ActualTypeClass IBaseLibrary.TypeClass

ActualValue POINTER TO BYTE

Message T_MaxString

TestInstancePath T_MaxString

AlreadyReported BOOL

FB_AssertArrayResultStatic

This

function block is responsible for keeping track of which array-asserts that have been made. The reason we need to keep track of these is because if the user does the same assert twice (because of running a test suite over several PLC-cycles) we want to know it so we don't print several times (if the assert fails). An instance of an array-assert is keyed/identified with the following parameters as key: - Array-size (in bytes) of the expecteds - Datatype of the expecteds - Array-size (in bytes) of the actuals - Datatype of the actuals - Message (string) - Test instance path (string)

Methods

Source Code
(*
    This function block is responsible for keeping track of which array-asserts that have been made.
    The reason we need to keep track of these is because if the user does the same assert twice
    (because of running a test suite over several PLC-cycles) we want to know it so we don't print several times
    (if the assert fails). An instance of an array-assert is keyed/identified with the following parameters as key:
    - Array-size (in bytes) of the expecteds
    - Datatype of the expecteds
    - Array-size (in bytes) of the actuals
    - Datatype of the actuals
    - Message (string)
    - Test instance path (string)
*)
FUNCTION_BLOCK FB_AssertArrayResultStatic
VAR
    // The total number of instances of each of the "AssertArrayResults"
    AssertArrayResults : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite] OF ST_AssertArrayResult;

    // The total number of unique asserts
    TotalArrayAsserts : UINT := 0;

    // Function block to get the current task cycle
    GetCurrentTaskIndex : GETCURTASKINDEX;

    // The total number of instances of each of the "AssertArrayResults"
    AssertArrayResultInstances : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite] OF ST_AssertArrayResultInstances;

    // The last PLC cycle count
    CycleCount : UDINT;

    // Only run first cycle
    FirstCycleExecuted : BOOL;
END_VAR

AddAssertArrayResult PRIVATE

Inputs

Source Code
METHOD PRIVATE AddAssertArrayResult
VAR_INPUT
    ExpectedsSize : UDINT;
    ExpectedsTypeClass : IBaseLibrary.TypeClass;
    ActualsSize : UDINT;
    ActualsTypeClass : IBaseLibrary.TypeClass;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR_INST
	AssertResultOverflow : BOOL := FALSE;
END_VAR
VAR
    sErrorString : T_MaxString;
END_VAR
IF TotalArrayAsserts < GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite THEN
	TotalArrayAsserts := TotalArrayAsserts + 1;
	AssertArrayResults[TotalArrayAsserts].ExpectedsSize := ExpectedsSize;
	AssertArrayResults[TotalArrayAsserts].ExpectedsTypeClass := ExpectedsTypeClass;
	AssertArrayResults[TotalArrayAsserts].ActualsSize := ActualsSize;
	AssertArrayResults[TotalArrayAsserts].ActualsTypeClass := ActualsTypeClass;
	AssertArrayResults[TotalArrayAsserts].Message := Message;
	AssertArrayResults[TotalArrayAsserts].TestInstancePath := TestInstancePath;
ELSE
	IF NOT AssertResultOverflow THEN
        sErrorString := CONCAT(STR1 := F_GetTestSuiteNameFromTestInstancePath(TestInstancePath := TestInstancePath),
                               STR2 := '. Max number of assertions exceeded. Check parameter MaxNumberOfAssertsForEachTestSuite.');
		GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
											MsgFmtStr := sErrorString,
											StrArg := '');
		AssertResultOverflow := TRUE;
	END_IF
END_IF

ExpectedsSize UDINT

ExpectedsTypeClass IBaseLibrary.TypeClass

ActualsSize UDINT

ActualsTypeClass IBaseLibrary.TypeClass

Message T_MaxString

TestInstancePath T_MaxString

CopyDetectionCountAndResetDetectionCountInThisCycle PRIVATE

Source Code
METHOD PRIVATE CopyDetectionCountAndResetDetectionCountInThisCycle
VAR
    IteratorCounter : UINT;
END_VAR
FOR IteratorCounter := 1 TO GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite BY 1 DO
    AssertArrayResultInstances[IteratorCounter].DetectionCount := AssertArrayResultInstances[IteratorCounter].DetectionCountThisCycle;
    AssertArrayResultInstances[IteratorCounter].DetectionCountThisCycle := 0;
END_FOR

CreateAssertResultInstance PRIVATE

Inputs

Source Code
METHOD PRIVATE CreateAssertResultInstance
VAR_INPUT
    ExpectedsSize : UDINT;
    ExpectedsTypeClass : IBaseLibrary.TypeClass;
    ActualsSize : UDINT;
    ActualsTypeClass : IBaseLibrary.TypeClass;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR
    IteratorCounter : UINT;
END_VAR
FOR IteratorCounter := 1 TO GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite BY 1 DO
    IF AssertArrayResultInstances[IteratorCounter].DetectionCount = 0 AND
        AssertArrayResultInstances[IteratorCounter].DetectionCountThisCycle = 0 THEN // Find first free spot
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ExpectedsSize := ExpectedsSize;
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ExpectedsTypeClass := ExpectedsTypeClass;
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ActualsSize := ActualsSize;
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ActualsTypeClass := ActualsTypeClass;
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.Message := Message;
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.TestInstancePath := TestInstancePath;
        AssertArrayResultInstances[IteratorCounter].DetectionCountThisCycle := 1;
        EXIT;
    END_IF
END_FOR

ExpectedsSize UDINT

ExpectedsTypeClass IBaseLibrary.TypeClass

ActualsSize UDINT

ActualsTypeClass IBaseLibrary.TypeClass

Message T_MaxString

TestInstancePath T_MaxString

GetDetectionCount PRIVATE

Inputs

Source Code
METHOD PRIVATE GetDetectionCount : UINT
VAR_INPUT
    ExpectedsSize : UDINT;
    ExpectedsTypeClass : IBaseLibrary.TypeClass;
    ActualsSize : UDINT;
    ActualsTypeClass : IBaseLibrary.TypeClass;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR
    IteratorCounter : UINT;
END_VAR
FOR IteratorCounter := 1 TO GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite BY 1 DO
    IF AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ExpectedsSize = ExpectedsSize AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ExpectedsTypeClass = ExpectedsTypeClass AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ActualsSize = ActualsSize AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ActualsTypeClass = ActualsTypeClass AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.Message = Message AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.TestInstancePath = TestInstancePath THEN
        GetDetectionCount := AssertArrayResultInstances[IteratorCounter].DetectionCount;
    END_IF
END_FOR

ExpectedsSize UDINT

ExpectedsTypeClass IBaseLibrary.TypeClass

ActualsSize UDINT

ActualsTypeClass IBaseLibrary.TypeClass

Message T_MaxString

TestInstancePath T_MaxString

GetDetectionCountThisCycle PRIVATE

Inputs

Source Code
METHOD PRIVATE GetDetectionCountThisCycle : UINT
VAR_INPUT
    ExpectedsSize : UDINT;
    ExpectedsTypeClass : IBaseLibrary.TypeClass;
    ActualsSize : UDINT;
    ActualsTypeClass : IBaseLibrary.TypeClass;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR
    IteratorCounter : UINT;
END_VAR
FOR IteratorCounter := 1 TO GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite BY 1 DO
    IF AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ExpectedsSize = ExpectedsSize AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ExpectedsTypeClass = ExpectedsTypeClass AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ActualsSize = ActualsSize AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ActualsTypeClass = ActualsTypeClass AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.Message = Message AND
       AssertArrayResultInstances[IteratorCounter].AssertArrayResult.TestInstancePath = TestInstancePath THEN

        GetDetectionCountThisCycle := AssertArrayResultInstances[IteratorCounter].DetectionCountThisCycle;
    END_IF
END_FOR

ExpectedsSize UDINT

ExpectedsTypeClass IBaseLibrary.TypeClass

ActualsSize UDINT

ActualsTypeClass IBaseLibrary.TypeClass

Message T_MaxString

TestInstancePath T_MaxString

GetNumberOfArrayAssertsForTest INTERNAL

Inputs

Source Code
METHOD INTERNAL GetNumberOfArrayAssertsForTest : UINT
VAR_INPUT
    CompleteTestInstancePath : T_MaxString;
END_VAR
VAR
    Counter : UINT;
    NumberOfArrayAsserts : UINT := 0;
END_VAR
IF TotalArrayAsserts > 0 THEN
    FOR Counter := 1 TO TotalArrayAsserts BY 1 DO
        IF AssertArrayResults[Counter].TestInstancePath = CompleteTestInstancePath THEN
            NumberOfArrayAsserts := NumberOfArrayAsserts + 1;
        END_IF
    END_FOR
END_IF

GetNumberOfArrayAssertsForTest := NumberOfArrayAsserts;

CompleteTestInstancePath T_MaxString

ReportResult INTERNAL

This

method is called in every assert and returns whether this particular assert has already been called. The reason one would like to know whether this assert has already been reported or not is to not report it several times to any logging service. Because a test-suite can consist of several tests, and certain tests can require the test to run over several cycles it means that certain asserts could be called several times and thus we need to keep track of which asserts we've already reported. The user of the framework should not need to care for any of this and he/she should be able to call the asserts in any way they find suitable.

To know what assert this is we need to check for the total combination of:
- Test message
- Test instance path
- Expecteds size (in bytes)
- Actuals size (in bytes)
- Expecteds datatype
- Actuals datatype
Theoretically we can have a situation where a test has three different asserts, each and one with the same test
message/test instance path/actuals size&datatype/expecteds size&datatype but called within the same or different
cycles. In order for us to handle all situations we need a simple algorithm that works according to:
- Keep track of how many instances the combination of test message/test instance path/expecteds size&datatype/
  actuals size&datatype we have. So for example, if we have called
  Assert(Exp := [5,4,3], Act := [5,4,3], 'Hello there', 'PRG.InstanceTestSuite.Test')
  two times in one cycle, we have two instances of that combination. This is done according to:
- Iterate all existing reports.
  - If we have a new PLC-cycle, set the current detection-count to zero.
  - If new report does not match in any of the above fields, create it (together with current PLC-cycle).
    Also store the information that we have one instance of this combination and +1 on the detection-count.
  - If new report matches in all of the above, +1 in the detection-count. If this detection-count is larger than
    the stored detection-count for this combination, create a new report and add +1 to the storage of
    the detection-count.

Inputs

Outputs

Source Code
(*
    This method is called in every assert and returns whether this particular assert has already been called.
    The reason one would like to know whether this assert has already been reported or not is to not report it several
    times to any logging service. Because a test-suite can consist of several tests, and certain tests can require the
    test to run over several cycles it means that certain asserts could be called several times and thus we need to
    keep track of which asserts we've already reported. The user of the framework should not need to care for any of
    this and he/she should be able to call the asserts in any way they find suitable.

    To know what assert this is we need to check for the total combination of:
    - Test message
    - Test instance path
    - Expecteds size (in bytes)
    - Actuals size (in bytes)
    - Expecteds datatype
    - Actuals datatype
    Theoretically we can have a situation where a test has three different asserts, each and one with the same test
    message/test instance path/actuals size&datatype/expecteds size&datatype but called within the same or different
    cycles. In order for us to handle all situations we need a simple algorithm that works according to:
    - Keep track of how many instances the combination of test message/test instance path/expecteds size&datatype/
      actuals size&datatype we have. So for example, if we have called
      Assert(Exp := [5,4,3], Act := [5,4,3], 'Hello there', 'PRG.InstanceTestSuite.Test')
      two times in one cycle, we have two instances of that combination. This is done according to:
    - Iterate all existing reports.
      - If we have a new PLC-cycle, set the current detection-count to zero.
      - If new report does not match in any of the above fields, create it (together with current PLC-cycle).
        Also store the information that we have one instance of this combination and +1 on the detection-count.
      - If new report matches in all of the above, +1 in the detection-count. If this detection-count is larger than
        the stored detection-count for this combination, create a new report and add +1 to the storage of
        the detection-count.
*)
METHOD INTERNAL ReportResult
VAR_INPUT
    ExpectedsSize : UDINT;
    ExpectedsTypeClass : IBaseLibrary.TypeClass;
    ActualsSize : UDINT;
    ActualsTypeClass : IBaseLibrary.TypeClass;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_VAR
VAR_OUTPUT
    AlreadyReported : BOOL := FALSE;
END_VAR
VAR
    LocationIndex : UINT;
    DataTypesNotEquals : BOOL;
    DataSizeNotEquals : BOOL;
    DataContentNotEquals : BOOL;
    CurrentCycleCount : UDINT;
    IteratorCounter : UINT;
    DetectionCountTemp : UINT := 0;
    FoundOne : BOOL;
    AdditionalIdenticalAssert : BOOL;
END_VAR
IF NOT FirstCycleExecuted THEN
    GetCurrentTaskIndex();
    FirstCycleExecuted := TRUE;
END_IF

CurrentCycleCount := TwinCAT_SystemInfoVarList._TaskInfo[GetCurrentTaskIndex.index].CycleCount;
(* Is current cycle the same as the last call to this method?
   If not, reset the detection count *)
IF CurrentCycleCount <> CycleCount THEN
    CopyDetectionCountAndResetDetectionCountInThisCycle();
END_IF

FOR IteratorCounter := 1 TO TotalArrayAsserts BY 1 DO
    IF AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ExpectedsSize = ExpectedsSize AND
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ExpectedsTypeClass = ExpectedsTypeClass AND
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ActualsSize = ActualsSize AND
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.ActualsTypeClass = ActualsTypeClass AND
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.Message = Message AND
        AssertArrayResultInstances[IteratorCounter].AssertArrayResult.TestInstancePath = TestInstancePath THEN
        AssertArrayResultInstances[IteratorCounter].DetectionCountThisCycle :=
            AssertArrayResultInstances[IteratorCounter].DetectionCountThisCycle + 1;
            FoundOne := TRUE;
        IF AssertArrayResultInstances[IteratorCounter].DetectionCountThisCycle >
                AssertArrayResultInstances[IteratorCounter].DetectionCount THEN // This assert is new
                AdditionalIdenticalAssert := TRUE;
        END_IF
        EXIT;
    END_IF
END_FOR

// If not found anything, create the first
IF NOT FoundOne THEN
    // No existing match found, create a new entry
    AddAssertArrayResult(ExpectedsSize := ExpectedsSize,
                         ExpectedsTypeClass := ExpectedsTypeClass,
                         ActualsSize := ActualsSize,
                         ActualsTypeClass := ActualsTypeClass,
                         Message := Message,
                         TestInstancePath := TestInstancePath);
    CreateAssertResultInstance(ExpectedsSize := ExpectedsSize,
                               ExpectedsTypeClass := ExpectedsTypeClass,
                               ActualsSize := ActualsSize,
                               ActualsTypeClass := ActualsTypeClass,
                               Message := Message,
                               TestInstancePath := TestInstancePath);
// An additional instance of this assert needs to be created
ELSIF AdditionalIdenticalAssert THEN
    AddAssertArrayResult(ExpectedsSize := ExpectedsSize,
                         ExpectedsTypeClass := ExpectedsTypeClass,
                         ActualsSize := ActualsSize,
                         ActualsTypeClass := ActualsTypeClass,
                         Message := Message,
                         TestInstancePath := TestInstancePath);
// In all other cases, this assert has already been reported, we don't need to do anything
ELSE
    AlreadyReported := TRUE;
END_IF

// Update the cycle count
CycleCount := TwinCAT_SystemInfoVarList._TaskInfo[GetCurrentTaskIndex.index].CycleCount;

ExpectedsSize UDINT

ExpectedsTypeClass IBaseLibrary.TypeClass

ActualsSize UDINT

ActualsTypeClass IBaseLibrary.TypeClass

Message T_MaxString

TestInstancePath T_MaxString

AlreadyReported BOOL

FB_TcUnitRunner

Methods

Source Code
// This function block is responsible for holding track of the tests and executing them.
FUNCTION_BLOCK FB_TcUnitRunner
VAR
    // Indication of whether all test suites have reported that they are finished
    AllTestSuitesFinished : BOOL := FALSE;
    AllTestSuitesFinishedTrigger : R_TRIG;

    //Test result information
    TestResults : FB_TestResults;

    (* Prints the results to ADS so that Visual Studio can display the results.
       This test result formatter can be replaced with something else than ADS *)
    AdsTestResultLogger : FB_AdsTestResultLogger(TestResults);
    TestResultLogger : I_TestResultLogger := AdsTestResultLogger;

    (* If this flag is set, it means that some external event triggered the
       request to abort running the test suites *)
    AbortRunningTestSuites : BOOL;

    // Publishes a xUnit compatible XML file
    xUnitXmlPublisher : FB_xUnitXmlPublisher(TestResults);
    XmlTestResultPublisher : I_TestResultLogger := xUnitXmlPublisher;
END_VAR

AbortRunningTestSuiteTests INTERNAL

Source Code
(* This function sets a flag which makes the runner stop running the tests
   in the test suites *)
METHOD INTERNAL AbortRunningTestSuiteTests
AbortRunningTestSuites := TRUE;

RunTestSuiteTests INTERNAL

Source Code
// This runs all the test suites in parallel
METHOD INTERNAL RunTestSuiteTests
VAR
    Counter : UINT := 0;
    BusyPrinting : BOOL;
    (* We need to hold a temporary state of the statistics 
       as we don't consider the tests to be completely finished until all test suites have executed completely.
       The reason we want to do it this way is because a test suite can run over several cycles. Only once all tests
       are finished (which might take many cycles), do we gather correct statistics *)
    NumberOfTestSuitesFinished : UINT := 0;
END_VAR
IF GVL_TcUnit.StartedAt = 0 THEN
    GVL_TcUnit.StartedAt := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter);
END_IF

// Run TcUnit test suites
IF NOT AllTestSuitesFinished THEN
    IF GVL_TcUnit.NumberOfInitializedTestSuites = 0 THEN
        AllTestSuitesFinished := TRUE;
    ELSIF GVL_TcUnit.NumberOfInitializedTestSuites > 0 THEN
        FOR Counter := 1 TO GVL_TcUnit.NumberOfInitializedTestSuites BY 1 DO
            IF GVL_TcUnit.TestSuiteAddresses[Counter]^.AreAllTestsFinished() THEN
                NumberOfTestSuitesFinished := NumberOfTestSuitesFinished + 1;
            ELSE
                GVL_TcUnit.CurrentTestSuiteBeingCalled := GVL_TcUnit.TestSuiteAddresses[Counter];
				GVL_TcUnit.CurrentTestSuiteBeingCalled^.SetStartedAtIfNotSet(Timestamp := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter));
                GVL_TcUnit.CurrentTestSuiteBeingCalled^();
				// This is not an efficient way to set the duration
				IF GVL_TcUnit.TestSuiteAddresses[Counter]^.AreAllTestsFinished() THEN
					GVL_TcUnit.CurrentTestSuiteBeingCalled^.CalculateDuration(FinishedAt := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter));
				END_IF
            END_IF
        END_FOR
        (* Check if some event triggered an abortion of running the tests, in that case abort it now.
           This can be accomplished by setting the result of the test suites run to finished. *)
        IF AbortRunningTestSuites THEN
            NumberOfTestSuitesFinished := GVL_TcUnit.NumberOfInitializedTestSuites;
        END_IF

        IF NumberOfTestSuitesFinished = GVL_TcUnit.NumberOfInitializedTestSuites THEN
            AllTestSuitesFinished := TRUE;
        END_IF
    END_IF
END_IF

// Store test suite results continuously
TestResults();

// Log test suite results continuously
TestResultLogger.LogTestSuiteResults();

// Publish the xUnit Xml file once if enabled
XmlTestResultPublisher.LogTestSuiteResults();

// Run the buffered ADS logger
GVL_TcUnit.AdsMessageQueue();

RunTestSuiteTestsInSequence INTERNAL

Inputs

Source Code
// This runs all the test suites in sequence (one after the other)
METHOD INTERNAL RunTestSuiteTestsInSequence
VAR_INPUT
    TimeBetweenTestSuitesExecution : TIME; // Time delay between a test suite is finished and the next test suite starts
END_VAR
VAR
    BusyPrinting : BOOL;
    (* We need to hold a temporary state of the statistics
       as we don't consider the tests to be completely finished until all test suites have executed completely.
       The reason we want to do it this way is because a test suite can run over several cycles. Only once all tests
       are finished (which might take many cycles), do we gather correct statistics *)
    NumberOfTestSuitesFinished : UINT := 0;
END_VAR
VAR_INST
    (* This variable holds which current test suite is being called, as we are running
       each one in a sequence (one by one) *)
     CurrentlyRunningTestSuite : UINT := 1;
     TimerBetweenExecutionOfTestSuites : TOF;
END_VAR
IF GVL_TcUnit.StartedAt = 0 THEN
    GVL_TcUnit.StartedAt := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter);
END_IF

TimerBetweenExecutionOfTestSuites(PT := TimeBetweenTestSuitesExecution);
// Run TcUnit test suites
IF NOT AllTestSuitesFinished THEN
    IF GVL_TcUnit.NumberOfInitializedTestSuites = 0 THEN
        AllTestSuitesFinished := TRUE;
    ELSIF GVL_TcUnit.NumberOfInitializedTestSuites > 0 THEN
        IF TimerBetweenExecutionOfTestSuites.Q THEN
            TimerBetweenExecutionOfTestSuites.IN := FALSE;
        END_IF
        IF GVL_TcUnit.TestSuiteAddresses[CurrentlyRunningTestSuite]^.AreAllTestsFinished() THEN
            IF CurrentlyRunningTestSuite <> GVL_TcUnit.NumberOfInitializedTestSuites THEN
                NumberOfTestSuitesFinished := NumberOfTestSuitesFinished + 1;
                CurrentlyRunningTestSuite := CurrentlyRunningTestSuite + 1;
                TimerBetweenExecutionOfTestSuites.IN := TRUE;
				GVL_TcUnit.CurrentTestSuiteBeingCalled^.CalculateDuration(FinishedAt := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter));
            END_IF
        ELSIF NOT TimerBetweenExecutionOfTestSuites.Q THEN
            GVL_TcUnit.CurrentTestSuiteBeingCalled := GVL_TcUnit.TestSuiteAddresses[CurrentlyRunningTestSuite];
			GVL_TcUnit.CurrentTestSuiteBeingCalled^.SetStartedAtIfNotSet(Timestamp := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter));
            GVL_TcUnit.CurrentTestSuiteBeingCalled^();
			// This is not an efficient way to set the duration
			IF GVL_TcUnit.TestSuiteAddresses[CurrentlyRunningTestSuite]^.AreAllTestsFinished() THEN
				GVL_TcUnit.CurrentTestSuiteBeingCalled^.CalculateDuration(FinishedAt := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter));
			END_IF
        END_IF
        (* Check if some event triggered an abortion of running the tests, in that case abort it now.
           This can be accomplished by setting the result of the test suites run to finished. *)
        IF AbortRunningTestSuites THEN
            NumberOfTestSuitesFinished := GVL_TcUnit.NumberOfInitializedTestSuites;
        END_IF
        IF NumberOfTestSuitesFinished = GVL_TcUnit.NumberOfInitializedTestSuites THEN
            AllTestSuitesFinished := TRUE;
        END_IF
    END_IF
END_IF

// Store test suite results continuously
TestResults();

// Log test suite results continuously
TestResultLogger.LogTestSuiteResults();

// Publish the xUnit Xml file once if enabled
XmlTestResultPublisher.LogTestSuiteResults();

// Run the buffered ADS logger
GVL_TcUnit.AdsMessageQueue();

TimeBetweenTestSuitesExecution TIME

Time delay between a test suite is finished and the next test suite starts

FB_Test

Methods

Source Code
// This function block holds all data that defines a test.
FUNCTION_BLOCK FB_Test
VAR
    TestName : T_MaxString;
    TestIsFinished : BOOL;
    TestIsSkipped : BOOL; // This is set to true, if test is disabled (by putting the string "disabled_" in front of the test name
    NumberOfAssertions : UINT;

    (* In which order/sequence relative to the order tests should this test be executed/evaluated.
       A value of 0 means it is not defined by TEST_ORDERED() but by un-ordered test (TEST()).
       A value <> 0 tells in which order this test will be executed/evaluated. The lower the number, the earlier it will execute. *)
    TestOrderNumber : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite); 

    // Failure parameters. If TestIsFailed is TRUE, the other parameters will hold values as well
    TestIsFailed : BOOL; // Indication of whether this test has at least one failed assert
    AssertionMessage : T_MaxString; // Assertion message for the first assertion in this test
    AssertionType : E_AssertionType; // Assertion type for the first assertion in this test

    StartedAt : LWORD; // Temporary variable to calculate the actual duration of the test, the value holds the cpu cycle counter when a test is started in 100ns precision
    Duration : LREAL; // Duration of the test in seconds
END_VAR

GetAssertionMessage INTERNAL

Source Code
METHOD INTERNAL GetAssertionMessage : T_MaxString
GetAssertionMessage := AssertionMessage;

GetAssertionType INTERNAL

Source Code
METHOD INTERNAL GetAssertionType : E_AssertionType
GetAssertionType := AssertionType;

GetDuration INTERNAL

Source Code
METHOD INTERNAL GetDuration : LREAL
GetDuration := Duration;

GetName INTERNAL

Source Code
METHOD INTERNAL GetName : T_MaxString;
GetName := TestName;

GetNumberOfAssertions INTERNAL

Source Code
METHOD INTERNAL GetNumberOfAssertions : UINT
GetNumberOfAssertions := NumberOfAssertions;

GetTestOrder INTERNAL

Source Code
// Gets in which order/sequence relative to the order tests should this test be executed/evaluated.
METHOD INTERNAL GetTestOrder : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
GetTestOrder := TestOrderNumber;

IsFailed INTERNAL

Source Code
METHOD INTERNAL IsFailed : BOOL
IsFailed := TestIsFailed;

IsFinished INTERNAL

Source Code
METHOD INTERNAL IsFinished : BOOL
IsFinished := TestIsFinished;

IsSkipped INTERNAL

Source Code
METHOD INTERNAL IsSkipped : BOOL
IsSkipped := TestIsSkipped;

SetAssertionMessage INTERNAL

Inputs

Source Code
// Sets the assertion message. If one already exists, it's not overwritten as we keep the first assertion in the test
METHOD INTERNAL SetAssertionMessage
VAR_INPUT
    AssertMessage : T_MaxString;
END_VAR
IF LEN(STR := AssertionMessage) = 0 THEN
    AssertionMessage := AssertMessage;
END_IF

AssertMessage T_MaxString

SetAssertionType INTERNAL

Inputs

Source Code
// Sets the assertion type. If one already exists, it's not overwritten as we keep the first assertion in the test
METHOD INTERNAL SetAssertionType
VAR_INPUT
    AssertType : E_AssertionType;
END_VAR
IF AssertionType = E_AssertionType.Type_UNDEFINED THEN
    AssertionType := AssertType;
END_IF

AssertType E_AssertionType

SetFailed INTERNAL

Source Code
METHOD INTERNAL SetFailed
TestIsFailed := TRUE;

SetFinishedAndDuration INTERNAL

Inputs

  • FinishedAt

    CPU cycle counter with 100ns precision

Source Code
METHOD INTERNAL SetFinishedAndDuration : BOOL
VAR_INPUT
    FinishedAt : LWORD; // CPU cycle counter with 100ns precision
END_VAR
TestIsFinished := TRUE;
Duration := LWORD_TO_LREAL(FinishedAt - StartedAt) * GVL_TcUnit.HundredNanosecondToSecond; // Seconds

FinishedAt LWORD

CPU cycle counter with 100ns precision

SetName INTERNAL

Inputs

Source Code
METHOD INTERNAL SetName
VAR_INPUT
    Name : T_MaxString;
END_VAR
TestName := Name;

Name T_MaxString

SetNumberOfAssertions INTERNAL

Inputs

Source Code
METHOD INTERNAL SetNumberOfAssertions
VAR_INPUT
    NoOfAssertions : UINT;
END_VAR
NumberOfAssertions := NoOfAssertions;

NoOfAssertions UINT

SetSkipped INTERNAL

Source Code
// Sets the test case to skipped
METHOD INTERNAL SetSkipped
TestIsSkipped := TRUE;

SetStartedAtIfNotSet INTERNAL

Inputs

  • Timestamp

    CPU cycle counter with 100ns precision

Source Code
METHOD INTERNAL SetStartedAtIfNotSet
VAR_INPUT
    Timestamp : LWORD; // CPU cycle counter with 100ns precision
END_VAR
IF StartedAt = 0 THEN
    StartedAt := Timestamp;
END_IF

Timestamp LWORD

CPU cycle counter with 100ns precision

SetTestOrder INTERNAL

Inputs

Source Code
// Sets in which order/sequence relative to the order tests should this test be executed/evaluated.
METHOD INTERNAL SetTestOrder
VAR_INPUT
    OrderNumber : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
TestOrderNumber := OrderNumber;

OrderNumber UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite)

FB_TestSuite

Methods

Source Code
(* This function block is responsible for holding the internal state of the test suite.
   Every test suite can have one or more tests, and every test can do one or more asserts.
   It's also responsible for providing all the assert-methods for asserting different data types.
   Only failed assertions are recorded.
*)
{attribute 'call_after_init'}
{attribute 'reflection'}
FUNCTION_BLOCK FB_TestSuite
VAR
    {attribute 'instance-path'}
    {attribute 'noinit'}
    InstancePath : T_MaxString;

    (* We need to have access to specific information of the current task that this test suite
       is executed in. This is for instance necessary when we need to know whether a test is
       defined already. The definition of a test that is defined already is that we call on it
       with the same name twice in the same cycle *)
    GetCurrentTaskIndex : GETCURTASKINDEX;

    NumberOfTests : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite) := 0;
    Tests : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite] OF FB_Test;
    (* Rising trigger of whether we have already notified the user of that the test name pointed to by the current
       position is a duplicate *)
    TestDuplicateNameTrigger : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite] OF R_TRIG;
    (* Last cycle count index for a specific test. Used to detect whether this test has already been defined in the
       current test suite *)
    TestCycleCountIndex : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite] OF UDINT;

    AssertResults : FB_AssertResultStatic;
    AssertArrayResults : FB_AssertArrayResultStatic;

    (* Prints the failed asserts to ADS so that Visual Studio can display the assert message.
       This assert formatter can be replaced with something else than ADS *)
    AdsAssertMessageFormatter : FB_AdsAssertMessageFormatter;
    AssertMessageFormatter : I_AssertMessageFormatter := AdsAssertMessageFormatter;

    (* Stores the CPU cycle count with 100ns precision. It also is an indication whether this 
       test suite has started running its tests (> 0 means it has started) *)
    StartedAt : LWORD;

    (* Duration it took to run all tests in this testsuite - including the overhead from TcUnit
       in seconds *)
    Duration : LREAL;

    // Number of ordered tests (created by TEST_ORDERED()) that this test suite contains
    NumberOfOrderedTests : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR

AddTest INTERNAL

Inputs

Source Code
METHOD INTERNAL AddTest : REFERENCE TO FB_Test
VAR_INPUT
    TestName : T_MaxString;
    IsTestOrdered : BOOL;
END_VAR
VAR
    IteratorCounter : UINT;
    ErrorMessage : T_MaxString;
    TestInstancePath : T_MaxString;
    FunctionCallResult : DINT;
    CycleCount : UDINT;
    TestWithThisNameAlreadyExists : BOOL := FALSE;
    LowerCasedTestName : T_MaxString;
    TrimmedTestName : T_MaxString;
    IgnoreCurrentTestCase : BOOL;
    NumberOfTestsToAnalyse : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
GVL_TcUnit.IgnoreCurrentTest := FALSE; // Reset the ignore current test flag

TrimmedTestName := F_LTrim(in := TestName);
TrimmedTestName := F_RTrim(in := TrimmedTestName);
LowerCasedTestName := F_ToLCase(in := TrimmedTestName);

// If test should be disabled, make sure to remove the "disabled_"-part of it from the test name
IF FIND(STR1 := LowerCasedTestName, STR2 := 'disabled_') = 1 THEN
    IgnoreCurrentTestCase := TRUE;
    TrimmedTestName := DELETE(STR := TrimmedTestName, LEN := LEN(STR := 'disabled_'), POS := 1);
END_IF

GetCurrentTaskIndex(); // Gets the task index of where this function block instance is being run in

(* Check if the test name already exists. Make sure there are no other tests with the same name already
   added for this test suite *)

CycleCount := TwinCAT_SystemInfoVarList._TaskInfo[GetCurrentTaskIndex.index].CycleCount;

// Limit the test analyse to the max array limit of 'Tests[]'
NumberOfTestsToAnalyse := GetNumberOfTestsToAnalyse();

// Iterate all the test names that up to this point have been added for this test suite
FOR IteratorCounter := 1 TO NumberOfTestsToAnalyse BY 1 DO
    IF Tests[IteratorCounter].GetName() = TrimmedTestName THEN
        TestWithThisNameAlreadyExists := TRUE;
        // Check if a test with this name has already been called in this PLC cycle
        IF TestCycleCountIndex[IteratorCounter] = CycleCount THEN
            GVL_TcUnit.IgnoreCurrentTest := TRUE;
            (* A test with this name already exists for this test suite and has already been called in this cycle.
               Send a message notification, but only if we have not done so already. *)
            TestDuplicateNameTrigger[IteratorCounter](CLK := TRUE);
            IF TestDuplicateNameTrigger[IteratorCounter].Q THEN // Rising edge detected. We have not reported this before
                TestInstancePath := F_RemoveInstancePathAndProjectNameFromTestInstancePath(TestInstancePath :=
                                                                                           GVL_TcUnit.CurrentTestSuiteBeingCalled^.GetInstancePath());
                ErrorMessage := 'Test with name $'%s$' already exists in test suite $'';
                ErrorMessage := CONCAT(STR1 := ErrorMessage, STR2 := TestInstancePath);
                ErrorMessage := CONCAT(STR1 := ErrorMessage, STR2 := '$'');
                GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                                    MsgFmtStr := ErrorMessage,
                                                    StrArg := TrimmedTestName);
            END_IF
        END_IF
        TestCycleCountIndex[IteratorCounter] := CycleCount;
    END_IF
END_FOR

IF NOT TestWithThisNameAlreadyExists THEN
    // Test has not been found. Add it.
    Tests[IteratorCounter].SetName(Name := TrimmedTestName);
    NumberOfTests := NumberOfTests + 1;
    TestCycleCountIndex[NumberOfTests] := CycleCount;
    IF IgnoreCurrentTestCase THEN
        Tests[IteratorCounter].SetSkipped();
    END_IF

    // Extra information to be added if test is ordered (called by TEST_ORDERED())
    IF IsTestOrdered THEN
        NumberOfOrderedTests := NumberOfOrderedTests + 1;
        Tests[IteratorCounter].SetTestOrder(OrderNumber := NumberOfOrderedTests);
    END_IF
END_IF

// Check if this test should be disabled
IF IgnoreCurrentTestCase THEN
    GVL_TcUnit.IgnoreCurrentTest := TRUE;
    RETURN;
END_IF

AddTest REF= GetTestByName(TrimmedTestName);

TestName T_MaxString

IsTestOrdered BOOL

AddTestNameToInstancePath INTERNAL

Inputs

Source Code
METHOD INTERNAL AddTestNameToInstancePath : T_MaxString
VAR_INPUT
    TestInstancePath : T_MaxString;
END_VAR
VAR
    CompleteTestInstancePath : T_MaxString;
END_VAR
CompleteTestInstancePath := CONCAT(STR1 := TestInstancePath, STR2 := '@');
AddTestNameToInstancePath := CONCAT(STR1 := CompleteTestInstancePath, STR2 := GVL_TcUnit.CurrentTestNameBeingCalled);

TestInstancePath T_MaxString

AreAllTestsFinished PUBLIC

Source Code
METHOD PUBLIC AreAllTestsFinished : BOOL
VAR
    Counter : UINT;
    GetCurTaskIndex : GETCURTASKINDEX;
    NumberOfTestsToAnalyse : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
AreAllTestsFinished := FALSE;

IF NumberOfTests > 0 THEN
    AreAllTestsFinished := TRUE;
    // Limit the test analyse to the max array limit of 'Tests[]'
    NumberOfTestsToAnalyse := GetNumberOfTestsToAnalyse();
    // A test is considered finished if it is finished running (i.e. set by TEST_FINISHED) or if it is skipped/disabled
    FOR Counter := 1 TO NumberOfTestsToAnalyse BY 1 DO
        AreAllTestsFinished := AreAllTestsFinished AND (Tests[Counter].IsFinished() OR Tests[Counter].IsSkipped());
    END_FOR
END_IF

(* If we have been running at least one cycle and no tests are registered, it means that this testsuite is empty
   and doesn't contain any test cases. In that case, ignore this testsuite. *)
GetCurTaskIndex();
IF NumberOfTests = 0 AND NOT TwinCAT_SystemInfoVarList._TaskInfo[GetCurTaskIndex.index].FirstCycle AND StartedAt > 0 THEN
	AreAllTestsFinished := TRUE;
END_IF

AssertArray2dEquals_LREAL PUBLIC

Inputs

  • Delta

    The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    LREAL 2d array with expected values

  • Actuals

    LREAL 2d array with actual values

Source Code
// Asserts that two LREAL 2D-arrays are equal to within a positive delta. If they are not, an assertion error is created.
METHOD PUBLIC AssertArray2dEquals_LREAL
VAR_IN_OUT
    Expecteds : ARRAY[*,*] OF LREAL; // LREAL 2d array with expected values
    Actuals : ARRAY[*,*] OF LREAL; // LREAL 2d array with actual values
END_VAR
VAR_INPUT
    Delta : LREAL; // The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    DimensionIndex : USINT; // Index when looping through Dimensions
    LowerBoundExpecteds : ARRAY[1..2] OF DINT; // Lower bounds of Expecteds array in each dimension
    UpperBoundExpecteds : ARRAY[1..2] OF DINT; // Upper bounds of Expecteds array in each dimension
    LowerBoundActuals : ARRAY[1..2] OF DINT; // Lower bounds of Actuals array in each dimension
    UpperBoundActuals : ARRAY[1..2] OF DINT; // Upper bounds of Actuals array in each dimension
    SizeOfExpecteds : ARRAY[1..2] OF DINT; // Size of Expecteds array in each dimension
    SizeOfActuals : ARRAY[1..2] OF DINT; // Size of Actuals array in each dimension
    Offset : ARRAY[1..2] OF DINT; // Current Array index offsets from Lower Bound in each dimension
    ExpectedArrayIndex : ARRAY[1..2] OF DINT; // Array of current Expected array indexes when looping through arrays
    ActualArrayIndex : ARRAY[1..2] OF DINT; // Array of current Actual array indexes when looping through arrays
    Expected : LREAL; // Single expected value
    Actual : LREAL; // Single actual value
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

(* UPPER_BOUND and LOWER_BOUND require literals for their second parameter,
   so they can't be called in a dimension-based loop.
   Thus we copy the array dimensions into arrays which accept variable indexes *)
LowerBoundExpecteds[1] := LOWER_BOUND(Expecteds, 1);
UpperBoundExpecteds[1] := UPPER_BOUND(Expecteds, 1);
LowerBoundExpecteds[2] := LOWER_BOUND(Expecteds, 2);
UpperBoundExpecteds[2] := UPPER_BOUND(Expecteds, 2);
LowerBoundActuals[1] := LOWER_BOUND(Actuals, 1);
UpperBoundActuals[1] := UPPER_BOUND(Actuals, 1);
LowerBoundActuals[2] := LOWER_BOUND(Actuals, 2);
UpperBoundActuals[2] := UPPER_BOUND(Actuals, 2);

FOR DimensionIndex := 1 TO 2 DO
    SizeOfExpecteds[DimensionIndex] := ABS(UpperBoundExpecteds[DimensionIndex] - LowerBoundExpecteds[DimensionIndex]) + 1;
    SizeOfActuals[DimensionIndex] := ABS(UpperBoundActuals[DimensionIndex] - LowerBoundActuals[DimensionIndex]) + 1;

    IF SizeOfExpecteds[DimensionIndex] <> SizeOfActuals[DimensionIndex] THEN
        Equals := FALSE;
        SizeEquals := FALSE;
    END_IF
END_FOR

IF SizeEquals THEN
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes in each dimension, which needs to be taken into account. *)
    FOR Offset[1] := 0 TO SizeOfExpecteds[1] - 1 DO // Iterate Dimension 1
        FOR Offset[2] := 0 TO SizeOfExpecteds[2] - 1 DO // Iterate Dimension 2
            // Update index variables
            FOR DimensionIndex := 1 TO 2 DO
                ExpectedArrayIndex[DimensionIndex] := LowerBoundExpecteds[DimensionIndex] + Offset[DimensionIndex];
                ActualArrayIndex[DimensionIndex] := LowerBoundActuals[DimensionIndex] + Offset[DimensionIndex];
            END_FOR

            // Get array element values
            Expected := Expecteds[ExpectedArrayIndex[1], ExpectedArrayIndex[2]];
            Actual := Actuals[ActualArrayIndex[1], ActualArrayIndex[2]]; 

            IF ABS(Expected - Actual) > Delta THEN
                Equals := FALSE;
                EXIT;
            END_IF
        END_FOR

        // Check for loop bailout
        IF NOT Equals THEN
            EXIT;
        END_IF
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds[1] * SizeOfExpecteds[2]),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_LREAL,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals[1] * SizeOfActuals[2]),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_LREAL,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array2D_LREAL,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = [';
        ExpectedString := CONCAT(STR1 := ExpectedString,STR2 := DINT_TO_STRING(LowerBoundExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(LowerBoundExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] (');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := 'x');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ')');

        ActualString := 'SIZE = [';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] (');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := 'x');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ')');
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := LREAL_TO_STRING(Expected));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := LREAL_TO_STRING(Actual));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Delta LREAL

The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*,*] OF LREAL

LREAL 2d array with expected values

Actuals ARRAY[*,*] OF LREAL

LREAL 2d array with actual values

AssertArray2dEquals_REAL PUBLIC

Inputs

  • Delta

    The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    REAL 2d array with expected values

  • Actuals

    REAL 2d array with actual values

Source Code
// Asserts that two REAL 2D-arrays are equal to within a positive delta. If they are not, an assertion error is created.
METHOD PUBLIC AssertArray2dEquals_REAL
VAR_IN_OUT
    Expecteds : ARRAY[*,*] OF REAL; // REAL 2d array with expected values
    Actuals : ARRAY[*,*] OF REAL; // REAL 2d array with actual values
END_VAR
VAR_INPUT
    Delta : REAL; // The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    DimensionIndex : USINT; // Index when looping through Dimensions
    LowerBoundExpecteds : ARRAY[1..2] OF DINT; // Lower bounds of Expecteds array in each dimension
    UpperBoundExpecteds : ARRAY[1..2] OF DINT; // Upper bounds of Expecteds array in each dimension
    LowerBoundActuals : ARRAY[1..2] OF DINT; // Lower bounds of Actuals array in each dimension
    UpperBoundActuals : ARRAY[1..2] OF DINT; // Upper bounds of Actuals array in each dimension
    SizeOfExpecteds : ARRAY[1..2] OF DINT; // Size of Expecteds array in each dimension
    SizeOfActuals : ARRAY[1..2] OF DINT; // Size of Actuals array in each dimension
    Offset : ARRAY[1..2] OF DINT; // Current Array index offsets from Lower Bound in each dimension
    ExpectedArrayIndex : ARRAY[1..2] OF DINT; // Array of current Expected array indexes when looping through arrays
    ActualArrayIndex : ARRAY[1..2] OF DINT; // Array of current Actual array indexes when looping through arrays
    Expected : REAL; // Single expected value
    Actual : REAL; // Single actual value
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

(* UPPER_BOUND and LOWER_BOUND require literals for their second parameter, 
   so they can't be called in a dimension-based loop.
   Thus we copy the ARRAY dimensions into arrays which accept variable indexes *)
LowerBoundExpecteds[1] := LOWER_BOUND(Expecteds, 1);
UpperBoundExpecteds[1] := UPPER_BOUND(Expecteds, 1);
LowerBoundExpecteds[2] := LOWER_BOUND(Expecteds, 2);
UpperBoundExpecteds[2] := UPPER_BOUND(Expecteds, 2);
LowerBoundActuals[1] := LOWER_BOUND(Actuals, 1);
UpperBoundActuals[1] := UPPER_BOUND(Actuals, 1);
LowerBoundActuals[2] := LOWER_BOUND(Actuals, 2);
UpperBoundActuals[2] := UPPER_BOUND(Actuals, 2);

FOR DimensionIndex := 1 TO 2 DO
    SizeOfExpecteds[DimensionIndex] := ABS(UpperBoundExpecteds[DimensionIndex] - LowerBoundExpecteds[DimensionIndex]) + 1;
    SizeOfActuals[DimensionIndex] := ABS(UpperBoundActuals[DimensionIndex] - LowerBoundActuals[DimensionIndex]) + 1;

    IF SizeOfExpecteds[DimensionIndex] <> SizeOfActuals[DimensionIndex] THEN
        Equals := FALSE;
        SizeEquals := FALSE;
    END_IF
END_FOR

IF SizeEquals THEN
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes in each dimension, which needs to be taken into account. *)
    FOR Offset[1] := 0 TO SizeOfExpecteds[1] - 1 DO // Iterate Dimension 1
        FOR Offset[2] := 0 TO SizeOfExpecteds[2] - 1 DO // Iterate Dimension 2
            // Update index variables
            FOR DimensionIndex := 1 TO 2 DO
                ExpectedArrayIndex[DimensionIndex] := LowerBoundExpecteds[DimensionIndex] + Offset[DimensionIndex];
                ActualArrayIndex[DimensionIndex] := LowerBoundActuals[DimensionIndex] + Offset[DimensionIndex];
            END_FOR

            // Get array element values
            Expected := Expecteds[ExpectedArrayIndex[1], ExpectedArrayIndex[2]];
            Actual := Actuals[ActualArrayIndex[1], ActualArrayIndex[2]]; 

            IF ABS(Expected - Actual) > Delta THEN
                Equals := FALSE;
                EXIT;
            END_IF
        END_FOR

        // Check for loop bailout
        IF NOT Equals THEN
            EXIT;
        END_IF
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds[1] * SizeOfExpecteds[2]),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_REAL,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals[1] * SizeOfActuals[2]),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_REAL,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array2D_REAL,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = [';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(LowerBoundExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(LowerBoundExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] (');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := 'x');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ')');

        ActualString := 'SIZE = [';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] (');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := 'x');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ')');
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := REAL_TO_STRING(Expected));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := REAL_TO_STRING(Actual));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Delta REAL

The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*,*] OF REAL

REAL 2d array with expected values

Actuals ARRAY[*,*] OF REAL

REAL 2d array with actual values

AssertArray3dEquals_LREAL PUBLIC

Inputs

  • Delta

    The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    LREAL 3d array with expected values

  • Actuals

    LREAL 3d array with actual values

Source Code
// Asserts that two LREAL 3D-arrays are equal to within a positive delta. If they are not, an assertion error is created.
METHOD PUBLIC AssertArray3dEquals_LREAL
VAR_IN_OUT
    Expecteds : ARRAY[*,*,*] OF LREAL; // LREAL 3d array with expected values
    Actuals : ARRAY[*,*,*] OF LREAL; // LREAL 3d array with actual values
END_VAR
VAR_INPUT
    Delta : LREAL; // The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    DimensionIndex : USINT;  // Index when looping through Dimensions
    LowerBoundExpecteds : ARRAY[1..3] OF DINT; // Lower bounds of Expecteds array in each dimension
    UpperBoundExpecteds : ARRAY[1..3] OF DINT; // Upper bounds of Expecteds array in each dimension
    LowerBoundActuals : ARRAY[1..3] OF DINT; // Lower bounds of Actuals array in each dimension
    UpperBoundActuals : ARRAY[1..3] OF DINT; // Upper bounds of Actuals array in each dimension
    SizeOfExpecteds : ARRAY[1..3] OF DINT; // Size of Expecteds array in each dimension
    SizeOfActuals : ARRAY[1..3] OF DINT; // Size of Actuals array in each dimension
    Offset : ARRAY[1..3] OF DINT; // Current Array index offsets from Lower Bound in each dimension
    ExpectedArrayIndex : ARRAY[1..3] OF DINT; // Array of current Expected array indexes when looping through arrays
    ActualArrayIndex : ARRAY[1..3] OF DINT; // Array of current Actual array indexes when looping through arrays
    Expected : LREAL; // Single expected value
    Actual : LREAL; // Single actual value
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

(* UPPER_BOUND and LOWER_BOUND require literals for their second parameter,
   so they can't be called in a dimension-based loop.
   Thus we copy the array dimensions into arrays which accept variable indexes *)
LowerBoundExpecteds[1] := LOWER_BOUND(Expecteds, 1);
UpperBoundExpecteds[1] := UPPER_BOUND(Expecteds, 1);
LowerBoundExpecteds[2] := LOWER_BOUND(Expecteds, 2);
UpperBoundExpecteds[2] := UPPER_BOUND(Expecteds, 2);
LowerBoundExpecteds[3] := LOWER_BOUND(Expecteds, 3);
UpperBoundExpecteds[3] := UPPER_BOUND(Expecteds, 3);
LowerBoundActuals[1] := LOWER_BOUND(Actuals, 1);
UpperBoundActuals[1] := UPPER_BOUND(Actuals, 1);
LowerBoundActuals[2] := LOWER_BOUND(Actuals, 2);
UpperBoundActuals[2] := UPPER_BOUND(Actuals, 2);
LowerBoundActuals[3] := LOWER_BOUND(Actuals, 3);
UpperBoundActuals[3] := UPPER_BOUND(Actuals, 3);

FOR DimensionIndex := 1 TO 3 DO
    SizeOfExpecteds[DimensionIndex] := ABS(UpperBoundExpecteds[DimensionIndex] - LowerBoundExpecteds[DimensionIndex]) + 1;
    SizeOfActuals[DimensionIndex] := ABS(UpperBoundActuals[DimensionIndex] - LowerBoundActuals[DimensionIndex]) + 1;

    IF SizeOfExpecteds[DimensionIndex] <> SizeOfActuals[DimensionIndex] THEN
        Equals := FALSE;
        SizeEquals := FALSE;
    END_IF
END_FOR

IF SizeEquals THEN
    (* Even though we know that both arrays are equal in size, the three arrays can start at three completely different
       indexes in each dimension, which needs to be taken into account. *)
    FOR Offset[1] := 0 TO SizeOfExpecteds[1] - 1 DO // Iterate Dimension 1
        FOR Offset[2] := 0 TO SizeOfExpecteds[2] - 1 DO // Iterate Dimension 2
            FOR Offset[3] := 0 TO SizeOfExpecteds[3] - 1 DO // Iterate Dimension 3
                // Update index variables
                FOR DimensionIndex := 1 TO 3 DO
                    ExpectedArrayIndex[DimensionIndex] := LowerBoundExpecteds[DimensionIndex] + Offset[DimensionIndex];
                    ActualArrayIndex[DimensionIndex] := LowerBoundActuals[DimensionIndex] + Offset[DimensionIndex];
                END_FOR

                // Get array element values
                Expected := Expecteds[ExpectedArrayIndex[1], ExpectedArrayIndex[2], ExpectedArrayIndex[3]];
                Actual := Actuals[ActualArrayIndex[1], ActualArrayIndex[2], ActualArrayIndex[3]]; 

                IF ABS(Expected - Actual) > Delta THEN
                    Equals := FALSE;
                    EXIT;
                END_IF
            END_FOR

            // Check for loop bailout
            IF NOT Equals THEN
                EXIT;
            END_IF
        END_FOR

        // Check for loop bailout
        IF NOT Equals THEN
            EXIT;
        END_IF
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds[1] * SizeOfExpecteds[2] * SizeOfExpecteds[3]),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_LREAL,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals[1] * SizeOfActuals[2] * SizeOfActuals[3]),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_LREAL,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array3D_LREAL,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = [';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(LowerBoundExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(LowerBoundExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(LowerBoundExpecteds[3]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[3]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] (');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := 'x');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := 'x');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[3]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ')');

        ActualString := 'SIZE = [';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[3]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[3]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] (');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := 'x');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := 'x');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[3]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ')');
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[3]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := LREAL_TO_STRING(Expected) );

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[3]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := LREAL_TO_STRING(Actual));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Delta LREAL

The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*,*,*] OF LREAL

LREAL 3d array with expected values

Actuals ARRAY[*,*,*] OF LREAL

LREAL 3d array with actual values

AssertArray3dEquals_REAL PUBLIC

Inputs

  • Delta

    The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    REAL 3d array with expected values

  • Actuals

    REAL 3d array with actual values

Source Code
// Asserts that two REAL 3D-arrays are equal to within a positive delta. If they are not, an assertion error is created.
METHOD PUBLIC AssertArray3dEquals_REAL
VAR_IN_OUT
    Expecteds : ARRAY[*,*,*] OF REAL; // REAL 3d array with expected values
    Actuals : ARRAY[*,*,*] OF REAL; // REAL 3d array with actual values
END_VAR
VAR_INPUT
    Delta : REAL; // The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    DimensionIndex : USINT;  // Index when looping through Dimensions
    LowerBoundExpecteds : ARRAY[1..3] OF DINT; // Lower bounds of Expecteds array in each dimension
    UpperBoundExpecteds : ARRAY[1..3] OF DINT; // Upper bounds of Expecteds array in each dimension
    LowerBoundActuals : ARRAY[1..3] OF DINT; // Lower bounds of Actuals array in each dimension
    UpperBoundActuals : ARRAY[1..3] OF DINT; // Upper bounds of Actuals array in each dimension
    SizeOfExpecteds : ARRAY[1..3] OF DINT; // Size of Expecteds array in each dimension
    SizeOfActuals : ARRAY[1..3] OF DINT; // Size of Actuals array in each dimension
    Offset : ARRAY[1..3] OF DINT; // Current Array index offsets from Lower Bound in each dimension
    ExpectedArrayIndex : ARRAY[1..3] OF DINT; // Array of current Expected array indexes when looping through arrays
    ActualArrayIndex : ARRAY[1..3] OF DINT; // Array of current Actual array indexes when looping through arrays
    Expected : REAL; // Single expected value
    Actual : REAL; // Single actual value
    ExpectedValueString : T_MaxString;
    ActualValueString : T_MaxString;
    FormatString : FB_FormatString; // String formatter for output messages
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

(* UPPER_BOUND and LOWER_BOUND require literals for their second parameter,
   so they can't be called in a dimension-based loop.
   Thus we copy the array dimensions into arrays which accept variable indexes *)
LowerBoundExpecteds[1] := LOWER_BOUND(Expecteds, 1);
UpperBoundExpecteds[1] := UPPER_BOUND(Expecteds, 1);
LowerBoundExpecteds[2] := LOWER_BOUND(Expecteds, 2);
UpperBoundExpecteds[2] := UPPER_BOUND(Expecteds, 2);
LowerBoundExpecteds[3] := LOWER_BOUND(Expecteds, 3);
UpperBoundExpecteds[3] := UPPER_BOUND(Expecteds, 3);
LowerBoundActuals[1] := LOWER_BOUND(Actuals, 1);
UpperBoundActuals[1] := UPPER_BOUND(Actuals, 1);
LowerBoundActuals[2] := LOWER_BOUND(Actuals, 2);
UpperBoundActuals[2] := UPPER_BOUND(Actuals, 2);
LowerBoundActuals[3] := LOWER_BOUND(Actuals, 3);
UpperBoundActuals[3] := UPPER_BOUND(Actuals, 3);

FOR DimensionIndex := 1 TO 3 DO
    SizeOfExpecteds[DimensionIndex] := ABS(UpperBoundExpecteds[DimensionIndex] - LowerBoundExpecteds[DimensionIndex]) + 1;
    SizeOfActuals[DimensionIndex] := ABS(UpperBoundActuals[DimensionIndex] - LowerBoundActuals[DimensionIndex]) + 1;

    IF SizeOfExpecteds[DimensionIndex] <> SizeOfActuals[DimensionIndex] THEN
        Equals := FALSE;
        SizeEquals := FALSE;
    END_IF
END_FOR

IF SizeEquals THEN
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes in each dimension, which needs to be taken into account. *)
    FOR Offset[1] := 0 TO SizeOfExpecteds[1] - 1 DO // Iterate Dimension 1
        FOR Offset[2] := 0 TO SizeOfExpecteds[2] - 1 DO // Iterate Dimension 2
            FOR Offset[3] := 0 TO SizeOfExpecteds[3] - 1 DO // Iterate Dimension 3
                // Update index variables
                FOR DimensionIndex := 1 TO 3 DO
                    ExpectedArrayIndex[DimensionIndex] := LowerBoundExpecteds[DimensionIndex] + Offset[DimensionIndex];
                    ActualArrayIndex[DimensionIndex] := LowerBoundActuals[DimensionIndex] + Offset[DimensionIndex];
                END_FOR

                // Get array element values
                Expected := Expecteds[ExpectedArrayIndex[1], ExpectedArrayIndex[2], ExpectedArrayIndex[3]];
                Actual := Actuals[ActualArrayIndex[1], ActualArrayIndex[2], ActualArrayIndex[3]]; 

                IF ABS(Expected - Actual) > Delta THEN
                    Equals := FALSE;
                    EXIT;
                END_IF
            END_FOR

            // Check for loop bailout
            IF NOT Equals THEN
                EXIT;
            END_IF
        END_FOR

        // Check for loop bailout
        IF NOT Equals THEN
            EXIT;
        END_IF
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds[1] * SizeOfExpecteds[2] * SizeOfExpecteds[3]),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_REAL,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals[1] * SizeOfActuals[2] * SizeOfActuals[3]),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_REAL,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array3D_REAL,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = [';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(LowerBoundExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(LowerBoundExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(LowerBoundExpecteds[3]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '..');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(UpperBoundExpecteds[3]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] (');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := 'x');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := 'x');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds[3]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ')');

        ActualString := 'SIZE = [';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(LowerBoundActuals[3]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '..');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(UpperBoundActuals[3]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] (');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := 'x');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := 'x');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals[3]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ')');
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[1]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[2]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ',');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedArrayIndex[3]));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := REAL_TO_STRING(Expected) );

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[1]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[2]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := ',');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualArrayIndex[3]));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := REAL_TO_STRING(Actual));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Delta REAL

The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*,*,*] OF REAL

REAL 3d array with expected values

Actuals ARRAY[*,*,*] OF REAL

REAL 3d array with actual values

AssertArrayEquals_BOOL PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    BOOL array with expected values

  • Actuals

    BOOL array with actual values

Source Code
// Asserts that two BOOL arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_BOOL
VAR_IN_OUT
    Expecteds : ARRAY[*] OF BOOL; // BOOL array with expected values
    Actuals : ARRAY[*] OF BOOL; // BOOL array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_BOOL,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_BOOL,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_BOOL,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := BOOL_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := BOOL_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF BOOL

BOOL array with expected values

Actuals ARRAY[*] OF BOOL

BOOL array with actual values

AssertArrayEquals_BYTE PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    BYTE array with expected values

  • Actuals

    BYTE array with actual values

Source Code
// Asserts that two BYTE arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_BYTE
VAR_IN_OUT
    Expecteds : ARRAY[*] OF BYTE; // BYTE array with expected values
    Actuals : ARRAY[*] OF BYTE; // BYTE array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedByteString : STRING;
    ActualByteString : STRING;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF


AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_BYTE,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_BYTE,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_BYTE,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedByteString := CONCAT(STR1 := '0x',
                                                  STR2 := BYTE_TO_HEXSTR(in := Expecteds[ExpectedsIndex],
                                                                                       iPrecision := 2,
                                                                                       bLoCase := FALSE));
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ExpectedByteString);

        ActualByteString := CONCAT(STR1 := '0x',
                                                STR2 := BYTE_TO_HEXSTR(in := Actuals[ActualsIndex],
                                                                                     iPrecision := 2,
                                                                                     bLoCase := FALSE));
        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := ActualByteString);
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF BYTE

BYTE array with expected values

Actuals ARRAY[*] OF BYTE

BYTE array with actual values

AssertArrayEquals_DINT PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    DINT array with expected values

  • Actuals

    DINT array with actual values

Source Code
// Asserts that two DINT arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_DINT
VAR_IN_OUT
    Expecteds : ARRAY[*] OF DINT; // DINT array with expected values
    Actuals : ARRAY[*] OF DINT; // DINT array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_DINT,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_DINT,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_DINT,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF DINT

DINT array with expected values

Actuals ARRAY[*] OF DINT

DINT array with actual values

AssertArrayEquals_DWORD PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    DWORD array with expected values

  • Actuals

    DWORD array with actual values

Source Code
// Asserts that two DWORD arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_DWORD
VAR_IN_OUT
    Expecteds : ARRAY[*] OF DWORD; // DWORD array with expected values
    Actuals : ARRAY[*] OF DWORD; // DWORD array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedDWordString : STRING;
    ActualDWordString : STRING;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_DWORD,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_DWORD,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_DWORD,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedDWordString := CONCAT(STR1 := '0x',
                                                   STR2 := DWORD_TO_HEXSTR(in := Expecteds[ExpectedsIndex],
                                                                                         iPrecision := 8,
                                                                                         bLoCase := FALSE));
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ExpectedDWordString);

        ActualDWordString := CONCAT(STR1 := '0x',
                                                 STR2 := DWORD_TO_HEXSTR(in := Actuals[ActualsIndex],
                                                                                       iPrecision := 8,
                                                                                       bLoCase := FALSE));
        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := ActualDWordString);
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF DWORD

DWORD array with expected values

Actuals ARRAY[*] OF DWORD

DWORD array with actual values

AssertArrayEquals_INT PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    INT array with expected values

  • Actuals

    INT array with actual values

Source Code
// Asserts that two INT arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_INT
VAR_IN_OUT
    Expecteds : ARRAY[*] OF INT; // INT array with expected values
    Actuals : ARRAY[*] OF INT; // INT array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_INT,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_INT,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_INT,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := INT_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := INT_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF INT

INT array with expected values

Actuals ARRAY[*] OF INT

INT array with actual values

AssertArrayEquals_LINT PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    LINT array with expected values

  • Actuals

    LINT array with actual values

Source Code
// Asserts that two LINT arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_LINT
VAR_IN_OUT
    Expecteds : ARRAY[*] OF LINT; // LINT array with expected values
    Actuals : ARRAY[*] OF LINT; // LINT array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_LINT,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_LINT,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_LINT,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := LINT_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := LINT_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF LINT

LINT array with expected values

Actuals ARRAY[*] OF LINT

LINT array with actual values

AssertArrayEquals_LREAL PUBLIC

Inputs

  • Delta

    The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    LREAL array with expected values

  • Actuals

    LREAL array with actual values

Source Code
// Asserts that two LREAL arrays are equal to within a positive delta. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_LREAL
VAR_IN_OUT
    Expecteds : ARRAY[*] OF LREAL; // LREAL array with expected values
    Actuals : ARRAY[*] OF LREAL; // LREAL array with actual values
END_VAR
VAR_INPUT
    Delta : LREAL; // The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF ABS(Expecteds[ExpectedsIndex] - Actuals[ActualsIndex]) > Delta THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_LREAL,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_LREAL,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_LREAL,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := LREAL_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := LREAL_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Delta LREAL

The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF LREAL

LREAL array with expected values

Actuals ARRAY[*] OF LREAL

LREAL array with actual values

AssertArrayEquals_LWORD PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    LWORD array with expected values

  • Actuals

    LWORD array with actual values

Source Code
// Asserts that two LWORD arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_LWORD
VAR_IN_OUT
    Expecteds : ARRAY[*] OF LWORD; // LWORD array with expected values
    Actuals : ARRAY[*] OF LWORD; // LWORD array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedLWordString : STRING;
    ActualLWordString : STRING;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_LWORD,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_LWORD,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_LWORD,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedLWordString := CONCAT(STR1 := '0x',
                                                  STR2 := LWORD_TO_HEXSTR(in := Expecteds[ExpectedsIndex],
                                                                                        iPrecision := 16,
                                                                                        bLoCase := FALSE));
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ExpectedLWordString);

        ActualLWordString := CONCAT(STR1 := '0x',
                                                 STR2 := LWORD_TO_HEXSTR(in := Actuals[ActualsIndex],
                                                                                       iPrecision := 16,
                                                                                       bLoCase := FALSE));
        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := ActualLWordString);
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF LWORD

LWORD array with expected values

Actuals ARRAY[*] OF LWORD

LWORD array with actual values

AssertArrayEquals_REAL PUBLIC

Inputs

  • Delta

    The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    REAL array with expected values

  • Actuals

    REAL array with actual values

Source Code
// Asserts that two REAL arrays are equal to within a positive delta. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_REAL
VAR_IN_OUT
    Expecteds : ARRAY[*] OF REAL; // REAL array with expected values
    Actuals : ARRAY[*] OF REAL; // REAL array with actual values
END_VAR
VAR_INPUT
    Delta : REAL; // The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF ABS(Expecteds[ExpectedsIndex] - Actuals[ActualsIndex]) > Delta THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_REAL,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_REAL,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_REAL,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := REAL_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := REAL_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Delta REAL

The maximum delta between the value of expected and actual for which both numbers are still considered equal, proportional to the expected value in that array cell

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF REAL

REAL array with expected values

Actuals ARRAY[*] OF REAL

REAL array with actual values

AssertArrayEquals_SINT PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    SINT array with expected values

  • Actuals

    SINT array with actual values

Source Code
// Asserts that two SINT arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_SINT
VAR_IN_OUT
    Expecteds : ARRAY[*] OF SINT; // SINT array with expected values
    Actuals : ARRAY[*] OF SINT; // SINT array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_SINT,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_SINT,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_SINT,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := SINT_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := SINT_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF SINT

SINT array with expected values

Actuals ARRAY[*] OF SINT

SINT array with actual values

AssertArrayEquals_UDINT PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    UDINT array with expected values

  • Actuals

    UDINT array with actual values

Source Code
// Asserts that two UDINT arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_UDINT
VAR_IN_OUT
    Expecteds : ARRAY[*] OF UDINT; // UDINT array with expected values
    Actuals : ARRAY[*] OF UDINT; // UDINT array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_UDINT,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_UDINT,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_UDINT,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := UDINT_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := 
CONCAT(STR1 := ActualString, STR2 := UDINT_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF UDINT

UDINT array with expected values

Actuals ARRAY[*] OF UDINT

UDINT array with actual values

AssertArrayEquals_UINT PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    UINT array with expected values

  • Actuals

    UINT array with actual values

Source Code
// Asserts that two UINT arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_UINT
VAR_IN_OUT
    Expecteds : ARRAY[*] OF UINT; // UINT array with expected values
    Actuals : ARRAY[*] OF UINT; // UINT array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_UINT,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_UINT,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_UINT,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := UINT_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := UINT_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF UINT

UINT array with expected values

Actuals ARRAY[*] OF UINT

UINT array with actual values

AssertArrayEquals_ULINT PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    ULINT array with expected values

  • Actuals

    ULINT array with actual values

Source Code
// Asserts that two ULINT arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_ULINT
VAR_IN_OUT
    Expecteds : ARRAY[*] OF ULINT; // ULINT array with expected values
    Actuals : ARRAY[*] OF ULINT; // ULINT array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_ULINT,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_ULINT,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_ULINT,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ULINT_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := ULINT_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF ULINT

ULINT array with expected values

Actuals ARRAY[*] OF ULINT

ULINT array with actual values

AssertArrayEquals_USINT PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    USINT array with expected values

  • Actuals

    USINT array with actual values

Source Code
// Asserts that two USINT arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_USINT
VAR_IN_OUT
    Expecteds : ARRAY[*] OF USINT; // USINT array with expected values
    Actuals : ARRAY[*] OF USINT; // USINT array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_USINT,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_USINT,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_USINT,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := USINT_TO_STRING(Expecteds[ExpectedsIndex]));

        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := USINT_TO_STRING(Actuals[ActualsIndex]));
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF USINT

USINT array with expected values

Actuals ARRAY[*] OF USINT

USINT array with actual values

AssertArrayEquals_WORD PUBLIC

Inputs

  • Message

    The identifying message for the assertion error

In/Outputs

  • Expecteds

    WORD array with expected values

  • Actuals

    WORD array with actual values

Source Code
// Asserts that two WORD arrays are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertArrayEquals_WORD
VAR_IN_OUT
    Expecteds : ARRAY[*] OF WORD; // WORD array with expected values
    Actuals : ARRAY[*] OF WORD; // WORD array with actual values
END_VAR
VAR_INPUT
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Equals : BOOL := TRUE;
    SizeEquals : BOOL := TRUE;
    Index : DINT;
    ExpectedString : T_MaxString;
    ActualString : T_MaxString;
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
    SizeOfExpecteds : DINT;
    SizeOfActuals : DINT;
    ExpectedDWordString : STRING;
    ActualDWordString : STRING;
    ExpectedsIndex : DINT;
    ActualsIndex : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());

SizeOfExpecteds := ABS(UPPER_BOUND(Expecteds, 1) - LOWER_BOUND(Expecteds, 1)) + 1;
SizeOfActuals := ABS(UPPER_BOUND(Actuals, 1) - LOWER_BOUND(Actuals, 1)) + 1;

IF SizeOfExpecteds <> SizeOfActuals THEN
    Equals := FALSE;
    SizeEquals := FALSE;
ELSE
    (* Even though we know that both arrays are equal in size, the two arrays can start at two completely different
       indexes, which needs to be taken into account for. *)
    ExpectedsIndex := LOWER_BOUND(Expecteds, 1); // The start position for the expecteds
    ActualsIndex := LOWER_BOUND(Actuals, 1); // The start position for the actuals
    FOR Index := 1 TO SizeOfExpecteds BY 1 DO
        IF Expecteds[ExpectedsIndex] <> Actuals[ActualsIndex] THEN
            Equals := FALSE;
            EXIT;
        END_IF
        ExpectedsIndex := ExpectedsIndex + 1;
        ActualsIndex := ActualsIndex + 1;
    END_FOR
END_IF

AssertArrayResults.ReportResult(ExpectedsSize := DINT_TO_UDINT(SizeOfExpecteds),
                                ExpectedsTypeClass := IBaseLibrary.TypeClass.TYPE_WORD,
                                ActualsSize := DINT_TO_UDINT(SizeOfActuals),
                                ActualsTypeClass := IBaseLibrary.TypeClass.TYPE_WORD,
                                Message := Message,
                                TestInstancePath := TestInstancePath,
                                AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND NOT Equals THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_Array_WORD,
                  AssertionMessage := Message);

    IF NOT SizeEquals THEN
        Message := CONCAT(STR1 := Message, STR2 := ', size of arrays not matching.');
        ExpectedString := 'SIZE = ';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(SizeOfExpecteds));
        ActualString := 'SIZE = ';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(SizeOfActuals));
    ELSE
        ExpectedDWordString := CONCAT(STR1 := '0x',
                                                  STR2 := WORD_TO_HEXSTR(in := Expecteds[ExpectedsIndex],
                                                                                       iPrecision := 4,
                                                                                       bLoCase := FALSE));
        ExpectedString := 'ARRAY[';
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := DINT_TO_STRING(ExpectedsIndex));
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := '] = ');
        ExpectedString := CONCAT(STR1 := ExpectedString, STR2 := ExpectedDWordString);

        ActualDWordString := CONCAT(STR1 := '0x',
                                                 STR2 := WORD_TO_HEXSTR(in := Actuals[ActualsIndex],
                                                                                      iPrecision := 4,
                                                                                      bLoCase := FALSE));
        ActualString := 'ARRAY[';
        ActualString := CONCAT(STR1 := ActualString, STR2 := DINT_TO_STRING(ActualsIndex));
        ActualString := CONCAT(STR1 := ActualString, STR2 := '] = ');
        ActualString := CONCAT(STR1 := ActualString, STR2 := ActualDWordString);
    END_IF

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedString,
                                            Actual := ActualString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Message T_MaxString

The identifying message for the assertion error

Expecteds ARRAY[*] OF WORD

WORD array with expected values

Actuals ARRAY[*] OF WORD

WORD array with actual values

AssertEquals PUBLIC

Asserts

that two objects (of any type) are equal. If they are not, an assertion error is created. For REAL and LREAL it's recommended to use the AssertEquals_REAL or AssertEquals_LREAL respectively as these give the possibility to specify a delta between the expected and actual value.

Inputs

  • Expected

    Expected value

  • Actual

    The value to check against expected

  • Message

    The identifying message for the assertion error

Source Code
(*
    Asserts that two objects (of any type) are equal. If they are not, an assertion error is created.
    For REAL and LREAL it's recommended to use the AssertEquals_REAL or AssertEquals_LREAL respectively
    as these give the possibility to specify a delta between the expected and actual value.
*)
METHOD PUBLIC AssertEquals
VAR_INPUT
    Expected : ANY; // Expected value
    Actual : ANY; // The value to check against expected
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    Count : DINT;
    ExpectedDataString : T_MaxString;
    ActualDataString : T_MaxString;
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;

    boolExpected : BOOL;
    boolActual : BOOL;
    byteExpected : BYTE;
    byteActual : BYTE;
    dateExpected : DATE;
    dateActual : DATE;
    dateAndTimeExpected : DATE_AND_TIME;
    dateAndTimeActual : DATE_AND_TIME;
    dintExpected : DINT;
    dintActual : DINT;
    dwordExpected : DWORD;
    dwordActual : DWORD;
    intExpected : INT;
    intActual : INT;
    lintExpected : LINT;
    lintActual : LINT;
    lrealExpected : LREAL;
    lrealActual : LREAL;
    ltimeExpected : LTIME;
    ltimeActual : LTIME;
    lwordExpected : LWORD;
    lwordActual : LWORD;
    realExpected : REAL;
    realActual : REAL;
    sintExpected : SINT;
    sintActual : SINT;
    stringExpected : T_MaxString;
    stringActual : T_MaxString;
    wstringExpected : WSTRING(255);
    wstringActual : WSTRING(255);
    timeExpected : TIME;
    timeActual : TIME;
    timeOfDayExpected : TIME_OF_DAY;
    timeOfDayActual : TIME_OF_DAY;
    udintExpected : UDINT;
    udintActual : UDINT;
    uintExpected : UINT;
    uintActual : UINT;
    ulintExpected : ULINT;
    ulintActual : ULINT;
    usintExpected : USINT;
    usintActual : USINT;
    wordExpected : WORD;
    wordActual : WORD;

    (* ANY comparison variables *)
    DataTypesNotEquals : BOOL; // The data type of the two ANY input parameters are not equal
    DataSizeNotEquals : BOOL; // The data size of the two ANY input parameters are not equal
    DataContentNotEquals : BOOL; // The data content of the two ANY input parameters are not equal
    IteratorCounter : DINT;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

(*
    This compares two instances of any object type and returns whether they
    are the same type, size and value or not. This is necessary for two reasons:
    1. So that we can know exactly what differs between the two input parameters
    2. It's not possible to do a comparison (= or <>) between two instances of ANY. 
*)
// Check whether the type of the inputs differs
IF Expected.TypeClass <> Actual.TypeClass THEN
    DataTypesNotEquals := TRUE;
END_IF

// Check whether the size of the inputs differs
IF NOT DataTypesNotEquals THEN
    (* Take special care of STRING and WSTRING, as these can be declared with different sizes,
       although their content might be the same *)
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_STRING THEN
        MEMCPY(destAddr := ADR(stringExpected), srcAddr := Expected.pValue, n := DINT_TO_UDINT(Expected.diSize));
        MEMCPY(destAddr := ADR(stringActual), srcAddr := Actual.pValue, n := DINT_TO_UDINT(Actual.diSize));
        DataSizeNotEquals := LEN(STR := stringExpected) <> LEN(STR := stringActual);
    ELSIF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_WSTRING THEN
        MEMCPY(destAddr := ADR(wstringExpected), srcAddr := Expected.pValue, n := DINT_TO_UDINT(Expected.diSize));
        MEMCPY(destAddr := ADR(wstringActual), srcAddr := Actual.pValue, n := DINT_TO_UDINT(Actual.diSize));
        DataSizeNotEquals := WLEN(STR := wstringExpected) <> WLEN(STR := wstringActual);
    ELSIF (Expected.diSize <> Actual.diSize) THEN
        DataSizeNotEquals := TRUE;
    END_IF
END_IF

// Even though the data type and size are equals, the contents may still differ
IF NOT DataTypesNotEquals AND NOT DataSizeNotEquals THEN
    // Compare each byte in the ANY-types
    FOR IteratorCounter := 0 TO Expected.diSize - 1 BY 1 DO
        IF Expected.pValue[IteratorCounter] <> Actual.pValue[IteratorCounter] THEN
            DataContentNotEquals := TRUE;
            EXIT;
        END_IF
    END_FOR
END_IF

(* First check whether the input data are any of the standard data types that are supported by TcUnit.
   In this case call the standard Assert-methods for that data. But before doing that we need to make sure
   that both the expected and actual:
   - Have both the same data type
   - Both have the same data size
*)
IF NOT DataTypesNotEquals AND NOT DataSizeNotEquals THEN

    // BOOL
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_BOOL THEN
        MEMCPY(destAddr := ADR(boolExpected), srcAddr := Expected.pValue, n := SIZEOF(BOOL));
        MEMCPY(destAddr := ADR(boolActual), srcAddr := Actual.pValue, n := SIZEOF(BOOL));
        AssertEquals_BOOL(Expected := boolExpected, Actual := boolActual, Message := Message);
        RETURN;
    END_IF

    // BYTE
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_BYTE THEN
        MEMCPY(destAddr := ADR(byteExpected), srcAddr := Expected.pValue, n := SIZEOF(BYTE));
        MEMCPY(destAddr := ADR(byteActual), srcAddr := Actual.pValue, n := SIZEOF(BYTE));
        AssertEquals_BYTE(Expected := byteExpected, Actual := byteActual, Message := Message);
        RETURN;
    END_IF

    // DATE
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_DATE THEN
        MEMCPY(destAddr := ADR(dateExpected), srcAddr := Expected.pValue, n := SIZEOF(DATE));
        MEMCPY(destAddr := ADR(dateActual), srcAddr := Actual.pValue, n := SIZEOF(DATE));
        AssertEquals_DATE(Expected := dateExpected, Actual := dateActual, Message := Message);
        RETURN;
    END_IF

    // DATE_AND_TIME
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_DATEANDTIME THEN
        MEMCPY(destAddr := ADR(dateAndTimeExpected), srcAddr := Expected.pValue, n := SIZEOF(DATE_AND_TIME));
        MEMCPY(destAddr := ADR(dateAndTimeActual), srcAddr := Actual.pValue, n := SIZEOF(DATE_AND_TIME));
        AssertEquals_DATE_AND_TIME(Expected := dateAndTimeExpected, Actual := dateAndTimeActual, Message := Message);
        RETURN;
    END_IF

    // DINT
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_DINT THEN
        MEMCPY(destAddr := ADR(dintExpected), srcAddr := Expected.pValue, n := SIZEOF(DINT));
        MEMCPY(destAddr := ADR(dintActual), srcAddr := Actual.pValue, n := SIZEOF(DINT));
        AssertEquals_DINT(Expected := dintExpected, Actual := dintActual, Message := Message);
        RETURN;
    END_IF

    // DWORD
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_DWORD THEN
        MEMCPY(destAddr := ADR(dwordExpected), srcAddr := Expected.pValue, n := SIZEOF(DWORD));
        MEMCPY(destAddr := ADR(dwordActual), srcAddr := Actual.pValue, n := SIZEOF(DWORD));
        AssertEquals_DWORD(Expected := dwordExpected, Actual := dwordActual, Message := Message);
        RETURN;
    END_IF

    // INT
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_INT THEN
        MEMCPY(destAddr := ADR(intExpected), srcAddr := Expected.pValue, n := SIZEOF(INT));
        MEMCPY(destAddr := ADR(intActual), srcAddr := Actual.pValue, n := SIZEOF(INT));
        AssertEquals_INT(Expected := intExpected, Actual := intActual, Message := Message);
        RETURN;
    END_IF

    // LINT
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_LINT THEN
        MEMCPY(destAddr := ADR(lintExpected), srcAddr := Expected.pValue, n := SIZEOF(LINT));
        MEMCPY(destAddr := ADR(lintActual), srcAddr := Actual.pValue, n := SIZEOF(LINT));
        AssertEquals_LINT(Expected := lintExpected, Actual := lintActual, Message := Message);
        RETURN;
    END_IF

    // LREAL
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_LREAL THEN
        MEMCPY(destAddr := ADR(lrealExpected), srcAddr := Expected.pValue, n := SIZEOF(LREAL));
        MEMCPY(destAddr := ADR(lrealActual), srcAddr := Actual.pValue, n := SIZEOF(LREAL));
        AssertEquals_LREAL(Expected := lrealExpected, Actual := lrealActual, Delta := 0.0, Message := Message);
        RETURN;
    END_IF

    // LTIME
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_LTIME THEN
        MEMCPY(destAddr := ADR(ltimeExpected), srcAddr := Expected.pValue, n := SIZEOF(LTIME));
        MEMCPY(destAddr := ADR(ltimeActual), srcAddr := Actual.pValue, n := SIZEOF(LTIME));
        AssertEquals_LTIME(Expected := ltimeExpected, Actual := ltimeActual, Message := Message);
        RETURN;
    END_IF

    // LWORD
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_LWORD THEN
        MEMCPY(destAddr := ADR(lwordExpected), srcAddr := Expected.pValue, n := SIZEOF(LWORD));
        MEMCPY(destAddr := ADR(lwordActual), srcAddr := Actual.pValue, n := SIZEOF(LWORD));
        AssertEquals_LWORD(Expected := lwordExpected, Actual := lwordActual, Message := Message);
        RETURN;
    END_IF

    // REAL
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_REAL THEN
        MEMCPY(destAddr := ADR(realExpected), srcAddr := Expected.pValue, n := SIZEOF(REAL));
        MEMCPY(destAddr := ADR(realActual), srcAddr := Actual.pValue, n := SIZEOF(REAL));
        AssertEquals_REAL(Expected := realExpected, Actual := realActual, Delta := 0.0, Message := Message);
        RETURN;
    END_IF

    // SINT
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_SINT THEN
        MEMCPY(destAddr := ADR(sintExpected), srcAddr := Expected.pValue, n := SIZEOF(SINT));
        MEMCPY(destAddr := ADR(sintActual), srcAddr := Actual.pValue, n := SIZEOF(SINT));
        AssertEquals_SINT(Expected := sintExpected, Actual := sintActual, Message := Message);
        RETURN;
    END_IF

    // STRING
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_STRING THEN
        MEMCPY(destAddr := ADR(stringExpected), srcAddr := Expected.pValue, n := DINT_TO_UDINT(Expected.diSize));
        MEMCPY(destAddr := ADR(stringActual), srcAddr := Actual.pValue, n := DINT_TO_UDINT(Actual.diSize));
        AssertEquals_STRING(Expected := stringExpected, Actual := stringActual, Message := Message);
        RETURN;
    END_IF

    // WSTRING
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_WSTRING THEN
        MEMCPY(destAddr := ADR(wstringExpected), srcAddr := Expected.pValue, n := DINT_TO_UDINT(Expected.diSize));
        MEMCPY(destAddr := ADR(wstringActual), srcAddr := Actual.pValue, n := DINT_TO_UDINT(Actual.diSize));
        AssertEquals_WSTRING(Expected := wstringExpected, Actual := wstringActual, Message := Message);
        RETURN;
    END_IF

    // TIME
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_TIME THEN
        MEMCPY(destAddr := ADR(timeExpected), srcAddr := Expected.pValue, n := SIZEOF(TIME));
        MEMCPY(destAddr := ADR(timeActual), srcAddr := Actual.pValue, n := SIZEOF(TIME));
        AssertEquals_TIME(Expected := timeExpected, Actual := timeActual, Message := Message);
        RETURN;
    END_IF

    // TIME_OF_DAY
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_TIMEOFDAY THEN
        MEMCPY(destAddr := ADR(timeOfDayExpected), srcAddr := Expected.pValue, n := SIZEOF(TIME_OF_DAY));
        MEMCPY(destAddr := ADR(timeOfDayActual), srcAddr := Actual.pValue, n := SIZEOF(TIME_OF_DAY));
        AssertEquals_TIME_OF_DAY(Expected := timeOfDayExpected, Actual := timeOfDayActual, Message := Message);
        RETURN;
    END_IF

    // UDINT
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_UDINT THEN
        MEMCPY(destAddr := ADR(udintExpected), srcAddr := Expected.pValue, n := SIZEOF(UDINT));
        MEMCPY(destAddr := ADR(udintActual), srcAddr := Actual.pValue, n := SIZEOF(UDINT));
        AssertEquals_UDINT(Expected := udintExpected, Actual := udintActual, Message := Message);
        RETURN;
    END_IF

    // UINT
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_UINT THEN
        MEMCPY(destAddr := ADR(uintExpected), srcAddr := Expected.pValue, n := SIZEOF(UINT));
        MEMCPY(destAddr := ADR(uintActual), srcAddr := Actual.pValue, n := SIZEOF(UINT));
        AssertEquals_UINT(Expected := uintExpected, Actual := uintActual, Message := Message);
        RETURN;
    END_IF

    // ULINT
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_ULINT THEN
        MEMCPY(destAddr := ADR(ulintExpected), srcAddr := Expected.pValue, n := SIZEOF(ULINT));
        MEMCPY(destAddr := ADR(ulintActual), srcAddr := Actual.pValue, n := SIZEOF(ULINT));
        AssertEquals_ULINT(Expected := ulintExpected, Actual := ulintActual, Message := Message);
        RETURN;
    END_IF

    // USINT
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_USINT THEN
        MEMCPY(destAddr := ADR(usintExpected), srcAddr := Expected.pValue, n := SIZEOF(USINT));
        MEMCPY(destAddr := ADR(usintActual), srcAddr := Actual.pValue, n := SIZEOF(USINT));
        AssertEquals_USINT(Expected := usintExpected, Actual := usintActual, Message := Message);
        RETURN;
    END_IF

    // WORD
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_WORD THEN
        MEMCPY(destAddr := ADR(wordExpected), srcAddr := Expected.pValue, n := SIZEOF(WORD));
        MEMCPY(destAddr := ADR(wordActual), srcAddr := Actual.pValue, n := SIZEOF(WORD));
        AssertEquals_WORD(Expected := wordExpected, Actual := wordActual, Message := Message);
        RETURN;
    END_IF

END_IF

(* If we've come to this row, it means that the data input is not any of the standard primitive data types, and thus
   we need to do special handling *)
TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

IF DataTypesNotEquals THEN
    ExpectedDataString := CONCAT(STR1 := '(Type class = ', STR2 := F_AnyTypeClassToString((Expected.TypeClass)));
    ExpectedDataString := CONCAT(STR1 := ExpectedDataString, STR2 := ')');
    ActualDataString := CONCAT(STR1 := '(Type class = ', STR2 := F_AnyTypeClassToString(Actual.TypeClass));
    ActualDataString := CONCAT(STR1 := ActualDataString, STR2 := ')');
ELSIF DataSizeNotEquals THEN
    (* Take special care of STRING and WSTRING, as these can be declared with different sizes,
       although their content might be the same *)
    IF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_STRING THEN
        MEMCPY(destAddr := ADR(stringExpected), srcAddr := Expected.pValue, n := DINT_TO_UDINT(Expected.diSize));
        MEMCPY(destAddr := ADR(stringActual), srcAddr := Actual.pValue, n := DINT_TO_UDINT(Actual.diSize));
        ExpectedDataString := CONCAT(STR1 := '(Data size (LEN) = ', STR2 := INT_TO_STRING(LEN(STR := stringExpected)));
        ActualDataString := CONCAT(STR1 := '(Data size (LEN) = ', STR2 := INT_TO_STRING(LEN(STR := stringActual)));
    ELSIF UDINT_TO_INT(Expected.TypeClass) = IBaseLibrary.TypeClass.TYPE_WSTRING THEN
        MEMCPY(destAddr := ADR(wstringExpected), srcAddr := Expected.pValue, n := DINT_TO_UDINT(Expected.diSize));
        MEMCPY(destAddr := ADR(wstringActual), srcAddr := Actual.pValue, n := DINT_TO_UDINT(Actual.diSize));
        ExpectedDataString := CONCAT(STR1 := '(Data size (WLEN) = ', STR2 := INT_TO_STRING(WLEN(STR := wstringExpected)));
        ActualDataString := CONCAT(STR1 := '(Data size (WLEN) = ', STR2 := INT_TO_STRING(WLEN(STR := wstringActual)));
    ELSE
        ExpectedDataString := CONCAT(STR1 := '(Data size = ', STR2 := DINT_TO_STRING(Expected.diSize));
        ActualDataString := CONCAT(STR1 := '(Data size = ', STR2 := DINT_TO_STRING(Actual.diSize));
    END_IF
    ExpectedDataString := CONCAT(STR1 := ExpectedDataString, STR2 := ')');
    ActualDataString := CONCAT(STR1 := ActualDataString, STR2 := ')');
ELSIF DataContentNotEquals THEN
    FOR Count := 0 TO MIN(Expected.diSize-1, 38) BY 1 DO // One byte will equal two characters (example: 255 = 0xff, 1 = 0x01)
        ExpectedDataString := CONCAT(STR1 := ExpectedDataString,
                                                  STR2 := BYTE_TO_HEXSTR(in := Expected.pValue[Count],
                                                                                       iPrecision := 2,
                                                                                       bLoCase := FALSE));
    END_FOR
    ExpectedDataString := CONCAT(STR1 := '0x', STR2 := ExpectedDataString);

    FOR Count := 0 TO MIN(Actual.diSize-1, 38) BY 1 DO // One byte will equal two characters (example: 255 = 0xff, 1 = 0x01)
        ActualDataString := CONCAT(STR1 := ActualDataString,
                                                STR2 := BYTE_TO_HEXSTR(in := Actual.pValue[Count],
                                                                                     iPrecision := 2,
                                                                                     bLoCase := FALSE));
    END_FOR
    ActualDataString := CONCAT(STR1 := '0x', STR2 := ActualDataString);
END_IF

AssertResults.ReportResult(ExpectedSize := DINT_TO_UDINT(Expected.diSize),
                           ExpectedTypeClass := UDINT_TO_INT(Expected.TypeClass),
                           ExpectedValue := Expected.pValue,
                           ActualSize := DINT_TO_UDINT(Actual.diSize),
                           ActualTypeClass := UDINT_TO_INT(Actual.TypeClass),
                           ActualValue := Actual.pValue,
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND (DataTypesNotEquals OR DataSizeNotEquals OR DataContentNotEquals) THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_ANY,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := ExpectedDataString,
                                            Actual := ActualDataString,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected ANY

Expected value

Actual ANY

The value to check against expected

Message T_MaxString

The identifying message for the assertion error

AssertEquals_BOOL PUBLIC

Inputs

  • Expected

    BOOL expected value

  • Actual

    BOOL actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two BOOLs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_BOOL
VAR_INPUT
    Expected : BOOL; // BOOL expected value
    Actual : BOOL; // BOOL actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_BOOL,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_BOOL,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_BOOL,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := BOOL_TO_STRING(Expected),
                                            Actual := BOOL_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected BOOL

BOOL expected value

Actual BOOL

BOOL actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_BYTE PUBLIC

Inputs

  • Expected

    BYTE expected value

  • Actual

    BYTE actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two BYTEs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_BYTE
VAR_INPUT
    Expected : BYTE; // BYTE expected value
    Actual : BYTE; // BYTE actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_BYTE,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_BYTE,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_BYTE,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := CONCAT(
                                                       STR1 := '0x',
                                                       STR2 := BYTE_TO_HEXSTR(in := Expected,
                                                                                            iPrecision := 2,
                                                                                            bLoCase := FALSE)),
                                            Actual := CONCAT(
                                                       STR1 := '0x',
                                                       STR2 := BYTE_TO_HEXSTR(in := Actual,
                                                                                            iPrecision := 2,
                                                                                            bLoCase := FALSE)),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected BYTE

BYTE expected value

Actual BYTE

BYTE actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_DATE PUBLIC

Inputs

  • Expected

    DATE expected value

  • Actual

    DATE actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two DATEs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_DATE
VAR_INPUT
    Expected : DATE; // DATE expected value
    Actual : DATE; // DATE actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_DATE,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_DATE,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_DATE,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := DATE_TO_STRING(Expected),
                                            Actual := DATE_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected DATE

DATE expected value

Actual DATE

DATE actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_DATE_AND_TIME PUBLIC

Inputs

  • Expected

    DATE_AND_TIME expected value

  • Actual

    DATE_AND_TIME actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two DATE_AND_TIMEs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_DATE_AND_TIME
VAR_INPUT
    Expected : DATE_AND_TIME; // DATE_AND_TIME expected value
    Actual : DATE_AND_TIME; // DATE_AND_TIME actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_DATEANDTIME,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_DATEANDTIME,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_DATE_AND_TIME,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := DT_TO_STRING(Expected),
                                            Actual := DT_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected DATE_AND_TIME

DATE_AND_TIME expected value

Actual DATE_AND_TIME

DATE_AND_TIME actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_DINT PUBLIC

Inputs

  • Expected

    DINT expected value

  • Actual

    DINT actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two DINTs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_DINT
VAR_INPUT
    Expected : DINT; // DINT expected value
    Actual : DINT; // DINT actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_DINT,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_DINT,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_DINT,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := DINT_TO_STRING(Expected),
                                            Actual := DINT_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected DINT

DINT expected value

Actual DINT

DINT actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_DWORD PUBLIC

Inputs

  • Expected

    DWORD expected value

  • Actual

    DWORD actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two DWORDs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_DWORD
VAR_INPUT
    Expected : DWORD; // DWORD expected value
    Actual : DWORD; // DWORD actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_DWORD,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_DWORD,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_DWORD,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := CONCAT(
                                                            STR1 := '0x',
                                                            STR2 := DWORD_TO_HEXSTR(in := Expected,
                                                                                                  iPrecision := 8,
                                                                                                  bLoCase := FALSE)),
                                            Actual := CONCAT(
                                                           STR1 := '0x',
                                                           STR2 := DWORD_TO_HEXSTR(in := Actual,
                                                                                                 iPrecision := 8,
                                                                                                 bLoCase := FALSE)),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected DWORD

DWORD expected value

Actual DWORD

DWORD actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_INT PUBLIC

Inputs

  • Expected

    INT expected value

  • Actual

    INT actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two INTs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_INT
VAR_INPUT
    Expected : INT; // INT expected value
    Actual : INT; // INT actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_INT,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_INT,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_INT,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := INT_TO_STRING(Expected),
                                            Actual := INT_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected INT

INT expected value

Actual INT

INT actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_LINT PUBLIC

Inputs

  • Expected

    LINT expected value

  • Actual

    LINT actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two LINTs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_LINT
VAR_INPUT
    Expected : LINT; // LINT expected value
    Actual : LINT; // LINT actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_LINT,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_LINT,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_LINT,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := LINT_TO_STRING(Expected),
                                            Actual := LINT_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected LINT

LINT expected value

Actual LINT

LINT actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_LREAL PUBLIC

Inputs

  • Expected

    LREAL expected value

  • Actual

    LREAL actual value

  • Delta

    The maximum delta between the absolute value of expected and actual for which both numbers are still considered equal

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two LREALs are equal to within a positive delta. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_LREAL
VAR_INPUT
    Expected : LREAL; // LREAL expected value
    Actual : LREAL; // LREAL actual value
    Delta : LREAL; // The maximum delta between the absolute value of expected and actual for which both numbers are still considered equal
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_LREAL,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_LREAL,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND ABS(Expected - Actual) > Delta THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_LREAL,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := LREAL_TO_STRING(Expected),
                                            Actual := LREAL_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected LREAL

LREAL expected value

Actual LREAL

LREAL actual value

Delta LREAL

The maximum delta between the absolute value of expected and actual for which both numbers are still considered equal

Message T_MaxString

The identifying message for the assertion error

AssertEquals_LTIME PUBLIC

Inputs

  • Expected

    LTIME expected value

  • Actual

    LTIME actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two LTIMEs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_LTIME
VAR_INPUT
    Expected : LTIME; // LTIME expected value
    Actual : LTIME; // LTIME actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_LTIME,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_LTIME,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_LTIME,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := LTIME_TO_STRING(Expected),
                                            Actual := LTIME_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected LTIME

LTIME expected value

Actual LTIME

LTIME actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_LWORD PUBLIC

Inputs

  • Expected

    LWORD expected value

  • Actual

    LWORD actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two LWORDs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_LWORD
VAR_INPUT
    Expected : LWORD; // LWORD expected value
    Actual : LWORD; // LWORD actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_LWORD,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_LWORD,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_LWORD,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := CONCAT(
                                                            STR1 := '0x',
                                                            STR2 := LWORD_TO_HEXSTR(in := Expected,
                                                                                                  iPrecision := 16,
                                                                                                  bLoCase := FALSE)),
                                            Actual := CONCAT(
                                                            STR1 := '0x',
                                                            STR2 := LWORD_TO_HEXSTR(in := Actual,
                                                                                                  iPrecision := 16,
                                                                                                  bLoCase := FALSE)),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected LWORD

LWORD expected value

Actual LWORD

LWORD actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_REAL PUBLIC

Inputs

  • Expected

    REAL expected value

  • Actual

    REAL actual value

  • Delta

    The maximum delta between the absolute value of expected and actual for which both numbers are still considered equal

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two REALs are equal to within a positive delta. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_REAL
VAR_INPUT
    Expected : REAL; // REAL expected value
    Actual : REAL; // REAL actual value
    Delta : REAL;  // The maximum delta between the absolute value of expected and actual for which both numbers are still considered equal
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_REAL,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_REAL,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND ABS(Expected - Actual) > Delta THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_REAL,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := REAL_TO_STRING(Expected),
                                            Actual := REAL_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected REAL

REAL expected value

Actual REAL

REAL actual value

Delta REAL

The maximum delta between the absolute value of expected and actual for which both numbers are still considered equal

Message T_MaxString

The identifying message for the assertion error

AssertEquals_SINT PUBLIC

Inputs

  • Expected

    SINT expected value

  • Actual

    SINT actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two SINTs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_SINT
VAR_INPUT
    Expected : SINT; // SINT expected value
    Actual : SINT; // SINT actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_SINT,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_SINT,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_SINT,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := SINT_TO_STRING(Expected),
                                            Actual := SINT_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected SINT

SINT expected value

Actual SINT

SINT actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_STRING PUBLIC

Inputs

  • Expected

    STRING expected value

  • Actual

    STRING actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two STRINGs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_STRING
VAR_INPUT
    Expected : T_MaxString; // STRING expected value
    Actual : T_MaxString; // STRING actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_STRING,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_STRING,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND (LEN(STR := Expected) <> LEN(STR := Actual) OR (Expected <> Actual)) THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_STRING,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := Expected,
                                            Actual := Actual,
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected T_MaxString

STRING expected value

Actual T_MaxString

STRING actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_TIME PUBLIC

Inputs

  • Expected

    TIME expected value

  • Actual

    TIME actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two TIMEs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_TIME
VAR_INPUT
    Expected : TIME; // TIME expected value
    Actual : TIME; // TIME actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_TIME,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_TIME,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_TIME,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := TIME_TO_STRING(Expected),
                                            Actual := TIME_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected TIME

TIME expected value

Actual TIME

TIME actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_TIME_OF_DAY PUBLIC

Inputs

  • Expected

    TIME_OF_DAY expected value

  • Actual

    TIME_OF_DAY actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two TIME_OF_DAYs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_TIME_OF_DAY
VAR_INPUT
    Expected : TIME_OF_DAY; // TIME_OF_DAY expected value
    Actual : TIME_OF_DAY; // TIME_OF_DAY actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_TIMEOFDAY,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_TIMEOFDAY,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_TIME_OF_DAY,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := TOD_TO_STRING(Expected),
                                            Actual := TOD_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected TIME_OF_DAY

TIME_OF_DAY expected value

Actual TIME_OF_DAY

TIME_OF_DAY actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_UDINT PUBLIC

Inputs

  • Expected

    UDINT expected value

  • Actual

    UDINT actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two UDINTs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_UDINT
VAR_INPUT
    Expected : UDINT; // UDINT expected value
    Actual : UDINT; // UDINT actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_UDINT,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_UDINT,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_UDINT,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := UDINT_TO_STRING(Expected),
                                            Actual := UDINT_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected UDINT

UDINT expected value

Actual UDINT

UDINT actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_UINT PUBLIC

Inputs

  • Expected

    UINT expected value

  • Actual

    UINT actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two UINTs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_UINT
VAR_INPUT
    Expected : UINT; // UINT expected value
    Actual : UINT; // UINT actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_UINT,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_UINT,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_UINT,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := UINT_TO_STRING(Expected),
                                            Actual := UINT_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected UINT

UINT expected value

Actual UINT

UINT actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_ULINT PUBLIC

Inputs

  • Expected

    ULINT expected value

  • Actual

    ULINT actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two ULINTs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_ULINT
VAR_INPUT
    Expected : ULINT; // ULINT expected value
    Actual : ULINT; // ULINT actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_ULINT,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_ULINT,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_ULINT,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := ULINT_TO_STRING(Expected),
                                            Actual := ULINT_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected ULINT

ULINT expected value

Actual ULINT

ULINT actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_USINT PUBLIC

Inputs

  • Expected

    USINT expected value

  • Actual

    USINT actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two USINTs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_USINT
VAR_INPUT
    Expected : USINT; // USINT expected value
    Actual : USINT; // USINT actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    AlreadyReported : BOOL;
    TestInstancePath : T_MaxString;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_USINT,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_USINT,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_USINT,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := USINT_TO_STRING(Expected),
                                            Actual := USINT_TO_STRING(Actual),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected USINT

USINT expected value

Actual USINT

USINT actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_WORD PUBLIC

Inputs

  • Expected

    WORD expected value

  • Actual

    WORD actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two WORDs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_WORD
VAR_INPUT
    Expected : WORD; // WORD expected value
    Actual : WORD; // WORD actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_WORD,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_WORD,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND Expected <> Actual THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_WORD,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := CONCAT(
                                                            STR1 := '0x',
                                                            STR2 := WORD_TO_HEXSTR(in := Expected,
                                                                                                 iPrecision := 4,
                                                                                                 bLoCase := FALSE)),
                                            Actual := CONCAT(
                                                            STR1 := '0x',
                                                            STR2 := WORD_TO_HEXSTR(in := Actual,
                                                                                                 iPrecision := 4,
                                                                                                 bLoCase := FALSE)),
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected WORD

WORD expected value

Actual WORD

WORD actual value

Message T_MaxString

The identifying message for the assertion error

AssertEquals_WSTRING PUBLIC

Inputs

  • Expected

    WSTRING expected value

  • Actual

    WSTRING actual value

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that two WSTRINGs are equal. If they are not, an assertion error is created.
METHOD PUBLIC AssertEquals_WSTRING
VAR_INPUT
    Expected : WSTRING(255); // WSTRING expected value
    Actual : WSTRING(255); // WSTRING actual value
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
VAR
    TestInstancePath : T_MaxString;
    AlreadyReported : BOOL;
END_VAR
IF GVL_TcUnit.IgnoreCurrentTest OR GVL_TcUnit.CurrentTestIsFinished THEN
    RETURN;
END_IF

TestInstancePath := AddTestNameToInstancePath(TestInstancePath := FindTestSuiteInstancePath());

AssertResults.ReportResult(ExpectedSize := SIZEOF(Expected),
                           ExpectedTypeClass := IBaseLibrary.TypeClass.TYPE_WSTRING,
                           ExpectedValue := ADR(Expected),
                           ActualSize := SIZEOF(Actual),
                           ActualTypeClass := IBaseLibrary.TypeClass.TYPE_WSTRING,
                           ActualValue := ADR(Actual),
                           Message := Message,
                           TestInstancePath := TestInstancePath,
                           AlreadyReported => AlreadyReported);

IF NOT AlreadyReported AND (WLEN(STR := Expected) <> WLEN(STR := Actual) OR (Expected <> Actual)) THEN
    SetTestFailed(AssertionType := E_AssertionType.Type_WSTRING,
                  AssertionMessage := Message);

    AssertMessageFormatter.LogAssertFailure(Expected := 'Not possible to print EXP unicode WSTRING value',
                                            Actual := 'Not possible to print ACT unicode WSTRING value',
                                            Message := Message,
                                            TestInstancePath := TestInstancePath);
END_IF

Expected WSTRING(255)

WSTRING expected value

Actual WSTRING(255)

WSTRING actual value

Message T_MaxString

The identifying message for the assertion error

AssertFalse PUBLIC

Inputs

  • Condition

    Condition to be checked

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that a condition is false. If it is not, an assertion error is created.
METHOD PUBLIC AssertFalse
VAR_INPUT
    Condition : BOOL; // Condition to be checked
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
AssertEquals_BOOL(Expected := FALSE,
                  Actual := Condition,
                  Message := Message);

Condition BOOL

Condition to be checked

Message T_MaxString

The identifying message for the assertion error

AssertTrue PUBLIC

Inputs

  • Condition

    Condition to be checked

  • Message

    The identifying message for the assertion error

Source Code
// Asserts that a condition is true. If it is not, an assertion error is created.
METHOD PUBLIC AssertTrue
VAR_INPUT
    Condition : BOOL; // Condition to be checked
    Message : T_MaxString; // The identifying message for the assertion error
END_VAR
AssertEquals_BOOL(Expected := TRUE,
                  Actual := Condition,
                  Message := Message);

Condition BOOL

Condition to be checked

Message T_MaxString

The identifying message for the assertion error

CalculateAndSetNumberOfAssertsForTest INTERNAL

Inputs

Source Code
METHOD INTERNAL CalculateAndSetNumberOfAssertsForTest
VAR_INPUT
    TestName : T_MaxString;
END_VAR
VAR
    TestInstancePath : T_MaxString;
    TotalNumberOfAsserts : UINT;
    NumberOfAsserts : UINT;
    NumberOfArrayAsserts : UINT;

    IteratorCounter : UINT;
END_VAR
TestInstancePath := AddTestNameToInstancePath(FindTestSuiteInstancePath());
NumberOfAsserts := AssertResults.GetNumberOfAssertsForTest(CompleteTestInstancePath := TestInstancePath);
NumberOfArrayAsserts := AssertArrayResults.GetNumberOfArrayAssertsForTest(CompleteTestInstancePath := TestInstancePath);
TotalNumberOfAsserts := NumberOfAsserts + NumberOfArrayAsserts;

IF TotalNumberOfAsserts > 0 THEN
    IF NumberOfTests > 0 THEN
        FOR IteratorCounter := 1 TO NumberOfTests BY 1 DO
            IF Tests[IteratorCounter].GetName() = TestName THEN
                Tests[IteratorCounter].SetNumberOfAssertions(NoOfAssertions := TotalNumberOfAsserts);
                RETURN;
            END_IF
        END_FOR
    END_IF
END_IF

TestName T_MaxString

CalculateDuration INTERNAL

Inputs

  • FinishedAt

    CPU cycle counter with 100ns precision

Source Code
METHOD INTERNAL CalculateDuration
VAR_INPUT
    FinishedAt : LWORD; // CPU cycle counter with 100ns precision
END_VAR
IF Duration = 0.0 THEN
	Duration := LWORD_TO_LREAL(FinishedAt - StartedAt) * GVL_TcUnit.HundredNanosecondToSecond; // Seconds
END_IF

FinishedAt LWORD

CPU cycle counter with 100ns precision

FB_init BOOL

Inputs

  • bInitRetains

    if TRUE, the retain variables are initialized (warm start / cold start)

  • bInCopyCode

    if TRUE, the instance afterwards gets moved into the copy code (online change)

Source Code
METHOD FB_init : BOOL
VAR_INPUT
    bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
    bInCopyCode : BOOL;  // if TRUE, the instance afterwards gets moved into the copy code (online change)
END_VAR
GVL_TcUnit.NumberOfInitializedTestSuites := GVL_TcUnit.NumberOfInitializedTestSuites + 1;
GVL_TcUnit.TestSuiteAddresses[GVL_TcUnit.NumberOfInitializedTestSuites] := THIS;

bInitRetains BOOL

if TRUE, the retain variables are initialized (warm start / cold start)

bInCopyCode BOOL

if TRUE, the instance afterwards gets moved into the copy code (online change)

FindTestSuiteInstancePath PRIVATE

Source Code
// Searches for the instance path of the calling function block
METHOD PRIVATE FindTestSuiteInstancePath : T_MaxString
FindTestSuiteInstancePath := GetInstancePath();

GetDuration INTERNAL

Source Code
METHOD INTERNAL GetDuration : LREAL
GetDuration := Duration;

GetHasStartedRunning INTERNAL

Source Code
METHOD INTERNAL GetHasStartedRunning : BOOL
GetHasStartedRunning := StartedAt > 0;

GetInstancePath INTERNAL

Source Code
METHOD INTERNAL GetInstancePath : T_MaxString
GetInstancePath := InstancePath;

GetNumberOfFailedTests INTERNAL

Source Code
METHOD INTERNAL GetNumberOfFailedTests : UINT
VAR
    Counter : UINT;
    FailedTestsCount : UINT;
    NumberOfTestsToAnalyse : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
    NumberOfTestOverArrayLimit : UINT;
END_VAR
// Limit the test analyse to the max array limit of 'Tests[]'
NumberOfTestsToAnalyse := GetNumberOfTestsToAnalyse();
IF GetNumberOfTests() > NumberOfTestsToAnalyse THEN
    NumberOfTestOverArrayLimit := GetNumberOfTests()-NumberOfTestsToAnalyse;
END_IF

FOR Counter := 1 TO NumberOfTestsToAnalyse BY 1 DO
    IF Tests[Counter].IsFailed() THEN
        FailedTestsCount := FailedTestsCount + 1;
    END_IF
END_FOR

GetNumberOfFailedTests := FailedTestsCount + NumberOfTestOverArrayLimit;

GetNumberOfSkippedTests INTERNAL

Source Code
METHOD INTERNAL GetNumberOfSkippedTests : UINT
VAR
    Counter : UINT;
    SkippedTestsCount : UINT;
    NumberOfTestsToAnalyse : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
// Limit the test analyse to the max array limit of 'Tests[]'
NumberOfTestsToAnalyse := GetNumberOfTestsToAnalyse();

FOR Counter := 1 TO NumberOfTestsToAnalyse BY 1 DO
    IF Tests[Counter].IsSkipped() THEN
        SkippedTestsCount := SkippedTestsCount + 1;
    END_IF
END_FOR

GetNumberOfSkippedTests := SkippedTestsCount;

GetNumberOfSuccessfulTests INTERNAL

Source Code
METHOD INTERNAL GetNumberOfSuccessfulTests : UINT
GetNumberOfSuccessfulTests := GetNumberOfTests() - GetNumberOfFailedTests() - GetNumberOfSkippedTests();

GetNumberOfTests INTERNAL

Source Code
METHOD INTERNAL GetNumberOfTests : UINT
GetNumberOfTests := NumberOfTests;

GetNumberOfTestsToAnalyse INTERNAL

Source Code
METHOD INTERNAL GetNumberOfTestsToAnalyse : UINT
GetNumberOfTestsToAnalyse := MIN(GetNumberOfTests(), GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);

GetTestByName REFERENCE TO FB_Test

Inputs

Source Code
METHOD GetTestByName : REFERENCE TO FB_Test
VAR_INPUT
  TestName : T_MaxString;
END_VAR
VAR
  IteratorCounter : UINT;
  NumberOfTestsToAnalyse : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestSuites);
END_VAR
NumberOfTestsToAnalyse := GetNumberOfTestsToAnalyse();
FOR IteratorCounter := 1 TO NumberOfTestsToAnalyse BY 1 DO
  IF Tests[IteratorCounter].GetName() = TestName THEN
    GetTestByName REF= Tests[IteratorCounter];
    RETURN;
	END_IF
END_FOR

TestName T_MaxString

GetTestByPosition INTERNAL

Inputs

Source Code
// This method returns the test at the n'th position, ranging from 1.. NumberOfTests
METHOD INTERNAL GetTestByPosition : FB_Test
VAR_INPUT
    Position : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
GetTestByPosition := Tests[Position];

Position UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite)

GetTestOrderNumber INTERNAL

Inputs

Source Code
METHOD INTERNAL GetTestOrderNumber : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
VAR_INPUT
    TestName : T_MaxString;
END_VAR
VAR
    IteratorCounter : UINT;
    NumberOfTestsToAnalyse : UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
// Limit the test analyse to the max array limit of 'Tests[]'
NumberOfTestsToAnalyse := GetNumberOfTestsToAnalyse();

FOR IteratorCounter := 1 TO NumberOfTestsToAnalyse BY 1 DO
    IF Tests[IteratorCounter].GetName() = TestName THEN
        GetTestOrderNumber := Tests[IteratorCounter].GetTestOrder();
        RETURN;
    END_IF
END_FOR

TestName T_MaxString

IsTestFinished INTERNAL

Inputs

Source Code
METHOD INTERNAL IsTestFinished : BOOL
VAR_INPUT
    TestName : T_MaxString;
END_VAR
VAR
    IteratorCounter : UINT;
    NumberOfTestsToAnalyse : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
// Limit the test analyse to the max array limit of 'Tests[]'
NumberOfTestsToAnalyse := GetNumberOfTestsToAnalyse();

IsTestFinished := FALSE;
FOR IteratorCounter := 1 TO NumberOfTestsToAnalyse BY 1 DO
    IF Tests[IteratorCounter].GetName() = TestName THEN
        IsTestFinished := Tests[IteratorCounter].IsFinished();
        RETURN;
    END_IF
END_FOR

TestName T_MaxString

SetStartedAtIfNotSet INTERNAL

Inputs

  • Timestamp

    CPU cycle counter with 100ns precision

Source Code
METHOD INTERNAL SetStartedAtIfNotSet
VAR_INPUT
    Timestamp : LWORD; // CPU cycle counter with 100ns precision
END_VAR
IF StartedAt = 0 THEN
    StartedAt := Timestamp;
END_IF

Timestamp LWORD

CPU cycle counter with 100ns precision

SetStartedAtTimeBasedOnCpuCounter INTERNAL

Source Code
METHOD INTERNAL SetStartedAtTimeBasedOnCpuCounter
StartedAt := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter);

SetTestFailed PRIVATE

Inputs

Source Code
METHOD PRIVATE SetTestFailed
VAR_INPUT
    AssertionType : E_AssertionType;
    AssertionMessage : T_MaxString;
END_VAR
VAR
    IteratorCounter : UINT;
    NumberOfTestsToAnalyse : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
// Limit the test analyse to the max array limit of 'Tests[]'
NumberOfTestsToAnalyse := GetNumberOfTestsToAnalyse();

FOR IteratorCounter := 1 TO NumberOfTestsToAnalyse BY 1 DO
    IF Tests[IteratorCounter].GetName() = GVL_TcUnit.CurrentTestNameBeingCalled THEN
        Tests[IteratorCounter].SetFailed();
        Tests[IteratorCounter].SetAssertionType(AssertType := AssertionType);
        Tests[IteratorCounter].SetAssertionMessage(AssertMessage := AssertionMessage);
    END_IF
END_FOR

AssertionType E_AssertionType

AssertionMessage T_MaxString

SetTestFinished INTERNAL

Inputs

Source Code
(* Marks the test as finished in this testsuite.
   Returns TRUE if test was found, and FALSE if a test with this name was not found in this testsuite *)
METHOD INTERNAL SetTestFinished : BOOL;
VAR_INPUT
    TestName : T_MaxString;
    FinishedAt : LWORD;
END_VAR
VAR
    IteratorCounter : UINT;
    NumberOfTestsToAnalyse : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
END_VAR
// Limit the test analyse to the max array limit of 'Tests[]'
NumberOfTestsToAnalyse := GetNumberOfTestsToAnalyse();
FOR IteratorCounter := 1 TO NumberOfTestsToAnalyse BY 1 DO
    IF Tests[IteratorCounter].GetName() = TestName THEN
		IF NOT Tests[IteratorCounter].IsFinished() THEN
        	Tests[IteratorCounter].SetFinishedAndDuration(FinishedAt := FinishedAt);
		END_IF
		SetTestFinished := TRUE;
        RETURN;
    END_IF
END_FOR
SetTestFinished := FALSE;

TestName T_MaxString

FinishedAt LWORD

FB_XmlControl

Organizes

parsing and composing of XML data. Data can be treated as STRING or char array. Buffer size of file can be set via GVL_Param_TcUnit (xUnitBufferSize)

Methods

Properties

Source Code
(*
    Organizes parsing and composing of XML data. Data can be treated as STRING or char array.
    Buffer size of file can be set via GVL_Param_TcUnit (xUnitBufferSize)
*)
FUNCTION_BLOCK FB_XmlControl
VAR
    XmlBuffer : FB_StreamBuffer;
    TagListBuffer : FB_StreamBuffer;
    Tags : T_MaxString;
    TagListSeekBuffer : FB_StreamBuffer;
    TagsSeek : STRING;
    TagBuffer : FB_StreamBuffer;
    Tag : T_MaxString;
    TagOpen: BOOL;
    Select : UDINT;
    SearchPosition : UDINT; 
END_VAR
VAR CONSTANT
    TAG_OPEN : STRING(1) := '<';
    TAG_CLOSE : STRING(1) := '>';
    END_TAG_CLOSE : STRING(2) := '/>';
    SPACE : STRING(1) := ' ';
    EQUALS : STRING(1) := '=';
    QUOTE : STRING(1) := '"';
    BACK_SLASH : STRING(1) := '\';
    FORWARD_SLASH : STRING(1) := '/';
    OPEN_COMMENT : STRING(5) := '<!-- ';
    CLOSE_COMMENT : STRING(4) := ' -->';
    TAB : STRING(2) := '$T';
    CR_LF : STRING(4) := '$R$N';

    // $OD : ASCII code for carriage return (CR)
    // $$ : to add a $R
    // $' : to add ' (apostrophe)
    // $L or $l : line feed
    // $N or $n : new line
    // $P or $p : next page
    // $R or $r : end of line
END_VAR

ClearBuffer PUBLIC

Source Code
// Clears the contents of the entire buffer.
METHOD PUBLIC ClearBuffer
SearchPosition := 0;
TagListSeekBuffer.Length := 0;
XmlBuffer.Length := 0;
TagsSeek := '';
Tag := '';

CloseTag PUBLIC

Closes

a Tag: XML: '

Method: XML.CloseTag();
Source Code
(*
    Closes a Tag:
    XML: <MyTag />'

    Method: XML.CloseTag();
*)
METHOD PUBLIC CloseTag : T_MaxString
VAR
    ClosedTag : T_MaxString;
END_VAR
IF TagOpen THEN
    XmlBuffer.Append := END_TAG_CLOSE;
    Select := TagListBuffer.FindBack(SearchString := FORWARD_SLASH);
    ClosedTag := TagListBuffer.CutOff(StartPos := Select);
    TagOpen := FALSE;
ELSE
    Select := TagListBuffer.FindBack(SearchString := FORWARD_SLASH);
    ClosedTag := TagListBuffer.CutOff(StartPos := Select);
    XmlBuffer.Append := TAG_OPEN;
    XmlBuffer.Append := ClosedTag;
    XmlBuffer.Append := TAG_CLOSE;
END_IF

CloseTag := ClosedTag;

NewComment PUBLIC

Adds

a comment XML:

XML.NewComment(Comment: = 'MyComment');

Inputs

Source Code
(*
    Adds a comment
    XML: <!-- MyComment -->

    XML.NewComment(Comment: = 'MyComment');
*)
METHOD PUBLIC NewComment
VAR_INPUT
    Comment : T_MaxString;
END_VAR
IF TagOpen THEN
    XmlBuffer.Append := TAG_CLOSE;
    TagOpen := FALSE;
END_IF;
XmlBuffer.Append := OPEN_COMMENT;
XmlBuffer.Append := Comment;
XmlBuffer.Append := CLOSE_COMMENT;

Comment T_MaxString

NewParameter PUBLIC

Must

be called after opening a new tag

XML.NewParameter(Name: = 'ParaName', Value: = 'Value');

Inputs

Source Code
(*
    Must be called after opening a new tag

    XML.NewParameter(Name: = 'ParaName', Value: = 'Value');
*)
METHOD PUBLIC NewParameter
VAR_INPUT
    Name : T_MaxString;
    Value : T_MaxString;
END_VAR
XmlBuffer.Append := SPACE;
XmlBuffer.Append := Name;
XmlBuffer.Append := EQUALS;
XmlBuffer.Append := QUOTE;
XmlBuffer.Append := Value;
XmlBuffer.Append := QUOTE;

Name T_MaxString

Value T_MaxString

NewTag PUBLIC

Creates

a new Tag: XML:

XML.NewTag(Name: = 'MyTag');

Inputs

Source Code
(*
    Creates a new Tag:
    XML: <MyTag>

    XML.NewTag(Name: = 'MyTag');
*)
METHOD PUBLIC NewTag
VAR_INPUT
    Name : T_MaxString;
END_VAR
IF TagOpen THEN
    XmlBuffer.Append := TAG_CLOSE;
END_IF;
XmlBuffer.Append := TAG_OPEN;
XmlBuffer.Append := Name;
TagOpen := TRUE;
TagListBuffer.Append := FORWARD_SLASH;
TagListBuffer.Append := Name;

Name T_MaxString

NewTagData PUBLIC

Inputs

Source Code
METHOD PUBLIC NewTagData
VAR_INPUT
    Data : T_MaxString;
END_VAR
XmlBuffer.Append := TAG_CLOSE;
XmlBuffer.Append := Data;
TagOpen := FALSE;

Data T_MaxString

SetBuffer PUBLIC

Inputs

Source Code
METHOD PUBLIC SetBuffer
VAR_INPUT
    PointerToBuffer : POINTER TO BYTE; // ADR(..)
    SizeOfBuffer : UDINT; // SIZEOF(..)
END_VAR
XmlBuffer.SetBuffer(PointerToBufferAddress:= PointerToBuffer, SizeOfBuffer := SizeOfBuffer);
TagListBuffer.SetBuffer(PointerToBufferAddress := ADR(Tags), SizeOfBuffer := SIZEOF(Tags));
TagListSeekBuffer.SetBuffer(PointerToBufferAddress := ADR(TagsSeek), SizeOfBuffer := SIZEOF(TagsSeek));
TagBuffer.SetBuffer(PointerToBufferAddress := ADR(Tag), SizeOfBuffer := SIZEOF(Tag));

PointerToBuffer POINTER TO BYTE

ADR(..)

SizeOfBuffer UDINT

SIZEOF(..)

ToStartBuffer PUBLIC

Jump

to the beginning of the XML data XML.ToStartBuffer();

Source Code
(*
    Jump to the beginning of the XML data
    XML.ToStartBuffer();
*)
METHOD PUBLIC ToStartBuffer
SearchPosition := 0;
TagListSeekBuffer.Length := 0;
TagsSeek := '';
Tag := '';

WriteDocumentHeader PUBLIC

Add

your own preffered fileheader like: XML:

Start with calling this method before appending any other tags!

XML.WriteDocumentHeader('<?xml version="1.0" encoding="UTF-8"?>');

Inputs

Source Code
(*
    Add your own preffered fileheader like:
    XML: <?xml version="1.0" encoding="UTF-8"?>

    Start with calling this method before appending any other tags!

    XML.WriteDocumentHeader('<?xml version="1.0" encoding="UTF-8"?>');
*)
METHOD PUBLIC WriteDocumentHeader
VAR_INPUT
    Header : T_MaxString;
END_VAR
XmlBuffer.Append := Header;

Header T_MaxString

Length UDINT Get

Source Code
Length := XmlBuffer.Length;

F_AnyToUnionValue

Inputs

Source Code
FUNCTION F_AnyToUnionValue : U_ExpectedOrActual
VAR_INPUT
    AnySize : UDINT;
    AnyTypeClass : IBaseLibrary.TypeClass;
    AnyValue : POINTER TO BYTE;
END_VAR
CASE AnyTypeClass OF
    IBaseLibrary.TypeClass.TYPE_BOOL :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.boolExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_BIT :
        {warning disable C0355}
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.bitExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);
        {warning enable C0355}

    IBaseLibrary.TypeClass.TYPE_BYTE :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.byteExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_WORD :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.wordExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_DWORD :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.dwordExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_LWORD :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.lwordExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_SINT :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.sintExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_INT :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.intExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_DINT :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.dintExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_LINT :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.lintExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_USINT :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.usintExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_UINT :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.uintExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_UDINT :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.udintExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_ULINT :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.ulintExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_REAL :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.realExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_LREAL :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.lrealExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_STRING :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.stringExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_WSTRING :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.wstringExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_TIME :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.timeExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_DATE :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.dateExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_DATEANDTIME :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.dateandtimeExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_TIMEOFDAY :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.timeofdayExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_POINTER :
        // None

    IBaseLibrary.TypeClass.TYPE_REFERENCE :
        // None

    IBaseLibrary.TypeClass.TYPE_SUBRANGE :
        // None

    IBaseLibrary.TypeClass.TYPE_ENUM :
        // None

    IBaseLibrary.TypeClass.TYPE_ARRAY :
        // None

    IBaseLibrary.TypeClass.TYPE_PARAMS :
        // None

    IBaseLibrary.TypeClass.TYPE_USERDEF :
        // None

    IBaseLibrary.TypeClass.TYPE_NONE :
        // None

    IBaseLibrary.TypeClass.TYPE_ANY :
        // None

    IBaseLibrary.TypeClass.TYPE_ANYBIT :
        // None

    IBaseLibrary.TypeClass.TYPE_ANYDATE :
        // None

    IBaseLibrary.TypeClass.TYPE_ANYINT :
        // None

    IBaseLibrary.TypeClass.TYPE_ANYNUM :
        // None

    IBaseLibrary.TypeClass.TYPE_ANYREAL :
        // None

    IBaseLibrary.TypeClass.TYPE_LAZY :
        // None

    IBaseLibrary.TypeClass.TYPE_LTIME :
        MEMCPY(destaddr := ADR(F_AnyToUnionValue.ltimeExpectedOrActual),
               srcAddr := AnyValue,
               n := AnySize);

    IBaseLibrary.TypeClass.TYPE_BITCONST :
        // None

    IBaseLibrary.TypeClass.TYPE_INTERFACE :
        // None

    ELSE
        // None
END_CASE

AnySize UDINT

AnyTypeClass IBaseLibrary.TypeClass

AnyValue POINTER TO BYTE

F_AnyTypeClassToString

Inputs

Source Code
// This function takes the type-class of a ANY-variable and returns the STRING representation of it.
FUNCTION F_AnyTypeClassToString : STRING
VAR_INPUT
    AnyTypeClass : __SYSTEM.TYPE_CLASS;
END_VAR
CASE UDINT_TO_INT(AnyTypeClass) OF
    IBaseLibrary.TypeClass.TYPE_BOOL :
        F_AnyTypeClassToString := 'BOOL';

    IBaseLibrary.TypeClass.TYPE_BIT :
        F_AnyTypeClassToString := 'BIT';

    IBaseLibrary.TypeClass.TYPE_BYTE :
        F_AnyTypeClassToString := 'BYTE';

    IBaseLibrary.TypeClass.TYPE_WORD :
        F_AnyTypeClassToString := 'WORD';

    IBaseLibrary.TypeClass.TYPE_DWORD :
        F_AnyTypeClassToString := 'DWORD';

    IBaseLibrary.TypeClass.TYPE_LWORD :
        F_AnyTypeClassToString := 'LWORD';

    IBaseLibrary.TypeClass.TYPE_SINT :
        F_AnyTypeClassToString := 'SINT';

    IBaseLibrary.TypeClass.TYPE_INT :
        F_AnyTypeClassToString := 'INT';

    IBaseLibrary.TypeClass.TYPE_DINT :
        F_AnyTypeClassToString := 'DINT';

    IBaseLibrary.TypeClass.TYPE_LINT :
        F_AnyTypeClassToString := 'LINT';

    IBaseLibrary.TypeClass.TYPE_USINT :
        F_AnyTypeClassToString := 'USINT';

    IBaseLibrary.TypeClass.TYPE_UINT :
        F_AnyTypeClassToString := 'UINT';

    IBaseLibrary.TypeClass.TYPE_UDINT :
        F_AnyTypeClassToString := 'UDINT';

    IBaseLibrary.TypeClass.TYPE_ULINT :
        F_AnyTypeClassToString := 'ULINT';

    IBaseLibrary.TypeClass.TYPE_REAL :
        F_AnyTypeClassToString := 'REAL';

    IBaseLibrary.TypeClass.TYPE_LREAL :
        F_AnyTypeClassToString := 'LREAL';

    IBaseLibrary.TypeClass.TYPE_STRING :
        F_AnyTypeClassToString := 'STRING';

    IBaseLibrary.TypeClass.TYPE_WSTRING :
        F_AnyTypeClassToString := 'WSTRING';

    IBaseLibrary.TypeClass.TYPE_TIME :
        F_AnyTypeClassToString := 'TIME';

    IBaseLibrary.TypeClass.TYPE_DATE :
        F_AnyTypeClassToString := 'DATE';

    IBaseLibrary.TypeClass.TYPE_DATEANDTIME :
        F_AnyTypeClassToString := 'DATEANDTIME';

    IBaseLibrary.TypeClass.TYPE_TIMEOFDAY :
        F_AnyTypeClassToString := 'TIMEOFDAY';

    IBaseLibrary.TypeClass.TYPE_POINTER :
        F_AnyTypeClassToString := 'POINTER';

    IBaseLibrary.TypeClass.TYPE_REFERENCE :
        F_AnyTypeClassToString := 'REFERENCE';

    IBaseLibrary.TypeClass.TYPE_SUBRANGE :
        F_AnyTypeClassToString := 'SUBRANGE';

    IBaseLibrary.TypeClass.TYPE_ENUM :
        F_AnyTypeClassToString := 'ENUM';

    IBaseLibrary.TypeClass.TYPE_ARRAY :
        F_AnyTypeClassToString := 'ARRAY';

    IBaseLibrary.TypeClass.TYPE_PARAMS :
        F_AnyTypeClassToString := 'PARAMS';

    IBaseLibrary.TypeClass.TYPE_USERDEF :
        F_AnyTypeClassToString := 'USERDEF';

    IBaseLibrary.TypeClass.TYPE_NONE :
        F_AnyTypeClassToString := 'NONE';

    IBaseLibrary.TypeClass.TYPE_ANY :
        F_AnyTypeClassToString := 'ANY';

    IBaseLibrary.TypeClass.TYPE_ANYBIT :
        F_AnyTypeClassToString := 'ANYBIT';

    IBaseLibrary.TypeClass.TYPE_ANYDATE :
        F_AnyTypeClassToString := 'ANYDATE';

    IBaseLibrary.TypeClass.TYPE_ANYINT :
        F_AnyTypeClassToString := 'ANYINT';

    IBaseLibrary.TypeClass.TYPE_ANYNUM :
        F_AnyTypeClassToString := 'ANYNUM';

    IBaseLibrary.TypeClass.TYPE_ANYREAL :
        F_AnyTypeClassToString := 'ANYREAL';

    IBaseLibrary.TypeClass.TYPE_LAZY :
        F_AnyTypeClassToString := 'LAZY';

    IBaseLibrary.TypeClass.TYPE_LTIME :
        F_AnyTypeClassToString := 'LTIME';

    IBaseLibrary.TypeClass.TYPE_BITCONST :
        F_AnyTypeClassToString := 'BITCONST';

    IBaseLibrary.TypeClass.TYPE_INTERFACE :
        F_AnyTypeClassToString := 'INTERFACE';

    ELSE
        F_AnyTypeClassToString := 'UNKNOWN';

END_CASE

AnyTypeClass __SYSTEM.TYPE_CLASS

F_AssertionTypeToString

Inputs

Source Code
(* Since TwinCAT 3.1.4024.x it's possible to do TO_STRING on enumerations,
   but for backward compability we need to do it manually *)
FUNCTION F_AssertionTypeToString : T_MaxString
VAR_INPUT
    AssertionType : E_AssertionType;
END_VAR
CASE AssertionType OF
    E_AssertionType.Type_UNDEFINED :
        F_AssertionTypeToString := 'UNDEFINED';

    E_AssertionType.Type_ANY :
        F_AssertionTypeToString := 'ANY';

    (* Primitive types *)
    E_AssertionType.Type_BOOL :
        F_AssertionTypeToString := 'BOOL';

    E_AssertionType.Type_BYTE :
        F_AssertionTypeToString := 'BYTE';

    E_AssertionType.Type_DATE :
        F_AssertionTypeToString := 'DATE';

    E_AssertionType.Type_DATE_AND_TIME :
        F_AssertionTypeToString := 'DATE_AND_TIME';

    E_AssertionType.Type_DINT :
        F_AssertionTypeToString := 'DINT';

    E_AssertionType.Type_DWORD :
        F_AssertionTypeToString := 'DWORD';

    E_AssertionType.Type_INT :
        F_AssertionTypeToString := 'INT';

    E_AssertionType.Type_LINT :
        F_AssertionTypeToString := 'LINT';

    E_AssertionType.Type_LREAL :
        F_AssertionTypeToString := 'LREAL';

    E_AssertionType.Type_LTIME :
        F_AssertionTypeToString := 'LTIME';

    E_AssertionType.Type_LWORD :
        F_AssertionTypeToString := 'LWORD';

    E_AssertionType.Type_REAL :
        F_AssertionTypeToString := 'REAL';

    E_AssertionType.Type_SINT :
        F_AssertionTypeToString := 'SINT';

    E_AssertionType.Type_STRING :
        F_AssertionTypeToString := 'STRING';

    E_AssertionType.Type_TIME :
        F_AssertionTypeToString := 'TIME';

    E_AssertionType.Type_TIME_OF_DAY :
        F_AssertionTypeToString := 'TIME_OF_DAY';

    E_AssertionType.Type_UDINT :
        F_AssertionTypeToString := 'UDINT';

    E_AssertionType.Type_UINT :
        F_AssertionTypeToString := 'UINT';

    E_AssertionType.Type_ULINT :
        F_AssertionTypeToString := 'ULINT';

    E_AssertionType.Type_USINT :
        F_AssertionTypeToString := 'USINT';

    E_AssertionType.Type_WORD :
        F_AssertionTypeToString := 'WORD';

    E_AssertionType.Type_WSTRING :
        F_AssertionTypeToString := 'WSTRING';


    (* Array types *)
    E_AssertionType.Type_Array2D_LREAL :
        F_AssertionTypeToString := 'Array2D_LREAL';

    E_AssertionType.Type_Array2D_REAL :
        F_AssertionTypeToString := 'Array2D_REAL';

    E_AssertionType.Type_Array3D_LREAL :
        F_AssertionTypeToString := 'Array3D_LREAL';

    E_AssertionType.Type_Array3D_REAL :
        F_AssertionTypeToString := 'Array3D_REAL';

    E_AssertionType.Type_Array_BOOL :
        F_AssertionTypeToString := 'Array_BOOL';

    E_AssertionType.Type_Array_BYTE :
        F_AssertionTypeToString := 'Array_BYTE';

    E_AssertionType.Type_Array_DINT :
        F_AssertionTypeToString := 'Array_DINT';

    E_AssertionType.Type_Array_DWORD :
        F_AssertionTypeToString := 'Array_DWORD';

    E_AssertionType.Type_Array_INT :
        F_AssertionTypeToString := 'Array_INT';

    E_AssertionType.Type_Array_LINT :
        F_AssertionTypeToString := 'Array_LINT';

    E_AssertionType.Type_Array_LREAL :
        F_AssertionTypeToString := 'Array_LREAL';

    E_AssertionType.Type_Array_LWORD :
        F_AssertionTypeToString := 'Array_LWORD';

    E_AssertionType.Type_Array_REAL :
        F_AssertionTypeToString := 'Array_REAL';

    E_AssertionType.Type_Array_SINT :
        F_AssertionTypeToString := 'Array_SINT';

    E_AssertionType.Type_Array_UDINT :
        F_AssertionTypeToString := 'Array_UDINT';

    E_AssertionType.Type_Array_UINT :
        F_AssertionTypeToString := 'Array_UINT';

    E_AssertionType.Type_Array_ULINT :
        F_AssertionTypeToString := 'Array_ULINT';

    E_AssertionType.Type_Array_USINT :
        F_AssertionTypeToString := 'Array_USINT';

    E_AssertionType.Type_Array_WORD :
        F_AssertionTypeToString := 'Array_WORD';

    ELSE
        F_AssertionTypeToString := 'UNDEFINED';

END_CASE

AssertionType E_AssertionType

F_GetCpuCounterAs64bit

In/Outputs

Source Code
(* Calls the passed GETCPUCOUNTER function block and
   converts the output of the GETCPUCOUNTER function block from 2x32bit variables to a
   single 64bit variable
*)
FUNCTION F_GetCpuCounterAs64bit : LWORD
VAR_IN_OUT CONSTANT
    CpuCounter : GETCPUCOUNTER;
END_VAR
CpuCounter();
F_GetCpuCounterAs64bit := SHL(DWORD_TO_LWORD(CpuCounter.cpuCntHiDW), 32) + DWORD_TO_LWORD(CpuCounter.cpuCntLoDW);

CpuCounter GETCPUCOUNTER

F_GetTestSuiteNameFromTestInstancePath

Inputs

Source Code
FUNCTION F_GetTestSuiteNameFromTestInstancePath : T_MaxString
VAR_INPUT
    TestInstancePath : T_MaxString;
END_VAR
VAR
    TestSuiteName : T_MaxString;
    FindPosition : INT;
END_VAR
TestSuiteName := F_RemoveInstancePathAndProjectNameFromTestInstancePath(TestInstancePath := TestInstancePath);
// Remove everything except the test suite name
FindPosition := FIND(STR1 := TestSuiteName, '.');
TestSuiteName := RIGHT(STR := TestSuiteName, SIZE := LEN(STR := TestSuiteName) - FindPosition);
F_GetTestSuiteNameFromTestInstancePath := TestSuiteName;

TestInstancePath T_MaxString

F_IsAnyEqualToUnionValue

Inputs

Source Code
FUNCTION F_IsAnyEqualToUnionValue : BOOL
VAR_INPUT
    ExpectedOrActual : U_ExpectedOrActual;
    ExpectedOrActualSize : UDINT;
    ExpectedOrActualTypeClass : IBaseLibrary.TypeClass;
    ExpectedOrActualValue : POINTER TO BYTE;
END_VAR
VAR
    AnyExpectedOrActual : U_ExpectedOrActual;
END_VAR
AnyExpectedOrActual := F_AnyToUnionValue(AnySize := ExpectedOrActualSize,
                                         AnyTypeClass := ExpectedOrActualTypeClass,
                                         AnyValue := ExpectedOrActualValue);

CASE ExpectedOrActualTypeClass OF

    IBaseLibrary.TypeClass.TYPE_BOOL :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.boolExpectedOrActual = AnyExpectedOrActual.boolExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_BIT :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.bitExpectedOrActual = AnyExpectedOrActual.bitExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_BYTE :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.byteExpectedOrActual = AnyExpectedOrActual.byteExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_WORD :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.wordExpectedOrActual = AnyExpectedOrActual.wordExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_DWORD :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.dwordExpectedOrActual = AnyExpectedOrActual.dwordExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_LWORD :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.lwordExpectedOrActual = AnyExpectedOrActual.lwordExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_SINT :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.sintExpectedOrActual = AnyExpectedOrActual.sintExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_INT :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.intExpectedOrActual = AnyExpectedOrActual.intExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_DINT :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.dintExpectedOrActual = AnyExpectedOrActual.dintExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_LINT :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.lintExpectedOrActual = AnyExpectedOrActual.lintExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_USINT :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.usintExpectedOrActual = AnyExpectedOrActual.usintExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_UINT :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.uintExpectedOrActual = AnyExpectedOrActual.uintExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_UDINT :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.udintExpectedOrActual = AnyExpectedOrActual.udintExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_ULINT :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.ulintExpectedOrActual = AnyExpectedOrActual.ulintExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_REAL :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.realExpectedOrActual = AnyExpectedOrActual.realExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_LREAL :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.lrealExpectedOrActual = AnyExpectedOrActual.lrealExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_STRING :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.stringExpectedOrActual = AnyExpectedOrActual.stringExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_WSTRING :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.wstringExpectedOrActual = AnyExpectedOrActual.wstringExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_TIME :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.timeExpectedOrActual = AnyExpectedOrActual.timeExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_DATE :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.dateExpectedOrActual = AnyExpectedOrActual.dateExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_DATEANDTIME :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.dateandtimeExpectedOrActual = AnyExpectedOrActual.dateandtimeExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_TIMEOFDAY :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.timeofdayExpectedOrActual = AnyExpectedOrActual.timeofdayExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_POINTER :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_REFERENCE :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_SUBRANGE :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_ENUM :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_ARRAY :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_PARAMS :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_USERDEF :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_NONE :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_ANY :
        (* Even though the data input of the ExpectedOrActual is ANY, this CASE-switch will never
           enter this case, but instead the type-class that it is an instance of. So for instance,
           if the ExpectedOrActual is an instance of INT, this case-switch will enter the 
           IBaseLibrary.TypeClass.TYPE_INT case. *)
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_ANYBIT :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_ANYDATE :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_ANYINT :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_ANYNUM :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_ANYREAL :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_LAZY :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_LTIME :
        F_IsAnyEqualToUnionValue := ExpectedOrActual.ltimeExpectedOrActual = AnyExpectedOrActual.ltimeExpectedOrActual;

    IBaseLibrary.TypeClass.TYPE_BITCONST :
        F_IsAnyEqualToUnionValue := FALSE;

    IBaseLibrary.TypeClass.TYPE_INTERFACE :
        F_IsAnyEqualToUnionValue := FALSE;

    ELSE
        F_IsAnyEqualToUnionValue := FALSE;
END_CASE

ExpectedOrActual U_ExpectedOrActual

ExpectedOrActualSize UDINT

ExpectedOrActualTypeClass IBaseLibrary.TypeClass

ExpectedOrActualValue POINTER TO BYTE

F_RemoveInstancePathAndProjectNameFromTestInstancePath

Inputs

Source Code
FUNCTION F_RemoveInstancePathAndProjectNameFromTestInstancePath : T_MaxString
VAR_INPUT
    TestInstancePath : T_MaxString;
END_VAR
VAR
    CharacterPositionOfProjectName : INT;
    ProjectNameWithDot : T_MaxString;
END_VAR
// Add the '.' character to the project name for search
ProjectNameWithDot := CONCAT(STR1 := TwinCAT_SystemInfoVarList._AppInfo.ProjectName, STR2 := '.');

// Find the character position of the beginning of the first occurence of the project name
CharacterPositionOfProjectName := FIND(STR1 := TestInstancePath, STR2 := ProjectNameWithDot);

IF CharacterPositionOfProjectName > 0 THEN
    TestInstancePath := DELETE(STR := TestInstancePath,
                               LEN := CharacterPositionOfProjectName-1 + LEN(ProjectNameWithDot),
                               POS := 1);
END_IF

// Check if the project name happens to be the same as namespace, and in that case, remove that as well
CharacterPositionOfProjectName := FIND(STR1 := TestInstancePath,
                                                    STR2 := ProjectNameWithDot);

IF CharacterPositionOfProjectName > 0 THEN
    TestInstancePath := DELETE(STR := TestInstancePath,
                               LEN := CharacterPositionOfProjectName - 1 +  LEN(STR := ProjectNameWithDot),
                               POS := 1);
END_IF

F_RemoveInstancePathAndProjectNameFromTestInstancePath := TestInstancePath;

TestInstancePath T_MaxString

IS_TEST_FINISHED

Inputs

Source Code
// Check if a certain test in the current suite is finished
FUNCTION IS_TEST_FINISHED : BOOL
VAR_INPUT
    TestName : T_MaxString;
END_VAR
VAR
    Counter : UINT;
    CurrentTest : FB_Test;
END_VAR
TestName := F_LTrim(in := F_RTrim(in := TestName));

IS_TEST_FINISHED := FALSE;

FOR Counter := 1 TO GVL_TcUnit.CurrentTestSuiteBeingCalled^.GetNumberOfTests() BY 1 DO
    CurrentTest := GVL_TcUnit.CurrentTestSuiteBeingCalled^.Tests[Counter];
    IF CurrentTest.TestName = TestName THEN
        IS_TEST_FINISHED := CurrentTest.IsFinished();
        RETURN;
    END_IF
END_FOR

TestName T_MaxString

RUN

This

function runs all test suites that have been initialized. The test suites are run in parallel (all at once).

Source Code
(*
    This function runs all test suites that have been initialized.
    The test suites are run in parallel (all at once).
*)
FUNCTION RUN
GVL_TcUnit.TcUnitRunner.RunTestSuiteTests();

RUN_IN_SEQUENCE

This

function runs all test suites that have been initialized. The test suites are run in sequence (one after the other).

Source Code
(*
    This function runs all test suites that have been initialized.
    The test suites are run in sequence (one after the other).
*)
FUNCTION RUN_IN_SEQUENCE
GVL_TcUnit.TcUnitRunner.RunTestSuiteTestsInSequence(TimeBetweenTestSuitesExecution := GVL_Param_TcUnit.TimeBetweenTestSuitesExecution);

TCUNIT_ADSLOGSTR

Inputs

Source Code
(* This function allows to put ADS strings into the TcUnit ADS message buffer. If ADSLOGSTR() is used directly,
   the messages can come out of sequence in relation to the message created by TcUnit as TcUnit buffers the messages to
   not overflow the ADS message router. By using this function, the ADS-messages are put in the same buffer as
   TcUnit is using for its output and thus the sequence will be correct *)
FUNCTION TCUNIT_ADSLOGSTR
VAR_INPUT
    msgCtrlMask : DWORD; // Message control mask
    msgFmtStr : T_MaxString; // Message format string
    strArg : T_MaxString; // STRING argument
END_VAR
GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := msgCtrlMask,
                                    MsgFmtStr := msgFmtStr,
                                    StrArg := strArg);

msgCtrlMask DWORD

Message control mask

msgFmtStr T_MaxString

Message format string

strArg T_MaxString

STRING argument

TEST

Inputs

Source Code
// This function declares a new test (if it has not been already declared in an earlier cycle)
FUNCTION TEST
VAR_INPUT
    TestName : T_MaxString;
END_VAR
VAR
    CounterTestSuiteAddress : UINT;
    Test : REFERENCE TO FB_Test;
END_VAR
TestName := F_LTrim(in := F_RTrim(in := TestName));

// Mark this test as the current one being executed
GVL_TcUnit.CurrentTestNameBeingCalled := TestName;

(* Check if combination of address for the test suite and test name already exists.
   For every test suite the name of the test case needs to be unique.
   If a test with this name already exists, don't add it to the available tests.
   Otherwise, add it to the available tests *)

FOR CounterTestSuiteAddress := 1 TO GVL_TcUnit.NumberOfInitializedTestSuites BY 1 DO
    // Look for the test suite by comparing to the one that is currently running
    IF GVL_TcUnit.TestSuiteAddresses[CounterTestSuiteAddress] = GVL_TcUnit.CurrentTestSuiteBeingCalled THEN
        Test REF= GVL_TcUnit.TestSuiteAddresses[CounterTestSuiteAddress]^.AddTest(TestName := TestName, IsTestOrdered := FALSE);
        GVL_TcUnit.CurrentTestIsFinished := GVL_TcUnit.TestSuiteAddresses[CounterTestSuiteAddress]^.IsTestFinished(TestName := TestName);
        IF __ISVALIDREF(Test) THEN
			Test.SetStartedAtIfNotSet(Timestamp := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter));
        END_IF
        RETURN;
    END_IF
END_FOR

TestName T_MaxString

TEST_FINISHED

Source Code
// Sets the currently running test as finished
FUNCTION TEST_FINISHED : BOOL
VAR
    TestName : T_MaxString;
    Counter : UINT := 0;
    FinishedAt : LWORD;
END_VAR
// Get the timestamp directly when the test has finished
FinishedAt := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter);

// Grab the currently running test name
TestName := GVL_TcUnit.CurrentTestNameBeingCalled;

TEST_FINISHED := FALSE;
(* Find the test suite and:
   1. Set the test in that test suite as finished
   2. Calculate and set the number of asserts made for that test
*)
FOR Counter := 1 TO GVL_TcUnit.NumberOfInitializedTestSuites BY 1 DO
    IF GVL_TcUnit.TestSuiteAddresses[Counter] = GVL_TcUnit.CurrentTestSuiteBeingCalled THEN
        GVL_TcUnit.TestSuiteAddresses[Counter]^.SetTestFinished(TestName := GVL_TcUnit.CurrentTestNameBeingCalled, FinishedAt := FinishedAt);
        GVL_TcUnit.TestSuiteAddresses[Counter]^.CalculateAndSetNumberOfAssertsForTest(TestName := GVL_TcUnit.CurrentTestNameBeingCalled);
        GVL_TcUnit.CurrentTestIsFinished := TRUE;
        TEST_FINISHED := TRUE;
        RETURN;
    END_IF
END_FOR

TEST_FINISHED_NAMED

Inputs

Source Code
(* Sets a test defined by TestName as finished. Note that the TestName-input must match
   a TestName that has been previously defined in this test suite. *)
FUNCTION TEST_FINISHED_NAMED
VAR_INPUT
    TestName : T_MaxString;
END_VAR
VAR
    Counter : UINT := 0;
    FoundTestName : BOOL := FALSE;
    FinishedAt : LWORD; // cpu cycle counter in 100ns precision
END_VAR
VAR_STAT
    FailedLookupCounter : UINT := 0;
    AlreadyPrintedFinalWarning : BOOL := FALSE;
END_VAR
VAR CONSTANT
    MaxNumberOfNonExistentTestNamesFailedLookups : UINT := 3;
END_VAR
TestName := F_LTrim(in := F_RTrim(in := TestName));
FinishedAt := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter);

(* Find the test suite and:
   1. Set the test in that test suite as finished
   2. Calculate and set the number of asserts made for that test
*)
FOR Counter := 1 TO GVL_TcUnit.NumberOfInitializedTestSuites BY 1 DO
    IF GVL_TcUnit.TestSuiteAddresses[Counter] = GVL_TcUnit.CurrentTestSuiteBeingCalled THEN
        FoundTestName := GVL_TcUnit.TestSuiteAddresses[Counter]^.SetTestFinished(TestName := TestName, FinishedAt := FinishedAt);
        IF NOT FoundTestName THEN
            IF FailedLookupCounter < MaxNumberOfNonExistentTestNamesFailedLookups THEN
                GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                                    MsgFmtStr := 'Failed to find test $'%s$'',
                                                    StrArg := TestName);
                FailedLookupCounter := FailedLookupCounter + 1;
                (* Abort TcUnit *)
                GVL_TcUnit.TcUnitRunner.AbortRunningTestSuiteTests();
            ELSIF NOT AlreadyPrintedFinalWarning THEN
                GVL_TcUnit.AdsMessageQueue.WriteLog(MsgCtrlMask := ADSLOG_MSGTYPE_ERROR,
                                                    MsgFmtStr := 'Excess failed attempts to mark test finished, failed. Further warnings will be suppressed',
                                                    StrArg := TestName);
                AlreadyPrintedFinalWarning := TRUE;
            END_IF
        ELSE
            GVL_TcUnit.TestSuiteAddresses[Counter]^.CalculateAndSetNumberOfAssertsForTest(TestName := TestName);
            GVL_TcUnit.CurrentTestIsFinished := TRUE;
        END_IF
        RETURN;
    END_IF
END_FOR

TestName T_MaxString

TEST_ORDERED

Inputs

Source Code
(* This function declares a new ordered test (if it has not been already declared in an earlier cycle).
   The test declared by this function will run in the order it is called, so if we have two tests:
   TEST_ORDERED('Test_1')
   TEST_ORDERED('Test_2')
   All asserts of Test_2 will be ignored until Test_1 is declared with TEST_FINISHED().

   The function returns TRUE if it's time to run this test (and thus asserts can be made)
   The function returns FALSE if it's not time to run this test (because a previous test has not been finished)
   If the function returns FALSE (and it's thus not time to run the test), any eventual asserts done for this 
   test will be ignored. It thus makes sense to call this function in a manner like:

    IF TEST_ORDERED('Testname') THEN
        fbFunctionBlockUnderTest();

        AssertEquals(Expected := 'SomeValue',
                     Actual := fbFunctionBlockUnderTest.Out,
                     Message := 'Test failed');
        TEST_FINISHED();
    END_IF

*)
FUNCTION TEST_ORDERED : BOOL
VAR_INPUT
    TestName : T_MaxString;
END_VAR
VAR
    CounterTestSuiteAddress : UINT;
    Test : REFERENCE TO FB_Test;
END_VAR
TestName := F_LTrim(in := F_RTrim(in := TestName));

// Mark this test as the current one being executed
GVL_TcUnit.CurrentTestNameBeingCalled := TestName;

(* Check if combination of address for the test suite and test name already exists.
   For every test suite the name of the test case needs to be unique.
   If a test with this name already exists, don't add it to the available tests.
   Otherwise, add it to the available tests *)

FOR CounterTestSuiteAddress := 1 TO GVL_TcUnit.NumberOfInitializedTestSuites BY 1 DO

    // Look for the test suite by comparing to the one that is currently running
    IF GVL_TcUnit.TestSuiteAddresses[CounterTestSuiteAddress] = GVL_TcUnit.CurrentTestSuiteBeingCalled THEN
        Test REF= GVL_TcUnit.TestSuiteAddresses[CounterTestSuiteAddress]^.AddTest(TestName := TestName, IsTestOrdered := TRUE);
        GVL_TcUnit.CurrentTestIsFinished := GVL_TcUnit.TestSuiteAddresses[CounterTestSuiteAddress]^.IsTestFinished(TestName := TestName);

        // Check that no previous code has set the currently running test to ignored (for example by setting the test to DISABLED
        IF NOT GVL_TcUnit.IgnoreCurrentTest THEN
            (* If the current test is not the current in the sequence of tests, set it to ignored until it is its turn to run
               If the current test is the one supposed to be run, check whether it has finished running or not *)
            IF GVL_TcUnit.TestSuiteAddresses[CounterTestSuiteAddress]^.GetTestOrderNumber(TestName := TestName) =
                GVL_TcUnit.CurrentlyRunningOrderedTestInTestSuite[CounterTestSuiteAddress] THEN
                // Check if test if finished, if so, increase the order number of this test suite to the next test
                IF GVL_TcUnit.CurrentTestIsFinished THEN
                    GVL_TcUnit.CurrentlyRunningOrderedTestInTestSuite[CounterTestSuiteAddress] := GVL_TcUnit.CurrentlyRunningOrderedTestInTestSuite[CounterTestSuiteAddress] + 1;
                    // No need to execute the test if it is already finished
                    TEST_ORDERED := FALSE;
                    GVL_TcUnit.IgnoreCurrentTest := TRUE;
                ELSE
                    // Start or continue executing test
                    IF __ISVALIDREF(Test) THEN
                        Test.SetStartedAtIfNotSet(Timestamp := F_GetCpuCounterAs64bit(GVL_TcUnit.GetCpuCounter));
                    END_IF
                    TEST_ORDERED := TRUE;
                END_IF
            ELSE
                // If this is not the current test that we are supposed to run, ignore it until it is its turn
                TEST_ORDERED := FALSE;
                GVL_TcUnit.IgnoreCurrentTest := TRUE;
			END_IF
        // This test should be ignored
        ELSE
            TEST_ORDERED := FALSE;
            GVL_TcUnit.IgnoreCurrentTest := TRUE;
		END_IF
        RETURN;
    END_IF
END_FOR

TestName T_MaxString

WRITE_PROTECTED_BOOL

Inputs

Source Code
FUNCTION WRITE_PROTECTED_BOOL
VAR_INPUT
    Ptr : POINTER TO BOOL;
    Value : BOOL;
END_VAR
Ptr^ := Value;

Ptr POINTER TO BOOL

Value BOOL

WRITE_PROTECTED_BYTE

Inputs

Source Code
FUNCTION WRITE_PROTECTED_BYTE
VAR_INPUT
    Ptr : POINTER TO BYTE;
    Value : BYTE;
END_VAR
Ptr^ := Value;

Ptr POINTER TO BYTE

Value BYTE

WRITE_PROTECTED_DATE

Inputs

Source Code
FUNCTION WRITE_PROTECTED_DATE
VAR_INPUT
    Ptr : POINTER TO DATE;
    Value : DATE;
END_VAR
Ptr^ := Value;

Ptr POINTER TO DATE

Value DATE

WRITE_PROTECTED_DATE_AND_TIME

Inputs

Source Code
FUNCTION WRITE_PROTECTED_DATE_AND_TIME
VAR_INPUT
    Ptr : POINTER TO DATE_AND_TIME;
    Value : DATE_AND_TIME;
END_VAR
Ptr^ := Value;

Ptr POINTER TO DATE_AND_TIME

Value DATE_AND_TIME

WRITE_PROTECTED_DINT

Inputs

Source Code
FUNCTION WRITE_PROTECTED_DINT
VAR_INPUT
    Ptr : POINTER TO DINT;
    Value : DINT;
END_VAR
Ptr^ := Value;

Ptr POINTER TO DINT

Value DINT

WRITE_PROTECTED_DWORD

Inputs

Source Code
FUNCTION WRITE_PROTECTED_DWORD
VAR_INPUT
    Ptr : POINTER TO DWORD;
    Value : DWORD;
END_VAR
Ptr^ := Value;

Ptr POINTER TO DWORD

Value DWORD

WRITE_PROTECTED_INT

Inputs

Source Code
FUNCTION WRITE_PROTECTED_INT
VAR_INPUT
    Ptr : POINTER TO INT;
    Value : INT;
END_VAR
Ptr^ := Value;

Ptr POINTER TO INT

Value INT

WRITE_PROTECTED_LREAL

Inputs

Source Code
FUNCTION WRITE_PROTECTED_LREAL
VAR_INPUT
    Ptr : POINTER TO LREAL;
    Value : LREAL;
END_VAR
Ptr^ := Value;

Ptr POINTER TO LREAL

Value LREAL

WRITE_PROTECTED_REAL

Inputs

Source Code
FUNCTION WRITE_PROTECTED_REAL
VAR_INPUT
    Ptr : POINTER TO REAL;
    Value : REAL;
END_VAR
Ptr^ := Value;

Ptr POINTER TO REAL

Value REAL

WRITE_PROTECTED_SINT

Inputs

Source Code
FUNCTION WRITE_PROTECTED_SINT
VAR_INPUT
    Ptr : POINTER TO SINT;
    Value : SINT;
END_VAR
Ptr^ := Value;

Ptr POINTER TO SINT

Value SINT

WRITE_PROTECTED_STRING

Inputs

Source Code
FUNCTION WRITE_PROTECTED_STRING
VAR_INPUT
    Ptr : POINTER TO STRING;
    Value : STRING;
END_VAR
Ptr^ := Value;

Ptr POINTER TO STRING

Value STRING

WRITE_PROTECTED_WSTRING

Inputs

Source Code
FUNCTION WRITE_PROTECTED_WSTRING
VAR_INPUT
    Ptr : POINTER TO WSTRING;
    Value : WSTRING;
END_VAR
Ptr^ := Value;

Ptr POINTER TO WSTRING

Value WSTRING

WRITE_PROTECTED_TIME

Inputs

Source Code
FUNCTION WRITE_PROTECTED_TIME
VAR_INPUT
    Ptr : POINTER TO TIME;
    Value : TIME;
END_VAR
Ptr^ := Value;

Ptr POINTER TO TIME

Value TIME

WRITE_PROTECTED_TIME_OF_DAY

Inputs

Source Code
FUNCTION WRITE_PROTECTED_TIME_OF_DAY
VAR_INPUT
    Ptr : POINTER TO TIME_OF_DAY;
    Value : TIME_OF_DAY;
END_VAR
Ptr^ := Value;

Ptr POINTER TO TIME_OF_DAY

Value TIME_OF_DAY

WRITE_PROTECTED_UDINT

Inputs

Source Code
FUNCTION WRITE_PROTECTED_UDINT
VAR_INPUT
    Ptr : POINTER TO UDINT;
    Value : UDINT;
END_VAR
Ptr^ := Value;

Ptr POINTER TO UDINT

Value UDINT

WRITE_PROTECTED_UINT

Inputs

Source Code
FUNCTION WRITE_PROTECTED_UINT
VAR_INPUT
    Ptr : POINTER TO UINT;
    Value : UINT;
END_VAR
Ptr^ := Value;

Ptr POINTER TO UINT

Value UINT

WRITE_PROTECTED_ULINT

Inputs

Source Code
FUNCTION WRITE_PROTECTED_ULINT
VAR_INPUT
    Ptr : POINTER TO ULINT;
    Value : ULINT;
END_VAR
Ptr^ := Value;

Ptr POINTER TO ULINT

Value ULINT

WRITE_PROTECTED_LWORD

Inputs

Source Code
FUNCTION WRITE_PROTECTED_LWORD
VAR_INPUT
    Ptr : POINTER TO LWORD;
    Value : LWORD;
END_VAR
Ptr^ := Value;

Ptr POINTER TO LWORD

Value LWORD

WRITE_PROTECTED_LINT

Inputs

Source Code
FUNCTION WRITE_PROTECTED_LINT
VAR_INPUT
    Ptr : POINTER TO LINT;
    Value : LINT;
END_VAR
Ptr^ := Value;

Ptr POINTER TO LINT

Value LINT

WRITE_PROTECTED_USINT

Inputs

Source Code
FUNCTION WRITE_PROTECTED_USINT
VAR_INPUT
    Ptr : POINTER TO USINT;
    Value : USINT;
END_VAR
Ptr^ := Value;

Ptr POINTER TO USINT

Value USINT

WRITE_PROTECTED_WORD

Inputs

Source Code
FUNCTION WRITE_PROTECTED_WORD
VAR_INPUT
    Ptr : POINTER TO WORD;
    Value : WORD;
END_VAR
Ptr^ := Value;

Ptr POINTER TO WORD

Value WORD

E_AssertionType

Source Code
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_AssertionType :
(
    Type_UNDEFINED := 0,
    Type_ANY,

    // Primitive types
    Type_BOOL,
    Type_BYTE,
    Type_DATE,
    Type_DATE_AND_TIME,
    Type_DINT,
    Type_DWORD,
    Type_INT,
    Type_LINT,
    Type_LREAL,
    Type_LTIME,
    Type_LWORD,
    Type_REAL,
    Type_SINT,
    Type_STRING,
    Type_TIME,
    Type_TIME_OF_DAY,
    Type_UDINT,
    Type_UINT,
    Type_ULINT,
    Type_USINT,
    Type_WORD,
    Type_WSTRING,

    // Array types
    Type_Array2D_LREAL,
    Type_Array2D_REAL,
    Type_Array3D_LREAL,
    Type_Array3D_REAL,
    Type_Array_BOOL,
    Type_Array_BYTE,
    Type_Array_DINT,
    Type_Array_DWORD,
    Type_Array_INT,
    Type_Array_LINT,
    Type_Array_LREAL,
    Type_Array_LWORD,
    Type_Array_REAL,
    Type_Array_SINT,
    Type_Array_UDINT,
    Type_Array_UINT,
    Type_Array_ULINT,
    Type_Array_USINT,
    Type_Array_WORD
) BYTE;
END_TYPE

E_XmlError

Source Code
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_XmlError :
(
    Ok := 0,
    ErrorMaxBufferLen := 1,
    ErrorStringLen := 2,
    Error := 3
) BYTE;
END_TYPE

ST_AdsLogStringMessage

Source Code
{attribute 'pack_mode' := '1'}
TYPE ST_AdsLogStringMessage :
STRUCT
	MsgCtrlMask : DWORD;
    MsgFmtStr : T_MaxString;
    StrArg : T_MaxString;
END_STRUCT
END_TYPE

MsgCtrlMask DWORD

MsgFmtStr T_MaxString

StrArg T_MaxString

ST_AssertResult

Source Code
TYPE ST_AssertResult :
STRUCT
    Expected : U_ExpectedOrActual;
    Actual : U_ExpectedOrActual;
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_STRUCT
END_TYPE

Expected U_ExpectedOrActual

Actual U_ExpectedOrActual

Message T_MaxString

TestInstancePath T_MaxString

ST_AssertResultInstances

Source Code
TYPE ST_AssertResultInstances :
STRUCT
    AssertResult : ST_AssertResult;
    DetectionCount : UINT; // Number of instances of the "AssertResult"
    DetectionCountThisCycle : UINT; // Number of instance of the "AssertResult" in this specific PLC-cycle
END_STRUCT
END_TYPE

AssertResult ST_AssertResult

DetectionCount UINT

Number of instances of the "AssertResult"

DetectionCountThisCycle UINT

Number of instance of the "AssertResult" in this specific PLC-cycle

ST_AssertArrayResult

Source Code
TYPE ST_AssertArrayResult :
STRUCT
    ExpectedsSize : UDINT; // Size in bytes of the expecteds-array
    ExpectedsTypeClass : IBaseLibrary.TypeClass; // The data type of the expecteds-array
    ActualsSize : UDINT; // Size in bytes of the actuals-array
    ActualsTypeClass : IBaseLibrary.TypeClass; // The data type of the actuals-array
    Message : T_MaxString;
    TestInstancePath : T_MaxString;
END_STRUCT
END_TYPE

ExpectedsSize UDINT

Size in bytes of the expecteds-array

ExpectedsTypeClass IBaseLibrary.TypeClass

The data type of the expecteds-array

ActualsSize UDINT

Size in bytes of the actuals-array

ActualsTypeClass IBaseLibrary.TypeClass

The data type of the actuals-array

Message T_MaxString

TestInstancePath T_MaxString

ST_AssertArrayResultInstances

Source Code
TYPE ST_AssertArrayResultInstances :
STRUCT
    AssertArrayResult : ST_AssertArrayResult;
    DetectionCount : UINT; // Number of instances of the "AssertArrayResult"
    DetectionCountThisCycle : UINT; // Number of instance of the "AssertArrayResult" in this specific PLC-cycle
END_STRUCT
END_TYPE

AssertArrayResult ST_AssertArrayResult

DetectionCount UINT

Number of instances of the "AssertArrayResult"

DetectionCountThisCycle UINT

Number of instance of the "AssertArrayResult" in this specific PLC-cycle

ST_TestCaseResult

Source Code
TYPE ST_TestCaseResult :
STRUCT
    TestName : T_MaxString;
    TestClassName : T_MaxString;
    TestIsFailed : BOOL;
    TestIsSkipped : BOOL;
    FailureMessage : T_MaxString;
    FailureType : E_AssertionType;
    NumberOfAsserts : UINT;
    Duration : LREAL; // in seconds
END_STRUCT
END_TYPE

TestName T_MaxString

TestClassName T_MaxString

TestIsFailed BOOL

TestIsSkipped BOOL

FailureMessage T_MaxString

FailureType E_AssertionType

NumberOfAsserts UINT

Duration LREAL

in seconds

ST_TestSuiteResult

Source Code
TYPE ST_TestSuiteResult :
STRUCT
    Name : T_MaxString; // Full class name
    Identity : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestSuites); // Should be 0..GVL_Param_TcUnit.MaxNumberOfTestSuites-1 but gives unknown compiler error
    NumberOfTests : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
    NumberOfFailedTests : UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);
    Duration : LREAL; // In seconds
    TestCaseResults : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite] OF ST_TestCaseResult;
END_STRUCT
END_TYPE

Name T_MaxString

Full class name

Identity UINT(0..GVL_Param_TcUnit.MaxNumberOfTestSuites)

Should be 0..GVL_Param_TcUnit.MaxNumberOfTestSuites-1 but gives unknown compiler error

NumberOfTests UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite)

NumberOfFailedTests UINT(0..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite)

Duration LREAL

In seconds

TestCaseResults ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite] OF ST_TestCaseResult

ST_TestSuiteResults

Source Code
TYPE ST_TestSuiteResults :
STRUCT
    // General test results
    NumberOfTestSuites : UINT; // The total number of test suites
    NumberOfTestCases : UINT; // The total number of test cases (for all test suites)
    NumberOfSuccessfulTestCases : UINT; // The total number of test cases that had all ASSERTS successful
    NumberOfFailedTestCases : UINT; // The total number of test cases that had at least one ASSERT failed
    Duration : LREAL; // Duration it took for all test suites to finish, in seconds

    // Test results for each individiual test suite
    TestSuiteResults : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestSuites] OF ST_TestSuiteResult;
END_STRUCT
END_TYPE

NumberOfTestSuites UINT

The total number of test suites

NumberOfTestCases UINT

The total number of test cases (for all test suites)

NumberOfSuccessfulTestCases UINT

The total number of test cases that had all ASSERTS successful

NumberOfFailedTestCases UINT

The total number of test cases that had at least one ASSERT failed

Duration LREAL

Duration it took for all test suites to finish, in seconds

TestSuiteResults ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestSuites] OF ST_TestSuiteResult

U_ExpectedOrActual

Source Code
TYPE U_ExpectedOrActual :
UNION
    boolExpectedOrActual : BOOL;
    bitExpectedOrActual : BIT;
    byteExpectedOrActual : BYTE;
    dwordExpectedOrActual : DWORD;
    lwordExpectedOrActual : LWORD;
    stringExpectedOrActual : T_MaxString;
    udintExpectedOrActual : UDINT;
    uintExpectedOrActual : UINT;
    usintExpectedOrActual : USINT;
    sintExpectedOrActual : SINT;
    intExpectedOrActual : INT;
    wordExpectedOrActual : WORD;
    dintExpectedOrActual : DINT;
    lintExpectedOrActual : LINT;
    ulintExpectedOrActual : ULINT;
    realExpectedOrActual : REAL;
    lrealExpectedOrActual : LREAL;
    wstringExpectedOrActual : WSTRING(255);
    timeExpectedOrActual : TIME;
    ltimeExpectedOrActual : LTIME;
    dateExpectedOrActual : DATE;
    dateandtimeExpectedOrActual : DATE_AND_TIME;
    timeofdayExpectedOrActual : TIME_OF_DAY;
END_UNION
END_TYPE

GVL_Param_TcUnit

Source Code
{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
    MaxNumberOfTestSuites : UINT := 1000;
    MaxNumberOfTestsForEachTestSuite : UINT := 100;
    MaxNumberOfAssertsForEachTestSuite : UINT := 1000;

    (* TcUnit logs complete test results. These include:
         - Number of test suites
         - Number of tests
         - Number of successful tests
         - Number of failed tests
         - Any eventual failed assertion (with the expected & actual value plus an user defined message)
       These are all printed to the ADS logger (Visual Studio error list) marked with ERROR criticality

       On top of this TcUnit also reports some statistics/extended information with HINT/INFO criticality.
       These statistics are more detailed results of the tests. This information is used when results are
       being collected by an external software (such as TcUnit-Runner) to do for example Jenkins integration.
       This extra information however takes time to print, so by setting the following parameter to FALSE
       it will speed up TcUnit finishing. *)
    LogExtendedResults : BOOL := TRUE;

    (* Enable (TRUE) or disable (FALSE) publishing of the xUnit Xml report *)
    xUnitEnablePublish : BOOL := FALSE;

    (* Default reserved PLC memory buffer used for composition of the xUnit xml file (64 kb default) *)
    xUnitBufferSize : UDINT := 65535;

    (* Default path and filename for the xunit testresults e.g.: for use with jenkins 

	   Use environment variables for a OS independent file path
	   - %TC_BOOTPRJPATH% -> 'C:\TwinCAT\3.1\Boot\' (Windows), '/usr/local/etc/TwinCAT/3.1/Boot/' (TC/BSD), '\Hard Disk\TwinCAT\3.1\Boot\' (Windows CE)
      - %TC_INSTALLPATH% -> 'C:\TwinCAT\3.1\' (Windows), '/usr/local/etc/TwinCAT/3.1/' (TC/BSD), '\Hard Disk\TwinCAT\3.1\' (Windows CE) *)
    xUnitFilePath : T_MaxString := '%TC_BOOTPRJPATH%tcunit_xunit_testresults.xml';

    (* This is the maximum number of ADS-messages that can be stored for reporting at the same time.
       Having a size of 2000 means that it's possible to report up to ~400 test cases in one single
       PLC cycle. Each entry consumes around 500 bytes, so with an example of a ring buffer size of
       2000 it means that TcUnit will consume around 1 MB of router memory. *) 
    AdsLogMessageFifoRingBufferSize : UINT := 2000;

    (* Time delay between a test suite is finished and the execution of the next test suite starts
       if using RUN_IN_SEQUENCE() *)
    TimeBetweenTestSuitesExecution : TIME := T#0MS;
END_VAR

MaxNumberOfTestSuites UINT

MaxNumberOfTestsForEachTestSuite UINT

MaxNumberOfAssertsForEachTestSuite UINT

LogExtendedResults BOOL

xUnitEnablePublish BOOL

xUnitBufferSize UDINT

xUnitFilePath T_MaxString

AdsLogMessageFifoRingBufferSize UINT

TimeBetweenTestSuitesExecution TIME

GVL_TcUnit

Source Code
{attribute 'no_assign'}
{attribute 'qualified_only'}
VAR_GLOBAL
    TcUnitRunner : FB_TcUnitRunner;

    (* Indication of whether the last instantiated test suite has an assert instance created *)
    TestSuiteIsRegistered : BOOL;

    (* Pointer to current test suite being called *)
    CurrentTestSuiteBeingCalled : POINTER TO FB_TestSuite;

    (* Current name of test being called *)
    CurrentTestNameBeingCalled : T_MaxString;

    (* Used to get the current cpu cycle counter to calculate the duration of tests and test suits, respectively *)
    GetCpuCounter : GETCPUCOUNTER;

    (* Stores the CPU cycle count with 100ns precision since the first entry into one of the Run methods (RUN, RUN_IN_SEQUENCE) *)
    StartedAt : LWORD;

    (* Duration it took for a complete run, in seconds *)
    Duration : LREAL;

    (* Whether or not the current test being called has finished running *)
    CurrentTestIsFinished : BOOL;

    (* This is a flag that indicates that the current test should be ignored, and
       thus that all assertions under it should be ignored as well. A test can be ignored either
       because the user has requested so, or because the test is a duplicate name *)
    IgnoreCurrentTest : BOOL;

    (* The assert function block instance should be 1:1 mapped to
       the test suite instance path. *)
    NumberOfInitializedTestSuites : UINT := 0;
    TestSuiteAddresses : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestSuites] OF POINTER TO FB_TestSuite;

    (* If the user is utilizing the TEST_ORDERED(), we need to keep track of which ordered test is currently running.
       We do this by defining an array, in where we can see which current TEST_ORDERED() is the one to be handled right now.
       The below array is only used for TEST_ORDERED()-tests.  *)
    CurrentlyRunningOrderedTestInTestSuite : ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestSuites] OF UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite);

    (* Buffered ADS message queue for output to the error list *)
    AdsMessageQueue : FB_AdsLogStringMessageFifoQueue;
END_VAR
VAR_GLOBAL CONSTANT
  (* Multiply a value that is given in 100ns units by this value to
     convert the value to seconds *)
  HundredNanosecondToSecond : LREAL := 1.0 / 10000000.0;
END_VAR

TcUnitRunner FB_TcUnitRunner

TestSuiteIsRegistered BOOL

CurrentTestSuiteBeingCalled POINTER TO FB_TestSuite

CurrentTestNameBeingCalled T_MaxString

GetCpuCounter GETCPUCOUNTER

StartedAt LWORD

Duration LREAL

CurrentTestIsFinished BOOL

IgnoreCurrentTest BOOL

NumberOfInitializedTestSuites UINT

TestSuiteAddresses ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestSuites] OF POINTER TO FB_TestSuite

CurrentlyRunningOrderedTestInTestSuite ARRAY[1..GVL_Param_TcUnit.MaxNumberOfTestSuites] OF UINT(1..GVL_Param_TcUnit.MaxNumberOfTestsForEachTestSuite)

AdsMessageQueue FB_AdsLogStringMessageFifoQueue

HundredNanosecondToSecond LREAL

Global_Version

Source Code
{attribute 'TcGenerated'}
{attribute 'no-analysis'}
{attribute 'linkalways'}
// This function has been automatically generated from the project information.
VAR_GLOBAL CONSTANT
	{attribute 'const_non_replaced'}
	stLibVersion_TcUnit : ST_LibVersion := (iMajor := 1, iMinor := 3, iBuild := 2, iRevision := 0, nFlags := 0, sVersion := '1.3.2');
END_VAR

stLibVersion_TcUnit ST_LibVersion