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:
Define a single parameter of structured type, where each field represents a function input and the final field represents the expected output.
See Write Parameterized Tests for C/C++ Functions Using Polyspace Test xUnit API.
Define separate parameters for each function input and specify a parameter combination strategy.
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:
Define each parameter using the
PST_PARAM_WITH_VALUESmacro. The macro has this format:Where:PST_PARAM_WITH_VALUES(parameterName,parameterType,displayFunction,parameterValues,numValues)is the name of the parameter.parameterNameis the data type of the parameter.parameterTypeis a display function that can customize the display of parameter values.displayFunctionIf you use parameters with fundamental types such as
int, enter a display function name of the formpst_format_param_, for example,typenamepst_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 formatpst_format_param_.typenameis an array that supplies values for the parameter.parameterValuesis the number of the values that the parameter can take.numValues
Add the parameters in the test configuration with the
PST_ADD_PARAMmacro. Specify a parameter combination approach,PST_PARAM_COMBINATION_SEQUENTIAL()orPST_PARAM_COMBINATION_EXHAUSTIVE().For example, for a simple test, the addition of two parameters with names
andparam1can look like this:param2The parameter combination approach determines how many parameter combinations are tested:PST_SIMPLE_TEST_CONFIG(NewTestCase) { PST_ADD_PARAM(param1); PST_ADD_PARAM(param2); PST_PARAM_COMBINATION_SEQUENTIAL(); }PST_PARAM_COMBINATION_EXHAUSTIVE(): Each value ofis paired with each value ofparam1when testing (exhaustive testing).param2PST_PARAM_COMBINATION_SEQUENTIAL(): Each value ofis paired with the corresponding value ofparam1when 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:param2Cannot 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.
When calling the assessment macros, instead of calling a variable, call a parameter with the dereference of the
PST_PARAM_PTRpointer-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 . Copy these files to a writable location and continue the tutorial. Here, polyspaceroot\polyspace\examples\doc_pstest\multi_parameter_tests is the Polyspace installation folder, for example, polyspacerootC:\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:
inputmust be one of{(UINT_MAX - 1), (UINT_MAX/2 - 1), (UINT_MAX/3 - 1), (UINT_MAX/4 - 1)}.nmust 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);
}
#endifSave 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_paramandn_param. Both parameters have data typeunsigned, use display functionspst_format_param_uintthat 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 casetest_multi_add_equivalence. The test configuration involves these macros:PST_ADD_PARAM: This macro adds the previously defined test parametersinput_paramandn_paramto the test configuration fortest_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 parameterinput_paramwith the corresponding value of the parametern_param(in this example, bothinput_paramandn_paramtake four values).
PST_SIMPLE_TEST_BODY: This macro defines the test body fortest_multi_add_equivalenceand 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 functiontimes_n_addwhen called with the same input values astimes_n_multi.PST_PARAM_PTR: This macro allows you to iterate through all values of a parameter,input_paramorn_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
<PSTUNIT_SOURCE> and <PSTUNIT_INCLUDE>, see Set Up C/C++ Testing and Code Profiling Using Self-Managed Builds.Run the test executable:
testrunner.exe
./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.