Skip to content
Snippets Groups Projects
Verified Commit 47fd8e06 authored by Thomas Lambert's avatar Thomas Lambert :helicopter:
Browse files

fix(input): fix edge cases for input parser

parent 75b10310
No related branches found
No related tags found
No related merge requests found
......@@ -2,8 +2,12 @@ function [alpha, cl, cd, cm, idxOpts] = parsepolarinputs(optList, varargin)
% PARSEPOLARINPUTS Parses the input and checks their validity.
% Returns the arrays for the main polar arrays (alpha, cl, cd, cm), as well as
% the index where the remaining options start.
% If some inputs were not provided (cd, cm), the functions returns an empty
% array for those.
% If some inputs were not provided (alpha, cl, cd, cm), the functions returns
% an empty array for those.
%
% This function also contains checks on every "polar" input (i.e. the Polar
% strcture or the four arrays: alpha, cl, cd and cm) to ensure they are
% correct.
% -----
%
% Usage:
......@@ -13,7 +17,6 @@ function [alpha, cl, cd, cm, idxOpts] = parsepolarinputs(optList, varargin)
% [...] = PARSEPOLARINPUTS (optList, ALPHA, CL, ...)
% [...] = PARSEPOLARINPUTS (optList, ALPHA, CL, CD, ...)
% [...] = PARSEPOLARINPUTS (optList, ALPHA, CL, CD, CM, ...)
%
% -----
% Copyright 2022 Thomas Lambert <t.lambert@uliege.be>
......@@ -24,12 +27,16 @@ function [alpha, cl, cd, cm, idxOpts] = parsepolarinputs(optList, varargin)
import af_tools.utils.*
% First ensures the option list is properly set
validateattributes(optList, {'char','string','cell'},{}, mfilename(), 'optList', 1)
% Initialize outputs
alpha = [];
cl = [];
cd = [];
cm = [];
% Parse inputs
hasNoInput = isempty(varargin) || isoption(optList, varargin{1});
if hasNoInput
idxOpts = 1;
......@@ -48,13 +55,13 @@ else
else
% Parse user-provided arrays
if length(varargin) >= 1
[alpha, idxOpts] = assignvar(optList, alpha, varargin, 1);
if length(varargin) >= 2
[cl, idxOpts] = assignvar(optList, cl, varargin, 2);
if length(varargin) >= 3
[cd, idxOpts] = assignvar(optList, cd, varargin, 3);
if length(varargin) >= 4
[cm, idxOpts] = assignvar(optList, cm, varargin, 4);
[alpha, idxOpts, foundOpts] = assignvar(optList, alpha, varargin, 1);
if length(varargin) >= 2 && ~foundOpts
[cl, idxOpts, foundOpts] = assignvar(optList, cl, varargin, 2);
if length(varargin) >= 3 && ~foundOpts
[cd, idxOpts, foundOpts] = assignvar(optList, cd, varargin, 3);
if length(varargin) >= 4 && ~foundOpts
[cm, idxOpts, ~] = assignvar(optList, cm, varargin, 4);
end
end
end
......@@ -70,8 +77,10 @@ cm = vecttocol(cm);
% Validate input arrays or inputs coming from Polar structure
validateattributes(alpha, {'numeric'},{'real', '2d', 'nonempty','increasing'}, mfilename(), 'alpha', 1)
validateattributes(cl, {'numeric'},{'real', 'nrows', size(alpha,1), 'nonempty'}, mfilename(), 'cl', 2)
if ~isempty(cl)
validateattributes(cl, {'numeric'},{'real', 'nrows', size(alpha,1), 'nonempty'}, mfilename(), 'cl', 2)
end
if ~isempty(cd)
validateattributes(cd, {'numeric'},{'real', 'nrows', size(alpha,1), 'nonempty'}, mfilename(), 'cd', 3)
end
......@@ -80,7 +89,7 @@ if ~isempty(cm)
end
% Standardize alpha size so we have one column per Polar as well
if size(alpha,2) == 1
if size(alpha,2) == 1 && ~isempty(cl)
alpha = repmat(alpha, [1, size(cl,2)]);
end
......@@ -106,12 +115,16 @@ cm = Polar.cm;
end
% ------------------------------------------------------------------------------
function [var, idxOpts] = assignvar(optList, var, data, idx)
function [var, idxOpts, foundOpts] = assignvar(optList, var, data, idx)
% ASSIGNVAR Assign varargin value to a variable, ouptut corresponding idxOpts.
idxOpts = idx+1;
foundOpts = false;
if ~isoption(optList, data{idx})
idxOpts = idx+1;
var = data{idx};
else
idxOpts = idx;
foundOpts = true;
end
end
......@@ -72,7 +72,7 @@ function [alphaExt, clExt, cdExt] = extendpolar(varargin)
% ------------------------------------------------------------------------------
% Check number of inputs
narginchk(0,7);
narginchk(0,8); % Normally 7, but will disregard cm anyway, so 8 is ok
% Import other functions from this package
import af_tools.findstall
......
% TEST_PARSEPOLARINPUTS Unitary tests for the parsepolarinputs sub-function
% Due to the importance of this small utility function, many tests were
% written. This should ensure maximum complicance in all possible context.
% Note:
% Matlab package functionality is not very test-framework friendly.
% Either the whole package has to be imported in EVERY SINGLE TEST or the
% functions must be called as <package>.function() everytime.
% The second option is preferred for the tests as it has the smaller scope.
% -----
% Copyright 2022 Thomas Lambert <t.lambert@uliege.be>
% ULiege - Aeroelasticity and Experimental Aerodynamics
% Apache 2.0 License
% https://gitlab.uliege.be/am-dept/matlab_airfoil_toolbox
% ------------------------------------------------------------------------------
%% Main test function
function tests = test_parsepolarinputs
tests = functiontests(localfunctions);
end
%% Setup and teardown
function setupOnce(testCase) % do not change function name
addpath('../.'); % Add repository to Matlab Path
addpath('./test_utils'); % Add utils to Matlab Path
% Set random number generator settings
testCase.TestData.currentRNG = rng;
end
function teardownOnce(testCase) % do not change function name
rmpath('../.'); % Remove repository from Matlab Path
rmpath('./test_utils'); % Remove utils from Matlab Path
% Restore the random number generator settings
s = testCase.TestData.currentRNG;
rng(s)
end
function teardown(~) % do not change function name
close all; % Close all figures that would have been openend
end
%% Check if errors and warnings are raised
function test_invalidOptsList(testCase)
% Ensures OptList has correct type
empty = [];
scal = 0;
logi = true;
% OptList must be a char, string or cell array
verifyError(testCase, @() af_tools.utils.parsepolarinputs(empty), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(scal), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(logi), 'MATLAB:parsepolarinputs:invalidType')
end
function test_invalidInputTypeWhenNoOpts(testCase)
% Ensures all inputs have the correct type
EMPTY_OPTS = {};
empty = [];
scal = 0;
logi = true;
char = 'test';
Struct = struct;
% 1. Polar (struct) or alpha (numeric)
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, empty), 'MATLAB:parsepolarinputs:expectedNonempty')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, logi), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, char), 'MATLAB:parsepolarinputs:invalidType')
% 2. cl (numeric)
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, logi), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, char), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, Struct), 'MATLAB:parsepolarinputs:invalidType')
% 3. cd (numeric)
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, logi), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, char), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, Struct), 'MATLAB:parsepolarinputs:invalidType')
% 4. cm (numeric)
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, scal, logi), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, scal, char), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, scal, Struct), 'MATLAB:parsepolarinputs:invalidType')
end
function test_invalidInputSizeWhenNoOpts(testCase)
% Ensures the cl, cd and cm have the proper dimenisons
EMPTY_OPTS = {};
dummy = (1:20)';
wrong = rand(8,4);
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, dummy, wrong, dummy, dummy), 'MATLAB:parsepolarinputs:incorrectNumrows')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, dummy, dummy, wrong, dummy), 'MATLAB:parsepolarinputs:incorrectNumrows')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, dummy, dummy, dummy, wrong), 'MATLAB:parsepolarinputs:incorrectNumrows')
end
function test_invalidAlphaInputWhenNoOpts(testCase)
% Ensures that alpha is increasing (row directions)
EMPTY_OPTS = {};
dummy = -(1:20)';
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, dummy), 'MATLAB:parsepolarinputs:expectedIncreasing')
end
function test_invalidPolarInputWhenNoOpts(testCase)
% Ensures that alpha is increasing (row directions)
EMPTY_OPTS = {};
Dummy = dummysinglepolar();
DummyAlpha = rmfield(Dummy,'alpha');
DummyCl = rmfield(Dummy,'cl');
DummyCd = rmfield(Dummy,'cd');
DummyCm = rmfield(Dummy,'cm');
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, DummyAlpha), 'MATLAB:nonExistentField')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, DummyCl), 'MATLAB:nonExistentField')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, DummyCd), 'MATLAB:nonExistentField')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, DummyCm), 'MATLAB:nonExistentField')
end
function test_invalidInputTypeWhithOpts(testCase)
% Ensures all inputs have the correct type
EMPTY_OPTS = {'option1','option2'};
empty = [];
scal = 0;
logi = true;
char = 'test';
Struct = struct;
% 1. Polar (struct) or alpha (numeric)
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, empty), 'MATLAB:parsepolarinputs:expectedNonempty')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, logi), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, char), 'MATLAB:parsepolarinputs:invalidType')
% 2. cl (numeric)
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, logi), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, char), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, Struct), 'MATLAB:parsepolarinputs:invalidType')
% 3. cd (numeric)
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, logi), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, char), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, Struct), 'MATLAB:parsepolarinputs:invalidType')
% 4. cm (numeric)
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, scal, logi), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, scal, char), 'MATLAB:parsepolarinputs:invalidType')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, scal, scal, scal, Struct), 'MATLAB:parsepolarinputs:invalidType')
end
function test_invalidInputSizeWithOpts(testCase)
% Ensures the cl, cd and cm have the proper dimenisons
EMPTY_OPTS = {'option1','option2'};
dummy = (1:20)';
wrong = rand(8,4);
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, dummy, wrong, dummy, dummy), 'MATLAB:parsepolarinputs:incorrectNumrows')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, dummy, dummy, wrong, dummy), 'MATLAB:parsepolarinputs:incorrectNumrows')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, dummy, dummy, dummy, wrong), 'MATLAB:parsepolarinputs:incorrectNumrows')
end
function test_invalidAlphaInputWithOpts(testCase)
% Ensures that alpha is increasing (row directions)
EMPTY_OPTS = {'option1','option2'};
dummy = -(1:20)';
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, dummy), 'MATLAB:parsepolarinputs:expectedIncreasing')
end
function test_invalidPolarInputWithOpts(testCase)
% Ensures that alpha is increasing (row directions)
EMPTY_OPTS = {'option1','option2'};
Dummy = dummysinglepolar();
DummyAlpha = rmfield(Dummy,'alpha');
DummyCl = rmfield(Dummy,'cl');
DummyCd = rmfield(Dummy,'cd');
DummyCm = rmfield(Dummy,'cm');
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, DummyAlpha), 'MATLAB:nonExistentField')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, DummyCl), 'MATLAB:nonExistentField')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, DummyCd), 'MATLAB:nonExistentField')
verifyError(testCase, @() af_tools.utils.parsepolarinputs(EMPTY_OPTS, DummyCm), 'MATLAB:nonExistentField')
end
%% Test if function returns expected outputs
function test_noInput(~)
% TODO: Find how to test if Matlab open uigetfiles prompt
end
function test_emptyInputsInPolar(testCase)
% Check empty inputs in Polar
EMPTY_OPTS = {};
AOA = 1:10;
EmptyPolar = struct('alpha',AOA,'cl',[],'cd',[],'cm',[]);
[alpha, cl, cd, cm, idxOpts] = af_tools.utils.parsepolarinputs(EMPTY_OPTS, EmptyPolar);
verifyEqual(testCase, alpha, AOA');
verifyEqual(testCase, cl, []);
verifyEqual(testCase, cd, []);
verifyEqual(testCase, cm, []);
verifyEqual(testCase, idxOpts, 2);
end
function test_emptyInputsArrays(testCase)
% Check empty arrays as inputs
EMPTY_OPTS = {};
AOA = 1:10;
cl = [];
cd = [];
cm = [];
[alpha, cl, cd, cm, idxOpts] = af_tools.utils.parsepolarinputs(EMPTY_OPTS, AOA, cl, cd, cm);
verifyEqual(testCase, alpha, AOA');
verifyEqual(testCase, cl, []);
verifyEqual(testCase, cd, []);
verifyEqual(testCase, cm, []);
verifyEqual(testCase, idxOpts, 5);
end
function test_normalInputsWithoutOpts(testCase)
% Check normal arrays inputs when options are set but not used
TEST_OPTS = {};
alphaIn = 1:10;
clIn = rand(10,2);
cdIn = rand(10,2);
cmIn = rand(10,2);
[alphaOut, clOut, cdOut, cmOut, idxOpts] = af_tools.utils.parsepolarinputs(TEST_OPTS, alphaIn, clIn, cdIn, cmIn);
verifyEqual(testCase, alphaOut, [alphaIn;alphaIn]');
verifyEqual(testCase, clOut, clIn);
verifyEqual(testCase, cdOut, cdIn);
verifyEqual(testCase, cmOut, cmIn);
verifyEqual(testCase, idxOpts, 5);
end
function test_normalInputsWithOptsNotInArgs(testCase)
% Check normal arrays inputs when options are set but not used
TEST_OPTS = {'option'};
alphaIn = 1:10;
clIn = rand(10,2);
cdIn = rand(10,2);
cmIn = rand(10,2);
[alphaOut, clOut, cdOut, cmOut, idxOpts] = af_tools.utils.parsepolarinputs(TEST_OPTS, alphaIn, clIn, cdIn, cmIn);
verifyEqual(testCase, alphaOut, [alphaIn;alphaIn]');
verifyEqual(testCase, clOut, clIn);
verifyEqual(testCase, cdOut, cdIn);
verifyEqual(testCase, cmOut, cmIn);
verifyEqual(testCase, idxOpts, 5);
end
function test_normalInputsWithOptsInArgs(testCase)
% Check normal arrays inputs when options are set and used
TEST_OPTS = {'option'};
alphaIn = 1:10;
value = 'optionvalue';
[alphaOut, clOut, cdOut, cmOut, idxOpts] = af_tools.utils.parsepolarinputs(TEST_OPTS, alphaIn, 'option', value);
verifyEqual(testCase, alphaOut, alphaIn');
verifyEqual(testCase, clOut, []);
verifyEqual(testCase, cdOut, []);
verifyEqual(testCase, cmOut, []);
verifyEqual(testCase, idxOpts, 2);
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment