diff --git a/.circleci/test-deploy.yml b/.circleci/test-deploy.yml index 9a1118a..93680b3 100644 --- a/.circleci/test-deploy.yml +++ b/.circleci/test-deploy.yml @@ -1,4 +1,4 @@ -version: 2.1 +version: 2.1 orbs: matlab: mathworks/matlab@dev:<> orb-tools: circleci/orb-tools@11.1 diff --git a/generateFolderSelectionStatement.m b/generateFolderSelectionStatement.m new file mode 100644 index 0000000..039e357 --- /dev/null +++ b/generateFolderSelectionStatement.m @@ -0,0 +1,25 @@ +function finalStatement= generateFolderSelectionStatement(statement) +import scriptgen.internal.unquoteText; +import scriptgen.internal.isAbsolutePath; + +statement = strtrim(strsplit(statement, {';', ':'})); +statement = cellfun(@(t) ['''' t ''''], strrep(statement, '''', ''''''), 'UniformOutput', false); + +constraints = {}; + +for i = 1:numel(statement) + folder = statement{i}; + + if ~strcmp(folder, unquoteText(folder)) && ~isAbsolutePath(unquoteText(folder)) + constraint = sprintf('StartsWithSubstring(fullfile(pwd, %s))', folder); + else + constraint = sprintf('StartsWithSubstring(%s)', folder); + end + + constraints{end+1} = constraint; %#ok +end + +statement = sprintf('HasBaseFolder(%s)', strjoin(constraints, ' | ')); +finalStatement = sprintf('suite = suite.selectIf(%s);', statement); +end + diff --git a/getCircleCISplitFiles.m b/getCircleCISplitFiles.m new file mode 100644 index 0000000..5dd35b2 --- /dev/null +++ b/getCircleCISplitFiles.m @@ -0,0 +1,95 @@ +function stdout = getCircleCISplitFiles(paramSplitType, paramSelectByTag, paramSelectByFolder, paramSourceFolder) +% getCircleCISplitFiles - splits test files using CircleCI split command. +% +% The getCircleCISplitFiles function provides a convenient way to +% split test files based on specified parameters. +% +% STDOUT = getCircleCISplitFiles(PARAMSPLITTYPE, PARAMSELECTBYTAG, +% PARAMSELECTBYFOLDER, PARAMSOURCEFOLDER) generates and returns a list of +% split test files. The parameters are as follows: +% - PARAMSPLITTYPE: The type of split (e.g., 'timings', 'filename' or 'filesize'). +% - PARAMSELECTBYTAG: A tag to filter tests by. +% - PARAMSELECTBYFOLDER: folder(s) to filter tests by. +% - PARAMSOURCEFOLDER: The source folder(s) to add to the path. +% +% These parameters are used to update the test suite and extract the list +% of MATLAB test files to be passed to the CircleCI test split command. +% +% Examples: +% +% resultFiles = getCircleCISplitFiles('timings', 'MyTag', 'MyFolder', 'src'); +% disp(resultFiles); + + + import matlab.unittest.selectors.HasTag; + import matlab.unittest.constraints.StartsWithSubstring; + import matlab.unittest.plugins.XMLPlugin; + import matlab.unittest.selectors.HasBaseFolder; + + if ~isempty(paramSourceFolder) + dirs = strtrim(strsplit(paramSourceFolder, {';', ':'})); + for i = numel(dirs):-1:1 + statement{i} = sprintf('addpath(genpath(''%s''));', strrep(dirs{i}, '''', '''''')); + eval(statement{i}); + end + end + + suite = testsuite(pwd, 'IncludingSubfolders', true); + + if ~isempty(paramSelectByTag) + Statement = sprintf('suite = suite.selectIf(HasTag(''%s''));', paramSelectByTag); + eval(Statement); + end + + if ~isempty(paramSelectByFolder) + Statement = generateFolderSelectionStatement(paramSelectByFolder); + eval(Statement); + end + + testFilePaths = {}; + + for i = 1:numel(suite) + baseFolder = suite(i).BaseFolder; + relativePath = strrep(baseFolder, [pwd, filesep], ''); + testClass = suite(i).TestParentName; + + if isstring(testClass) + testClass = char(testClass); + end + + testFilePath = fullfile(relativePath, strcat(testClass, '.m')); + testFilePaths{end+1} = testFilePath; + end + + testNames= unique(testFilePaths); + + if strcmp(paramSplitType, 'timings') + [~, testNames, ~] = cellfun(@fileparts, testNames, 'UniformOutput', false); + end + + tempAllFile = tempname; + tempErrorFile = tempname; + fid = fopen(tempAllFile, 'w'); + fprintf(fid, '%s\n', testNames{:}); + fclose(fid); + + if strcmp(paramSplitType, 'filename') + command = sprintf('circleci tests split %s 2> %s', tempAllFile, tempErrorFile); + else + command = sprintf('circleci tests split --split-by=%s %s 2> %s', paramSplitType, tempAllFile, tempErrorFile); + end + [~, stdout] = system(command); + + stderr = fileread(tempErrorFile); + disp(stderr); + + + stdout = strsplit(stdout, '\n'); + stdout = stdout(~cellfun('isempty', stdout)); + stdout = strtrim(stdout); + stdout = stdout(:)'; + [~, stdout, ~] = cellfun(@fileparts, stdout, 'UniformOutput', false); + + delete(tempAllFile); + delete(tempErrorFile); +end \ No newline at end of file diff --git a/src/commands/run-tests.yml b/src/commands/run-tests.yml index dfd7e88..23e1bed 100644 --- a/src/commands/run-tests.yml +++ b/src/commands/run-tests.yml @@ -40,6 +40,11 @@ parameters: parameter requires a Parallel Computing Toolbox license. type: boolean default: false + split-type: + description: > + Option to use CircleCI Test Split feature, specified as 'timings', 'filename' or 'filesize'. + type: string + default: '' output-detail: description: > Amount of event detail displayed for the test run, specified as `none`, `terse`, `concise`, `detailed`, @@ -128,6 +133,7 @@ steps: PARAM_TEST_RESULTS_PDF: <> PARAM_STRICT: <> PARAM_USE_PARALLEL: <> + PARAM_SPLIT_TYPE: <> PARAM_OUTPUT_DETAIL: <> PARAM_LOGGING_LEVEL: <> PARAM_STARTUP_OPTIONS: <> diff --git a/src/scripts/run-command.sh b/src/scripts/run-command.sh index b90ce1a..39b0c26 100644 --- a/src/scripts/run-command.sh +++ b/src/scripts/run-command.sh @@ -52,4 +52,4 @@ ${PARAM_COMMAND} EOF # run MATLAB command -"${tmpdir}/bin/run-matlab-command$binext" "setenv('MW_ORIG_WORKING_FOLDER', cd('${scriptdir//\'/\'\'}'));$script" $PARAM_STARTUP_OPTIONS +"${tmpdir}/bin/run-matlab-command$binext" "setenv('MW_ORIG_WORKING_FOLDER', cd('${scriptdir//\'/\'\'}'));$script" $PARAM_STARTUP_OPTIONS \ No newline at end of file diff --git a/src/scripts/run-tests.sh b/src/scripts/run-tests.sh index 2cb5698..8aafed2 100644 --- a/src/scripts/run-tests.sh +++ b/src/scripts/run-tests.sh @@ -61,24 +61,32 @@ if [[ "$os" = CYGWIN* || "$os" = MINGW* || "$os" = MSYS* ]]; then fi "${tmpdir}/bin/run-matlab-command$binext" "\ - addpath('${gendir}/scriptgen');\ - testScript = genscript('Test',\ - 'JUnitTestResults','${PARAM_TEST_RESULTS_JUNIT}',\ - 'CoberturaCodeCoverage','${PARAM_CODE_COVERAGE_COBERTURA}',\ - 'HTMLCodeCoverage','${PARAM_CODE_COVERAGE_HTML}',\ - 'SourceFolder','${PARAM_SOURCE_FOLDER}',\ - 'SelectByFolder','${PARAM_SELECT_BY_FOLDER}',\ - 'SelectByTag','$PARAM_SELECT_BY_TAG',\ - 'CoberturaModelCoverage','${PARAM_MODEL_COVERAGE_COBERTURA}',\ - 'HTMLModelCoverage','${PARAM_MODEL_COVERAGE_HTML}',\ - 'SimulinkTestResults','${PARAM_TEST_RESULTS_SIMULINK_TEST}',\ - 'HTMLTestReport','${PARAM_TEST_RESULTS_HTML}',\ - 'PDFTestReport','${PARAM_TEST_RESULTS_PDF}',\ - 'Strict',${PARAM_STRICT},\ - 'UseParallel',${PARAM_USE_PARALLEL},\ - 'OutputDetail','${PARAM_OUTPUT_DETAIL}',\ - 'LoggingLevel','${PARAM_LOGGING_LEVEL}');\ - disp('Running MATLAB script with contents:');\ - disp(testScript.Contents);\ - fprintf('__________\n\n');\ - run(testScript);" $PARAM_STARTUP_OPTIONS +addpath('${gendir}/scriptgen');\ +if ~isempty('${PARAM_SPLIT_TYPE}') + circleciTestFiles = getCircleCISplitFiles('${PARAM_SPLIT_TYPE}', '${PARAM_SELECT_BY_TAG}', '${PARAM_SELECT_BY_FOLDER}', '${PARAM_SOURCE_FOLDER}'); +else + circleciTestFiles = ''; +end;\ +testScript = genscript('Test', ... + 'JUnitTestResults', '${PARAM_TEST_RESULTS_JUNIT}', ... + 'CoberturaCodeCoverage', '${PARAM_CODE_COVERAGE_COBERTURA}', ... + 'HTMLCodeCoverage', '${PARAM_CODE_COVERAGE_HTML}', ... + 'SourceFolder', '${PARAM_SOURCE_FOLDER}', ... + 'SelectByFolder', '${PARAM_SELECT_BY_FOLDER}', ... + 'SelectByTag', '${PARAM_SELECT_BY_TAG}', ... + 'CoberturaModelCoverage', '${PARAM_MODEL_COVERAGE_COBERTURA}', ... + 'HTMLModelCoverage', '${PARAM_MODEL_COVERAGE_HTML}', ... + 'SimulinkTestResults', '${PARAM_TEST_RESULTS_SIMULINK_TEST}', ... + 'HTMLTestReport', '${PARAM_TEST_RESULTS_HTML}', ... + 'PDFTestReport', '${PARAM_TEST_RESULTS_PDF}', ... + 'Strict', ${PARAM_STRICT}, ... + 'UseParallel', ${PARAM_USE_PARALLEL}, ... + 'OutputDetail', '${PARAM_OUTPUT_DETAIL}', ... + 'LoggingLevel', '${PARAM_LOGGING_LEVEL}', ... + 'CircleCITestFiles', circleciTestFiles);\ +disp('Running MATLAB script with contents:');\ +disp(testScript.Contents);\ +fprintf('__________\n\n');\ +run(testScript);" $PARAM_STARTUP_OPTIONS + + \ No newline at end of file