Build out MATLAB test framework and core integration coverage

This commit is contained in:
Silas Oettinghaus
2026-03-24 17:23:02 +01:00
parent 9446dfb888
commit a592ebefda
154 changed files with 2281 additions and 1305 deletions

View File

@@ -1,47 +1,123 @@
function generateTests()
function summary = generateTests(options)
%GENERATETESTS Generate placeholder tests for class files without tests.
if ismac
cd('/Users/silasoettinghaus/Documents/MATLAB/imdd_simulation');
else
cd('C:\Users\Silas\Documents\MATLAB\imdd_simulation');
end
% Define the root folders for classes and tests
classesFolder = 'Classes'; % Folder containing the class files
testsFolder = 'Tests'; % Folder where test files should be created
% Create the Test folder if it doesn't exist
if ~exist(testsFolder, 'dir')
mkdir(testsFolder);
arguments
options.force (1, 1) logical = false
options.verbose (1, 1) logical = true
end
% Get all .m files in the Classes folder and its subfolders
classFiles = dir(fullfile(pwd,classesFolder, '**', '*.m'));
testsRoot = fileparts(mfilename('fullpath'));
repoRoot = fileparts(testsRoot);
classesRoot = fullfile(repoRoot, 'Classes');
% Iterate over all class files
for i = 1:length(classFiles)
classFilePath = fullfile(classFiles(i).folder, classFiles(i).name);
[~, className, ~] = fileparts(classFilePath);
classFiles = collectMFiles(classesRoot);
classInfos = struct('className', {}, 'relativeDir', {}, 'classFile', {});
baseNameCounts = containers.Map('KeyType', 'char', 'ValueType', 'double');
% Create corresponding test file name
testFileName = [className, '_test.m'];
testFilePath = strrep(classFilePath, classesFolder, testsFolder); % Replace folder
testFilePath = fullfile(fileparts(testFilePath), testFileName); % Append test filename
for idx = 1:numel(classFiles)
classFile = fullfile(classFiles(idx).folder, classFiles(idx).name);
if ~isClassFile(classFile)
continue
end
% Check if the test file already exists
if ~exist(testFilePath, 'file')
% Create the test subfolder if it doesn't exist
testSubFolder = fileparts(testFilePath);
if ~exist(testSubFolder, 'dir')
mkdir(testSubFolder);
end
[relativeDir, className] = relativeClassInfo(classFile, classesRoot);
% Write the skeleton test file
createTestFile(className, testFilePath);
classInfos(end + 1) = struct( ... %#ok<AGROW>
'className', className, ...
'relativeDir', relativeDir, ...
'classFile', classFile);
key = lower(className);
if isKey(baseNameCounts, key)
baseNameCounts(key) = baseNameCounts(key) + 1;
else
baseNameCounts(key) = 1;
end
end
addpath(genpath(testsFolder))
generated = 0;
skippedExisting = 0;
for idx = 1:numel(classInfos)
info = classInfos(idx);
key = lower(info.className);
testClassName = buildTestClassName( ...
info.className, info.relativeDir, baseNameCounts(key) > 1);
testDir = fullfile(testsRoot, info.relativeDir);
testFile = fullfile(testDir, [testClassName, '.m']);
if ~options.force
if isfile(testFile)
skippedExisting = skippedExisting + 1;
continue
end
end
if ~isfolder(testDir)
mkdir(testDir);
end
createTestFile(testClassName, info.className, info.classFile, testFile);
generated = generated + 1;
end
summary = struct( ...
'generated', generated, ...
'skippedExisting', skippedExisting, ...
'classCount', numel(classInfos));
if options.verbose
fprintf(['Generated %d placeholder tests, skipped %d existing tests, ', ...
'tracked %d class files.\n'], ...
summary.generated, summary.skippedExisting, summary.classCount);
end
end
function tf = isClassFile(filePath)
source = fileread(filePath);
tf = contains(source, 'classdef');
end
function [relativeDir, className] = relativeClassInfo(classFile, classesRoot)
relativePath = strrep(classFile, [classesRoot, filesep], '');
[relativeDir, className] = fileparts(relativePath);
end
function testClassName = buildTestClassName(className, relativeDir, hasDuplicateBaseName)
if ~hasDuplicateBaseName
testClassName = [className, '_test'];
else
if isempty(relativeDir)
prefix = 'root';
else
prefix = regexprep(relativeDir, '[^A-Za-z0-9]+', '_');
prefix = regexprep(prefix, '^_+|_+$', '');
end
testClassName = [prefix, '_', className, '_test'];
end
if ~isletter(testClassName(1))
testClassName = ['T_', testClassName];
end
end
function files = collectMFiles(rootFolder)
pathString = genpath(rootFolder);
folders = regexp(pathString, pathsep, 'split');
folders = folders(~cellfun('isempty', folders));
files = dir(fullfile(rootFolder, '*.m'));
files = files([]); % empty struct with dir() fields
for idx = 1:numel(folders)
currentFiles = dir(fullfile(folders{idx}, '*.m'));
if isempty(currentFiles)
continue
end
currentFiles = currentFiles(~[currentFiles.isdir]);
files = [files; currentFiles]; %#ok<AGROW>
end
end