A few days ago, my colleague Mariano published a blog post on the Developer Zone describing how to run a MATLAB Test suite on a Continuous Integration server every time changes are pushed to a Git repository branch.
As you probably guessed, as soon as I saw that, I had to implement it in a Simulink context. Let's see how that went.
The Big Picture
Here is the big picture of the workflow:
The main steps are:
- We have a Simulink Project with source control integration configured to a GitLab repository
- In the Simulink Project, we have a test suite created using Simulink Test
- Every time we commit and push changes to a specific branch of the Git repository, the remote repository notifies a Jenkins server
- As soon as it gets notified, the Jenkins server pulls the commit, launches MATLAB, opens the Simulink Project and runs the Simulink Test test suite
- If the test passes, the Jenkins server creates a merge request in GitLab.
- If a test fails, the Simulink Test results are saved and a bug issue in GitLab is created.
This last step of saving the Simulink Test session is very important. As you will see below, it allows me to analyze the simulation results locally without re-running the failed test, saving me a lot of time if the simulation I am testing takes a long time to simulate.
Now it's time to go into the details.
Here is a quick recap of the steps to go through outside the MATLAB environment, in Jenkins and GitLab. See Mariano's post for more details:
- In Jenkins, specify the repository URL, the branch to build and my credentials
- In Jenkins, set Build Trigger to build when a change is pushed to GitLab
- In Jenkins, specify the build command to launch MATLAB and run my tests
- In GitLab, setup a Push Event Web hook to the GitLab CI Service URL of Jenkins. This will be triggered every time I will push changes to my testing branch
- The tests are run through the MATLAB Testing Framework. Thanks to that, my test suite can include tests created using Simulink Test and using the MATLAB Testing Framework. As they say where I come from: A test is a test!
- The functions writeMergeRequest() and writeIssue() contain code similar to Mariano's post, using webwrite to create a merge request or an issue in GitLab using its API.
- I create a folder to store the results in case of failure
- sltest.testmanager.exportResults is used to save the test results in case of a failure.
Now let's move to the fun stuff :-)
For this example, we will reuse the same example we used before in this post where we tested that the results of a simulation was matching results from a generated executable using Software-In-The-Loop simulation:
The post Simulation Based Testing with Simulink Test Manager describes in detail how to set up the test, so I will not repeat it here.
What I added for this blog is to create a Simulink Project, adding all the files to it and configure the source control integration.
Running the Simulink Test test suite on the Jenkins Server
In Jenkins, we need to specify a build command. This build command will launch the MATLAB executable in "nodisplay" mode and use the -r flag to run some MATLAB code (See this image). In my case, the MATLAB code is:
The function runMyTests is similar to the one presented in Mariano's post. Here is what it looks like:
Things to notice:
Inspecting Test Failure
Depending on how your Jenkins server is configured, you could write MATLAB code to put the test results at any place convenient for you. By default, I can go grab my results file in the Jenkins workspace.
Once I get the results file, I can simply double-click on it in MATLAB and the Test Manager will show what went wrong.
I will repeat it... since I often deal with simulations that take hours to run, getting the results file from the server saves me a lot of time since I do not need to re-run the simulation on my machine.
Now it's your turn
Are you incorporating change management and continuous integration tools like Git and Jenkins in your team-based Model-Based Design projects? If yes, tell us more about your setup in the comments below.