Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
# MATLAB<sup>&reg;</sup>/SimBiology<sup>&reg;</sup> DevOps Workflow Example

This workshop provides hands-on experience using some of MATLAB's powerful software testing and automation features.<br>
It is based on the workshop [Generating Tests for Your MATLAB<sup>&reg;</sup> Code](https://github.com/mathworks/Generating-Tests-for-Your-MATLAB-Code-Workshop).
It showcases the development of a Web App to simulate a SimBiology model. The App is developed using the [model-view-controller (MVC)](https://www.mathworks.com/company/technical-articles/developing-matlab-apps-using-the-model-view-controller-pattern.html) software architecture pattern and is
based on the workshop [Generating Tests for Your MATLAB<sup>&reg;</sup> Code](https://github.com/mathworks/Generating-Tests-for-Your-MATLAB-Code-Workshop) developed by [Adam Sifounakis](https://github.com/asifouna).
<br><br>

## About the workshop
Expand All @@ -27,4 +28,4 @@ Step-by-step workshop instructions can be found in:
* [WorkshopGuide.m](WorkshopGuide.m)
<br><br>

Copyright 2025 The MathWorks, Inc.
Copyright 2026 The MathWorks, Inc.
28 changes: 14 additions & 14 deletions WorkshopGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ In this workshop, you will:
## Table of Contents
&emsp;[Workshop Requirements](#H_34C2FB57)

&emsp;[Part 1: Getting the workshop files and configuring GitHub for automated testing and results publishing](#TMP_877c)
&emsp;[Part 1: Getting the workshop files and configuring GitHub for automated testing and results publishing](#TMP_3984)

&emsp;[Part 2: Generating your first tests](#TMP_020d)
&emsp;[Part 2: Generating your first tests](#TMP_95f4)

&emsp;[Part 3: Finding existing tests and measuring coverage](#TMP_4b61)
&emsp;[Part 3: Finding existing tests and measuring coverage](#TMP_8279)

&emsp;[Part 4: Updating badges, committing our changes, and pushing to GitHub](#TMP_74db)
&emsp;[Part 4: Updating badges, committing our changes, and pushing to GitHub](#TMP_1d39)

&emsp;[Part 5: Create a pull request, watch GitHub Actions automatically test your changes and publish results](#TMP_651a)
&emsp;[Part 5: Create a pull request, watch GitHub Actions automatically test your changes and publish results](#TMP_2a59)

&emsp;[Part 6: Compile the App in the CI workflow and download the artifact](#TMP_85dd)
&emsp;[Part 6: Compile the App in the CI workflow and download the artifact](#TMP_2c41)

&emsp;[Workshop wrap\-up and additional information](#TMP_230e)
&emsp;[Workshop wrap\-up and additional information](#TMP_86ec)

<!-- End Toc -->
<a id="H_34C2FB57"></a>
Expand All @@ -56,7 +56,7 @@ The following steps cover all of the things you will need to successfully comple
- The workshop leverages the free repository and CI capabilities offered by GitHub and GitHub Actions
- Go to: [**https://github.com/signup**](https://github.com/signup)

<a id="TMP_877c"></a>
<a id="TMP_3984"></a>

# Part 1: Getting the workshop files and configuring GitHub for automated testing and results publishing

Expand Down Expand Up @@ -308,7 +308,7 @@ Click on the 'Run App' shortcut to start the app in MATLAB:
![image_20.png](WorkshopGuide_media/image_20.png)


<a id="TMP_020d"></a>
<a id="TMP_95f4"></a>

# Part 2: Generating your first tests

Expand Down Expand Up @@ -529,7 +529,7 @@ Congratulations! You just created multiple tests for your MATLAB code!

It was easier than you thought, right?

<a id="TMP_4b61"></a>
<a id="TMP_8279"></a>

# Part 3: Finding existing tests and measuring coverage

Expand Down Expand Up @@ -868,7 +868,7 @@ It looks like we've achieved full statement coverage for [`generateSimFun`](./co
![image_65.png](WorkshopGuide_media/image_65.png)


<a id="TMP_74db"></a>
<a id="TMP_1d39"></a>

# Part 4: Updating badges, committing our changes, and pushing to GitHub

Expand Down Expand Up @@ -1088,7 +1088,7 @@ At this point, all of your changes will be pushed to GitHub.
![image_85.png](WorkshopGuide_media/image_85.png)


<a id="TMP_651a"></a>
<a id="TMP_2a59"></a>

# Part 5: Create a pull request, watch GitHub Actions automatically test your changes and publish results

Expand Down Expand Up @@ -1249,7 +1249,7 @@ The code coverage report looks like this:

Now anyone that visits your repository can immediately see the quality of your code, explore your test and code coverage results, and will have more confidence in the code you are writing!

<a id="TMP_85dd"></a>
<a id="TMP_2c41"></a>

# Part 6: Compile the App in the CI workflow and download the artifact

Expand Down Expand Up @@ -1353,7 +1353,7 @@ You can now download the CTF file and upload it to your Web App Server using you

[https://vdi\-wd1ah2\-348.dhcp.mathworks.com:9999/webapps/home/login.html](https://vdi-wd1ah2-348.dhcp.mathworks.com:9999/webapps/home/login.html)

<a id="TMP_230e"></a>
<a id="TMP_86ec"></a>

# Workshop wrap\-up and additional information

Expand Down
9 changes: 3 additions & 6 deletions code/ConcTimecourseView.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
classdef ConcTimecourseView < handle

properties ( Access = private )
Model

properties ( Access = private )
ConcColors = [0.30,0.75,0.93;...
0.86,0.55,0.41;...
0.91,0.73,0.42]; % colors to plot concentrations
Expand Down Expand Up @@ -60,7 +58,6 @@
obj.DataListener = dataListener;

% save objects
obj.Model = model;
obj.Axes = ax;

end % constructor
Expand All @@ -70,8 +67,8 @@

methods ( Access = private )

function update(obj,~,~)
t = obj.Model.SimDataTable;
function update(obj,srcModel,~)
t = srcModel.SimDataTable;

set(obj.lhDrug,'XData',t.Time, 'YData',t.Drug);
set(obj.lhReceptor,'XData',t.Time, 'YData',t.Receptor);
Expand Down
9 changes: 2 additions & 7 deletions code/LampView.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
LampObj
end

properties ( Access=private )
Model
end

properties( Access = private )
DataListener % listener
end
Expand All @@ -42,7 +38,6 @@
obj.DataListener = dataListener;

obj.LampObj = lampObj;
obj.Model = model;

end % constructor

Expand All @@ -68,8 +63,8 @@

methods ( Access = private )

function update(obj,~,~)
obj.IsOn = obj.Model.ROIsBetweenThresholds;
function update(obj,srcModel,~)
obj.IsOn = srcModel.ROIsBetweenThresholds;
end % update

end % private method
Expand Down
8 changes: 3 additions & 5 deletions code/NCAView.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
classdef NCAView < handle

properties ( Access = private )
Model
properties ( Access = private )
NCApanel
GridLayout
BackgroundColor = [1,1,1]
Expand Down Expand Up @@ -67,7 +66,6 @@
obj.DataListener = dataListener;

% save objects
obj.Model = model;
obj.NCAoptions = opt;
obj.NCApanel = ncapanel;
obj.GridLayout = gl;
Expand All @@ -89,10 +87,10 @@

methods ( Access = private )

function update(obj,~,~)
function update(obj,srcModel,~)

% compute NCA parameters and display them in table
ncaParameters = sbionca(obj.Model.SimDataTable, obj.NCAoptions);
ncaParameters = sbionca(srcModel.SimDataTable, obj.NCAoptions);
obj.NCAtable.ColumnName = ncaParameters.Properties.VariableNames(2:end);
obj.NCAtable.Data = ncaParameters(:,2:end);

Expand Down
9 changes: 3 additions & 6 deletions code/ROTimecourseView.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
classdef ROTimecourseView < handle

properties ( Access = private )
Model

properties ( Access = private )
ThresholdStyle = {'Color','r','Linewidth',2,'LineStyle','--',...
'FontWeight','bold','LabelVerticalAlignment','middle'}; % style for threshold lines

Expand Down Expand Up @@ -60,7 +58,6 @@
obj.DataListener = dataListener;

% save objects
obj.Model = model;
obj.Axes = ax;

end % constructor
Expand All @@ -70,8 +67,8 @@

methods ( Access = private )

function update(obj,~,~)
t = obj.Model.SimDataTable;
function update(obj,srcModel,~)
t = srcModel.SimDataTable;

set(obj.lhRO,'XData',t.Time, 'YData',100*t.RO);

Expand Down
7 changes: 7 additions & 0 deletions resources/project/Project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@
</Category>
</Info>
</File>
<File Location="tLampView.m">
<Info>
<Category UUID="FileClassCategory">
<Label UUID="test"/>
</Category>
</Info>
</File>
</File>
<File Location=".gitattributes">
<Info/>
Expand Down
72 changes: 72 additions & 0 deletions tests/tLampView.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
% This test file was generated by Copilot. Validate generated output before use.
classdef tLampView < matlab.unittest.TestCase
properties
Model
LampViewObj
Parent
end

methods(TestMethodSetup)
function createLampView(testCase)
testCase.Parent = figure('Visible', 'off'); % Create a hidden figure for the lamp
testCase.Model = SimulationModel(); % Assuming SimulationModel is defined elsewhere
testCase.LampViewObj = LampView(testCase.Parent, testCase.Model);
end
end

methods(Test)
function testIsOnTrue(testCase)
simulate(testCase.Model, [0.5234,0.0485,0.0934,119,150,24]);

testCase.verifyTrue(testCase.LampViewObj.IsOn);
end

function testIsOnFalse(testCase)
simulate(testCase.Model, [0.5234,0.0485,0.0934,119,200,24]);

testCase.verifyFalse(testCase.LampViewObj.IsOn);
end

function testLampColorSuccess(testCase)
testCase.LampViewObj.IsOn = true;
expectedColor = testCase.LampViewObj.LampColorSucess;

actualColor = testCase.LampViewObj.LampObj.Color;

testCase.verifyEqual(actualColor, expectedColor, 'Lamp color should be success color when IsOn is true.');
end

function testLampColorFailure(testCase)
testCase.LampViewObj.IsOn = false;
expectedColor = testCase.LampViewObj.LampColorFailure;

actualColor = testCase.LampViewObj.LampObj.Color;

testCase.verifyEqual(actualColor, expectedColor, 'Lamp color should be failure color when IsOn is false.');
end

function testTooltipSuccess(testCase)
testCase.LampViewObj.IsOn = true;
expectedTooltip = char(compose("Target occupancy remains\n between thresholds"));

actualTooltip = testCase.LampViewObj.LampObj.Tooltip;

testCase.verifyEqual(actualTooltip, expectedTooltip, 'Tooltip should indicate success when IsOn is true.');
end

function testTooltipFailure(testCase)
testCase.LampViewObj.IsOn = false;
expectedTooltip = char(compose("Target occupancy does not remain\n between thresholds"));

actualTooltip = testCase.LampViewObj.LampObj.Tooltip;

testCase.verifyEqual(actualTooltip, expectedTooltip, 'Tooltip should indicate failure when IsOn is false.');
end
end

methods(TestMethodTeardown)
function closeFigure(testCase)
close(testCase.Parent);
end
end
end