주요 콘텐츠

Compare and Display Values for Complex C/C++ Data Types Using Polyspace Test xUnit API

The Polyspace® Test™ xUnit API provides predefined assessment macros to compare and display values for all fundamental data types. For example, to check for equality of two integers, you can use the macro PST_VERIFY_EQ_INT. For the full list of predefined macros, see Assessment Macros in Polyspace Test API for C/C++ Code.

For more complex data types, you have to write your own comparison and display functions to compare and display values. You can then provide these function names to a Polyspace Test xUnit API macro that supports custom functions and write tests for complex data types in a manner similar to fundamental data types.

This example shows how to define your own assessment macros for complex data types.

Prerequisites

To compile tests authored using the Polyspace Test xUnit API, you are required to know some file paths in advance. For your convenience, you can define environment variables to stand for the file paths, or otherwise include the file paths in your build. For more information, see Set Up C/C++ Testing and Code Profiling Using Self-Managed Builds.

Workflow

How to Define Assessments with Custom Comparison and Display Functions

To compare two integers, actualValue and expectedValue, you perform an assessment such as:

PST_VERIFY_EQ_INT(actualValue, expectedValue)
The assessment compares the two integers and displays the value of actualValue if the comparison fails.

Suppose that your code contains a structured data type such as:

typedef struct Point {
    int x;
    int y; 
} Point;
To perform assessments with variables of type Point:

  1. Write your own comparison function, funcComp, to check for equality of two Point objects, and your own display function, funcDisp, to display their values.

  2. Provide these function names to the PST_VERIFY_EQ_CUSTOM macro and define your own assessment macro MY_VERIFY_EQ_Point that works exclusively on variables of type Point:

    #define MY_VERIFY_EQ_Point(actualValue, expectedValue) \
        PST_VERIFY_EQ_CUSTOM(actualValue, expectedValue, funcComp, funcDisp);

    Defining a new macro such as MY_VERIFY_EQ_Point is optional. You could have directly invoked the macro PST_VERIFY_EQ_CUSTOM for comparing two Point objects. Defining a new macro saves you from having to specify the comparison and display functions each time you want to perform assessments on Point objects.

Just like PST_VERIFY_EQ_INT can compare two integers for equality, you can now use the assessment macro MY_VERIFY_EQ_Point to compare equality of two Point objects.

Format of Comparison and Display Functions

The comparison and display functions must follow a strict format:

  • A function funcComp that compares two objects must have this signature:

    int funcComp (const void *objLeft, const void *objRight);
    The function must behave like the strcmp function in C/C++ and return 0 if the comparison succeeds.

  • A function funcDisp that displays an object value must have this signature:

    void funcDisp (char *buffer, const pst_size_t bufferSize, const void* objToDisplay);
    See also Custom Test Result Display Functions for Complex C/C++ Data Types.

Since both functions take const void* pointers to accept the objects to compare or display, before using the pointers, cast them back to the correct object pointer type. The Polyspace Test xUnit API provides the macro PST_STATIC_CAST to cast from one type to another. For example, in a comparison function that takes two objects of type Point, you can convert the function parameters from const void* to Point*:

int funcComp (const void* lhs, const void* rhs) {
    const Point* lhs_Point = PST_STATIC_CAST(const Point*, lhs);
    const Point* rhs_Point = PST_STATIC_CAST(const Point*, rhs);
    ...
}

Example

Example Files

Copy code from steps 1 and 2 into .c files.

Alternatively, find the files for this tutorial in the folder polyspaceroot\polyspace\examples\doc_pstest\complex_assessments. Copy these files to a writable location and continue the tutorial. Here, polyspaceroot is the Polyspace installation folder, for example, C:\Program Files\Polyspace\R2026a.

Inspect Function Under Test and Requirements

The function setOrigin initializes the value of a global structure origin of type Point, and the function shiftOrigin modifies this structure.

#include "example.h"

Point origin;

void setOrigin(void) {
    origin.x = 0;
    origin.y = 0;
}

void shiftOrigin(int shift) {
    origin.x = shift;
}
Save these functions in a file example.c.

A header file, example.h, defines the structured data type.

typedef struct Point {
    int x;
    int y; 
}Point;

extern Point origin;

Suppose you have to test that the two functions when executed one after another result in the correct value of the global structure. You can write a custom comparison function to compare two Point objects in an assessment and a custom display function to display their values if the assessment fails.

Write Test

The following test defines a custom comparison function, verifyXCoord, to compare two Point objects and a custom display function, displayPoint, to display their values if the assessment fails:

  • The comparison function verifyXCoord compares only the field x of two Point objects. If the comparison succeeds, the function returns 0.

  • The display function displayPoint displays the value of a Point object in a specific format. For example, if the object value is {0 , 0}, you see:

    x:0, y:0

The macro MY_VERIFY_EQ_Point compares two objects by using this custom comparison and display function. Users of this macro do not need to look up the appropriate comparison and display functions, and can simply provide the objects to compare.

#include <pstunit.h>
#include "example.h"

/* Function returns 0 if x-coordinate of lhs is equal to x-coordinate of rhs*/
int verifyXCoord (const void* lhs, const void* rhs) {
    const Point* lhs_Point = PST_STATIC_CAST(const Point*, lhs);
    const Point* rhs_Point = PST_STATIC_CAST(const Point*, rhs);
    return !(lhs_Point->x == rhs_Point->x);
}

void displayPoint(char* buff, const pst_size_t buff_size, const void* val) {
    const Point* val_Point = PST_STATIC_CAST(const Point*, val);
    pst_format(buff, buff_size, "x:%d, y:%d", val_Point->x, val_Point->y);
}

#define MY_VERIFY_EQ_Point(lhs, rhs) \
    PST_VERIFY_EQ_CUSTOM(lhs, rhs, verifyXCoord, displayPoint);
    

/* Tested functions */

Point setOrigin(void);
Point shiftOrigin(int);

PST_SIMPLE_TEST_CONFIG(test_origin_xShift) {
   
}

PST_SIMPLE_TEST_BODY(test_origin_xShift) {
    int xShift;
    
    xShift = 2;
    Point expectedValue = {2,0};
    Point expectedValueIncorrect = {3,0};
    
    setOrigin();
    shiftOrigin(xShift);

    /* Assessments. */
    PST_ASSESSMENT_ID("1");
    MY_VERIFY_EQ_Point(origin, expectedValue);
    
    PST_ASSESSMENT_ID("2");
    MY_VERIFY_EQ_Point(origin, expectedValueIncorrect);

}

PST_REGFCN(myRegFcn) {
    PST_ADD_SIMPLE_TEST(test_origin_xShift);
}

#ifndef PSTEST_BUILD
int main(int argc, char *argv[]) {
    PST_REGFCN_CALL(myRegFcn);
    return PST_MAIN(argc, argv);
}
#endif

Save this code in a file test.c.

The test consists of these macros from the Polyspace Test xUnit API:

  • PST_SIMPLE_TEST_CONFIG – This macro defines the test configuration for the test case test_origin_xShift.

  • PST_SIMPLE_TEST_BODY – This macro defines the test body for test_origin_xShift and contains the call to the function under test.

  • PST_VERIFY_EQ_Custom – This macro checks for the equality of two objects using a custom comparison function. If the comparison fails, a custom display function is used to display the object values.

The remaining macros, PST_REGFCN, PST_ADD_SIMPLE_TEST and PST_REGFCN_CALL, register the test.

Execute Test and Inspect Results

Compile the files example.c and test.c along with files that ship with Polyspace Test. Here, the file example.h must be in the same folder as the source example.c.

gcc example.c test.c <PSTUNIT_SOURCE> -I . -I <PSTUNIT_INCLUDE> -o testrunner
For more information on the file paths <PSTUNIT_SOURCE> and <PSTUNIT_INCLUDE>, see Set Up C/C++ Testing and Code Profiling Using Self-Managed Builds.

Run the test executable:

testrunner.exe
(or ./testrunner in Linux®).

The test contains two uses of the PST_VERIFY_EQ_Point to perform two comparisons. The first comparison succeeds and the second fails, leading to an overall test failure. The Polyspace Test xUnit API reports the failing values in a custom display format.

| Running tests   |            |
|-----------------|------------|
| Running         | suite      | pst_default_suite
|-----------------|------------|
| Running         | test       | test_origin_xShift
|     VERIFY FAIL |            |
custom_assessment.c:51: Verify failed: '2'
Expression 'verifyXCoord(&(origin), &(expectedValueIncorrect)) == 0' evaluated to false
lhs="x:2, y:0" rhs="x:3, y:0"
|            FAIL | test       | test_origin_xShift
|-----------------|------------|
|            FAIL | suite      | pst_default_suite

|        |  Total | Passed | Failed | Incomplete
|--------|--------|--------|--------|------------
| Suites |      1 |      0 |      1 |          0
|  Tests |      1 |      0 |      1 |          0