function testresult = setproperty_test()
% SETPROPERTY_TEST  Test function for setproperty
%
% More detailed description of the test goes here.
%
%
%   See also

%% Copyright notice
%   --------------------------------------------------------------------
%   Copyright (C) 2011 <COMPANY>
%       Thijs
%
%       <EMAIL>
%
%       <ADDRESS>
%
%   This library is free software: you can redistribute it and/or
%   modify it under the terms of the GNU Lesser General Public
%   License as published by the Free Software Foundation, either
%   version 2.1 of the License, or (at your option) any later version.
%
%   This library is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%   Lesser General Public License for more details.
%
%   You should have received a copy of the GNU Lesser General Public
%   License along with this library. If not, see <http://www.gnu.org/licenses/>.
%   --------------------------------------------------------------------

% This tools is part of <a href="http://OpenEarth.Deltares.nl">OpenEarthTools</a>.
% OpenEarthTools is an online collaboration to share and manage data and
% programming tools in an open source, version controlled environment.
% Sign up to recieve regular updates of this function, and to contribute
% your own tools.

%% Version <http://svnbook.red-bean.com/en/1.5/svn.advanced.props.special.keywords.html>
% Created: 20 Sep 2011
% Created with Matlab version: 7.11.0.584 (R2010b)

% $Id$
% $Date$
% $Author$
% $Revision$
% $HeadURL$
% $Keywords: $

MTestCategory.WorkInProgress;

% preserve warning state
warningState = warning;
warning('off') %#ok<WNOFF>

%% test simple field assignements
% basic input for testing
OPT1.string             = 'a';
OPT1.uint8              = uint8(1);
OPT1.struct1.single     = single(3);
OPT1.struct1.double     = 6;
OPT1.struct2.a.logical  = true;

% define expected output
OPT2.string             = 'A';
OPT2.uint8              = uint8(1);
OPT2.struct2            = 'nostruct';

% OPTexp is expected OPT struct
OPTexp                  = OPT1;
OPTexp.string           = 'A';
OPTexp.uint8            = uint8(1);
OPTexp.struct2          = 'nostruct';

% SETexp is expected Set struct
SETexp.string           = true;
SETexp.uint8            = true;
SETexp.struct1          = false;
SETexp.struct2          = true;

% DEFexp is expected Default struct
DEFexp.string           = false;
DEFexp.uint8            = true;
DEFexp.struct1          = true;
DEFexp.struct2          = false;

testOutcome             = [];
testMsg                 = [];

% do tests
% input as stuct
lastwarn('');
[OPTtst,SETtst,DEFtst]  = setproperty(OPT1,{OPT2});
testOutcome(end+1)      = isequalwithequalnans(OPTtst,OPTexp);
testOutcome(end+1)      = isequalwithequalnans(SETtst,SETexp);
testOutcome(end+1)      = isequalwithequalnans(DEFtst,DEFexp);
[msg, id]               = lastwarn; 
testMsg(end+1)          = strcmpi(id,''); % verify no warning has been thrown

% input as keyword value
lastwarn('');
[OPTtst,SETtst,DEFtst]  = setproperty(OPT1,{'string','A','uint8',uint8(1),'struct2','nostruct'},'onClassChange','warn');
testOutcome(end+1)      = isequalwithequalnans(OPTtst,OPTexp);
testOutcome(end+1)      = isequalwithequalnans(SETtst,SETexp);
testOutcome(end+1)      = isequalwithequalnans(DEFtst,DEFexp);
[msg, id]               = lastwarn; 
testMsg(end+1)          = strcmpi(id,'SETPROPERTY:ClassChange'); % verify correct warning has been thrown

%% test sub field assignements
% define expected output
clear OPT1 OPT2 OPTexp SETexp DEFexp

OPT1.uint8                = uint8(1);
OPT1.struct1.double       = 1;
OPT1.struct1.single       = single(3);
OPT1.struct2.a.logical    = true;

OPT2.struct1.double       = 1; 
OPT2.struct2.a.logical    = false; 

OPTexp.uint8              = uint8(1);
OPTexp.struct1.double     = 1;
OPTexp.struct2.a.logical  = false;

% SETexp is expected Set struct
SETexp.uint8              = false;
SETexp.struct1            = true;
SETexp.struct2            = true;

% DEFexp is expected Default struct
DEFexp.uint8              = true;
DEFexp.struct1            = false;
DEFexp.struct2            = false;

% test output with expected outcome: 2 structs, latter in cell
lastwarn('');
[OPTtst,SETtst,DEFtst]    = setproperty(OPT1,{OPT2});
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
testOutcome(end+1)        = isequalwithequalnans(SETtst,SETexp);
testOutcome(end+1)        = isequalwithequalnans(DEFtst,DEFexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,''); % verify no warning has been thrown

% test output with expected outcome: n (n=5) structs
lastwarn('');
[OPTtst,SETtst,DEFtst]    = setproperty(OPT1,OPT2,OPT2,OPT2,OPT2,OPT2,OPT2);
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
testOutcome(end+1)        = isequalwithequalnans(SETtst,SETexp);
testOutcome(end+1)        = isequalwithequalnans(DEFtst,DEFexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,''); % verify no warning has been thrown

% test output with expected outcome: 2 structs, latter in cell
lastwarn('');
[OPTtst,SETtst,DEFtst]    = setproperty(OPT1,OPT2,struct2arg(OPT2));
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
testOutcome(end+1)        = isequalwithequalnans(SETtst,SETexp);
testOutcome(end+1)        = isequalwithequalnans(DEFtst,DEFexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,''); % verify no warning has been thrown

% test output with expected outcome: Kees case, encryptographed version of previous case
lastwarn('');
[OPTtst,SETtst,DEFtst]    = setproperty(OPT1, [{OPT2} struct2arg(OPT2)]);
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
testOutcome(end+1)        = isequalwithequalnans(SETtst,SETexp);
testOutcome(end+1)        = isequalwithequalnans(DEFtst,DEFexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,''); % verify no warning has been thrown

% test output with expected outcome: n (n=5) structs, with traling <keyword,values>
% e.g.:  h = harmanal_predict(t,FIT,'onExtraField','silentIgnore')
lastwarn('');
[OPTtst,SETtst,DEFtst]    = setproperty(OPT1,OPT2,OPT2,OPT2,OPT2,OPT2,OPT2,struct2arg(OPT2));
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
testOutcome(end+1)        = isequalwithequalnans(SETtst,SETexp);
testOutcome(end+1)        = isequalwithequalnans(DEFtst,DEFexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,''); % verify no warning has been thrown

% CRASHES: [OPTtst,SETtst,DEFtst]    = setproperty(OPT1,OPT2,OPT2,OPT2,OPT2,OPT2,OPT2,{struct2arg(OPT2)});
% CRASHES: [OPTtst,SETtst,DEFtst]    = setproperty({OPT1},struct2arg(OPT2));
% CRASHES: [OPTtst,SETtst,DEFtst]    = setproperty(OPT1,{struct2arg(OPT2)}); % does not work for substructs

% for property value pairs, substructure assignement keeps rest of
% structures intact
OPTexp.struct1.single     = single(3);
DEFexp.struct1            = true;

SETexp                    = rmfield(SETexp,{'struct1','struct2'});
DEFexp                    = rmfield(DEFexp,{'struct1','struct2'});

SETexp.struct1.single     = false;
SETexp.struct1.double     = true;
SETexp.struct2.a.logical  = true;

DEFexp.struct1.single     = true;
DEFexp.struct1.double     = true;
DEFexp.struct2.a.logical  = false;

lastwarn('');
[OPTtst,SETtst,DEFtst]    = setproperty(OPT1,{'struct1.double',1,'struct2.a.logical',false},'onClassChange','warn');
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
testOutcome(end+1)        = isequalwithequalnans(SETtst,SETexp);
testOutcome(end+1)        = isequalwithequalnans(DEFtst,DEFexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,''); % verify no warning has been thrown

%% test behaviour on different methods: silentAppend, warnAppend, silentignore, warnIgnore and error

lastwarn(''); % reset warning
OPTtst                    = setproperty(OPT1,{'struct2.a.logical',false,'extraProp1',1,'extraProp2.subfield',2},'onExtraField','silentIgnore');
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,''); % verify no warning has been thrown

lastwarn(''); % reset warning
OPTtst                    = setproperty(OPT1,{'struct2.a.logical',false,'extraProp1',1,'extraProp2.subfield',2},'onExtraField','warnIgnore');
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,'SETPROPERTY:ExtraField');  % verify correct warning has been thrown

% adjust OPTexp with appended extraProp fields
OPTexp.extraProp1          = 1;
OPTexp.extraProp2.subfield = 2;

lastwarn(''); % reset warning
OPTtst                    = setproperty(OPT1,{'struct2.a.logical',false,'extraProp1',1,'extraProp2.subfield',2},'onExtraField','silentAppend');
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,''); % verify no warning has been thrown

lastwarn(''); % reset warning
OPTtst                    = setproperty(OPT1,{'struct2.a.logical',false,'extraProp1',1,'extraProp2.subfield',2},'onExtraField','warnAppend');
testOutcome(end+1)        = isequalwithequalnans(OPTtst,OPTexp);
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,'SETPROPERTY:ExtraField');  % verify correct warning has been thrown

try % invalid field name
    OPTtst                = setproperty(OPT1,{'1_nvalidName',1,'validName.@notValidName',2},'onExtraField','error'); %#ok<*NASGU>
catch ME
    testMsg(end+1)        = strcmpi(ME.identifier,'SETPROPERTY:invalidFieldName');  % verify correct error has been thrown
end

try % invalid field name
    OPTtst                = setproperty(OPT1,{'1_nvalidName',1,'validName.@notValidName',2},'onExtraField','error'); %#ok<*NASGU>
catch ME
    testMsg(end+1)        = strcmpi(ME.identifier,'SETPROPERTY:invalidFieldName');  % verify correct error has been thrown
end

%% test warnOnClassChange and errorOnClassChange

lastwarn(''); % reset warning
OPTtst                    = setproperty(OPT1,{'uint8',2},'onClassChange','warn'); %#ok<*NASGU>
[msg, id]                 = lastwarn; 
testMsg(end+1)            = strcmpi(id,'SETPROPERTY:ClassChange');  % verify correct warning has been thrown

try % class change
    OPTtst                = setproperty(OPT1,{'uint8',2},'onClassChange','error'); %#ok<*NASGU>
catch ME
    testMsg(end+1)        = strcmpi(ME.identifier,'SETPROPERTY:ClassChange');  % verify correct error has been thrown
end


%% test error messages on erroneous input

try % too many keywords
    OPTtst                = setproperty(OPT1,{'string','A','double',4,'struct','q'},...
        'onClassChange','warnings','onClassChange','warnings','onClassChange','warnings');
catch ME
    testMsg(end+1)        = strcmpi(ME.identifier,'MATLAB:nargchk:tooManyInputs'); % verify correct error has been thrown
end

try % invalid keyword value pairs
    OPTtst                = setproperty(OPT1,{'string','A','double',4,'struct','q'},'oddKeyword');
catch ME
    testMsg(end+1)        = strcmpi(ME.identifier,'SETPROPERTY:varargin'); % verify correct error has been thrown
end

try % invalid value for onClassChange
    OPTtst                = setproperty(OPT1,{'string','A','double',4,'struct','q'},'onClassChange','warnings');
catch ME
    testMsg(end+1)        = strcmpi(ME.identifier,'SETPROPERTY:InvalidMethod'); % verify correct error has been thrown
end

try % wrong inputs
    OPTtst                = setproperty(OPT1,{'prop1'});
catch ME
    testMsg(end+1)        = strcmpi(ME.identifier,'SETPROPERTY:inputCell'); % verify correct error has been thrown
end

try % wrong inputs, non-struct single input
    OPTtst                = setproperty(OPT1,{'prop1'});
catch ME
    testMsg(end+1)        = strcmpi(ME.identifier,'SETPROPERTY:inputCell'); % verify correct error has been thrown
end

try % wrong inputs, odd number of inputs 
    OPTtst               = setproperty(OPT1,{'prop1',1,'prop2'});
catch ME
    testMsg(end+1)       = strcmpi(ME.identifier,'SETPROPERTY:inputCell'); % verify correct error has been thrown
end

try % wrong inputs, non-char property name
    OPTtst               = setproperty(OPT1,{'prop1',1,2,'prop2'});
catch ME
    testMsg(end+1)       = strcmpi(ME.identifier,'SETPROPERTY:inputCell'); % verify correct error has been thrown
end

OPTtst                    = setproperty(OPT1,[]);
testOutcome(end+1)        = isequalwithequalnans(OPT1,OPTtst);

OPTtst                    = setproperty(OPT1,{[]});
testOutcome(end+1)        = isequalwithequalnans(OPT1,OPTtst);

%%

testresult = all(testOutcome) && all(testMsg);

% preserve
warning(warningState);
