%CVTEST  Test Specification
%
%   CLASS_ID = CVTEST( ROOT ) Create a test specification for the Simulink
%   model containing ROOT.  ROOT can be the name of the Simulink model or the 
%   handle to a Simulink model.  ROOT can also be a name or handle to a 
%   subsystem within the model, in which case only this subsystem and its 
%   descendents are instrumented for analysis.
%
%   CLASS_ID = CVTEST( ROOT, LABEL) creates a test with the given label. The
%   label is used when reporting results. 
%
%   CLASS_ID = CVTEST( ROOT, LABEL, SETUPCMD) creates a test with a setup
%   command that is executed in the base MATLAB workspace just prior to running
%   the instrumented simulation.  The setup command is useful for loading data
%   just prior to a test.
%
%   Example:
%   open_system('slvnvdemo_ratelim_harness');
%   testObj = cvtest(['slvnvdemo_ratelim_harness', '/Adjustable Rate Limiter']);
%   testObj.label = 'Gain within slew limits';
%   testObj.setupCmd = 'load(''slvnvdemo_ratelim_harness_data.mat'');';
%   testObj.settings.decision = 1;
%   testObj.settings.overflowsaturation = 1;
%   cvdo = cvsim(testObj);
%
%   See also CVSIM, CVSAVE, CVLOAD.

%   Copyright 1990-2016 The MathWorks, Inc.

classdef cvtest < SlCov.CovEngineProxy
    
    properties (GetAccess=public, SetAccess=private, Dependent=true)
        modelcov
        rootPath
    end
    
    properties (Dependent=true)
        label
        setupCmd
        settings
        modelRefSettings
        emlSettings
        options
        filter
    end
    
    properties (Hidden=true, Dependent=true)
        sfcnSettings
    end
           
    methods
        %% Public method: cvtest ------------------------------------------
        %  Abstract:
        %    constructor
        function this = cvtest(varargin)
            
            % Don't accept no input argument as cvtest does for displaying
            % the help associated with this class
            narginchk(1, 3);
            
                                    
            % Check for renamed models and update data dictionartestdata.covBoundaryAbsToly
            cvprivate('model_name_refresh');
            
            % convert Strings in input to char arrays
            [varargin{:}] = convertStringsToChars(varargin{:});
            
            modelId = [];
            switch nargin
                case 1 % could be create, or id conversion
                    switch class(varargin{1})
                        case 'double'
                            % create a new test object for the
                            if ishandle(varargin{1})
                                [modelId, path ] = resolve_model_and_path(varargin{1});
                                create_new_test(this, modelId, path );
                            elseif cv('ishandle',varargin{1})
                                this.id = varargin{1};
                            else
                                error(message('Slvnv:simcoverage:cvtest:BadInput'));
                            end
                            
                        case 'char' % create a new test object for the
                            [modelId, path ] = resolve_model_and_path(varargin{1});
                            create_new_test(this, modelId, path );
                                                        
                        otherwise
                            if isa(varargin{1}, class(this))
                                this = varargin{1};
                                return
                            end
                    end
                    
                case 2
                    [modelId, path ] = resolve_model_and_path(varargin{1});
                    create_new_test(this, modelId, path );
                    install_test_label(this.id,varargin{2})
                case 3
                    [modelId, path ] = resolve_model_and_path(varargin{1});
                    create_new_test(this, modelId, path );
                    install_test_label(this.id,varargin{2})
                    install_setup_cmd(this.id,varargin{3})
                otherwise
                    [modelId, path ] = resolve_model_and_path(varargin{1});
                    create_new_test(this, modelId, path );
                    install_test_label(this.id,varargin{2})
                    install_setup_cmd(this.id,varargin{3})
            end
            
            if ~isempty(modelId)
                modelName = SlCov.CoverageAPI.getModelcovName(modelId);
               [status, msgId] = cvi.TopModelCov.checkLicense(get_param(modelName, 'handle'));
               if status == 0
                    error(message(msgId));
               end
               copyMetricsFromModel(this, modelName);
            end
            
        end
        
        function res = valid(testObj)
            res = true;
            id = testObj.id;
    
            if ~cv('ishandle', id) || isempty(cv('get', id, '.isa')) || ...
                    cv('get', id, '.isa') ~= cv('get', 'default', 'testdata.isa')

                res = false;
            end
        end
                        
        %% Public method defined in separate file -------------------------
        %
        display(testObj) 
        
        %% Public setter/getter for the dependent properties --------------
        %
        function value = get.settings(this)
            metricNames = cvi.MetricRegistry.getAllSettingsMetricNames();
            value = [];
            for idx = 1:numel(metricNames)
                metric = metricNames{idx};
                value.(metric) = getSettingsMetricValue(this, metric);
            end
        end
        
        function set.settings(this, value)
    	    metNames = fieldnames(value);
            for idx = 1:numel(metNames)
                metricName = metNames{idx};
                setMetric(this, metricName, value.(metricName));
            end
        end
        
        function value = get.modelRefSettings(this)
            value.enable = cv('get', this.id, 'testdata.mldref_enable');
            value.excludeTopModel = cv('get', this.id, 'testdata.mldref_excludeTopModel');
            value.excludedModels = cv('get', this.id, 'testdata.mldref_excludedModels');
        end
        
        function set.modelRefSettings(this, value)
            enableStr = convertStringsToChars(value.enable);
            if ~ischar(enableStr)
                enableStr = 'on';
            end
            cv('set', this.id, 'testdata.mldref_enable',  enableStr);
            cv('set', this.id, 'testdata.mldref_excludeTopModel', value.excludeTopModel);
            cv('set', this.id, 'testdata.mldref_excludedModels', convertStringsToChars(value.excludedModels));
        end
        
        function value = get.options(this)
            value.forceBlockReduction = cv('get', this.id, 'testdata.forceBlockReductionOff');
            
            cvs = this.getSlcovSettings;
            value.useTimeInterval = cvs.useTimeInterval;
            value.intervalStartTime =  cvs.intervalStartTime;
            value.intervalStopTime = cvs.intervalStopTime;
            value.covBoundaryRelTol = cvs.covBoundaryRelTol;
            value.covBoundaryAbsTol = cvs.covBoundaryAbsTol;
            
            if SlCov.isMaskingMcdcCovFeatureOn
                value.mcdcMode = SlCov.McdcMode(cv('get', this.id, 'testdata.mcdcMode'));
            end
        end
        
        function set.options(this, value)
            
            cv('set', this.id, 'testdata.forceBlockReductionOff', value.forceBlockReduction);
            
            cvs = this.getSlcovSettings;
            cvs.covBoundaryRelTol = value.covBoundaryRelTol;
            cvs.covBoundaryAbsTol = value.covBoundaryAbsTol;
            cvs.useTimeInterval = value.useTimeInterval;
            cvs.intervalStartTime = value.intervalStartTime;
            cvs.intervalStopTime =  value.intervalStopTime;
            
            if SlCov.isMaskingMcdcCovFeatureOn
                cv('set', this.id, 'testdata.mcdcMode', SlCov.McdcMode(value.mcdcMode));
            end
        end
        
        function value = get.modelcov(this)
            value = cv('get', this.id, 'testdata.modelcov');
        end
        
        function value = get.rootPath(this)
            value = cv('GetTestRootPath', this.id);
        end  
        
        function value = get.label(this)
            value = cv('get', this.id, 'testdata.label');
        end
        
        function set.label(this, value)
            cv('set', this.id, 'testdata.label', convertStringsToChars(value));
        end        
        
        function value = get.setupCmd(this)
            value = cv('get', this.id, 'testdata.mlSetupCmd');
        end
        
        function set.setupCmd(this, value)
            cv('set', this.id, 'testdata.mlSetupCmd', convertStringsToChars(value));
        end         
        
        function value = get.emlSettings(this)
            value.enableExternal = cv('get', this.id, 'testdata.covExternalEMLEnable');
        end
        
        function set.emlSettings(this, value)
            cv('set', this.id, 'testdata.covExternalEMLEnable', value.enableExternal);
        end 
        
        function value = get.filter(this)
            value.fileName = cv('get', this.id, 'testdata.covFilter');
        end
        
        function set.filter(this, value)
            % NOTE(eroy): for backward compatibility!
            if isfield(value, 'filename')
                val = value.filename;
            elseif isfield(value, 'fileName')
               val = value.fileName;
            else
                val = '';
            end
            cv('set', this.id, 'testdata.covFilter', convertStringsToChars(val));
        end         
        
        function value = get.sfcnSettings(this)
            value.enableSfcn = cv('get', this.id, 'testdata.covSFcnEnable');
        end
        
        function set.sfcnSettings(this, value)
            cv('set', this.id, 'testdata.covSFcnEnable', value.enableSfcn);
        end         
    end
    methods (Static, Hidden)    
        function testId = create(modelId)
            testId = cv('new', 'testdata', ...
                    '.type', 'CMDLINE_TST', ...
                    '.modelcov', modelId, ...
                    '.dbVersion', SlCov.CoverageAPI.getDbVersion());
            cvtest.setMf0(testId);
        end
        
        function testId = setMf0(testId)
            %TBDD get the mf.zero.model from modelcov?
            model = mf.zero.Model;
            cv('set',testId, 'testdata.mf0.model', model); % need to keep the mf0 model instance
            cv('set',testId, 'testdata.mf0.settings', slcov.settings(model));
        end
        function testId = deleteMf0(testId)
            settings = cv('get',testId, 'testdata.mf0.settings');
            if ~isempty(settings)
                settings.destroy;
            end

            model = cv('get',testId, 'testdata.mf0.model');
            if ~isempty(model)
                model.destroy;
            end
        end

    end
    methods (Hidden)
        %% Public methods defined in separate file ------------------------
        %
        activate(testObj, modelcovId)
        outTestObj = clone(testObj, varargin)
        testObj = copyMetricsFromModel(testObj, modelName)
        copySettings(testObj, fromTestObj)
        [enabled, enabledTO] = getEnabledMetricNames(testObj)
        value = getMetricValue(cvtest, metricName)
        value = getTOMetricValue(cvtest, metricName)
        cvtest= setAllMetric(cvtest, value)
        cvtest= setMetric(cvtest, metricName, value)
        setMetricDataOn(cvtest, metricNames)         
        value = getSettingsMetricValue(cvtest, metricName)
        
        function settings = getSlcovSettings(testObj)
            settings  = cv('get', testObj.id, 'testdata.mf0.settings');
        end
        
        function value = getCutPath(this)
            value = cv('get', this.id, 'testdata.cutPath');
        end
        
        function setCutPath(this, value)
            cv('set', this.id, 'testdata.cutPath', value);
        end         

    end
    
end
%% ------------------------------------------------------------------------
% RESOLVE_MODEL_AND_PATH - Find the Simulink model and
% its associated coverage object and resolve the path
% to the coverage root.
%=================
function path = getPath(model, block)
        path = getfullname(block);
        bdName = get_param(model,'Name');
		bdlength = length(bdName);
		path = path((bdlength+2):end);	
end
%=================
function [modelId, path ] = resolve_model_and_path(block)
    
    try
        if ischar(block)
            if exist(block, 'file') == 4
                [~, block, ~] = fileparts(block);
            end

            block = get_param(block,'Handle');
        end
        model = bdroot(block);
    catch Mex
        error(message('Slvnv:simcoverage:cvtest:OpenModel'));
    end

	path = '';
    if (block ~= model)
        if ~isa(get_param(block, 'object'), 'Simulink.SubSystem')
            error(message('Slvnv:simcoverage:cvtest:BadInputNotSubsystem', getfullname(block)));
        end
        path = getPath(model, block);
    end
    
    modelId = get_param(model,'CoverageId');
	if ~cv('ishandle', modelId) || ...
       ~strcmpi(getfullname(model), SlCov.CoverageAPI.getModelcovName(modelId)) %harness model might store incorrect id
		[~, modelId] = cvi.TopModelCov.setup(model);
	end		

end

%% ------------------------------------------------------------------------
% CREATE_NEW_TEST - Find the Simulink model and
% its associated coverage object and resolve the path
% to the coverage root.
function testId = create_new_test(testObj, modelId,path, ~)

	% Create the testdata object
   testId = cvtest.create(modelId);
   
   cv('SetTestRootPath', testId, path);
   testObj.id = testId;
   
   

	% Add this test to the link-list of pending tests
	cv('PendingTestAdd',modelId,testId);
    

end

%% ------------------------------------------------------------------------
% INSTALL_TEST_NAME - Set the setupcmd string.
function install_test_label(testId,label)

	if ~ischar(label)
		error(message('Slvnv:simcoverage:cvtest:InvalidArgumentForTestName'))
	end

	cv('set',testId,'.label',label);

end

%% ------------------------------------------------------------------------
% INSTALL_SETUP_CMD - Set the setupcmd string.
function install_setup_cmd(testId,cmd)

	if ~ischar(cmd)
		error(message('Slvnv:simcoverage:cvtest:InvalidArgumentForSetupCommandString'))
	end

	cv('set',testId,'.mlSetupCmd',cmd);

end



