Build out MATLAB test framework and core integration coverage
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user