function test_suite = test_parcellfun
% tests for cosmo_cartprod
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
test_functions = localfunctions();
catch % no problem; early Matlab versions can use initTestSuite fine
end
initTestSuite;
function test_parcellfun_single_proc()
nproc = 1;
helper_test_parcellfun(nproc);
function test_cosmo_parallel_get_nproc_available()
warning_state = cosmo_warning();
cleaner = onCleanup(@()cosmo_warning(warning_state));
cosmo_warning('reset');
cosmo_warning('off');
nproc = cosmo_parallel_get_nproc_available();
assert(nproc >= 1);
% should not have shown any warnings
w = cosmo_warning();
assert(isempty(w.shown_warnings), sprintf('warning shown: %s', ...
w.shown_warnings{:}));
function test_parcellfun_multi_proc()
nproc = cosmo_parallel_get_nproc_available();
if nproc == 1
cosmo_notify_test_skipped('No parallel process available');
return
end
helper_test_parcellfun(nproc);
function helper_test_parcellfun(nproc)
warning_state = cosmo_warning();
cleaner = onCleanup(@()cosmo_warning(warning_state));
cosmo_warning('off');
% try various functions
funcs = {@func_identity, ...
@func_reverse, ...
@numel, ...
@(x)numel(x) > 0 ...
};
% try with both uniform output and without
arg_cell = {{}, ...
{'UniformOutput', false}};
% various number of dimensions
ndim = {0, 1, 2, 3, 4}; % 0=empty
combis = cosmo_cartprod({funcs, arg_cell, ndim});
n = size(combis, 1);
for c_i = 1:n
combi = combis(c_i, :);
func_arg = combi{1};
other_arg = combi{2};
rand_str_ndim = combi{3};
rand_cellstr = generate_rand_cellstr(rand_str_ndim);
func = @()cosmo_parcellfun(nproc, func_arg, rand_cellstr, ...
other_arg{:});
ref_func = @() cellfun(func_arg, rand_cellstr, other_arg{:});
assert_equal_result_or_both_exception_thrown(func, ref_func);
end
function assert_equal_result_or_both_exception_thrown(f, g)
try
f_result = f();
f_exception = false;
catch
f_exception = lasterror();
end
try
g_result = g();
g_exception = false;
catch
g_exception = lasterror();
end
f_threw_exception = isstruct(f_exception);
g_threw_exception = isstruct(g_exception);
if f_threw_exception
if ~g_threw_exception
f_exception.message = sprintf('only f threw exception: %s', ...
f_exception.message);
rethrow(f_exception);
end
else
if g_threw_exception
g_exception.message = sprintf('only g threw exception: %s', ...
g_exception.message);
rethrow(g_exception);
end
assertEqual(f_result, g_result);
end
function test_illegal_arguments
warning_state = cosmo_warning();
cleaner = onCleanup(@()cosmo_warning(warning_state));
cosmo_warning('off');
aet = @(varargin)assertExceptionThrown(@() ...
cosmo_parcellfun(varargin{:}), '');
% first argument is not scalar integer
aet(0, @numel, {1, 2});
aet(1.5, @numel, {1, 2});
aet([2 3], @numel, {1, 2});
% second argument is not a function handle
aet(2, 'a', {1, 2});
aet(2, cell(0), {1, 2});
% third argument is not a cell
aet(2, @numel, [1, 2]);
aet(2, @numel, struct);
% no uniform output
% (note: Octave accepts output when using
% @(x)[x x]
% This may be a bug)
aet(1, @(x) repmat(x, 1, x), {1; 2});
aet(2, @(x) repmat(x, 1, x), {1; 2});
function rand_cellstr = generate_rand_cellstr(ndim)
switch ndim
case 0
sz = [0, 0];
case 1
sz = [0, 10];
case 2
sz = [1, 1];
otherwise
sz = floor(rand(1, 1 + ndim) * 4);
end
randstr = @(unused)char(rand(1, floor(rand() * 10)) * 24 + 65);
rand_cellstr = arrayfun(randstr, zeros(sz), 'UniformOutput', false);
function y = func_identity(x)
y = x;
function y = func_reverse(x)
y = x;
y(end:-1:1) = x(:);