diff --git a/.github/badges/tests.svg b/.github/badges/tests.svg
index 7afe4fa..7199f22 100644
--- a/.github/badges/tests.svg
+++ b/.github/badges/tests.svg
@@ -1 +1 @@
-
+
diff --git a/code/+matbox/+tasks/+internal/createBadge.m b/code/+matbox/+tasks/+internal/createBadge.m
index 60a1c01..83af5f6 100644
--- a/code/+matbox/+tasks/+internal/createBadge.m
+++ b/code/+matbox/+tasks/+internal/createBadge.m
@@ -1,4 +1,4 @@
-function createBadge(label, message, color, projectRootDirectory)
+function createBadge(label, message, color, projectRootDirectory, options)
% createBadge - Write a badge as JSON or as a legacy SVG file
%
% Writes a badge description as a JSON file (docs/reports/badges) when
@@ -10,17 +10,23 @@ function createBadge(label, message, color, projectRootDirectory)
% pybadges Python package, for callers without a render step: local runs
% and repositories pinned to older matbox-actions versions. This
% fallback will be removed in a future major release.
+%
+% Optional name/value arguments:
+% Counts - Scalar struct of named counts added to the badge JSON for
+% segmented rendering. The legacy SVG fallback ignores it.
arguments
label (1,1) string
message (1,1) string
color (1,1) string
projectRootDirectory (1,1) string {mustBeFolder}
+ options.Counts struct {mustBeScalarOrEmpty} = struct.empty
end
if strcmpi(getenv("MATBOX_BADGE_FORMAT"), "json")
matbox.utility.writeBadgeJSONFile(label, message, color, ...
- "OutputFolder", fullfile(projectRootDirectory, "docs", "reports", "badges"))
+ "OutputFolder", fullfile(projectRootDirectory, "docs", "reports", "badges"), ...
+ "Counts", options.Counts)
else
matbox.utility.createBadgeSvg(label, message, color, projectRootDirectory)
end
diff --git a/code/+matbox/+tasks/codecheckToolbox.m b/code/+matbox/+tasks/codecheckToolbox.m
index 1fc1dac..d7d4918 100644
--- a/code/+matbox/+tasks/codecheckToolbox.m
+++ b/code/+matbox/+tasks/codecheckToolbox.m
@@ -93,8 +93,16 @@ function createCodeIssuesBadge(issueCount, projectRootDir)
color = "red";
end
+ % The counts let matbox-actions draw one segment per severity. The
+ % label, message and color still describe a complete single badge
+ % for renderers that ignore the counts.
+ counts = struct( ...
+ "error", issueCount.Error, ...
+ "warning", issueCount.Warning, ...
+ "info", issueCount.Info);
+
matbox.tasks.internal.createBadge("code issues", ...
- string(issueCount.Total), color, projectRootDir)
+ string(issueCount.Total), color, projectRootDir, "Counts", counts)
end
function ME = throwUnresolvedCodeIssuesException()
diff --git a/code/+matbox/+utility/writeBadgeJSONFile.m b/code/+matbox/+utility/writeBadgeJSONFile.m
index b5189b0..1658f2c 100644
--- a/code/+matbox/+utility/writeBadgeJSONFile.m
+++ b/code/+matbox/+utility/writeBadgeJSONFile.m
@@ -24,6 +24,10 @@ function writeBadgeJSONFile(label, message, color, projectRootDirectory, options
%
% FileName - Name of file excluding extension
%
+% Counts - Scalar struct of named counts written to a "counts" field,
+% for renderers that draw one segment per count. The field
+% is omitted when Counts is empty (default).
+%
% Example:
%
% writeBadgeJSONFile("Test Coverage", "90%", "green") creates a file
@@ -44,6 +48,7 @@ function writeBadgeJSONFile(label, message, color, projectRootDirectory, options
projectRootDirectory (1,1) string = missing
options.OutputFolder (1,1) string = missing
options.FileName (1,1) string = missing
+ options.Counts struct {mustBeScalarOrEmpty} = struct.empty
end
if ismissing(options.OutputFolder)
@@ -62,6 +67,9 @@ function writeBadgeJSONFile(label, message, color, projectRootDirectory, options
badgeInfo.label = label;
badgeInfo.message = message;
badgeInfo.color = color;
+ if ~isempty(options.Counts)
+ badgeInfo.counts = options.Counts;
+ end
badgeJSON = jsonencode(badgeInfo);
if ismissing(options.FileName)
diff --git a/tools/tests/+matboxtools/+unittest/TasksTest.m b/tools/tests/+matboxtools/+unittest/TasksTest.m
index a6eedb9..d0d795b 100644
--- a/tools/tests/+matboxtools/+unittest/TasksTest.m
+++ b/tools/tests/+matboxtools/+unittest/TasksTest.m
@@ -55,6 +55,23 @@ function testCodecheckToolboxCreatesBadgeJson(testCase)
testCase.verifyEqual(badgeInfo.label, 'code issues')
testCase.verifyTrue(ismember(string(badgeInfo.color), ...
["green", "yellow", "red"]))
+
+ % The per-severity counts must add up to the total in the message
+ testCase.assertTrue(isfield(badgeInfo, "counts"), ...
+ "Expected badge JSON to contain a counts field.")
+ counts = badgeInfo.counts;
+ testCase.verifyEqual(sort(string(fieldnames(counts))), ...
+ ["error"; "info"; "warning"])
+ testCase.verifyEqual(counts.error + counts.warning + counts.info, ...
+ str2double(badgeInfo.message))
+ end
+
+ function testWriteBadgeJSONFileOmitsCountsByDefault(testCase)
+ matbox.utility.writeBadgeJSONFile("tests", "1 passed", "green", ...
+ "OutputFolder", pwd)
+
+ badgeInfo = jsondecode(fileread(fullfile(pwd, "tests.json")));
+ testCase.verifyFalse(isfield(badgeInfo, "counts"))
end
function testPackageToolbox(testCase)