FB_AssertResultStatic  Function Block

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)


Variables

Name Type Default Description
AssertResults ARRAY[1..GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite] OF ST_AssertResult
TotalAsserts UINT 0
GetCurrentTaskIndex GETCURTASKINDEX
AssertResultInstances ARRAY[1..GVL_Param_TcUnit.MaxNumberOfAssertsForEachTestSuite] OF ST_AssertResultInstances
CycleCount UDINT
FirstCycleExecuted BOOL

Methods

AddAssertResult PRIVATE

Parameters

Name Type Description
ExpectedSize UDINT
ExpectedTypeClass IBaseLibrary.TypeClass
ExpectedValue POINTER TO BYTE
ActualSize UDINT
ActualTypeClass IBaseLibrary.TypeClass
ActualValue POINTER TO BYTE
Message T_MaxString
TestInstancePath T_MaxString
Implementation
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
CopyDetectionCountAndResetDetectionCountInThisCycle PRIVATE
Implementation
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

Parameters

Name Type Description
ExpectedSize UDINT
ExpectedTypeClass IBaseLibrary.TypeClass
ExpectedValue POINTER TO BYTE
ActualSize UDINT
ActualTypeClass IBaseLibrary.TypeClass
ActualValue POINTER TO BYTE
Message T_MaxString
TestInstancePath T_MaxString
Implementation
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
GetDetectionCount PRIVATE

Parameters

Name Type Description
ExpectedSize UDINT
ExpectedTypeClass IBaseLibrary.TypeClass
ExpectedValue POINTER TO BYTE
ActualSize UDINT
ActualTypeClass IBaseLibrary.TypeClass
ActualValue POINTER TO BYTE
Message T_MaxString
TestInstancePath T_MaxString
Implementation
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
GetDetectionCountThisCycle PRIVATE

Parameters

Name Type Description
ExpectedSize UDINT
ExpectedTypeClass IBaseLibrary.TypeClass
ExpectedValue POINTER TO BYTE
ActualSize UDINT
ActualTypeClass IBaseLibrary.TypeClass
ActualValue POINTER TO BYTE
Message T_MaxString
TestInstancePath T_MaxString
Implementation
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
GetNumberOfAssertsForTest INTERNAL

Parameters

Name Type Description
CompleteTestInstancePath T_MaxString
Implementation
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;
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.

Parameters

Name Type Description
ExpectedSize UDINT
ExpectedTypeClass IBaseLibrary.TypeClass
ExpectedValue POINTER TO BYTE
ActualSize UDINT
ActualTypeClass IBaseLibrary.TypeClass
ActualValue POINTER TO BYTE
Message T_MaxString
TestInstancePath T_MaxString

Outputs

Name Type Default Description
AlreadyReported BOOL FALSE
Implementation
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;

Used by

Declaration source
(*
    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