function summary = generateTests(options) %GENERATETESTS Generate placeholder tests for class files without tests. arguments options.force (1, 1) logical = false options.verbose (1, 1) logical = true end testsRoot = fileparts(mfilename('fullpath')); repoRoot = fileparts(testsRoot); classesRoot = fullfile(repoRoot, 'Classes'); classFiles = collectMFiles(classesRoot); classInfos = struct('className', {}, 'relativeDir', {}, 'classFile', {}); baseNameCounts = containers.Map('KeyType', 'char', 'ValueType', 'double'); for idx = 1:numel(classFiles) classFile = fullfile(classFiles(idx).folder, classFiles(idx).name); if ~isClassFile(classFile) continue end [relativeDir, className] = relativeClassInfo(classFile, classesRoot); classInfos(end + 1) = struct( ... %#ok 'className', className, ... 'relativeDir', relativeDir, ... 'classFile', classFile); key = lower(className); if isKey(baseNameCounts, key) baseNameCounts(key) = baseNameCounts(key) + 1; else baseNameCounts(key) = 1; end end 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 end end