主要内容

Test Multi-Input C/C++ Functions Using Multiple Parameters and Parameter Combination Strategies

You can test a C/C++ function for several input values using the Polyspace® Test™ xUnit API. Instead of explicitly calling the function under test several times with different inputs, you can call the function once with a test parameter and draw the parameter values from a test data array (parameterized tests). See Write Parameterized Tests for C/C++ Functions Using Polyspace Test xUnit API. For multi-input functions, you can define a test parameter for each function input and specify one of these parameter combination strategies:

  • Exhaustive strategy: This strategy specifies that each value of a function input must be combined with each value of other inputs.

  • Sequential strategy: This strategy specifies that each value of a function input must be combined with the corresponding value of other inputs.

This example shows how to write a parameterized test with multiple parameters and use parameter combination strategies to avoid enumerating all parameter combinations.

Prerequisites

This topic describes test authoring using the Polyspace Test xUnit API. To compile these tests, 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

When to Define Single Test Parameter Versus Multiple Parameters

When authoring a parameterized test for a multi-input function, consider one of these approaches:

This example shows the latter approach to authoring parameterized tests for multi-input functions. You parameterize the function inputs separately, drawing parameter values from separate test data arrays. This approach is simpler than defining a single test parameter combining all inputs because:

  • You can extend the test data more easily. For example, if you add a value for one function input, you do not have to enumerate all combinations of this value with values of other inputs.

  • The tests are simpler to write. The test parameters can have fundamental data types and use the default display functions that come with the Polyspace Test xUnit API. For test parameters of structured types, you have to write a custom display function to display the parameter values.

However, you cannot always write a multi-parameter test for multi-input functions. For example, if you explicitly enumerate the expected values from a function in your tests, defining a single parameter with all inputs and the expected output is a better strategy. Multi-parameter tests are more useful when you do not need to explicitly enumerate the expected values, for example, when testing the equivalence of two functions.

How to Define Multiple Test Parameters

To author a parameterized test with multiple parameters:

  1. Define each parameter using the PST_PARAM_WITH_VALUES macro. The macro has this format:

    PST_PARAM_WITH_VALUES(parameterName, parameterType, displayFunction, parameterValues, numValues)
    Where:

    • parameterName is the name of the parameter.

    • parameterType is the data type of the parameter.

    • displayFunction is a display function that can customize the display of parameter values.

      If you use parameters with fundamental types such as int, enter a display function name of the form pst_format_param_typename, for example, pst_format_param_int. For aggregate types, define and use your own display function. To align with the display function name for fundamental types, name your custom display function using the format pst_format_param_typename.

    • parameterValues is an array that supplies values for the parameter.

    • numValues is the number of the values that the parameter can take.

  2. Add the parameters in the test configuration with the PST_ADD_PARAM macro. Specify a parameter combination approach, PST_PARAM_COMBINATION_SEQUENTIAL() or PST_PARAM_COMBINATION_EXHAUSTIVE().

    For example, for a simple test, the addition of two parameters with names param1 and param2 can look like this:

    PST_SIMPLE_TEST_CONFIG(NewTestCase) {
         PST_ADD_PARAM(param1);
         PST_ADD_PARAM(param2);
         PST_PARAM_COMBINATION_SEQUENTIAL();
    }
    The parameter combination approach determines how many parameter combinations are tested:

    • PST_PARAM_COMBINATION_EXHAUSTIVE(): Each value of param1 is paired with each value of param2 when testing (exhaustive testing).

    • PST_PARAM_COMBINATION_SEQUENTIAL(): Each value of param1 is paired with the corresponding value of param2 when testing (sequential testing). Note that in the sequential parameter combination strategy, the number of values of each parameter must be the same. Otherwise, you see this error when running the tests:

      Cannot execute the test with sequential parameter combination, 
      Different number of elements found between parameters.
      

    If you do not specify a parameter combination approach, an exhaustive parameter combination is used. However, for easier readability of the test code, it is recommended that you explicitly specify a parameter combination approach.

  3. When calling the assessment macros, instead of calling a variable, call a parameter with the dereference of the PST_PARAM_PTR pointer-like macro. For example:

    PST_VERIFY_LT_INT(func(*PST_PARAM_PTR(param1), *PST_PARAM_PTR(param2)),0);

Example

Example Files

Find the files for this tutorial in the folder polyspaceroot\polyspace\examples\doc_pstest\multi_parameter_tests. 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 functions times_n_multi and times_n_add both return the product of their two inputs, which is subject to a saturation at UINT_MAX.

#include <limits.h> 

unsigned times_n_multi (unsigned input, unsigned n) {
    if(n > 10)
        return -1; //Return error for large multipliers
    if(UINT_MAX/n < input)
        return UINT_MAX;
    else
        return input * n;
}

unsigned times_n_add (unsigned input, unsigned n) {
    int i, sum = 0;
    if(n > 10)
        return -1; //Return error for large multipliers
    for (i = 1; i <= n; i++) {
        if(input > UINT_MAX - input) {
            sum = UINT_MAX;
            break;
        }
        else {
            sum += input;
        }
    }
    return sum;
}

Save this function in a file example.c.

Suppose you have to test that the functions times_n_multi and times_n_add are indeed functionally equivalent, that is, they return the same value for the same inputs. You have to perform this test for these values of the function inputs:

  • input must be one of {(UINT_MAX - 1), (UINT_MAX/2 - 1), (UINT_MAX/3 - 1), (UINT_MAX/4 - 1)}.

  • n must be one of {1, 2, 3, 4}

You can combine corresponding values of input and n (sequential testing) or combine each value of input with all values of n (exhaustive testing).

Write Test

This test defines two test parameters for the two function inputs, input and n, and combines corresponding values of input and n when testing. In other words, the testing follows a sequential parameter combination approach:

#include <pstunit.h>
#include <limits.h>

unsigned times_n_multi (unsigned input, unsigned n);
unsigned times_n_add (unsigned input, unsigned n);

int input_data_sequential[] = {(UINT_MAX - 1),  (UINT_MAX/2 - 1), 
   (UINT_MAX/3 - 1), (UINT_MAX/4 - 1)};
int n_data_sequential[] = {1, 2, 3, 4};

PST_PARAM_WITH_VALUES(input_param, unsigned, pst_format_param_uint, input_data_sequential, 4);
PST_PARAM_WITH_VALUES(n_param, unsigned, pst_format_param_uint, n_data_sequential, 4);

PST_SIMPLE_TEST_CONFIG(test_multi_add_equivalence) {
    PST_ADD_PARAM(input_param);
    PST_ADD_PARAM(n_param);
    PST_PARAM_COMBINATION_SEQUENTIAL();
}

PST_SIMPLE_TEST_BODY(test_multi_add_equivalence) {
    PST_VERIFY_EQ_UINT(
              times_n_multi(*PST_PARAM_PTR(input_param),*PST_PARAM_PTR(n_param))
              ,
              times_n_add(*PST_PARAM_PTR(input_param),*PST_PARAM_PTR(n_param))
              );
}

PST_REGFCN(myRegFcn) {
    PST_ADD_SIMPLE_TEST(test_multi_add_equivalence);
}

#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_sequential.c.

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

  • PST_PARAM_WITH_VALUES – This macro defines two test parameters, input_param and n_param. Both parameters have data type unsigned, use display functions pst_format_param_uint that come with the Polyspace Test xUnit API and draw four values from the previously defined test data arrays.

  • PST_SIMPLE_TEST_CONFIG: This macro defines the test configuration for the test case test_multi_add_equivalence. The test configuration involves these macros:

    • PST_ADD_PARAM: This macro adds the previously defined test parameters input_param and n_param to the test configuration for test_multi_add_equivalence.

    • PST_PARAM_COMBINATION_SEQUENTIAL: This macro ensures that you are combining previously added parameters using a sequential approach. The macro matches each value of the parameter input_param with the corresponding value of the parameter n_param (in this example, both input_param and n_param take four values).

  • PST_SIMPLE_TEST_BODY: This macro defines the test body for test_multi_add_equivalence and contains calls to functions under test. The test body involves these macros:

    • PST_ASSERT_EQ_UINT: This macro checks if the first argument is equal to the second. The first argument is the return value of the function, times_n_multi, with input values from the test data arrays. The second argument is the return value of the function times_n_add when called with the same input values as times_n_multi.

    • PST_PARAM_PTR: This macro allows you to iterate through all values of a parameter, input_param or n_param.

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_sequential.c along with files shipped with Polyspace Test, as usual:

gcc example.c test_sequential.c <PSTUNIT_SOURCE> -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®).

Since the test data contains four values, you see the results of four tests. Since the functions under test are functionally equivalent, you see that all tests pass.

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

Further Exploration: Exhaustive Parameter Combination

Instead of combining corresponding values of test parameters input_param and n_param, you can follow an exhaustive parameter combination approach by combining each value of input_param with all values of n_param.

In this example, replace PST_PARAM_COMBINATION_SEQUENTIAL() with PST_PARAM_COMBINATION_EXHAUSTIVE(). When you run the test, you execute 16 test cases. The macro combines each value of input_param with all four values of n_param.

See Also

Topics