├── .circleci
└── config.yml
├── .github
└── workflows
│ └── ci.yml
├── .gitlab-ci.yml
├── Jenkinsfile
├── License.txt
├── README.md
├── SECURITY.md
├── azure-pipelines.yml
├── buildfile.m
├── code
└── dayofyear.m
└── tests
├── ParameterizedTestExample.m
└── TestExamples.m
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 | orbs:
3 | matlab: mathworks/matlab@1
4 | jobs:
5 | build:
6 | machine:
7 | image: ubuntu-2204:current
8 | steps:
9 | - checkout
10 | - matlab/install
11 | - matlab/run-tests:
12 | source-folder: code
13 |
14 | # As an alternative to run-tests, you can use run-command to execute a MATLAB script, function, or statement.
15 | # - matlab/run-command:
16 | # command: addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with MATLAB Actions
2 |
3 | name: MATLAB Build
4 |
5 | # Controls when the action will run.
6 | on:
7 | # Triggers the workflow on push or pull request events but only for the main branch
8 | push:
9 | branches: [ main ]
10 | pull_request:
11 | branches: [ main ]
12 |
13 | # Allows you to run this workflow manually from the Actions tab
14 | workflow_dispatch:
15 |
16 | jobs:
17 | # This workflow contains a single job called "build"
18 | build:
19 | # The type of runner that the job will run on
20 | runs-on: ubuntu-latest
21 |
22 | # Steps represent a sequence of tasks that will be executed as part of the job
23 | steps:
24 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
25 | - uses: actions/checkout@v4
26 |
27 | # Sets up MATLAB on a GitHub-hosted runner
28 | - name: Set up MATLAB
29 | uses: matlab-actions/setup-matlab@v2
30 |
31 | # Runs a set of commands using the runners shell
32 | - name: Run all tests
33 | uses: matlab-actions/run-tests@v2
34 | with:
35 | source-folder: code
36 |
37 | # You can use "run-build" to invoke the MATLAB build tool and run build tasks
38 | #- name: Run the default "test" task in the build file
39 | # uses: matlab-actions/run-build@v2
40 |
41 | # You can use "run-command" to execute custom MATLAB scripts, functions, or statements
42 | #- name: Run custom testing procedure
43 | # uses: matlab-actions/run-command@v2
44 | # with:
45 | # command: disp('Running my custom testing procedure!'); addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);
46 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | stages:
2 | - test
3 |
4 | matlab-test-job:
5 | stage: test
6 | script:
7 | - matlab -batch "addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);"
8 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent any
3 | stages {
4 | stage('Run MATLAB Tests') {
5 | steps {
6 | runMATLABTests(
7 | sourceFolder: ['code']
8 | )
9 |
10 | // As an alternative to runMATLABTests, you can use runMATLABCommand to execute a MATLAB script, function, or statement.
11 | // runMATLABCommand "addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);"
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/License.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2022, The MathWorks, Inc.
2 | All rights reserved.
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
5 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
6 | 3. In all cases, the software is, and all modifications and derivatives of the software shall be, licensed to you solely for use in conjunction with MathWorks products and service offerings.
7 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | | **Azure® DevOps** | **CircleCI®** | **GitHub® Actions** |
2 | |:---------------------------|:-----------------:|:----------------------------:|
3 | | [](https://dev.azure.com/iat-ci/ci-configuration-examples/_build)
 | [](https://app.circleci.com/pipelines/github/mathworks/ci-configuration-examples)
| [](https://github.com/mathworks/ci-configuration-examples/actions/workflows/ci.yml)
|
4 |
5 |
6 | # Continuous Integration (CI) configuration examples for MATLAB®
7 |
8 | Are you interested in automating your testing with CI?
9 |
10 | Not sure how to connect MATLAB with CI systems?
11 |
12 | We've got you covered!
13 |
14 | This repository makes it easy to run your MATLAB tests on some of the most common CI platforms. The configuration files take care of setting up MATLAB to work with the CI system and automatically executing your MATLAB tests.
15 |
16 | For advanced workflows, use the [`advanced-ci-configuration-examples`](https://github.com/mathworks/advanced-ci-configuration-examples) repository instead.
17 |
18 |
19 |
20 |
21 | ## Getting started
22 | ### Quick start guide
23 | Here's how to quickly get this repository running on a CI system:
24 | 1. Fork the repository to your own GitHub account
25 | 2. Go to one of the supported CI platforms and install the available MATLAB CI plugin ***Note:*** GitHub Actions does not require this step
26 | 3. Create a new CI job using your forked repository
27 | 4. Enjoy using CI with MATLAB!
28 |
29 | That's really it!
30 |
31 | To use your MATLAB code or Simulink® models with this repository, simply replace the existing code and tests in "code" and "tests" with your own code and tests. There's no need to modify any of the CI configuration files because they are all completely agnostic of the MATLAB code being used.
32 |
33 | ***Notes:***
34 | * In most cases, the configuration files will automatically get picked up by the CI platform during step 3, but some platforms may require you to specify which file to use
35 | * The default branch for this repository is "main"
36 |
37 |
38 |
39 |
40 | ### Step-by-step getting started guide for GitHub Actions
41 | Our Continuous Integration with MATLAB and GitHub Actions Workshop provides a step-by-step guide for getting started with GitHub Actions.
42 | * https://github.com/mathworks/ci-with-matlab-and-github-actions-workshop
43 |
44 |
45 |
46 |
47 |
48 | ## Badges
49 |
50 | Badges look really great, but they're not always easy to set up. Take a look at the badges, badge code, and reference links below to get started with badges for your repository.
51 |
52 |
53 |
54 | | **Azure DevOps** | |
55 | |:---------------------------|:-|
56 | | Badges | [](https://dev.azure.com/iat-ci/ci-configuration-examples/_build)
 |
57 | | Badge Code | \[\!\[Build Status](https[]()://dev.azure.com/***AZURE_DEVOPS_ORG***/***AZURE_DEVOPS_PROJECT_NAME***/_apis/build/status/***GITHUB_USERNAME***.***GITHUB_REPO_NAME***)](https[]()://dev.azure.com/***AZURE_DEVOPS_ORG***/***AZURE_DEVOPS_PROJECT_NAME***/_build)
\!\[Azure DevOps Coverage](https[]()://img.shields.io/azure-devops/coverage/***AZURE_DEVOPS_ORG***/***AZURE_DEVOPS_PROJECT_NAME***/***AZURE_DEVOPS_DEFINITION_ID***) |
58 | | Badge Help | [Blog with helpful information for setting up Azure DevOps badges](https://gregorsuttie.com/2019/03/20/azure-devops-add-your-build-status-badges-to-your-wiki/) |
59 |
60 |
61 |
62 | | **CircleCI** | |
63 | |:------------------|:-|
64 | | Badge | [](https://circleci.com/gh/mathworks/ci-configuration-examples) |
65 | | Badge Code | \[\!\[CircleCI](https[]()://circleci.com/***SOURCE_CONTROL_SYSTEM***/***GITHUB_USERNAME***/***GITHUB_REPO_NAME***.svg?style=svg)](https[]()://circleci.com/***SOURCE_CONTROL_SYSTEM***/***GITHUB_USERNAME***/***GITHUB_REPO_NAME***) |
66 | | Badge Help | [CircleCI documentation for setting up badges](https://circleci.com/docs/2.0/status-badges "CircleCI documentation for setting up badges") |
67 |
68 |
69 |
70 | | **GitHub Actions** | |
71 | |:-----------------------------|:-|
72 | | Badge | [](https://github.com/mathworks/ci-configuration-examples/actions/workflows/ci.yml) |
73 | | Badge Code | \[\!\[MATLAB](https[]()://github.com/***GITHUB_USERNAME***/***GITHUB_REPO_NAME***/actions/workflows/ci.yml/badge.svg)](https[]()://github.com/***GITHUB_USERNAME***/***GITHUB_REPO_NAME***/actions/workflows/ci.yml) |
74 | | Badge Help | [GitHub Actions documentation for setting up badges](https://docs.github.com/en/actions/managing-workflow-runs/adding-a-workflow-status-badge) |
75 |
76 |
77 |
78 | | **GitLab CI/CD** | |
79 | |:--------------------------|:-|
80 | | Badge Code | \[\!\[Pipeline Status](https[]()://gitlab.com/***GITLAB_PROJECT_PATH***/badges/***DEFAULT_BRANCH_NAME***/pipeline.svg)](https[]()://gitlab.com/***GITLAB_PROJECT_PATH***) |
81 | | Badge Help | [GitLab CI/CD documentation for setting up badges](https://docs.gitlab.com/ee/user/project/badges.html "GitLab CI/CD documentation for setting up badges") |
82 |
83 |
84 |
85 | **How to use the Badge Code:**
86 | 1. Copy-paste the badge code into your README.md file
87 | * The badge code you copy should start with "`[!`" and not "`\[\!`"
88 | 2. Replace all ***BOLD+ITALIC*** names with your specific credentials/names
89 | * Replace ***GITHUB_ORG*** with your GitHub organization name (usually your GitHub username)
90 | * Replace ***GITHUB_REPO_NAME*** with the name of your GitHub repository
91 | * Replace ***SOURCE_CONTROL_SYSTEM*** with the name of the source control system you are pointing to (gh = GitHub, bb = BitBucket)
92 | * Replace ***AZURE_DEVOPS_ORG*** with the name of your Azure DevOps organization name (usually your Azure DevOps username)
93 | * Replace ***AZURE_DEVOPS_PROJECT_NAME*** with the name of the Azure DevOps project that will run the CI job
94 | * Replace ***AZURE_DEVOPS_DEFINITION_ID*** with the definition ID for your Azure DevOps pipeline
95 | * To find the definition ID for your Azure DevOps pipeline, you must:
96 | * go to your Azure DevOps project
97 | * select "Pipelines" from the left-side navigation menu
98 | * select the pipeline you want to get coverage for
99 | * look at the end of the resulting URL for the number in "definitionId=###"
100 | * Replace ***GITLAB_PROJECT_PATH*** with the path of your GitLab project
101 | * Replace ***DEFAULT_BRANCH_NAME*** with the repository branch name you want to get the pipeline status from
102 |
103 |
104 |
105 |
106 |
107 | ## Supported CI platforms
108 | * Azure DevOps
109 | * CircleCI
110 | * GitHub Actions
111 | * Jenkins™
112 | * GitLab CI/CD
113 |
114 |
115 |
116 |
117 | ## About the code
118 | The primary goal of this repository is to provide a set of configuration files as templates that illustrate how to run MATLAB on various CI platforms (e.g., Azure DevOps, CircleCI, GitHub Actions, Jenkins).
119 |
120 | Each of these pipeline definitions does four things:
121 |
122 | 1. Install the latest MATLAB release on a Linux®-based build agent
123 | 2. Run all MATLAB tests in the root of your repository, including its subfolders
124 | 3. Produce a test results report (if necessary)
125 | 4. Produce a code coverage report in Cobertura XML format for the source folder
126 | * Currently, only Azure DevOps supports code coverage directly
127 | * To see an example of using [Codecov](https://about.codecov.io/) to show coverage results, please refer to [https://github.com/mathworks/matlab-codecov-example](https://github.com/mathworks/matlab-codecov-example)
128 |
129 | The example MATLAB code example `dayofyear.m` is a simple function takes a date string `"mm/dd/yyyy"` and returns the day-of-year number.
130 |
131 | Notes:
132 | * MATLAB already includes a day-of-year calculation using `day(d,"dayofyear")`, where `d` is a datetime object. This code is only used as an example since it is a concept that is familiar to most people.
133 | * The code coverage is intentionally set below 100% to show how missing coverage looks with badges. Uncomment the last test in `TestExamples.m` to increase the coverage to 100%.
134 |
135 | There are 2 test classes provided:
136 | 1. TestExamples.m - A simple set of equality and negative tests
137 | 2. ParameterizedTestExamples.m - A set of 12 equality tests set up using the parameterized test format
138 |
139 | The repository includes these files:
140 |
141 | | **File Path** | **Description** |
142 | |:---------------------------|:----------------|
143 | | [`code/dayofyear.m`](code/dayofyear.m) | The [`dayofyear`](code/dayofyear.m) function returns the day-of-year number for a given date string "mm/dd/yyyy" |
144 | | [`tests/TestExamples.m`](tests/TestExamples.m) | The [`TestExamples`](tests/TestExamples.m) class provides a few equality and negative tests for the [`dayofyear`](code/dayofyear.m) function |
145 | | [`tests/ParameterizedTestExample.m`](tests/ParameterizedTestExample.m) | The [`ParameterizedTestExample`](tests/ParameterizedTestExample.m) class provides 12 tests for the [`dayofyear`](code/dayofyear.m) function using the parameterized test format |
146 | | [`azure-pipelines.yml`](###Azure-DevOps) | The [`azure-pipelines.yml`](azure-pipelines.yml) file defines the pipeline that runs on [Azure DevOps](https://marketplace.visualstudio.com/items?itemName=MathWorks.matlab-azure-devops-extension). |
147 | | [`.circleci/config.yml`](###CircleCI) | The [`config.yml`](.circleci/config.yml) file defines the pipeline that runs on [CircleCI](https://circleci.com/orbs/registry/orb/mathworks/matlab) |
148 | | [`.github/workflows/ci.yml`](###GitHub-Actions) | The [`ci.yml`](.github/workflows/ci.yml) file defines the pipeline that runs on [GitHub Actions](https://github.com/matlab-actions/overview) |
149 | | [`Jenkinsfile`](###Jenkins) | The [`Jenkinsfile`](Jenkinsfile) file defines the pipeline that runs on [Jenkins](https://plugins.jenkins.io/matlab/) |
150 | | [`.gitlab-ci.yml`](###GitLab-CI/CD) | The [`.gitlab-ci.yml`](.gitlab-ci.yml) file defines the pipeline that runs on [GitLab CI/CD](https://docs.gitlab.com/ee/ci/) |
151 |
152 |
153 |
154 |
155 | ## CI configuration files
156 |
157 | ### Azure DevOps
158 | ```yml
159 | pool:
160 | vmImage: ubuntu-latest
161 | steps:
162 | - task: InstallMATLAB@1
163 | - task: RunMATLABTests@1
164 | inputs:
165 | sourceFolder: code
166 | codeCoverageCobertura: code-coverage/coverage.xml
167 | testResultsJUnit: test-results/results.xml
168 | - task: PublishTestResults@2
169 | inputs:
170 | testResultsFormat: 'JUnit'
171 | testResultsFiles: 'test-results/results.xml'
172 | - task: PublishCodeCoverageResults@1
173 | inputs:
174 | codeCoverageTool: 'Cobertura'
175 | summaryFileLocation: 'code-coverage/coverage.xml'
176 | pathToSources: 'code/'
177 |
178 | # As an alternative to RunMATLABTests, you can use RunMATLABCommand to execute a MATLAB script, function, or statement.
179 | # - task: RunMATLABCommand@1
180 | # inputs:
181 | # command: addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);
182 | ```
183 |
184 |
185 |
186 | ### CircleCI
187 | ```yml
188 | version: 2.1
189 | orbs:
190 | matlab: mathworks/matlab@1
191 | jobs:
192 | build:
193 | machine:
194 | image: ubuntu-2204:current
195 | steps:
196 | - checkout
197 | - matlab/install
198 | - matlab/run-tests:
199 | source-folder: code
200 |
201 | # As an alternative to run-tests, you can use run-command to execute a MATLAB script, function, or statement.
202 | # - matlab/run-command:
203 | # command: addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);
204 | ```
205 |
206 |
207 |
208 | ### GitHub Actions
209 | ```yml
210 | # This is a basic workflow to help you get started with MATLAB Actions
211 |
212 | name: MATLAB Build
213 |
214 | # Controls when the action will run.
215 | on:
216 | # Triggers the workflow on push or pull request events but only for the main branch
217 | push:
218 | branches: [ main ]
219 | pull_request:
220 | branches: [ main ]
221 |
222 | # Allows you to run this workflow manually from the Actions tab
223 | workflow_dispatch:
224 |
225 | jobs:
226 | # This workflow contains a single job called "build"
227 | build:
228 | # The type of runner that the job will run on
229 | runs-on: ubuntu-latest
230 |
231 | # Steps represent a sequence of tasks that will be executed as part of the job
232 | steps:
233 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
234 | - uses: actions/checkout@v4
235 |
236 | # Sets up MATLAB on a GitHub-hosted runner
237 | - name: Set up MATLAB
238 | uses: matlab-actions/setup-matlab@v2
239 |
240 | # Runs a set of commands using the runners shell
241 | - name: Run all tests
242 | uses: matlab-actions/run-tests@v2
243 | with:
244 | source-folder: code
245 |
246 | # You can use "run-build" to invoke the MATLAB build tool and run build tasks
247 | #- name: Run the default "test" task in the build file
248 | # uses: matlab-actions/run-build@v2
249 |
250 | # You can use "run-command" to execute custom MATLAB scripts, functions, or statements
251 | #- name: Run custom testing procedure
252 | # uses: matlab-actions/run-command@v2
253 | # with:
254 | # command: disp('Running my custom testing procedure!'); addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);
255 | ```
256 |
257 |
258 |
259 | ### Jenkins
260 | ```groovy
261 | pipeline {
262 | agent any
263 | stages {
264 | stage('Run MATLAB Tests') {
265 | steps {
266 | runMATLABTests(
267 | sourceFolder: 'code'
268 | )
269 |
270 | // As an alternative to runMATLABTests, you can use runMATLABCommand to execute a MATLAB script, function, or statement.
271 | // runMATLABCommand "addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);"
272 | }
273 | }
274 | }
275 | }
276 | ```
277 |
278 |
279 |
280 | ### GitLab CI/CD
281 | ```yml
282 | stages:
283 | - matlab-test
284 |
285 | matlab-test:
286 | stage: matlab-test
287 | script:
288 | - matlab -batch "addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);"
289 | ```
290 |
291 |
292 |
293 | ## Caveats
294 | * On cloud-hosted agents provided by Azure DevOps, CircleCI, and GitHub Actions, you need a [MATLAB batch licensing token](https://github.com/mathworks-ref-arch/matlab-dockerfile/blob/main/alternates/non-interactive/MATLAB-BATCH.md#matlab-batch-licensing-token) if your project is private or if your pipeline includes transformation products, such as MATLAB Coder™ and MATLAB Compiler™. You can request a token by contacting MathWorks® at [batch-tokens@mathworks.com](mailto:batch-tokens@mathworks.com).
295 |
296 |
297 |
298 |
299 | ## See Also
300 | - [Advanced Continuous Integration (CI) configuration examples for MATLAB](https://github.com/mathworks/advanced-ci-configuration-examples)
301 | - [Continuous Integration with MATLAB and Simulink](https://www.mathworks.com/solutions/continuous-integration.html)
302 |
303 |
304 |
305 |
306 | ## Contact Us
307 | If you have any questions or suggestions, please contact MathWorks at [continuous-integration@mathworks.com](mailto:continuous-integration@mathworks.com).
308 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting Security Vulnerabilities
2 |
3 | If you believe you have discovered a security vulnerability, please report it to
4 | [security@mathworks.com](mailto:security@mathworks.com). Please see
5 | [MathWorks Vulnerability Disclosure Policy for Security Researchers](https://www.mathworks.com/company/aboutus/policies_statements/vulnerability-disclosure-policy.html)
6 | for additional information.
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | pool:
2 | vmImage: ubuntu-latest
3 | steps:
4 | - task: InstallMATLAB@1
5 | - task: RunMATLABTests@1
6 | inputs:
7 | sourceFolder: code
8 | codeCoverageCobertura: code-coverage/coverage.xml
9 | testResultsJUnit: test-results/results.xml
10 | - task: PublishTestResults@2
11 | condition: always()
12 | inputs:
13 | testResultsFormat: 'JUnit'
14 | testResultsFiles: 'test-results/results.xml'
15 | - task: PublishCodeCoverageResults@1
16 | condition: always()
17 | inputs:
18 | codeCoverageTool: 'Cobertura'
19 | summaryFileLocation: 'code-coverage/coverage.xml'
20 | pathToSources: 'code/'
21 |
22 | # As an alternative to RunMATLABTests, you can use RunMATLABCommand to execute a MATLAB script, function, or statement.
23 | # - task: RunMATLABCommand@1
24 | # inputs:
25 | # command: addpath('code'); results = runtests('IncludeSubfolders', true); assertSuccess(results);
26 |
--------------------------------------------------------------------------------
/buildfile.m:
--------------------------------------------------------------------------------
1 | function plan = buildfile
2 |
3 | % Add the source folder to the path
4 | addpath("code");
5 |
6 | % Create a plan
7 | plan = buildplan(localfunctions);
8 |
9 | % Add a task to run tests
10 | plan("test") = matlab.buildtool.tasks.TestTask("tests");
11 |
12 | % Make the "test" task the default task in the plan
13 | plan.DefaultTasks = "test";
14 |
15 | end
16 |
17 |
--------------------------------------------------------------------------------
/code/dayofyear.m:
--------------------------------------------------------------------------------
1 | function doy = dayofyear(mmddyy,dateFormat)
2 | %DAYOFYEAR Converts a date string ("mm/dd/yyyy") to the day number of the
3 | %year.
4 |
5 | % NOTE: MATLAB already does easily this using:
6 | % doy = day(d,"dayofyear")
7 | % where d is a datetime object
8 |
9 | % Copyright 2022 The MathWorks, Inc.
10 |
11 | arguments
12 | mmddyy string;
13 | dateFormat (1,1) string {mustBeMember(dateFormat,["mm/dd/yyyy","dd/mm/yyyy"])} = "mm/dd/yyyy";
14 | end
15 |
16 | % Check that mmddyy was provided in the appropriate format
17 | if numel(split(mmddyy,"/")) ~= 3
18 | error("dayofyear:InvalidDateFormat","Invalid date string. Expected date formatted as dd/mm/yyyy.")
19 | end
20 |
21 | % Create a datetime object depending on the dateFormat provided
22 | if dateFormat == "mm/dd/yyyy"
23 | d = datetime(mmddyy,"Format","MM/dd/uuuu");
24 | else
25 | d = datetime(mmddyy,"Format","dd/MM/uuuu");
26 | end
27 |
28 | % Initialize the days per month
29 | daysPerMonth = [ ...
30 | 31; % January
31 | 28; % February
32 | 31; % March
33 | 30; % April
34 | 31; % May
35 | 30; % June
36 | 31; % July
37 | 31; % August
38 | 30; % September
39 | 31; % October
40 | 30; % November
41 | 31]; % December
42 |
43 | % Check for leap year
44 | if mod(d.Year,4) == 0
45 | % This is a leap year, so change February to 29 days
46 | daysPerMonth(2) = 29;
47 | end
48 |
49 | % Calculate day of year
50 | doy = sum(daysPerMonth(1:d.Month-1)) + d.Day;
51 |
52 |
53 | end
54 |
55 |
--------------------------------------------------------------------------------
/tests/ParameterizedTestExample.m:
--------------------------------------------------------------------------------
1 | classdef ParameterizedTestExample < matlab.unittest.TestCase
2 | % Creates 12 test points, one test point for the 15th day of every month of 2021
3 |
4 | % Copyright 2022 The MathWorks, Inc.
5 |
6 | properties (TestParameter)
7 | monthNum = num2cell(1:12);
8 | dayNum = {15};
9 | yearNum = {2021};
10 | end
11 |
12 | methods (Test)
13 | function testDayofyear(testCase,monthNum,dayNum,yearNum)
14 | % Convert numeric values to mm/dd/yyyy string
15 | % Note: MATLAB will automatically convert numbers to strings
16 | % when performing number+string arithmetic
17 | dateStr = monthNum + "/" + dayNum + "/" + yearNum;
18 |
19 | % Compute expected result
20 | dt = datetime(dateStr,"Format","MM/dd/uuuu");
21 | doyExpected = day(dt,"dayofyear");
22 |
23 | % Compute actual result
24 | doyActual = dayofyear(dateStr);
25 |
26 | % Verify that the actual result matches the expected result
27 | testCase.verifyEqual(doyActual,doyExpected)
28 | end
29 | end
30 |
31 | end
32 |
33 |
--------------------------------------------------------------------------------
/tests/TestExamples.m:
--------------------------------------------------------------------------------
1 | classdef TestExamples < matlab.unittest.TestCase
2 | % TestExamples contains a set of 4 simple tests:
3 | % 1) an equality test for a non-leap year date
4 | % 2) an equality test for a leap year date
5 | % 3) a negative test for an invalid date format input
6 | % 4) a negative test for a correct date format but an invalid date
7 | % 5) an equality test for a non-leap year date using the alternate
8 | % dateFormat (COMMENTED OUT)
9 | %
10 | % Notes:
11 | % A) A negative test verifies that the code errors/fails in an
12 | % expected way (e.g., the code gives the right error for a
13 | % specific bad input)
14 | % B) The 5th test is included for completeness, but is commented
15 | % out to illustrate missing code coverage in continous
16 | % integration (CI) systems
17 |
18 | % Copyright 2022 The MathWorks, Inc.
19 |
20 | methods (Test)
21 |
22 | function testNonLeapYear(testCase)
23 | % Create non-leap year date of March 1st, 2021
24 | dateStr = "03/01/2021";
25 |
26 | % Calculate expected result
27 | dt = datetime(dateStr,"Format","MM/dd/uuuu");
28 | doyExpected = day(dt,"dayofyear");
29 |
30 | % Get actual result
31 | doyActual = dayofyear(dateStr);
32 |
33 | % Verify that the two are equal
34 | testCase.verifyEqual(doyActual,doyExpected)
35 | end
36 |
37 | function testLeapYear(testCase)
38 | % Create leap year date of March 1st, 2020
39 | dateStr = "03/01/2020";
40 |
41 | % Calculate expected result
42 | dt = datetime(dateStr,"Format","MM/dd/uuuu");
43 | doyExpected = day(dt,"dayofyear");
44 |
45 | % Get actual result
46 | doyActual = dayofyear(dateStr);
47 |
48 | % Verify that the two are equal
49 | testCase.verifyEqual(doyActual,doyExpected)
50 | end
51 |
52 | function testInvalidDateFormat(testCase)
53 | % Create invalid date of April 1st, 2021
54 | dateStr = "04-01-2021";
55 |
56 | % Verify that our function throws an error
57 | testCase.verifyError(@() dayofyear(dateStr),"dayofyear:InvalidDateFormat");
58 | end
59 |
60 | function testCorrectDateFormatButInvalidDate(testCase)
61 | % Create invalid date of February 30th, 2021
62 | dateStr = "02/30/2021";
63 |
64 | % Verify that our function throws an error
65 | testCase.verifyError(@() dayofyear(dateStr),"MATLAB:datetime:ParseErr");
66 | end
67 |
68 | % function testAlternateDateFormat(testCase)
69 | % % Create date of April 1st, 2021 in alternate date format
70 | % dateStr = "01/04/2021";
71 | % dateFormat = "dd/mm/yyyy";
72 | %
73 | % % Calculate expected result
74 | % dt = datetime(dateStr,"Format","dd/MM/uuuu");
75 | % doyExpected = day(dt,"dayofyear");
76 | %
77 | % % Get actual result
78 | % doyActual = dayofyear(dateStr,dateFormat);
79 | %
80 | % % Verify that the two are equal
81 | % testCase.verifyEqual(doyActual,doyExpected)
82 | % end
83 |
84 | end
85 |
86 | end
87 |
88 |
--------------------------------------------------------------------------------