Skip to content

Commit ad16e6f

Browse files
committed
Complete Parameters test; parameters deals with strings better; added test for repelems
1 parent 29266f0 commit ad16e6f

File tree

5 files changed

+225
-42
lines changed

5 files changed

+225
-42
lines changed

+exp/Parameters.m

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ function set(obj, name, value, description, units)
8181
end
8282

8383
function str = title(obj, name)
84+
% TITLE Turns param struct field name into title for UI label
85+
% Input name must be a fieldname in Struct or cell array thereof.
86+
% The returned str is the fieldname with a space inserted between
87+
% upper case letters, and the first letter capitalized. If units
88+
% field is present, the unit is added to the string in brackets
89+
%
90+
% Example:
91+
% obj.title('numRepeats') % returns 'Num repeats'
92+
% obj.title({'numRepeats', 'rewardVolume'})
93+
% % returns {'Num repeats', 'Reward volume (ul)'}
94+
% See also INDIVTITLE
8495
if iscell(name)
8596
str = mapToCell(@obj.indivTitle, name);
8697
else
@@ -126,7 +137,7 @@ function makeTrialSpecific(obj, name)
126137
n = numTrialConditions(obj); % Number of trial conditions (table rows)
127138
if n < 1; n = 2; end % If there are none, let's add two conditions
128139
% Repeat value accross all trial conditions
129-
if isnumeric(currValue) || islogical(currValue)
140+
if isnumeric(currValue) || islogical(currValue) || isstring(currValue)
130141
newValue = repmat(currValue, 1, n);
131142
else
132143
newValue = repmat({currValue}, 1, n);
@@ -167,7 +178,7 @@ function makeGlobal(obj, name, newValue)
167178
end
168179

169180
function [globalParams, trialParams] = assortForExperiment(obj)
170-
% divide into global and trial-specific parameter structures
181+
% Divide into global and trial-specific parameter structures
171182

172183
%% group trial-specific parameters into a struct with length of parameters
173184
% the second dimension (number of columns) specifies parameters for
@@ -219,6 +230,9 @@ function makeGlobal(obj, name, newValue)
219230
end
220231

221232
function [ctrl, label] = ui(obj, name, parent)
233+
% FIXME method not used at all(?)
234+
% Seems to reuse code put into indivTitle method
235+
% Doesn't return uicontrols if units aren't '°' or 's'
222236
ctrl = [];
223237
label = [];
224238
unitField = [name 'Units'];

cb-tools/burgbox/repelems.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
% specified by the corresponding element of 'n'. e.g.
55
% REPELEMS([0 1 2], [2 1 3]) % returns [0 0 1 2 2 2]
66
%
7+
% See also exp.Parameters/toConditionServer
8+
%
79
% Part of Burgbox
810

911
% 2013-06 CB created

tests/ParamEditorTest.m

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,44 +14,40 @@
1414
end
1515

1616
methods (TestClassSetup)
17-
function killFigures(testCase)
17+
function setup(testCase)
18+
% Hide figures and add teardown function to restore settings
1819
testCase.FigureVisibleDefault = get(0,'DefaultFigureVisible');
1920
% set(0,'DefaultFigureVisible','off');
20-
end
21-
22-
function loadData(testCase)
21+
testCase.addTearDown(@set, 0, 'DefaultFigureVisible', testCase.FigureVisibleDefault);
22+
testCase.addTearDown(@close, testCase.Figure);
23+
2324
% Loads validation data
2425
% Graph data is a cell array where each element is the graph number
2526
% (1:3) and within each element is a cell of X- and Y- axis values
2627
% respecively
27-
% load('data/parameters.mat', 'parameters')
2828
testCase.Parameters = exp.choiceWorldParams;
29-
end
30-
31-
function setupEditor(testCase)
29+
3230
% Check paths file
3331
assert(endsWith(which('dat.paths'), fullfile('tests','+dat','paths.m')));
3432
% Create stand-alone panel
33+
testCase.startMeasuring();
3534
testCase.ParamEditor = eui.ParamEditor;
35+
testCase.stopMeasuring();
3636
testCase.Figure = gcf();
3737
testCase.fatalAssertTrue(isa(testCase.ParamEditor, 'eui.ParamEditor'))
3838
end
39+
3940
end
40-
41-
methods (TestClassTeardown)
42-
function restoreFigures(testCase)
43-
set(0,'DefaultFigureVisible',testCase.FigureVisibleDefault);
44-
close(testCase.Figure)
45-
end
46-
end
47-
41+
4842
methods (TestMethodSetup)
4943
function buildParams(testCase)
5044
% Re-build the parameters before each test so that changes in
5145
% previous test don't persist
5246
PE = testCase.ParamEditor;
5347
pars = exp.Parameters(testCase.Parameters);
48+
testCase.startMeasuring();
5449
PE.buildUI(pars);
50+
testCase.stopMeasuring();
5551
% Number of global parameters: find all text labels
5652
nGlobalLabels = numel(findobj(testCase.Figure, 'Style', 'text'));
5753
nGlobalInput = numel(findobj(testCase.Figure, 'Style', 'checkbox', '-or', 'Style', 'edit'));
@@ -87,8 +83,10 @@ function test_makeConditional(testCase)
8783
% Set the focused object to one of the parameter labels
8884
set(testCase.Figure, 'CurrentObject', ...
8985
findobj(testCase.Figure, 'String', 'rewardVolume'))
86+
testCase.startMeasuring();
9087
testCase.verifyWarningFree(c.MenuSelectedFcn, ...
9188
'Problem making parameter conditional');
89+
testCase.stopMeasuring();
9290
% Verify change in UI elements
9391
testCase.verifyTrue(numel(gLabels()) == nGlobalLabels-1, ...
9492
'Global parameter UI element not removed')

tests/ParametersTest.m

Lines changed: 173 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
properties
44
% Parameters object
55
Parameters
6+
nConditional = 6
67
end
78

89
properties %(MethodSetupParameter)
@@ -13,78 +14,226 @@
1314
methods (TestClassSetup)
1415

1516
function loadData(testCase)
16-
% Loads validation data
17-
% Graph data is a cell array where each element is the graph number
18-
% (1:3) and within each element is a cell of X- and Y- axis values
19-
% respecively
20-
% load('data/parameters.mat', 'parameters')
21-
testCase.ParamStruct = exp.choiceWorldParams;
17+
% Sets a test parameter structure
18+
% testCase.ParamStruct = exp.choiceWorldParams;
19+
n = testCase.nConditional;
20+
testCase.ParamStruct = struct(...
21+
'numRepeats', repmat(100,1,n),...
22+
'numRepeatsUnits', '#',...
23+
'numRepeatsDescription', 'No. of repeats of each condition',...
24+
'charParam', 'test',...
25+
'charParamUnits', 'normalised',...
26+
'charParamDescription', 'test char array parameter',...
27+
'strParam', "testStr",...
28+
'arrayParam', magic(n),...
29+
'arrayParamUnits', 'mW',...
30+
'logicalArrayParam', true(1,n),...
31+
'logicalParam', false,...
32+
'logicalParamUnits', 'logical',...
33+
'doubleParam', 3,...
34+
'functionParam', @(pars,rig)exp.configureChoiceExperiment(exp.ChoiceWorld,pars,rig));
2235
end
2336

2437
function setupClass(testCase)
2538
% Check paths file
2639
assert(endsWith(which('dat.paths'), fullfile('tests','+dat','paths.m')));
2740
% Create stand-alone panel
41+
testCase.startMeasuring('gen_empty_obj');
2842
testCase.Parameters = exp.Parameters();
29-
testCase.fatalAssertTrue(isa(testCase.ParamEditor, 'exp.Parameters'))
43+
testCase.stopMeasuring('gen_empty_obj');
44+
testCase.fatalAssertTrue(isa(testCase.Parameters, 'exp.Parameters'))
3045
end
3146
end
3247

3348
methods (TestMethodSetup)
3449
function buildParams(testCase)
35-
% Re-build the parameters before each test so that changes in
50+
% Re-load the param structure before each test so that changes in
3651
% previous test don't persist
37-
PE = testCase.ParamEditor;
38-
pars = exp.Parameters(testCase.Parameters);
39-
PE.buildUI(pars);
40-
% Number of global parameters: find all text labels
41-
nGlobalLabels = numel(findobj(testCase.Figure, 'Style', 'text'));
42-
nGlobalInput = numel(findobj(testCase.Figure, 'Style', 'checkbox', '-or', 'Style', 'edit'));
43-
% Find Condition Table
44-
conditionTable = findobj(testCase.Figure, '-property', 'ColumnName');
45-
% Ensure all global params have UI input and label
46-
testCase.fatalAssertTrue(nGlobalLabels == numel(PE.Parameters.GlobalNames))
47-
testCase.fatalAssertTrue(nGlobalInput == numel(PE.Parameters.GlobalNames))
48-
% Ensure all conditional params have column in table
49-
testCase.fatalAssertTrue(isequal(size(conditionTable.Data), ...
50-
[PE.Parameters.numTrialConditions, numel(PE.Parameters.TrialSpecificNames)]))
52+
testCase.startMeasuring('setStruct');
53+
testCase.Parameters.Struct = testCase.ParamStruct;
54+
testCase.stopMeasuring('setStruct');
55+
testCase.fatalAssertTrue(length(testCase.Parameters.TrialSpecificNames) == 3)
56+
testCase.fatalAssertTrue(length(testCase.Parameters.GlobalNames) == 5)
57+
testCase.fatalAssertTrue(length(testCase.Parameters.Names) == 8)
5158
end
5259
end
5360

5461
methods (Test)
5562

5663
function test_isTrialSpecific(testCase)
64+
p = testCase.Parameters;
65+
% Verfy that array with n columns > 1 is trial specific
66+
testCase.verifyTrue(p.isTrialSpecific('numRepeats'))
67+
% Verify that array with n columns == 1 is not trial specific
68+
testCase.verifyTrue(~p.isTrialSpecific('doubleParam'))
69+
testCase.verifyTrue(~p.isTrialSpecific('charParam'))
5770
end
5871

5972
function test_numTrialConditions(testCase)
73+
p = testCase.Parameters;
74+
testCase.verifyTrue(p.numTrialConditions == testCase.nConditional)
75+
testCase.verifyTrue(p.numTrialConditions == size(p.Struct.numRepeats,2))
6076
end
6177

6278
function test_title(testCase)
79+
p = testCase.Parameters;
80+
% Test single param title
81+
testCase.verifyEqual(p.title('numRepeats'), 'Num repeats')
82+
% Test array of param titles, including those with units
83+
testCase.startMeasuring('title');
84+
str = p.title({'numRepeats', 'charParam', 'arrayParam', 'logicalParam'});
85+
testCase.stopMeasuring('title');
86+
expected = {'Num repeats', 'Char param', 'Array param (mW)', 'Logical param'};
87+
testCase.verifyTrue(isequal(str, expected))
6388
end
6489

6590
function test_assortForExperiment(testCase)
91+
p = testCase.Parameters;
92+
testCase.startMeasuring('assort');
93+
[globalParams, trialParams] = testCase.verifyWarningFree(@()p.assortForExperiment);
94+
testCase.stopMeasuring('assort');
95+
testCase.verifyTrue(isstruct(globalParams))
96+
testCase.verifyTrue(isequal(fieldnames(globalParams), p.GlobalNames))
97+
testCase.verifyTrue(isequal(size(trialParams), [1 testCase.nConditional]))
98+
testCase.verifyTrue(isequal(fieldnames(trialParams), p.TrialSpecificNames))
6699
end
67100

68101
function test_makeGlobal(testCase)
102+
p = testCase.Parameters;
103+
testCase.startMeasuring('makeGlobal');
104+
p.makeGlobal('numRepeats')
105+
testCase.stopMeasuring('makeGlobal');
106+
testCase.verifyTrue(~p.isTrialSpecific('numRepeats'))
107+
testCase.verifyTrue(numel(p.Struct.numRepeats)==1)
108+
p.makeGlobal('arrayParam')
109+
expected = magic(testCase.nConditional);
110+
expected = expected(:,1);
111+
testCase.verifyTrue(isequal(p.Struct.arrayParam, expected))
112+
p.makeGlobal('logicalArrayParam', false)
113+
testCase.verifyEqual(p.Struct.logicalArrayParam, false)
114+
try
115+
p.makeGlobal('numRepeats')
116+
catch ex
117+
testCase.verifyEqual(ex.message, '''numRepeats'' is already global')
118+
end
69119
end
70120

71121
function test_removeConditions(testCase)
122+
p = testCase.Parameters;
123+
testCase.startMeasuring('removeConditions');
124+
p.removeConditions([2,4,6]);
125+
testCase.stopMeasuring('removeConditions');
126+
% Expected result for arrayParam
127+
expected = magic(testCase.nConditional);
128+
expected = expected(:,[1,3,5]);
129+
130+
testCase.verifyTrue(p.numTrialConditions == 3)
131+
testCase.verifyTrue(isequal(p.Struct.arrayParam, expected))
72132
end
73133

74134
function test_description(testCase)
135+
p = testCase.Parameters;
136+
testCase.verifyEqual(p.description('numRepeats'), 'No. of repeats of each condition')
137+
testCase.verifyTrue(isempty(p.description('arrayParam')))
138+
testCase.verifyTrue(numel(p.description({'numRepeats', 'charParam'}))==2)
75139
end
76140

77141
function test_toConditionServer(testCase)
142+
% Test behaviour when numRepeats is conditional
143+
p = testCase.Parameters;
144+
testCase.startMeasuring('toConditionServer');
145+
[cs, globalParams, trialParams] = p.toConditionServer;
146+
testCase.stopMeasuring('toConditionServer');
147+
testCase.verifyTrue(isa(cs, 'exp.PresetConditionServer'))
148+
testCase.verifyTrue(isstruct(globalParams))
149+
testCase.verifyTrue(isequal(fieldnames(globalParams), p.GlobalNames))
150+
testCase.verifyTrue(isequal(size(trialParams), [1 sum(p.Struct.numRepeats)]))
151+
% Verify randomised
152+
noneRandom = magic(testCase.nConditional);
153+
noneRandom = [repmat(noneRandom(:,1),1,p.Struct.numRepeats(1)) ...
154+
repmat(noneRandom(:,2),1,p.Struct.numRepeats(2))];
155+
result = [trialParams.arrayParam];
156+
testCase.verifyTrue(~isequal(result(:,1:size(noneRandom,2)), noneRandom), ...
157+
'Trial conditions likely not randomised by default')
158+
% Verify that numRepeats was removed
159+
testCase.verifyTrue(strcmp('numRepeats', setdiff(p.TrialSpecificNames,fieldnames(trialParams))))
160+
161+
% Test behaviour when numRepeats is global
162+
p.makeGlobal('numRepeats', 20)
163+
[~, globalParams, trialParams] = p.toConditionServer;
164+
testCase.verifyTrue(isequal(size(trialParams), [1 20*testCase.nConditional]))
165+
testCase.verifyTrue(strcmp('numRepeats', setdiff(p.GlobalNames,fieldnames(globalParams))))
166+
167+
% Test behaviour when randomiseConditions is set
168+
p.set('randomiseConditions', false, ...
169+
'Flag for whether to randomise trial conditions', 'logical')
170+
[~, ~, trialParams] = p.toConditionServer;
171+
result = [trialParams.arrayParam];
172+
noneRandom = repmat(magic(testCase.nConditional), 1, 20);
173+
testCase.verifyTrue(isequal(result, noneRandom), ...
174+
'Trial conditions randomised')
175+
% Test param overide
176+
[~, ~, trialParams] = p.toConditionServer(true);
177+
result = [trialParams.arrayParam];
178+
testCase.verifyTrue(~isequal(result, noneRandom), ...
179+
'Expected trial conditions to be randomised')
78180
end
79181

80182
function test_makeTrialSpecific(testCase)
183+
p = testCase.Parameters;
184+
% Test making simply numerical param trial specific
185+
testCase.startMeasuring('makeTrialSpecific');
186+
p.makeTrialSpecific('doubleParam')
187+
testCase.stopMeasuring('makeTrialSpecific');
188+
testCase.verifyTrue(p.isTrialSpecific('doubleParam'))
189+
testCase.verifyTrue(isequal(p.Struct.doubleParam, repmat(3,1,testCase.nConditional)))
190+
testCase.verifyTrue(ismember('doubleParam', p.TrialSpecificNames))
191+
% Test making char array trial specific
192+
p.makeTrialSpecific('charParam')
193+
testCase.verifyTrue(p.isTrialSpecific('charParam'))
194+
testCase.verifyEqual(numel(p.Struct.charParam), testCase.nConditional)
195+
testCase.verifyTrue(iscell(p.Struct.charParam))
196+
% Test making string trial specific
197+
p.makeTrialSpecific('strParam')
198+
testCase.verifyTrue(p.isTrialSpecific('strParam'))
199+
testCase.verifyTrue(isequal(size(p.Struct.strParam), [1 6]))
200+
testCase.verifyTrue(isstring(p.Struct.strParam))
201+
try
202+
p.makeTrialSpecific('numRepeats')
203+
catch ex
204+
testCase.verifyEqual(ex.message, '''numRepeats'' is already trial-specific')
205+
end
81206
end
82207

83208
function test_set(testCase)
209+
p = testCase.Parameters;
210+
% Test setting simple param with units and description
211+
testCase.startMeasuring('setPar');
212+
p.set('randomiseConditions', true, ...
213+
'Flag for whether to randomise trial conditions', 'logical')
214+
testCase.stopMeasuring('setPar');
215+
testCase.verifyTrue(ismember('randomiseConditions', p.GlobalNames))
216+
testCase.verifyTrue(p.Struct.randomiseConditions == true)
217+
testCase.verifyTrue(isfield(p.Struct, 'randomiseConditionsDescription'))
218+
testCase.verifyTrue(isfield(p.Struct, 'randomiseConditionsUnits'))
219+
220+
% Test setting a conditional parameter
221+
p.set('conditionPar', eye(testCase.nConditional))
222+
testCase.verifyTrue(ismember('conditionPar', p.TrialSpecificNames))
223+
testCase.verifyTrue(isfield(p.Struct, 'conditionParDescription') ...
224+
&& isempty(p.Struct.conditionParDescription))
225+
testCase.verifyTrue(~isfield(p.Struct, 'conditionParUnits'))
226+
227+
% Test setting various arrays
228+
p.set('globalPar', true(testCase.nConditional, 1))
229+
testCase.verifyTrue(ismember('globalPar', p.GlobalNames))
230+
p.set('globalPar', false(1, testCase.nConditional+1))
231+
testCase.verifyTrue(ismember('globalPar', p.TrialSpecificNames))
84232
end
85233

86-
function test_ui(testCase)
87-
end
234+
% function test_ui(testCase)
235+
% p = testCase.Parameters;
236+
% end
88237

89238
end
90239

0 commit comments

Comments
 (0)