function TTEST_SETUP;
% TTEST_SETUP
% This file generates all test functions
% For safety reasons, it should be called from the directory where this functions resides
% Files starting with %TTEST_AUTOGENERATE are overwritten silently
%
% See also: INIT

% Todo: Write: _EXIT, _DEATH
%              ADD_FAILURE(), ADD_FAILURE_AT("file_path", line_number);
%       Return test name in error message

 %#ok<*DEFNU>

printwarning = false;
olddir = '';
if( exist(fullfile(pwd, 'INIT'), 'file')~=2 );
    olddir = cd( fileparts(which('INIT')) );
    printwarning = true; end;

fprintf( 'The TTEST suite is installed in the directory: %s\n', pwd );
if( printwarning );
    fprintf( 'Warning: Function should be called from the directory where  ''TTEST_SETUP.m'' is located.\n' ); 
    a = input( 'Do you want to proceed (y/n): ', 's' ); 
    if( ~isequal(a,'y') );
        return; end; end;

GENERATE_TEST_FUNCTION( 'FAIL' );
GENERATE_TEST_FUNCTION( 'SUCCEED' );
GENERATE_TEST_FUNCTION( 'FALSE' );
GENERATE_TEST_FUNCTION( 'TRUE' );

GENERATE_TEST_FUNCTION( 'EQ' );
GENERATE_TEST_FUNCTION( 'NE' );
GENERATE_TEST_FUNCTION( 'LE' );
GENERATE_TEST_FUNCTION( 'LT' );
GENERATE_TEST_FUNCTION( 'GE' );
GENERATE_TEST_FUNCTION( 'GT' );
GENERATE_TEST_FUNCTION( 'STREQ' );
GENERATE_TEST_FUNCTION( 'STRNE' );
GENERATE_TEST_FUNCTION( 'STRCASEEQ' );
GENERATE_TEST_FUNCTION( 'STRCASENE' );

GENERATE_TEST_FUNCTION( 'PRED' );
GENERATE_TEST_FUNCTION( 'NPRED' );
GENERATE_TEST_FUNCTION( 'NEAR' );
GENERATE_TEST_FUNCTION( 'DOUBLE_EQ' );
GENERATE_TEST_FUNCTION( 'FLOAT_EQ' );  %FLOAT_EQ and SINGLE_EQ are the same function
GENERATE_TEST_FUNCTION( 'SINGLE_EQ' ); %FLOAT_EQ is provided to have the same interface as GTESTs

GENERATE_TEST_FUNCTION( 'WARNING' );
GENERATE_TEST_FUNCTION( 'ERROR' );
GENERATE_TEST_FUNCTION( 'NO_THROW' );

if( ~isempty(olddir) );
    cd( olddir ); end;

        
end


function GENERATE_TEST_FUNCTION( name, ~ )

    message = [ '%TTEST_AUTOGENERATE' newline '%This file is auto-generated. Do not modify it. Changes may be overwritten.' newline];

    %make function name, filename
    functionname_exp = ['EXPECT_' name];
    functionname_ass = ['ASSERT_' name];
    
    filename{1} = [fullfile( pwd, functionname_exp ) '.m'];
    filename{2} = [fullfile( pwd, functionname_ass ) '.m'];
    
    %make header
    header{1} = ['function ret = ' functionname_exp '( varargin )' newline newline];
    header{2} = ['function ret = ' functionname_ass '( varargin )' newline newline];
        
    %make pre-test part
    pretestpart1{1} = ['global TTEST_ERROR_EXPECT;' newline ];
    pretestpart1{2} = ['global TTEST_ERROR_ASSERT;' newline ];   
    
    pretestpart2 = [
        'global TTEST_ERRORFLAG;' newline newline ...
        'ret = true;' newline ...
        'try;' newline ];   
      
    %make post-test part
    posttestpart1 = [
        'catch;' newline ...
        '    error( ''TTEST_EVAL:format'', ''Wrong format of varargin'' ); end;' newline newline...
        'if( isequal(ret,false) );' newline   ...
        '    TTEST_ERRORFLAG = true;' newline newline];
    
    posttestpart2{1} = [
        '    try;' newline ...
        '        if( isequal(TTEST_ERROR_EXPECT,''exp'') );' newline ...
        '            TTEST_ERROR_EXPECT_WORKER();' newline ...
        '        elseif( isequal(TTEST_ERROR_EXPECT,''ass'') );' newline ...
        '            TTEST_ERROR_ASSERT_WORKER();' newline ...
        '        else;' newline ...
        '            feval( TTEST_ERROR_EXPECT ); end;' newline];
    posttestpart2{2} = [
        '    try;' newline ...
        '        if( isequal(TTEST_ERROR_ASSERT,''exp'') );' newline ...
        '            TTEST_ERROR_EXPECT_WORKER();' newline ...
        '        elseif( isequal(TTEST_ERROR_ASSERT,''ass'') );' newline ...
        '            TTEST_ERROR_ASSERT_WORKER();' newline ...
        '        else;' newline ...
        '            feval( TTEST_ERROR_ASSERT ); end;' newline];
    
    posttestpart3 = [
        '    catch me;' newline ...
        '        if( isequal(me.identifier,''MATLAB:assertion:failed'') );' newline  ...
        '            assert( false ); ' newline  ...
        '        else; ' newline  ...
        '            rethrow( me ); end; end; end;' newline];
    endline = ['end' newline newline];
        
  
    %save file
    for i = 1:2
        text{i} = [message header{i} pretestpart1{i} pretestpart2 eval(name) posttestpart1 posttestpart2{i} posttestpart3 endline]; %#ok<AGROW>
        try
            val = fileread( filename{i} );
            if( numel(val)>=19 && isequal(val(1:19),'%TTEST_AUTOGENERATE') )
                overwrite = true; 
            else
                overwrite = false; end
        catch
            overwrite = true; end;
        
        if( ~overwrite );
            fprintf( 'Do you want to overwrite the file: %s\n', filename{i} );
            a = input( 'y/n: ', 's' ); 
            if( isequal(a,'y') );
                overwrite = true; end; end;
            
        if( overwrite )
            fid = fopen( filename{i}, 'wt' );
            fprintf( fid, '%s', text{i} );
            if( fid>=0 )
                fclose( fid ); end; end; end;
end

% test codes
%%%%%%%%%%%%%%%%%%%%%
function ret = FAIL
ret = [
    '    ret = false;' newline];
end

function ret = SUCCEED
ret = ['    ret = true;' newline];
end
    
function ret = EQ 
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( ~isequal(varargin{1},varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs == rhs (but they should be @isequal), where'');' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = NE 
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( isequal(varargin{1},varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs ~= rhs (but they should be @isequal), where'' );' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = LE
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( ~(varargin{1}<=varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs > rhs (but they should be <=), where'' );' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = LT
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( ~(varargin{1}<varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs >= rhs (but they should be <), where'' );' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = GE
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( ~(varargin{1}>=varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs < rhs (but they should be >=), where'' );' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = GT
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( ~(varargin{1}>varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs <= rhs (but they should be >), where'' );' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = STREQ
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( ~isstring(varargin{1}) && ~ischar(varargin{1}) || ~isstring(varargin{2}) && ~ischar(varargin{2}) || ~strcmp(varargin{1},varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs ~= rhs (but they should be ==), where'' );' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = STRNE
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( ~isstring(varargin{1}) && ~ischar(varargin{1}) || ~isstring(varargin{2}) && ~ischar(varargin{2}) || strcmp(varargin{1},varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs == rhs (but they should be ~=), where'' );' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = STRCASEEQ
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( ~isstring(varargin{1}) && ~ischar(varargin{1}) || ~isstring(varargin{2}) && ~ischar(varargin{2}) || ~strcmpi(varargin{1},varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs ~= rhs (but they should be == up to case), where'' );' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = STRCASENE
ret = [
    '    while( ~isempty(varargin) );' newline ...
    '        if( ~isstring(varargin{1}) && ~ischar(varargin{1}) || ~isstring(varargin{2}) && ~ischar(varargin{2}) || strcmpi(varargin{1},varargin{2}) );' newline ...
    '            ret = false;' newline ...
    '            TTEST_FPRINTF( ''\nlhs == rhs up to case (but they should be ~=), where'' );' newline ...
    '            TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '            TTEST_DISP( varargin{1} );' newline ...
    '            TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '            TTEST_DISP( varargin{2} ); end;' newline ...
    '    varargin(1:2) = []; end;' newline];
end

function ret = TRUE
ret = [
    '   if( ~varargin{1} );' newline ...
    '       TTEST_FPRINTF( ''\nArgument is false (but should be true), where'' );' newline ...
    '       TTEST_FPRINTF( ''\nargument = '' );' newline ...
    '       TTEST_DISP( varargin{1} );' newline ...    
    '       ret = false; end;' newline];
end

function ret = FALSE
ret = [
    '   if( varargin{1} );' newline ...
    '       TTEST_FPRINTF( ''\nArgument is true (but should be false), where'' );' newline ...
    '       TTEST_FPRINTF( ''\nargument = '' );' newline ...
    '       TTEST_DISP( varargin{1} );' newline ...        
    '       ret = false; end;' newline];
end

function ret = PRED
ret = [
    '    pred = varargin{1};' newline ...
    '    val = varargin(2:end);' newline ...
    '    if( ~pred(val{:}) );' newline ...
    '        ret = false;' newline ...
    '        TTEST_FPRINTF( ''Value(s) do(es) not fulfil predicate (but it/they should), where'' );' newline ...
    '        TTEST_FPRINTF( ''\nPredicate = '' );' newline ...    
    '        TTEST_DISP( pred );' newline ...
    '        for ii = 1:numel( val );' newline ...
    '            TTEST_FPRINTF( [''\nValue '' num2str(ii) '' = ''] );' newline ...
    '            TTEST_DISP( val{ii} ); end; end;' newline];
end

function ret = NPRED
ret = [
    '    pred = varargin{1};' newline ...
    '    val = varargin(2:end);' newline ...
    '    if( pred(val{:}) );' newline ...
    '        ret = false;' newline ...
    '        TTEST_FPRINTF( ''Value(s) do(es) fulfil predicate (but it/they should), where'' );' newline ...
    '        TTEST_FPRINTF( ''\nPredicate = '' );' newline ...    
    '        TTEST_DISP( pred );' newline ...
    '        for ii = 1:numel( val );' newline ...
    '            TTEST_FPRINTF( [''\nValue '' num2str(ii) '' = ''] );' newline ...
    '            TTEST_DISP( val{ii} ); end; end;' newline];
end

function ret = PRED1; ret = PRED; end
function ret = PRED2; ret = PRED; end
function ret = NPRED1; ret = NPRED; end
function ret = NPRED2; ret = NPRED; end
function ret = NOT_PRED; ret = NPRED; end
function ret = NOT_PRED1; ret = NPRED; end
function ret = NOT_PRED2; ret = NPRED; end

function ret = NEAR
ret = [
    '    val = norm( varargin{1}-varargin{2} );' newline ...
    '    if( val>varargin{3} );' newline ...
    '        ret = false;' newline ...
    '        TTEST_FPRINTF( ''\n\nlhs != rhs (but they should be almost equal), where'' );' newline ...
    '        TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '        TTEST_DISP( varargin{1} );' newline ...
    '        TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '        TTEST_DISP( varargin{2} );' newline ...
    '        TTEST_FPRINTF( ''\nDifference = %.15g\n'', val );' newline ...
    '        TTEST_FPRINTF( ''Allowed difference = %.15g\n'', varargin{3} ); end;' newline];
end

function ret = DOUBLE_EQ
ret = [
    '    if( nargin==2 );' newline ...
    '        factor = 4; ' newline ...
    '    else' newline ...
    '        factor = varargin{3}; end;' newline newline...
    '    val = norm( varargin{1}-varargin{2} );' newline ...
    '    maxdiff = factor*eps(double(norm(varargin{1})));' newline ...
    '    if( val>maxdiff );' newline ...
    '        ret = false;' newline ...
    '        TTEST_FPRINTF( ''\n\nlhs != rhs (but they should be almost equal), where'');' newline ...
    '        TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '        TTEST_DISP( varargin{1} );' newline ...
    '        TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '        TTEST_DISP( varargin{2} );' newline ...
    '        TTEST_FPRINTF( ''\nDifference norm(lhs-rhs) = %.15g\n'', val );' newline ...
    '        TTEST_FPRINTF( ''Allowed difference       = %.15g'', maxdiff ); end;' newline];
end

function ret = SINGLE_EQ
ret = [
    '    if( nargin==2 );' newline ...
    '        factor = 4; ' newline ...
    '    else' newline ...
    '        factor = varargin{3}; end;' newline newline...
    '    val = norm( varargin{1}-varargin{2} );' newline ...
    '    maxdiff = factor*eps(single(norm(varargin{1})));' newline ...
    '    if( val>maxdiff );' newline ...
    '        ret = false;' newline ...
    '        TTEST_FPRINTF( ''\n\nlhs != rhs (but they should be almost equal), where'');' newline ...
    '        TTEST_FPRINTF( ''\nlhs = '' );' newline ...
    '        TTEST_DISP( varargin{1} );' newline ...
    '        TTEST_FPRINTF( ''\nrhs = '' );' newline ...
    '        TTEST_DISP( varargin{2} );' newline ...
    '        TTEST_FPRINTF( ''\nDifference norm(lhs-rhs) = %.15g\n'', val );' newline ...
    '        TTEST_FPRINTF( ''Allowed difference       = %.15g'', maxdiff ); end;' newline];
end

function ret = FLOAT_EQ; ret = SINGLE_EQ; end



function ret = NO_THROW %for EXPECT and ASSERT macro
ret = [    
    '    cmd = varargin{1};' newline ...
    '    if( nargin>1 );' newline ...
    '        id = varargin(2:end);' newline ...
    '    else;' newline ...
    '        id = {}; end;' newline ...
    '    lastwarn( ''TTEST_NOWARN'' );' newline ...
    '    if( ~isstring(cmd) && ~ischar(cmd) );' newline ...
    '        error( ''TTEST_EVAL:format'', ''\ncmd must be a string passable to evalin()'' ); end;' newline newline ...
    '    try;' newline ...
    '        evalin( ''caller'', cmd );' newline ...
    '    catch Me;' newline ...
    '        %error was thrown' newline ...
    '        switch Me.identifier' newline ...
    '            case id;' newline ...
    '                %do nothing' newline ...
    '            otherwise;' newline ...
    '                ret = false;' newline ...
    '                TTEST_FPRINTF( ''\nError was thrown, but no error was expected. \n  Error: %s\n'', Me.identifier ); end; end;' newline newline ...
    '    %check if warning was thrown' newline ...
    '    [warnMsg,warnID] = lastwarn;' newline ...
    '    switch warnID;' newline ...
    '        case id;' newline ...
    '            %do nothing' newline ...
    '        otherwise;' newline ...
    '            if( ~isequal(warnMsg,''TTEST_NOWARN'') );' newline ...
    '                ret = false;' newline ...
    '                TTEST_FPRINTF( ''\nWarning was thrown, but no warning was expected. \n  Warning: %s\n  Warning ID: %s\n'', warnMsg, warnID ); end; end;' newline];
end
   
function ret = WARNING
ret = [
    '    cmd = varargin{1};' newline ...
    '    s = warning; %save warning settings' newline ...
    '    lastwarn( ''TTEST_NOWARN'' );' newline ...
    '    for i = 2:nargin; %make warnings to error' newline ...
    '        warning( ''error'', varargin{i} ); end; %#ok<CTPCT>' newline newline ...
    '    if( nargin>1 );' newline ...
    '        id = varargin(2:end);' newline ...
    '    else;' newline ...
    '        id = {}; end;' newline ...
    '    %check input' newline ...
    '    if( ~isstring(cmd) && ~ischar(cmd) );' newline ...
    '        error( ''TTEST_EVAL:format'', ''\ncmd must be a string passable to evalin()'' ); end;' newline newline ...    
    '    try;' newline ...
    '        evalc( ''evalin( ''''caller'''', cmd );'' ); %execute command' newline ...
    '    if( nargin>1 );' newline ...
    '        TTEST_FPRINTF( ''\nExpected warning not thrown:\nExpected Warning: '' );' newline ...
    '        TTEST_DISP( varargin(2:end) );' newline ...
    '        if( ~isequal(lastwarn,''TTEST_NOWARN'') );' newline ...
    '            [a,b] = lastwarn;' newline ...
    '            TTEST_FPRINTF( ''\nThrown warning: %s:%s\n'', b,a); end;' newline ...
    '        ret = false; end;' newline ...
    '    catch me;' newline ...
    '        lastwarn( ''TTEST_ERROR'' );' newline ...
    '        if( ~isempty(id) );' newline ...
    '            switch me.identifier' newline ...
    '                case id; %check if at least one correct warning is thrown' newline ...
    '                    %do nothing' newline ...
    '                otherwise;' newline ...
    '                    ret = false;' newline ...
    '                    TTEST_FPRINTF( ''\nFalse warning thrown:\n   Expected Warning: '' );' newline ...
    '                    TTEST_DISP( varargin(2:end) );' newline ...
    '                    TTEST_FPRINTF( ''   Thrown warning: '');' newline ...
    '                    TTEST_DISP( me.identifier ); end;' newline ...
    '        else;' newline ...
    '            TTEST_FPRINTF( ''\nFalse warning/error thrown: %s:%s\n'', me.identifier, me.message );' newline ...
    '            ret = false; end; end;' newline newline ...
    '    if( isequal(lastwarn,''TTEST_NOWARN'') );' newline ...
    '        TTEST_FPRINTF( ''\n Expected warning, but no warning was thrown.\n '' );' newline ...
    '        ret = false; end;' newline newline...
    '    warning( s ); % redo warning settings' newline];
end

function ret = ERROR
ret = [
    '    cmd = varargin{1};' newline ...
    '    %check input' newline ...
    '    if( ~isstring(cmd) && ~ischar(cmd) );' newline ...
    '        error( ''TTEST_EVAL:format'', ''\ncmd must be a string passable to evalin()'' ); end;' newline ...
    '    if( nargin>1 );' newline ...
    '        id = varargin(2:end);' newline ...
    '    else;' newline ...
    '        id = {}; end;' newline ...    
    '    try;' newline ...
    '        evalin( ''caller'', cmd );' newline ...
    '        TTEST_FPRINTF( ''\nNo Error was thrown, but error was expected.\n'');' newline ...
    '        ret = false;' newline ...
    '    catch me;' newline ...
    '        if( ~isempty(id) );' newline ...
    '            id = varargin(2:end);' newline ...
    '            switch me.identifier' newline ...
    '                case id;' newline ...
    '                    % do nothing' newline ...
    '                otherwise;' newline ...
    '                    ret = false;' newline ...
    '                    TTEST_FPRINTF( ''\nFalse error thrown.\n   Thrown Error: %s\n   Expected error: %s\n'', me.identifier, cmd ); end; end; end;' newline];
end



function dummy; end %Generates an error, if the 'end' of a function is missing. 