Skip to content

Commit

Permalink
Merge pull request #2432 from farid-zare/developPlus
Browse files Browse the repository at this point in the history
Develop plus
  • Loading branch information
farid-zare authored Feb 14, 2025
2 parents 8b7ea83 + 05806df commit 4208c95
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 117 deletions.
35 changes: 0 additions & 35 deletions .github/workflows/cobratoolboxCI.yml

This file was deleted.

42 changes: 42 additions & 0 deletions .github/workflows/cobratoolboxCI_step1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: cobratoolboxCI (merge, test, and upload)
on:
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:

permissions:
contents: read

jobs:
build:
runs-on: self-hosted
steps:
- name: Check out merged PR code
uses: actions/checkout@v4

- name: Run MATLAB Tests
run: |
matlab -batch "run('initCobraToolbox.m'); run('test/testAll.m');"
- name: Convert JUnit to CTRF
run: |
npx junit-to-ctrf ./testReport.junit.xml -o ./ctrf/ctrf-report.json
- name: Upload CTRF Artifact
uses: actions/upload-artifact@v4
with:
name: testReport
path: ./ctrf/ctrf-report.json

- name: Save PR Number
run: echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV

- name: Upload PR Number as Artifact
run: echo $PR_NUMBER > pr_number.txt
shell: bash

- name: Upload PR Number Artifact
uses: actions/upload-artifact@v4
with:
name: pr_number
path: pr_number.txt
46 changes: 46 additions & 0 deletions .github/workflows/cobratoolboxCI_step2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: testReport (Comment on PR)

on:
workflow_run:
workflows: ['cobratoolboxCI (merge, test, and upload)']
types: [completed]

permissions:
contents: write
pull-requests: write

jobs:
publish-report:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success'
steps:

- name: Download CTRF Artifact
uses: dawidd6/action-download-artifact@v8
with:
name: testReport
run_id: ${{ github.event.workflow_run.id }}
path: artifacts

- name: Download PR Number Artifact
uses: dawidd6/action-download-artifact@v8
with:
name: pr_number
run_id: ${{ github.event.workflow_run.id }}
path: pr_number

- name: Read PR Number
id: read_pr_number
run: |
PR_NUMBER=$(cat pr_number/pr_number.txt)
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
- name: Publish Test Report
uses: ctrf-io/[email protected]
with:
report-path: 'artifacts/ctrf-report.json'
summary-report: true
failed-report: true
issue: ${{ env.PR_NUMBER }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
171 changes: 89 additions & 82 deletions test/testAll.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
fprintf([' \\_____| \\_____/ |_____/ |_| \\_\\ |_| |_| |\n']);
fprintf(' | \n\n');

% Define CI boolean
CIenv = false;

% request explicitly from the user to launch test suite locally
% if contains(getenv('HOME'), 'vmhadmin') || contains(getenv('HOME'), 'jenkins')
if contains(getenv('HOME'), 'saleh')
% Running in CI environment
% fprintf('Running test in Jenkins/CI environment\n');
fprintf('Running test in cobratoolbox/CI environment\n');

fprintf('Running test in cobratoolbox/CI environment\n');

% Set CI boolean to true
CIenv = true;

% on the CI, always reset the path to make absolutely sure, that we test
% the current version
restoredefaultpath;
Expand All @@ -29,7 +33,7 @@
while isempty(reply)
reply = input([' -> Do you want to launch the test suite locally? Time estimate: more than 60 minutes Y/N: '], 's');
end

if strcmpi(reply, 'y') || strcmpi(reply, 'yes')
launchTestSuite = true;
else
Expand Down Expand Up @@ -74,7 +78,7 @@
testDirContent = getFilesInDir('type', 'all'); % Get all currently present files in the folder.
testDirPath = pwd;
cd(currentDir);
%{
%{
if ~isempty(strfind(getenv('HOME'), 'jenkins')) || ~isempty(strfind(getenv('USERPROFILE'), 'jenkins'))
WAITBAR_TYPE = 0;
Expand All @@ -86,7 +90,7 @@
else
WAITBAR_TYPE = 1;
end
%}
%}
if verLessThan('matlab', '8.2')
error('The testsuite of The COBRA Toolbox can only be run with MATLAB R2014b+.')
end
Expand All @@ -102,7 +106,7 @@
% only retain the lines that end with .txt and .m and
% are not comments and point to files in the /src folder
ignoredPatterns = {'^.{0,3}$', ... % Is smaller than four.
['^[^s][^r][^c][^' regexptranslate('escape', filesep) ']']}; % does not start with src/
['^[^s][^r][^c][^' regexptranslate('escape', filesep) ']']}; % does not start with src/
filterPatterns = {'\.txt$', '\.m$'}; % Is either a .m file or a .txt file.
ignoreFiles = getIgnoredFiles(ignoredPatterns, filterPatterns);

Expand All @@ -122,10 +126,10 @@
while ~feof(fid)
lineOfFile = strtrim(char(fgetl(fid)));
if length(lineOfFile) > 0 && length(strfind(lineOfFile(1), '%')) ~= 1 ...
&& length(strfind(lineOfFile, 'end')) ~= 1 && length(strfind(lineOfFile, 'otherwise')) ~= 1 ...
&& length(strfind(lineOfFile, 'switch')) ~= 1 && length(strfind(lineOfFile, 'else')) ~= 1 ...
&& length(strfind(lineOfFile, 'case')) ~= 1 && length(strfind(lineOfFile, 'function')) ~= 1
nCodeLines = nCodeLines + 1;
&& length(strfind(lineOfFile, 'end')) ~= 1 && length(strfind(lineOfFile, 'otherwise')) ~= 1 ...
&& length(strfind(lineOfFile, 'switch')) ~= 1 && length(strfind(lineOfFile, 'else')) ~= 1 ...
&& length(strfind(lineOfFile, 'case')) ~= 1 && length(strfind(lineOfFile, 'function')) ~= 1
nCodeLines = nCodeLines + 1;

elseif length(lineOfFile) == 0
nEmptyLines = nEmptyLines + 1;
Expand All @@ -142,11 +146,11 @@

grades = {'A', 'B', 'C', 'D', 'E', 'F'};
intervals = [0, 3;
3, 6;
6, 9;
9, 12;
12, 15;
15, 100];
3, 6;
6, 9;
9, 12;
12, 15;
15, 100];

grade = 'F';
for i = 1:length(intervals)
Expand Down Expand Up @@ -182,75 +186,78 @@
sumFailed = sum(resultTable.Failed);

fprintf(['\n > ', num2str(sumFailed), ' tests failed. ', num2str(sumSkipped), ' tests were skipped due to missing requirements.\n\n']);
%% NEW: Generate JUnit XML report for Codecov

xmlFileName = 'testReport.junit.xml';

fid = fopen(xmlFileName, 'w');
if fid == -1
error('Could not open file for writing: %s', xmlFileName);
end
if CIenv
%% NEW: Generate JUnit XML report for Codecov

fprintf(fid, '<?xml version="1.0" encoding="UTF-8"?>\n');
xmlFileName = 'testReport.junit.xml';

numTests = height(resultTable);
numFailures = sum(resultTable.Failed);
numErrors = sum(resultTable.Failed);
numSkipped = sum(resultTable.Skipped);
fid = fopen(xmlFileName, 'w');
if fid == -1
error('Could not open file for writing: %s', xmlFileName);
end

% Compute total time and also count how many are "failures" vs. "errors"
totalTime = sum(resultTable.Time);
fprintf(fid, '<?xml version="1.0" encoding="UTF-8"?>\n');

% 1) Wrap in <testsuites> -- typical JUnit format
fprintf(fid, '<testsuites name="COBRA Toolbox Test Suites" tests="%d" failures="%d" errors="%d" time="%.3f">\n', ...
numTests, numFailures, numErrors, totalTime);
numTests = height(resultTable);
numFailures = sum(resultTable.Failed);
numErrors = sum(resultTable.Failed);
numSkipped = sum(resultTable.Skipped);

% 2) A single <testsuite> inside
fprintf(fid, ' <testsuite name="COBRA Toolbox Test Suite" tests="%d" failures="%d" errors="%d" skipped="%d" time="%.3f">\n', ...
numTests, numFailures, numErrors, numSkipped, totalTime);
% Compute total time and also count how many are "failures" vs. "errors"
totalTime = sum(resultTable.Time);

% 3) Loop over each test case
for i = 1:numTests
testName = resultTable.TestName{i};
if isnan(resultTable.Time(i))
tVal = 0;
else
tVal = resultTable.Time(i);
end
% 1) Wrap in <testsuites> -- typical JUnit format
fprintf(fid, '<testsuites name="COBRA Toolbox Test Suites" tests="%d" failures="%d" errors="%d" time="%.3f">\n', ...
numTests, numFailures, numErrors, totalTime);

% Start the <testcase> tag
fprintf(fid, ' <testcase classname="COBRA Toolbox" name="%s" time="%.3f"', testName, tVal);

if resultTable.Passed(i)
% Passed => just close
fprintf(fid, '/>\n');
elseif resultTable.Skipped(i)
% Skipped => <skipped/>
fprintf(fid, '>\n');
fprintf(fid, ' <skipped message="%s"/>\n', escapeXML(resultTable.Details{i}));
fprintf(fid, ' </testcase>\n');
else
% Not passed, not skipped => either <failure> or <error>
% Check the .Error or .Details to decide
errMsg = result(i).Error.message; % or getReport()

% Heuristic: if "Assertion" => <failure>, else <error>.
if contains(errMsg, 'Assertion') || contains(errMsg, 'assert')
fprintf(fid, '>\n');
fprintf(fid, ' <failure message="%s"/>\n', escapeXML(errMsg));
% 2) A single <testsuite> inside
fprintf(fid, ' <testsuite name="COBRA Toolbox Test Suite" tests="%d" failures="%d" errors="%d" skipped="%d" time="%.3f">\n', ...
numTests, numFailures, numErrors, numSkipped, totalTime);

% 3) Loop over each test case
for i = 1:numTests
testName = resultTable.TestName{i};
if isnan(resultTable.Time(i))
tVal = 0;
else
fprintf(fid, '>\n');
fprintf(fid, ' <error message="%s"/>\n', escapeXML(errMsg));
tVal = resultTable.Time(i);
end

fprintf(fid, ' </testcase>\n');
% Start the <testcase> tag
fprintf(fid, ' <testcase name="%s" time="%.3f"', testName, tVal);

if resultTable.Passed(i)
% Passed => just close
fprintf(fid, '/>\n');
elseif resultTable.Skipped(i)
% Skipped => <skipped/>
fprintf(fid, '>\n');
fprintf(fid, ' <skipped message="%s"/>\n', escapeXML(resultTable.Details{i}));
fprintf(fid, ' </testcase>\n');
else
% Not passed, not skipped => either <failure> or <error>
% Check the .Error or .Details to decide
errMsg = result(i).Error.message; % or getReport()

% Heuristic: if "Assertion" => <failure>, else <error>.
if contains(errMsg, 'Assertion') || contains(errMsg, 'assert')
fprintf(fid, '>\n');
fprintf(fid, ' <failure message="%s"/>\n', escapeXML(errMsg));
else
fprintf(fid, '>\n');
fprintf(fid, ' <error message="%s"/>\n', escapeXML(errMsg));
end

fprintf(fid, ' </testcase>\n');
end
end
end

% Close out the suite and suites
fprintf(fid, ' </testsuite>\n');
fprintf(fid, '</testsuites>\n');
fclose(fid);
% Close out the suite and suites
fprintf(fid, ' </testsuite>\n');
fprintf(fid, '</testsuites>\n');
fclose(fid);
end
%% End of XML generation

% count the number of covered lines of code
Expand Down Expand Up @@ -364,13 +371,13 @@

%% Local helper function to escape XML special characters.
function out = escapeXML(in)
if isempty(in)
out = '';
return;
end
out = strrep(in, '&', '&amp;');
out = strrep(out, '<', '&lt;');
out = strrep(out, '>', '&gt;');
out = strrep(out, '"', '&quot;');
out = strrep(out, '''', '&apos;');
if isempty(in)
out = '';
return;
end
out = strrep(in, '&', '&amp;');
out = strrep(out, '<', '&lt;');
out = strrep(out, '>', '&gt;');
out = strrep(out, '"', '&quot;');
out = strrep(out, '''', '&apos;');
end

0 comments on commit 4208c95

Please sign in to comment.