Skip to content

Commit 5e4da84

Browse files
committed
update as per review
1 parent d9e9371 commit 5e4da84

6 files changed

Lines changed: 96 additions & 58 deletions

File tree

action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ inputs:
9191
default: true
9292
code-coverage-metrics:
9393
description: >-
94-
Level of coverage metrics to collect
94+
Code coverage metrics to collect
9595
required: false
9696
default: auto
9797
runs:

plugins/+matlab/+unittest/+internal/+services/+plugins/CodeCoverageSummaryPluginService.m

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,48 @@
77
hasCoverageCobertura = ~isempty(getenv('MW_INPUT_CODE_COVERAGE_COBERTURA'));
88
hasCoverageRequest = hasCoverageHTML || hasCoverageCobertura;
99

10-
if strcmpi(getenv("MW_INPUT_GENERATE_SUMMARY"), "true") && ~hasCoverageRequest
11-
% Get metric level from environment variable
12-
metricLevel = getenv('MW_INPUT_CODE_COVERAGE_METRICS');
10+
metricsStr = strtrim(getenv('MW_INPUT_CODE_COVERAGE_METRICS'));
11+
if strcmpi(getenv("MW_INPUT_GENERATE_SUMMARY"), "true") && ~hasCoverageRequest && ~isempty(metricsStr)
12+
% Parse metrics from environment variable (space-separated)
13+
metrics = strsplit(strtrim(metricsStr));
14+
15+
% Resolve 'auto' to appropriate metrics
16+
if isscalar(metrics) && strcmpi(metrics{1}, 'auto')
17+
if any(strcmp({ver().Name}, 'MATLAB Test')) && license('test', 'MATLAB_Test')
18+
if ~isMATLABReleaseOlderThan("R2023a")
19+
metrics = {'mcdc'};
20+
else
21+
metrics = {'statement'};
22+
end
23+
else
24+
metrics = {'statement'};
25+
end
26+
end
1327

1428
% Create a shared CoverageResult format object
1529
format = matlab.unittest.plugins.codecoverage.CoverageResult;
16-
30+
1731
% Create an array to hold multiple plugins
1832
plugins = matlab.unittest.plugins.TestRunnerPlugin.empty(0);
19-
33+
2034
% Get source folder from environment variable
2135
sourceFolder = getenv('MW_INPUT_SOURCE_FOLDER');
2236
if isempty(sourceFolder)
2337
sourceFolder = pwd;
2438
end
25-
26-
coveragePlugin = matlab.unittest.plugins.CodeCoveragePlugin.forFolder(...
27-
sourceFolder, 'Producing', format, 'MetricLevel', metricLevel);
39+
40+
if isMATLABReleaseOlderThan("R2026b")
41+
coveragePlugin = matlab.unittest.plugins.CodeCoveragePlugin.forFolder(...
42+
sourceFolder, 'Producing', format, 'MetricLevel', metrics);
43+
else
44+
coveragePlugin = matlab.unittest.plugins.CodeCoveragePlugin.forFolder(...
45+
sourceFolder, 'Producing', format, 'Metrics', metrics);
46+
end
2847

2948
plugins(end+1) = coveragePlugin;
30-
49+
3150
% Add the summary plugin with the same format object
32-
summaryPlugin = testframework.CodeCoverageSummaryPlugin(format, metricLevel);
51+
summaryPlugin = testframework.CodeCoverageSummaryPlugin(format, metrics);
3352
plugins(end+1) = summaryPlugin;
3453
else
3554
plugins = matlab.unittest.plugins.TestRunnerPlugin.empty(1,0);

plugins/+testframework/CodeCoverageSummaryPlugin.m

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
properties (Access=private)
55
CoverageFormat
6-
MetricLevel
6+
Metrics
77
end
88

99
methods
10-
function plugin = CodeCoverageSummaryPlugin(coverageFormat, metricLevel)
10+
function plugin = CodeCoverageSummaryPlugin(coverageFormat, metrics)
1111
plugin.CoverageFormat = coverageFormat;
12-
plugin.MetricLevel = metricLevel;
12+
plugin.Metrics = metrics;
1313
end
1414
end
1515

@@ -28,29 +28,28 @@ function runSession(plugin, pluginData)
2828

2929
% Create coverage summary structure
3030
coverageDetails = struct();
31-
coverageDetails.MetricLevel = plugin.MetricLevel;
32-
33-
% Always get function and statement coverage (available for all levels)
31+
32+
% Always get function and statement coverage
3433
functionCoverage = coverageSummary(result, "function");
3534
statementCoverage = coverageSummary(result, "statement");
36-
35+
3736
coverageDetails.FunctionCoverage = sumCoverage(functionCoverage);
3837
coverageDetails.StatementCoverage = sumCoverage(statementCoverage);
39-
40-
% Get decision coverage if metric level is decision, condition, or mcdc
41-
if ismember(plugin.MetricLevel, {'decision', 'condition', 'mcdc'})
38+
39+
% Get decision coverage if metrics contains decision, condition, or mcdc
40+
if any(ismember({'decision', 'condition', 'mcdc'}, plugin.Metrics))
4241
decisionCoverage = coverageSummary(result, "decision");
4342
coverageDetails.DecisionCoverage = sumCoverage(decisionCoverage);
4443
end
45-
46-
% Get condition coverage if metric level is condition or mcdc
47-
if ismember(plugin.MetricLevel, {'condition', 'mcdc'})
44+
45+
% Get condition coverage if metrics contains condition or mcdc
46+
if any(ismember({'condition', 'mcdc'}, plugin.Metrics))
4847
conditionCoverage = coverageSummary(result, "condition");
4948
coverageDetails.ConditionCoverage = sumCoverage(conditionCoverage);
5049
end
51-
52-
% Get MC/DC coverage if metric level is mcdc
53-
if strcmp(plugin.MetricLevel, 'mcdc')
50+
51+
% Get MC/DC coverage if metrics contains mcdc
52+
if any(ismember({'mcdc'}, plugin.Metrics))
5453
mcdcCoverage = coverageSummary(result, "mcdc");
5554
coverageDetails.MCDCCoverage = sumCoverage(mcdcCoverage);
5655
end

src/index.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ async function run() {
1313
const architecture = process.arch;
1414
const workspaceDir = process.cwd();
1515

16+
const codeCoverageMetrics = core.getInput("code-coverage-metrics").toLowerCase().trim();
17+
1618
const options: scriptgen.RunTestsOptions = {
1719
JUnitTestResults: core.getInput("test-results-junit"),
1820
CoberturaCodeCoverage: core.getInput("code-coverage-cobertura"),
@@ -29,19 +31,10 @@ async function run() {
2931
UseParallel: core.getBooleanInput("use-parallel"),
3032
OutputDetail: core.getInput("output-detail"),
3133
LoggingLevel: core.getInput("logging-level"),
34+
CodeCoverageMetrics: codeCoverageMetrics,
3235
};
3336

3437
const generateSummary = core.getBooleanInput("generate-summary");
35-
var codeCoverageMetrics = core.getInput("code-coverage-metrics").toLowerCase();
36-
37-
// Validate metric level
38-
const validMetricLevels = ["auto", "statement", "decision", "condition", "mcdc"];
39-
if (!validMetricLevels.includes(codeCoverageMetrics)) {
40-
core.warning(
41-
`Invalid metric level '${codeCoverageMetrics}'. Using the default value ('auto') instead.`,
42-
);
43-
codeCoverageMetrics = "auto";
44-
}
4538

4639
const command = scriptgen.generateCommand(options);
4740
const startupOptions = core.getInput("startup-options").split(" ");
@@ -51,7 +44,7 @@ async function run() {
5144
env: {
5245
...process.env,
5346
MW_BATCH_LICENSING_ONLINE: "true", // Remove when online batch licensing is the default
54-
MW_INPUT_CODE_COVERAGE_METRICS: codeCoverageMetrics,
47+
MW_INPUT_CODE_COVERAGE_METRICS: options.CodeCoverageMetrics!,
5548
MW_INPUT_SOURCE_FOLDER: options.SourceFolder!, // Add source folder to environment
5649
MW_INPUT_CODE_COVERAGE_HTML: options.HTMLCodeCoverage!,
5750
MW_INPUT_CODE_COVERAGE_COBERTURA: options.CoberturaCodeCoverage!,

src/scriptgen.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2020-2022 The MathWorks, Inc.
1+
// Copyright 2020-2026 The MathWorks, Inc.
22

33
import * as path from "path";
44

@@ -22,6 +22,15 @@ export interface RunTestsOptions {
2222
UseParallel?: boolean;
2323
OutputDetail?: string;
2424
LoggingLevel?: string;
25+
CodeCoverageMetrics?: string;
26+
}
27+
28+
function formatMetricsCellArray(metrics: string | undefined): string {
29+
if (!metrics || metrics.trim() === "") {
30+
return "{}";
31+
}
32+
const items = metrics.trim().split(/\s+/).map(m => `'${m}'`).join(",");
33+
return `{${items}}`;
2534
}
2635

2736
/**
@@ -30,6 +39,7 @@ export interface RunTestsOptions {
3039
* @param options scriptgen options for running tests.
3140
*/
3241
export function generateCommand(options: RunTestsOptions): string {
42+
const metricsCellArray = formatMetricsCellArray(options.CodeCoverageMetrics);
3343
const command = `
3444
addpath('${path.join(import.meta.dirname, "scriptgen")}');
3545
testScript = genscript('Test',
@@ -47,7 +57,8 @@ export function generateCommand(options: RunTestsOptions): string {
4757
'Strict',${options.Strict || false},
4858
'UseParallel',${options.UseParallel || false},
4959
'OutputDetail','${options.OutputDetail || ""}',
50-
'LoggingLevel','${options.LoggingLevel || ""}'
60+
'LoggingLevel','${options.LoggingLevel || ""}',
61+
'Metrics',${metricsCellArray}
5162
);
5263
disp('Running MATLAB script with contents:');
5364
disp(testScript.Contents);

src/scriptgen.unit.test.ts

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2020-2022 The MathWorks, Inc.
1+
// Copyright 2020-2026 The MathWorks, Inc.
22

33
import * as scriptgen from "./scriptgen.js";
44

@@ -20,6 +20,7 @@ describe("command generation", () => {
2020
UseParallel: false,
2121
OutputDetail: "",
2222
LoggingLevel: "",
23+
CodeCoverageMetrics: "",
2324
};
2425

2526
const actual = scriptgen.generateCommand(options);
@@ -40,18 +41,30 @@ describe("command generation", () => {
4041
expect(actual.includes("'UseParallel',false")).toBeTruthy();
4142
expect(actual.includes("'OutputDetail',''")).toBeTruthy();
4243
expect(actual.includes("'LoggingLevel',''")).toBeTruthy();
44+
expect(actual.includes("'Metrics',{}")).toBeTruthy();
4345

4446
const expected =
45-
`genscript('Test', 'JUnitTestResults','', 'CoberturaCodeCoverage','', 'HTMLCodeCoverage','',
46-
'SourceFolder','', 'PDFTestReport','', 'HTMLTestReport','', 'SimulinkTestResults','',
47-
'CoberturaModelCoverage','', 'HTMLModelCoverage','', 'SelectByTag','', 'SelectByFolder','',
48-
'Strict',false, 'UseParallel',false, 'OutputDetail','', 'LoggingLevel','')`.replace(
47+
`genscript('Test', 'JUnitTestResults','', 'CoberturaCodeCoverage','', 'HTMLCodeCoverage','',
48+
'SourceFolder','', 'PDFTestReport','', 'HTMLTestReport','', 'SimulinkTestResults','',
49+
'CoberturaModelCoverage','', 'HTMLModelCoverage','', 'SelectByTag','', 'SelectByFolder','',
50+
'Strict',false, 'UseParallel',false, 'OutputDetail','', 'LoggingLevel','',
51+
'Metrics',{})`.replace(
4952
/\s+/g,
5053
"",
5154
);
5255
expect(actual.replace(/\s+/g, "").includes(expected)).toBeTruthy();
5356
});
5457

58+
it("contains genscript invocation with single metrics value", () => {
59+
const options: scriptgen.RunTestsOptions = {
60+
CodeCoverageMetrics: "statement",
61+
};
62+
63+
const actual = scriptgen.generateCommand(options);
64+
65+
expect(actual.includes("'Metrics',{'statement'}")).toBeTruthy();
66+
});
67+
5568
it("contains genscript invocation with all options specified", () => {
5669
const options: scriptgen.RunTestsOptions = {
5770
JUnitTestResults: "test-results/results.xml",
@@ -69,6 +82,7 @@ describe("command generation", () => {
6982
UseParallel: true,
7083
OutputDetail: "Detailed",
7184
LoggingLevel: "Detailed",
85+
CodeCoverageMetrics: "mcdc type-size",
7286
};
7387

7488
const actual = scriptgen.generateCommand(options);
@@ -97,23 +111,25 @@ describe("command generation", () => {
97111
expect(actual.includes("'UseParallel',true")).toBeTruthy();
98112
expect(actual.includes("'OutputDetail','Detailed'")).toBeTruthy();
99113
expect(actual.includes("'LoggingLevel','Detailed'")).toBeTruthy();
114+
expect(actual.includes("'Metrics',{'mcdc','type-size'}")).toBeTruthy();
100115

101-
const expected = `genscript('Test',
102-
'JUnitTestResults','test-results/results.xml',
116+
const expected = `genscript('Test',
117+
'JUnitTestResults','test-results/results.xml',
103118
'CoberturaCodeCoverage','code-coverage/coverage.xml',
104-
'HTMLCodeCoverage','code-coverage/coverage.html',
119+
'HTMLCodeCoverage','code-coverage/coverage.html',
105120
'SourceFolder','source',
106-
'PDFTestReport','test-results/pdf-results.pdf',
107-
'HTMLTestReport','test-results/html-results.html',
121+
'PDFTestReport','test-results/pdf-results.pdf',
122+
'HTMLTestReport','test-results/html-results.html',
108123
'SimulinkTestResults','test-results/simulinkTest.mldatx',
109-
'CoberturaModelCoverage','test-results/modelcoverage.xml',
110-
'HTMLModelCoverage','test-results/modelcoverage.html',
111-
'SelectByTag','FeatureA',
112-
'SelectByFolder','test/tools;test/toolbox',
113-
'Strict',true,
114-
'UseParallel',true,
115-
'OutputDetail','Detailed',
116-
'LoggingLevel','Detailed' )`.replace(/\s+/g, "");
124+
'CoberturaModelCoverage','test-results/modelcoverage.xml',
125+
'HTMLModelCoverage','test-results/modelcoverage.html',
126+
'SelectByTag','FeatureA',
127+
'SelectByFolder','test/tools;test/toolbox',
128+
'Strict',true,
129+
'UseParallel',true,
130+
'OutputDetail','Detailed',
131+
'LoggingLevel','Detailed',
132+
'Metrics',{'mcdc','type-size'})`.replace(/\s+/g, "");
117133
expect(actual.replace(/\s+/g, "").includes(expected)).toBeTruthy();
118134
});
119135
});

0 commit comments

Comments
 (0)