classdef meqhelp_test < meq_test
  % Tests for meqhelp and other various help-related checks
  %
  % [+MEQ MatlabEQuilibrium Toolbox+] Swiss Plasma Center EPFL Lausanne 2022. All rights reserved.
  
  properties(TestParameter)
    codename = {'fbt','fgs','liu'};
    helptype = {'parameters','outputs','inputs','functions'};
    tok = {'ana','tcv'};
  end
  
  properties
    t = 1;
  end
  
  methods(Test,TestTags={'Slow'})
    function test_meqhelp(testCase,codename)
      fname = meqhelp(codename,'L usage',true);
      testCase.assertTrue(logical(exist(fname,'file')));
      fname = meqhelp(codename,'G usage',true);
      testCase.assertTrue(logical(exist(fname,'file')));
      fname = meqhelp(codename,'P usage',true);
      testCase.assertTrue(logical(exist(fname,'file')));
    end
  end
  
  methods(Test,TestTags={'Unit'})
    function test_help_presence(testCase)
      % Check that all .m files have a help header
      IgnoredPaths = {'csfc','obj','slprj','.git','tutorials','genlib'};
      files = dir(fullfile(testCase.meqpath,'**/*.m'));
      files = files(~contains({files.folder},IgnoredPaths));
      % Make sure current directory is always restored
      oldpwd = pwd;
      cleanupObj = onCleanup(@() cd(oldpwd));
      passed = true; % init
      for ii=1:numel(files)
        myfile = files(ii);
        filepath = fullfile(myfile.folder,myfile.name);
        [~,fname,~] = fileparts(myfile.name);
        cd(myfile.folder);
        helptext = help(filepath);
        if isempty(helptext) || ...
            contains(helptext,{sprintf('%s is a class',fname),sprintf('%s is a script',fname),sprintf('%s is a function',fname)})
          fprintf('%s does not seem to have a help header\n',filepath);
          passed = false;
        end
      end
      testCase.verifyTrue(passed,'At least one file does not have a help header');
    end
    
    function test_help_displays(testCase,codename,helptype)
      % test display functions for help of various code/helptype
      % combinations
      meqhelp(codename,helptype);
      testCase.assumeTrue(true);
    end
    
    function test_help_displays_mex(testCase)
      % test display functions for mex functions
      meqhelp('liu','mex');
      testCase.assumeTrue(true);
    end
    
    function test_parameters_ana(testCase,codename)
      % test that code parameters are documented
      testCase.test_parameters(codename,'ana');
    end
    function test_outputs_ana(testCase,codename)
      % test that outputs are documented
      testCase.test_outputs(codename,'ana');
    end
  end
  
  methods(Test,TestTags={'TCV'})
    function test_parameters_tcv(testCase,codename)
      testCase.test_parameters(codename,'tcv');
    end
    function test_outputs_tcv(testCase,codename)
      testCase.test_outputs(codename,'tcv');
    end
  end
  
  methods
    function test_parameters(testCase,codename,tok)
      shot = myshot(tok);
      % Check that all parameters are documented
      code=str2func(codename);
      L=code(tok,shot,testCase.t);
      
      meqpfile='meqp.m';
      codep=sprintf('%sp.m',codename);
      meqptok=sprintf('meqp%s.m',tok);
      codeptok = sprintf('%sp%s.m',codename,tok);
      helptxt = strcat(help(codeptok),help(codep),help(meqptok),help(meqpfile));
      
      for ifield=fieldnames(L.P)'
        helpstr = sprintf(' .%s ',ifield{:});
        nhelp = numel(strfind(helptxt,helpstr)); % code help (e.g. liup)
        if nhelp==0
          msg = sprintf('Parameter %s is not documented for %s, tok:%s\n',ifield{:},codename,tok);
          fprintf(msg);
        elseif nhelp>1
          msg = sprintf('Parameter %s is documented more than once between %s, %s, %s and %s, remove duplication\n',...
            ifield{:},codeptok,codep,meqptok,meqpfile);
          fprintf(msg);
        end
      end
      
      % help without field
      for ihelpfile={codeptok,codep,meqptok,meqpfile}
        helpstr=regexp(help(ihelpfile{:}),'%\s\.([\w]+?\s)','match');  % match ".parameter"
        for ihelp=helpstr
          helppar=deblank(ihelp{:}(2:end));
          if ~isfield(L.P,helppar)
            msg=sprintf('Parameter %s listed in %s.m has no corresponding field in L.P for %s, tok:%s\n',helppar,ihelpfile{:},codename,tok);
            fprintf(msg);
          end
        end
      end
    end
    
    function test_outputs(testCase,codename,tok)
      shot = myshot(tok);
      % Check that all outputs are documented 
      codet = sprintf('%st',codename);
      helptxt = strcat(help('meqt'),help(codet));
      code=str2func(codename);
      
      [~,~,LY]=code(tok,shot,testCase.t);
      
      % Check fields that don't have help
      for ifield=fieldnames(LY)'
        helpstr = sprintf(' .%s ',ifield{:});
        nhelp = numel(strfind(helptxt,helpstr)); % code help (e.g. liut)
        if nhelp==0
          msg = sprintf('Output %s is not documented for %s, tok:%s\n',ifield{:},codename,tok);
          testCase.verifyFail(msg);
        elseif nhelp>1
          msg = sprintf('Output %s is documented more than once between %s and %s, remove duplication\n',ifield{:},codet,'meqt');
          testCase.verifyFail(msg);
        end
      end
      
      % check help without field
      for ihelpfile={'meqt',codet}
        helpstr=regexp(help(ihelpfile{:}),'%\s\.([\w]+?\s)','match');  % match ".output"
        for ihelp=helpstr
          helppar=deblank(ihelp{:}(3:end));
          if ~isfield(LY,helppar)
            msg = sprintf('Output %s listed in %s has no corresponding field in LY for %s, tok:%s\n',helppar,ihelpfile{:},codename,tok);
            fprintf(msg)
          end
        end
      end
    end
  end
end

function shot=myshot(tok)
switch tok
  case 'tcv', shot = 61400;
  case 'ana', shot = 1;
end
end