Skip to content

Commit 7f7b71e

Browse files
committed
add the plugin
1 parent ec4a48f commit 7f7b71e

2 files changed

Lines changed: 193 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
classdef CodeCoverageSummaryPluginService < matlab.buildtool.internal.services.ciplugins.CITestRunnerPluginService
2+
% Copyright 2025 The MathWorks, Inc.
3+
4+
methods
5+
function plugins = providePlugins(~, ~)
6+
% Check if MATLAB Test license is available
7+
if license('test', 'matlab_test')
8+
% Create a shared CoverageResult format object
9+
format = matlab.unittest.plugins.codecoverage.CoverageResult;
10+
11+
% Create an array to hold multiple plugins
12+
plugins = matlab.unittest.plugins.TestRunnerPlugin.empty(0);
13+
14+
% Include MC/DC coverage metrics
15+
16+
17+
%coveragePlugin = matlab.unittest.plugins.CodeCoveragePlugin.forFile(...
18+
% targetFile, 'Producing', format, 'MetricLevel', 'mcdc');
19+
20+
21+
22+
addpath("C:\Users\tagupta\Downloads\common-utils\plugins\sourceFolder");
23+
24+
coveragePlugin = matlab.unittest.plugins.CodeCoveragePlugin.forFolder(...
25+
"C:\Users\tagupta\Downloads\common-utils\plugins\sourceFolder", 'Producing', format, 'MetricLevel', 'mcdc');
26+
27+
plugins(end+1) = coveragePlugin;
28+
29+
% Add the summary plugin with the same format object
30+
summaryPlugin = testframework.CodeCoverageSummaryPlugin(format);
31+
plugins(end+1) = summaryPlugin;
32+
else
33+
plugins = matlab.unittest.plugins.TestRunnerPlugin.empty(1,0);
34+
end
35+
end
36+
end
37+
end
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
classdef CodeCoverageSummaryPlugin < matlab.unittest.plugins.TestRunnerPlugin
2+
% Copyright 2025 The MathWorks, Inc.
3+
4+
properties (Access=private)
5+
CoverageFormat
6+
end
7+
8+
methods
9+
function plugin = CodeCoverageSummaryPlugin(coverageFormat)
10+
% Constructor: accepts CoverageResult format object
11+
plugin.CoverageFormat = coverageFormat;
12+
end
13+
end
14+
15+
methods (Access=protected)
16+
function runSession(plugin, pluginData)
17+
% Checkout MATLAB Test license
18+
license('checkout', 'matlab_test');
19+
20+
% Run the session first (this ensures coverage data is collected)
21+
runSession@matlab.unittest.plugins.TestRunnerPlugin(plugin, pluginData);
22+
23+
% Now extract and save coverage data
24+
if isempty(plugin.CoverageFormat.Result)
25+
warning('CodeCoverageSummaryPlugin:NoCoverageData', ...
26+
'No coverage data collected. Ensure CodeCoveragePlugin is added with the same format object.');
27+
return;
28+
end
29+
30+
result = plugin.CoverageFormat.Result;
31+
disp("result is ");
32+
disp(result);
33+
% Get coverage summaries for all metrics
34+
%[statementCoverage, statementDetails] = coverageSummary(result, "statement");
35+
statementCoverage = coverageSummary(result, "statement");
36+
functionCoverage = coverageSummary(result, "function");
37+
decisionCoverage = coverageSummary(result, "decision");
38+
conditionCoverage = coverageSummary(result, "condition");
39+
mcdcCoverage = coverageSummary(result, "mcdc");
40+
41+
% Create coverage summary structure
42+
coverageDetails = struct();
43+
disp("statement coverage");
44+
disp(statementCoverage);
45+
coverageDetails.StatementCoverage = aggregateCoverage(statementCoverage);
46+
coverageDetails.FunctionCoverage = aggregateCoverage(functionCoverage);
47+
coverageDetails.DecisionCoverage = aggregateCoverage(decisionCoverage);
48+
coverageDetails.ConditionCoverage = aggregateCoverage(conditionCoverage);
49+
coverageDetails.MCDCCoverage = aggregateCoverage(mcdcCoverage);
50+
51+
52+
% % Add all coverage metrics
53+
% coverageDetails.StatementCoverage = struct(...
54+
% 'Executed', statementExec, ...
55+
% 'Total', statementTotal, ...
56+
% 'Percentage', calculatePercentage(statementCoverage) ...
57+
% );
58+
%
59+
% coverageDetails.FunctionCoverage = struct(...
60+
% 'Executed', functionCoverage(1), ...
61+
% 'Total', functionCoverage(2), ...
62+
% 'Percentage', calculatePercentage(functionCoverage) ...
63+
% );
64+
%
65+
% coverageDetails.DecisionCoverage = struct(...
66+
% 'Executed', decisionCoverage(1), ...
67+
% 'Total', decisionCoverage(2), ...
68+
% 'Percentage', calculatePercentage(decisionCoverage) ...
69+
% );
70+
%
71+
% coverageDetails.ConditionCoverage = struct(...
72+
% 'Executed', conditionCoverage(1), ...
73+
% 'Total', conditionCoverage(2), ...
74+
% 'Percentage', calculatePercentage(conditionCoverage) ...
75+
% );
76+
%
77+
% coverageDetails.MCDCCoverage = struct(...
78+
% 'Executed', mcdcCoverage(1), ...
79+
% 'Total', mcdcCoverage(2), ...
80+
% 'Percentage', calculatePercentage(mcdcCoverage) ...
81+
% );
82+
83+
% % Add detailed information
84+
% if ~isempty(functionDetails) && isfield(functionDetails, 'function')
85+
% coverageDetails.FunctionDetails = functionDetails.function;
86+
% end
87+
%
88+
% if ~isempty(statementDetails) && isfield(statementDetails, 'statement')
89+
% coverageDetails.StatementDetails = statementDetails.statement;
90+
% end
91+
%
92+
% if ~isempty(decisionDetails) && isfield(decisionDetails, 'decision')
93+
% coverageDetails.DecisionDetails = decisionDetails.decision;
94+
% end
95+
%
96+
% if ~isempty(conditionDetails) && isfield(conditionDetails, 'condition')
97+
% coverageDetails.ConditionDetails = conditionDetails.condition;
98+
% end
99+
%
100+
% if ~isempty(mcdcDetails) && isfield(mcdcDetails, 'mcdc')
101+
% coverageDetails.MCDCDetails = mcdcDetails.mcdc;
102+
% end
103+
104+
% Determine file path for coverage results
105+
if ~isempty(getenv("RUNNER_TEMP")) && ~isempty(getenv("GITHUB_RUN_ID"))
106+
% GitHub Actions environment
107+
coverageArtifactFile = fullfile(getenv("RUNNER_TEMP"), "matlabCoverageResults" + getenv("GITHUB_RUN_ID") + ".json");
108+
else
109+
% Local environment
110+
coverageArtifactFile = fullfile(pwd, "matlabCoverageResults.json");
111+
end
112+
113+
% If coverage results artifact exists, update the same file
114+
if isfile(coverageArtifactFile)
115+
coverageResults = {jsondecode(fileread(coverageArtifactFile))};
116+
else
117+
coverageResults = {};
118+
end
119+
coverageResults{end+1} = coverageDetails;
120+
121+
JsonCoverageResults = jsonencode(coverageResults, "PrettyPrint", true);
122+
[fID, msg] = fopen(coverageArtifactFile, "w");
123+
if fID == -1
124+
warning("codecoverage:CodeCoverageSummaryPlugin:UnableToOpenFile",...
125+
"Could not open a file for GitHub coverage result table due to: %s", msg);
126+
else
127+
closeFile = onCleanup(@()fclose(fID));
128+
fprintf(fID, '%s', JsonCoverageResults);
129+
disp("Coverage data written to: " + coverageArtifactFile);
130+
end
131+
end
132+
end
133+
end
134+
135+
136+
% Helper function to aggregate coverage data from multiple files
137+
function coverageStruct = aggregateCoverage(coverageMatrix)
138+
% Split the vector in half: first half is executed, second half is total
139+
executed = sum(coverageMatrix(:, 1));
140+
total = sum(coverageMatrix(:, 2));
141+
142+
coverageStruct = struct(...
143+
'Executed', executed, ...
144+
'Total', total, ...
145+
'Percentage', calculatePercentage([executed, total]) ...
146+
);
147+
end
148+
149+
% function to calculate percentage
150+
function percentage = calculatePercentage(coverageData)
151+
if coverageData(2) == 0
152+
percentage = NaN; % Avoid division by zero
153+
else
154+
percentage = (coverageData(1) / coverageData(2)) * 100;
155+
end
156+
end

0 commit comments

Comments
 (0)