Conduct Unit Testing on Imported Custom Code by Using the Wizard
This example shows how to use the Code Importer wizard to import custom C code for a heat pump controller into Simulink for unit testing. Unit tests test one or more functions in isolation from the custom code library. For unit tests, the Simulink Test Code Importer wizard generates a test sandbox and a library containing a C Caller block for each specified function from the custom code. For more information see Importing and Testing Custom C/C++ Code.
This example uses only tempController.c
file to create and import a test sandbox into Simulink. You use the sandbox for performing unit testing only on the heatpumpController
function in the tempController.c
file and not on the complete code. Generating the sandbox automatically creates stubs for the utility functions used by the heatpumpController
function, absoluteTempDifference
and pumpDirection
. Since the utility functions are not defined in the tempController.c
file, and the utils.c
and utils.h
files are not included, the code importer creates stubs so the code does not error.
Heat Pump Controller Custom Code
The complete code for the heat pump controller is in these C code source and header files:
The source files are in the src
directory:
tempController.c
utils.c
The header files are in the include
directory:
tempController.h
utils.h
controllerTypes.h
The tempController.c
file contains the algorithm for the custom C code for a heat pump unit. The heatpumpController
function in that file uses the room temperature (Troom_in
) and the set temperature (Tset
) as inputs. The output is the pump_control_bus
type structure with signals that control the fan, heat pump, and the direction of the heat pump (heat or cool). The fields in the pump_control_bus
structure are fan_cmd
, pump_cmd
, and pump_dir
. The pump_control_bus
structure type is defined in the controllerTypes.h
file. The output of the heatpumpController
function is:
The output of the heatpumpController
algorithm is summarized in the following table:
The heatpumpController
function uses two utility functions, absoluteTempDifference
and pumpDirection
, which are defined in the utils.c
file. The absoluteTempDifference
function returns the absolute difference between Tset
and Troom_in
as a double. The pumpDirection
function returns one of these PumpDirection
type enum values:
The PumpDirection
enum type
is defined in the controllerTypes.h
file.
Open the Code Importer Wizard
To open the Simulink Test C/C++ Code Importer wizard, first open the Simulink Test Manager.
sltest.testmanager.view
Then, select New > Test for C/C++ code.
Specify the Simulink Library and Testing Method
The wizard opens and displays the Welcome tab. Click Start to begin the import process.
On the Settings tab:
Enter the Simulink library file name. The generated Simulink library, test sandbox directory, and test file (MLDATX) use this name. Enter
heatpumpController
.Specify the Output folder in which to save the generated artifacts. Enter the name of a writable folder.
Select C Code Unit Testing as the testing method.
The C Code Unit Testing method tests one C file or a subset of your custom code in isolation. This method creates a test sandbox from the specified source file or files, and imports the test sandbox into Simulink. Because the test sandbox only contains a subset of the C files, the Code Importer wizard automatically creates stubs for all of the undefined symbols used in the C files. This method supports testing C code only. For information on Integration Testing, see the TestType
property of sltest.CodeImporter
.
Click Next.
Specify the Custom Code to Import
On the Specify Custom Code tab:
In Source files, specify the source file that contains the function to import for unit testing. Enter
.\src\tempController.c
.In Include directories, specify the directories on which the specified source files depend. Enter
.\include
.Defines specifies the compiler-specific defines. For this example, leave this field blank.
Click Next.
Specify the Test Sandbox Settings
On the Analyze tab, specify the sandbox settings.
Select the output test sandbox mode
Each of the three test sandbox modes has settings that determine how the Code Importer wizard generates the sandbox and the sandbox artifacts.
Select Generate aggregated header. This test sandbox mode generates a minimal aggregated header file for the specified source file. The aggregated header file contains all the declarations of the symbols used by the specified source file.
Select the output test sandbox settings
Select only Copy source files. For this option, the Code Importer wizard copies the specified source file to the sandbox src
directory.
For information on the other output test sandbox modes and settings, see the properties of sltest.CodeImporter.SandboxSettings
.
Click Next to create the test sandbox with the specified settings.
Create the Test Sandbox
The specified library file name determines the name of the generated sandbox. For this example, the sandbox name is heatpumpController_sandbox
.
This example uses tempController.c
and performs unit testing only on the heatpumpController
function. Generating the sandbox automatically creates stubs for the absoluteTempDifference
and pumpDirection
utility functions used by the heatpumpController
function, Since the utility functions are not defined in the tempController.c
file, the stubs prevent the code from erroring.
When the sandbox is created, the confirmation screen displays.
The output sandbox folder contains the following subfolders:
src: This folder contains the copied source file or files, which for this example is
tempController.c
.include
: Depending on the test sandbox mode, this folder contains either theaggregatedHeader.h
orinterfaceHeader.h
file. This example uses theaggregatedHeader.h
file. The folder also contains and copies of the other header files required for the compilation of the specified source file or files. TheaggregatedHeader.h
file contains all the declarations of the symbols used by the specified source file. TheinterfaceHeader.h
contains the declarations for functions, global variables, and types used by the specified source files. Simulink uses the generated interface header file during the import process.autostub
: This folder contains theauto_stub.c
andauto_stub.h
files, which hold the automatically generated stubs forabsoluteTempDifference
andpumpDirection
utility functions.
manualstub
: This folder contains theman_stub.c
andman_stub.h
files, which define any manually specified stubs. By default, these files do not define any functions.
NOTE: Do not modify the files in the src
, include
, or autosub
folders of the generated sandbox.
After the sandbox has been created, you can view the created files:
Click Auto stub source and Auto stub header to open the
auto_stub.c
and auto_stub.h files, respectively. Inauto_stub.c
, you can find the stubs for the utility functions used byheatpumpController
,absoluteTempDifference
andpumpDirection
. Inauto_stub.h
, you can find theextern
declarations of the stubbed functions.Click Manual stub header and Manual stub source to open the
man_stub.h
andman_stub.c
files, respectively. Because you selected Generate aggregated header for the sandbox mode, sandbox,man_stub.h
includes theaggregatedHeader.h
file. By default, the manual stub files do not have any function definitions.
Click Next.
Specify Import Settings
Specify Using Global Variables as Function Interfaces
When the wizard detects global variables in the sandbox or custom code, you have the option to use those variables as input or outputs for the function and ports on the created C Caller block. For more information, see Automatically infer global variables as function interfaces.
Select Enable global variables as function interface option.
Click Next.
Select Functions to Import
On the Import page, select the heatpumpController
function to import into the Simulink library.
Click Next.
Set the Block Port Specifications
For the function you selected on the previous page, the wizard generates a function port specification. The selected ports are used for the generated C Caller block. Note that If the code to import had a function with a pointer output, you would need to specify the size of the output port on this page of the wizard.
In this example, the port specification table lists the formal arguments, Tset
, Troom_in
, and out
, for the heatpumpController
function, and six more from the automatically generated stubs. Because you selected Enable global variables as function interface, the wizard creates ports that are generated from the stubs for the absoluteTempDifference
and pumpDirection
functions. The stubs are in auto_stub.c
.
These are the automatically generated global variables in the auto_stub.c
file:
/*************************************************************************/ /* Generated Global Variables for Stubbed Functions Interface */ /*************************************************************************/ double SLStubIn_absoluteTempDifference_p1; double SLStubIn_absoluteTempDifference_p2; double SLStubOut_absoluteTempDifference; double SLStubIn_pumpDirection_p1; double SLStubIn_pumpDirection_p2; PumpDirection SLStubOut_pumpDirection; double absoluteTempDifference(double absoluteTempDifference_p1, double absoluteTempDifference_p2) { SLStubIn_absoluteTempDifference_p1 = absoluteTempDifference_p1; SLStubIn_absoluteTempDifference_p2 = absoluteTempDifference_p2; return SLStubOut_absoluteTempDifference; } PumpDirection pumpDirection(double pumpDirection_p1, double pumpDirection_p2) { SLStubIn_pumpDirection_p1 = pumpDirection_p1; SLStubIn_pumpDirection_p2 = pumpDirection_p2; return SLStubOut_pumpDirection; }
In the automatically generated stubs for absoluteTempDifference
, the global variables SLStubIn_absoluteTempDifference_p1
and SLStubIn_absoluteTempDifference_p2
save the input arguments. The function returns the value stored in SLStubOut_absoluteTempDifference
as its output. Similarly, pumpDirection
saves the input arguments and returns SLStubOut_pumpDirection
.
In this example, absoluteTempDifference
, SLStubIn_absoluteTempDifference_p1
and SLStubIn_absoluteTempDifference_p2
are outputs. The global variable SLStubOut_absoluteTempDifference
is an input.
Do not make any changes to the port specification. Click Next.
Specify Types to Import
Select the types to import into Simulink. Because the pump_control_bus
and PumpDirection
types are required by the heatpumpController function, they are selected and dimmed. The wizard creates a Simulink data dictionary containing these types and links the dictionary to the generated library.
Click Next to display a summary of the generated library. Click Next again to continue.
Create a Test Harness
Select Automatically create test harness for all imported functions.
Click Next to generate the Simulink library.
After the code imports, the wizard creates a library attached to a Simulink data dictionary that defines pump_control_bus
and PumpDirection
as a Simulink.Bus
object and a Simulink enumeration signal, respectively.
The C Caller block created in the Simulink library is:
Click in the lower right corner of the block to open its internal test harness:
On the Code Import Successful page, click Change the MATLAB folder to the output folder.
Do not click Finish. Continue to the following section to manually update the stubs for undefined symbols.
Update Test Sandbox with Manual Stubbing
To manually create the stub files for absoluteTempDifference
and pumpDirection
, click the Analyze tab in the wizard toolbar and then click Next twice to display this page:
The Code Importer detects the sandbox you created. Select Overwrite and then, click Next.
Outside of the wizard, but leaving the wizard open, copy the files in the updated_manualstub
directory and paste them into the manualstub
directory of the sandbox.
In the man_stub.c
file, edit the function definition of absoluteTempDifference
:
double absoluteTempDifference(double Tset, double Troom_in){ return (double)fabs(Tset - Troom_in); }
and the function definition of pumpDirection
:
PumpDirection pumpDirection(double Tset, double Troom_in){ return Tset > Troom_in ? HEATING : COOLING; }
Return to the Code Importer wizard.
After modifying the manual stub files, return to the wizard and click Update Sandbox.
Because you manually defined the definitions for the absoluteTempDifference
and pumpDirection
functions, there are no symbols to automatically stub, no artifacts are generated for the autostub
directory in the sandbox and it is empty.
Click Next until you reach the block port specification page:
Because the manual implementation of absoluteTempDifference
and pumpDirection
does not have any global variables, only the formal arguments and the return argument appear as ports.
Click Next.
On the Create Simulink Library tab, select Overwrite.
Click Next through the remaining pages until you reach the Code Import Successful page.
Then click Finish. If desired, click Yes in the dialog box that opens to save the current import settings as a JSON file, which you use to reload the settings into another Code Importer wizard session.
Test the Imported Code
In the Test Manager, in the Test Browser pane expand the heatpumpController test file and heatpumpController/heatpumpController test suite. Then select the heatpumpController_Harness1 test case.
Specify Simulation Stop Time
In the System Under Test section, expand the Simulation Setting and Release Overrides section and set the Stop Time to 200.
Add Inputs
In the Inputs section, at the bottom of the External Inputs table, click Add to open the Add Input dialog box.
In the Input File Specification, enter heatpumpControllerHarnessInput.xlsx
.
Under Input Mapping, select Block Name
as the Mapping Mode and click Map Inputs. After the inputs appear in the Mapping Status table, click OK.
Add Assessments
In the Logical and Temporal Assessments section, add assessments for each temperature condition:
For information on assessments, see Assess Temporal Logic by Using Temporal Assessments.
In the Symbols pane, add the symbols definitions:
Run the Test and View Results
Click Run to run the test.
When the test completes, in the Results and Artifacts pane, expand the Results. All of the assessments pass.
To view the coverage results, select heatpumpController_Harness1 under Results and expand the Coverage Results section. The coverage is 100% for both the tempController.c
and man_stub.c
files.