Contenido principal

Test Functions Containing Complex Data Types Using Python API for Polyspace

R2026b

You can author graphical tests for your C/C++ code base entirely at the command line by using the Python® API for Polyspace®. This example demonstrates how to write tests using the API for functions that take in the following datatypes as input:

  • Structures

  • Enumerations

  • Unions

  • Pointers

  • Classes

Before writing tests using the Python API, you can familiarize yourself with the testing structure using the Polyspace Platform user interface. For an introduction to writing tests in the user interface, see Write C/C++ Unit Tests in Polyspace Platform User Interface. For an overview of how to add tests for various data types in the user interface, see Specify Test Inputs in Polyspace Platform User Interface.

For more information on customizing tests for simple functions using the Python API, see Author Graphical Tests Using Python API for Polyspace.

Prerequisites

Make sure you are using a supported Python version and you are able to import the polyspace.project and polyspace.test Python modules without errors. For more information, see Set Up Python API for Polyspace. The examples below use source files that are available with a Polyspace installation.

Add Files and Parse Code

Import the modules polyspace.project and polyspace.test, create a project, and add the source files in the folders <polyspaceroot>\polyspace\examples\doc_pstest\complex_types\src and <polyspaceroot>\polyspace\examples\doc_pstest\cpp_tabular_tests\src. To allow for source files written in C and C++, set the build configuration language to "C_CPP". Then, use the polyspace.project.parseCode function to parse the source code and create a test suite for the project.

# Import the required modules.
import polyspace.project
import polyspace.test
import os

# Create project, add files, and set the build configuration language. 
proj = polyspace.project.Project("newProject")
c_examples_path = os.path.join(polyspace.__install_path__, "polyspace", "examples", "doc_pstest", "complex_types", "src")
cpp_examples_path = os.path.join(polyspace.__install_path__, "polyspace", "examples", "doc_pstest", "cpp_tabular_tests", "src")
proj.Code.Files.add(os.path.join(c_examples_path, "allTypes.c"))
proj.Code.Files.add(os.path.join(cpp_examples_path, "GridPosition.cpp"))
proj.IncludePaths.add(c_examples_path)
proj.IncludePaths.add(cpp_examples_path)

# Set the build configuration language to C/C++ and parse the source code.
proj.ActiveBuildConfiguration.Language = "C_CPP"
codeInfo = polyspace.project.parseCode(proj)

# Create a test suite for the project.
suite = proj.TestSuites.create("AllTypesTestSuite")

The polyspace.project.parseCode function returns an instance of the polyspace.project.CodeInfo class that contains information about functions, types, and globals in the source code. You can use the methods of this class to retrieve this information and use them to author your test cases.

Add Simple Test for Function with Enumeration Input

Create a test case and test step for the colorValue function. Assign the input and assessment values.

# Create a test case and test step for the colorValue function.
enumCase = suite.TestCases.create("ColorValueTest")
functionColorValue = codeInfo.getFunctionBySignature("int colorValue(MyEnum n)")
enumStep = enumCase.TestSteps.createTabular("ColorValueStep",functionColorValue)

# Set the input value of the enum to "RED" and set the assessment value to 0. 
enumStep.Inputs["n"].Value = "RED"
enumStep.Assessments["pst_call_out"].Value = "0"

Note

You can only assign strings to the Value property of an input, assessment, parameter, test data, or observable. Therefore, put quotes around the values you want to assign. For example:

  • To specify the integer 42, assign the string "42" to the Value property.

  • To specify the double 3.14, assign the string "3.14" to the Value property.

  • To specify the string "My String", assign the string '"My String"' to the Value property.

  • To specify the address of a variable var, assign the string "&amp;var" to the Value property.

When generating code for your tests, Polyspace Test™ uses the content of the string to reconstruct the value of the input, assessment, parameter, test data, or observable.

Add Simple Test for Function with Structure Input

Create a simple test for the negateStruct function which takes in a structure passed by value as input and returns a structure with negated member values. When attaching inputs and assessments to a test using the Polyspace Python API, we access fields by indexing into arrays, where the parameters and field names act as indices. Attach an input structure with fields x=1.0 and y=2.0 to the test by indexing into the Inputs property. Check the output against the assessment values x=-1.0 and y=-2.0.

# Create a test case and test step for the negateStruct function. 
structCase = suite.TestCases.create("NegateStructTest")
functionNegateStruct = codeInfo.getFunctionBySignature("MyStruct negateStruct(MyStruct s)")
structStep = structCase.TestSteps.createTabular("NegateStructStep",functionNegateStruct)

# Update the input value for the structure s to have values x=1.0 and y=2.0
structStep.Inputs["s"]["x"].Value = "1.0"
structStep.Inputs["s"]["y"].Value = "2.0"

# Update the assessment which is automatically created.
structStep.Assessments["pst_call_out"]["x"].Value = "-1.0"
structStep.Assessments["pst_call_out"]["y"].Value = "-2.0"

Add Simple Test for Function with Union Input

Unions are similar to structures, but all its members share a single memory location. When writing Polyspace tests for a function that takes in a union, you can only specify the value of one member of the union at a time. To set the value of one of the members of the input union MyUnion, index into the Inputs property.

# Create a test case and test step for the doubleVal function.
unionCase = suite.TestCases.create("DoubleValTest")
functionDoubleVal = codeInfo.getFunctionBySignature("int doubleVal(MyUnion u, int useFloat)")
unionStep = unionCase.TestSteps.createTabular("DoubleValStep",functionDoubleVal)

# Set the intValue input of the union u by indexing into the union. Set the useFloat parameter to 0.
unionStep.Inputs["u"]["intValue"].Value = "5"
unionStep.Inputs["useFloat"].Value = "0"

# Set the assessment value to be the expected return value. 
unionStep.Assessments["pst_call_out"].Value = "10"

Add Simple Test for Function with Pointer Input

Create a simple test for the moveCoord function which takes in a pointer to a structure. To add an input or assessment for the pointer c, you must first create a test data object. Set the values in your pointer target by indexing into the test data object.

# Create a test case and test step for the moveCoord function. 
pointerCase = suite.TestCases.create("MoveCoordTest")
functionMoveCoord = codeInfo.getFunctionBySignature("void moveCoord(MyStruct *c)")
pointerStep = pointerCase.TestSteps.createTabular("MoveCoordStep",functionMoveCoord)

# Create a test data object for the inputs and set the type to be an array of length 1 of the Coord structure.
inputData = pointerCase.TestData.create("moveInputData",codeInfo.getType("MyStruct[1]"))

# Assign the input values of the structure by indexing into the test data object. 
inputData[0]["x"].Value = "1.0"
inputData[0]["y"].Value = "2.0"

# Set the value of the pointer target c to the test data object. 
pointerStep.Inputs["c"].Value = inputData

# Create a test data object for the assessments and assign their values. 
expectedData = pointerCase.TestData.create("moveExpectedData",codeInfo.getType("MyStruct[1]"))
expectedData[0]["x"].Value = "1.5"
expectedData[0]["y"].Value = "2.3"
pointerStep.Assessments["c"].Value = expectedData

Add Simple Test for Class Member Function

In a simple test for a C++ member function, you can create a class instance using a constructor call and then invoke the member function. Create a test case and test step for the GridPosition::getX() member function. Set the input value of pst_obj to GridPosition(2,1). Assessments are automatically added for pst_obj and pst_call_out. Delete the assessment for pst_obj and set the expected value of the pst_call_out assessment to 2.

# Create a test case and test step for the member function GridPosition::getX().
classCase = suite.TestCases.create("getXTest")
functionGetX = codeInfo.getFunctionBySignature("int32_t GridPosition::getX()")
classStep = classCase.TestSteps.createTabular("getXStep", functionGetX)

# Update inputs and assessments. 
classStep.Inputs["pst_obj"].Value = "GridPosition(2,1)"
classStep.Assessments["pst_call_out"].Value = "2"
classStep.Assessments.pop("pst_obj")

Create Multistep Test for Class Member Functions

When testing a C++ class, you might have to invoke several member functions on the same object in a multistep test. To reuse the same object across multiple steps, create a test data object for your test case and refer to this test data object in each test step.

In this example you will write a multistep test that will do the following: Set the data members x and y to (1,1) using the member function GridPosition::setPosition(int32_t, int32_t). Shift both data members by 1 using the member function GridPosition::shiftPosition(int32_t, int32_t). Check that the new data member values correspond to (2,2) using the member functions GridPosition::getX() and GridPosition::getY().

Create a test case which will have several test steps and add a test data object to the test case. Then add the following test steps:

  1. Create a test step for the member function GridPosition::setPosition(int32_t, int32_t) and set the inputs of xPos and yPos to 1. Set the input value of pst_obj to the test data object and remove the automatically generated assessment for pst_obj.

  2. Create a test step for the member function GridPosition::shiftPosition(int32_t, int32_t) and set both the inputs xShift and yShift to 1. Set the input value of pst_obj to the test data object and remove the automatically generated assessment for pst_obj.

  3. Create a test step for the member function GridPosition::getX(). Set the input value of pst_obj to the test data object. Set the value of the pst_call_out assessment to 2 and remove the automatically generated assessment for pst_obj.

  4. Create a test step for the member function GridPosition::getY(). Set the input value of pst_obj to the test data object. Set the value of the pst_call_out assessment to 2 and remove the automatically generated assessment for pst_obj.

# Create a test case. Add a test data object to the test case and set the type to the GridPosition class.
classCase = suite.TestCases.create("MultistepTest")
testData = classCase.TestData.create("data_pst_obj", codeInfo.getType("GridPosition"))

# Create a test step for the member function GridPosition::setPosition(int32_t, int32_t).
functionSetPosition = codeInfo.getFunctionBySignature("GridPosition::setPosition(int32_t, int32_t)")
setPositionStep = classCase.TestSteps.createTabular("Step1", functionSetPosition)

# Set both the inputs xPos and yPos to 1. Set the input value of pst_obj to the test data object and remove the assessment for pst_obj.
setPositionStep.Inputs["xPos"].Value = "1"
setPositionStep.Inputs["yPos"].Value = "1"
setPositionStep.Inputs["pst_obj"].Value = testData
setPositionStep.Assessments.pop("pst_obj")

# Create a new test step for the member function GridPosition::shiftPosition(int32_t, int32_t).
functionShiftPosition = codeInfo.getFunctionBySignature("GridPosition::shiftPosition(int32_t, int32_t)")
shiftPositionStep = classCase.TestSteps.createTabular("Step2", functionShiftPosition)

# Set both the inputs xShift and yShift to 1. Set the value of the pst_obj input to the test data object and remove the assessment for pst_obj.
shiftPositionStep.Inputs["xShift"].Value = "1"
shiftPositionStep.Inputs["yShift"].Value = "1"
shiftPositionStep.Inputs["pst_obj"].Value = testData
shiftPositionStep.Assessments.pop("pst_obj")

# Create a new test step for the member function GridPosition::getX().
functionGetX = codeInfo.getFunctionBySignature("int32_t GridPosition::getX()")
getXStep = classCase.TestSteps.createTabular("Step3", functionGetX)

# Set the input value of pst_obj to the test data object. Set the value of the pst_call_out assessment to 2 and remove the assessment for pst_obj.
getXStep.Inputs["pst_obj"].Value = testData
getXStep.Assessments["pst_call_out"].Value = "2"
getXStep.Assessments.pop("pst_obj")

# Create a new test step for the member function GridPosition::getY().
functionGetY = codeInfo.getFunctionBySignature("int32_t GridPosition::getY()")
getYStep = classCase.TestSteps.createTabular("Step4", functionGetY)

# Set the input value of pst_obj to the test data object. Set the value of the pst_call_out assessment to 2 and remove the assessment for pst_obj.
getYStep.Inputs["pst_obj"].Value = testData
getYStep.Assessments["pst_call_out"].Value = "2"
getYStep.Assessments.pop("pst_obj")

Build and Run Tests

Run the tests added to the project.

res = polyspace.test.run(proj)

See Also

| | | | | | |

Topics