%% Filter Coverage Results Using a Script
% This example shows how to programmatically filter objects and outcomes
% from coverage results.

% Copyright 2020 The MathWorks, Inc.

%% Open the Model and Enable Coverage Analysis
% First, load the model into memory.
%

modelName = 'slvnvdemo_covfilt';
load_system(modelName);

%%
% Configure the coverage settings for the model by using a
% <docid:simulink_ref#bvn9b0e |Simulink.SimulationInput|> object.
%

simIn = Simulink.SimulationInput(modelName);
simIn = simIn.setModelParameter('CovEnable','on');
simIn = simIn.setModelParameter('CovMetricStructuralLevel','MCDC');
simIn = simIn.setModelParameter('StopTime','20');
simIn = simIn.setModelParameter('CovSaveSingleToWorkspaceVar','on');
simIn = simIn.setModelParameter('CovSaveName','covData');

%%
% For a list of coverage parameters, see <docid:slcoverage_ref#buwlv0o-1 Coverage
% Settings>.

%%
% Simulate the model using the |SimulationInput| object as the input.
%

simOut = sim(simIn);

%% View Decision Coverage Results
% View the coverage results before applying a filter. You can access the
% decision coverage results using |decisioninfo|.
%

saturationInitial = decisioninfo(covData,'slvnvdemo_covfilt/Saturation');
percentSaturationCov = 100 * saturationInitial(1)/saturationInitial(2)

%%
% The Saturation block has 50% decision coverage. If you do not intend for
% this block to be satisfied, you can filter a missing objective outcome so
% that it is no longer reported as missing coverage. First, you need a
% selector for the unsatisfied objective outcome that you want to filter.
%

%% Create a Selector
% You can directly create a selector using the appropriate constructor.
% In this case, you would use |slcoverage.MetricSelector|.
%

%%
% Because the objective being justified is a decision outcome, the first
% input to the metric selector constructor is
% |slcoverage.MetricSelectorType.DecisionOutcome|. The second input is the
% block handle. The last two are the index of the objective to justify and
% the index of the outcome of that objective, respectively. Because the
% |input > lower limit| decision objective is the first objective for the
% Saturation block, its objective index is |1|. Because the |false| outcome
% of this objective is the first outcome, its outcome index is also |1|.
%

metricSel = slcoverage.MetricSelector(slcoverage.MetricSelectorType.DecisionOutcome,...
'slvnvdemo_covfilt/Saturation',1,1)

%%
% You can also use |slcoverage.Selector.allSelectors| to see the available
% selectors for the Saturation block.
%

saturationAllSels = slcoverage.Selector.allSelectors('slvnvdemo_covfilt/Saturation')

%%
% You can also see the objective and outcome indices by using the
% |allSelectors| method. Use the |Description| name-value pair to search
% for |F|.
%

falseSelectors = slcoverage.Selector.allSelectors('slvnvdemo_covfilt/Saturation',...
'Description','F')

%%
% There are two false case selectors in the Saturation block. The first
% selector is |F outcome of input > lower limit|.

falseSel = falseSelectors(1)

%%
% The |falseSel| selector is the same one we constructed manually using
% |slcoverage.MetricSelector|. The objective and outcome indices are
% properties of the resulting selector object.
%

%% Create a Justification Rule
% Create a filter object by using |slcoverage.Filter|. You can set the
% filter file name and filter description by using the methods
% |setFilterName| and |setFilterDescription|, respectively.
%

filt = slcoverage.Filter;
setFilterName(filt,'slcoverage_filter');
setFilterDescription(filt,'Example Filter');

%%
% Create a filter rule by using |slcoverage.FilterRule|. The first input to
% |FilterRule| is the selector for the block or outcome you want to filter.
% This can be a selector you create, or one you retrieve from
% |allSelectors|.
%

%%
% The second input is the rationale for filtering the outcome or block.
% This is specified as a character array.
%

%%
% The third input is the filter mode you want to use. The two coverage
% filter modes are justify and exclude. Use justify mode to filter
% individual coverage objective outcomes such as |F outcome of input >
% lower limit|. Use exclude mode to filter entire model elements or
% blocks, which means that the block and its descendants, if applicable, are
% ignored. In this example, use justify mode to specify that you want to
% filter a specific outcome.
%

rule = slcoverage.FilterRule(metricSel,'rate > 0',slcoverage.FilterMode.Justify);

%%
% Add the rule to the filter using |addRule|.
%

filt.addRule(rule);

%%
% Save the filter to a filter file using the |save| method. Then apply the
% filter file to the |cvdata| object by assigning the |filter| property to
% the new filter file.
%

filt.save('covfilter');
covData.filter = 'covfilter';

%%
% Re-generate the coverage results for the Saturation block using the
% filtered |cvdata| object.
%

filteredSaturation = decisioninfo(covData,'slvnvdemo_covfilt/Saturation');
percentSaturationFilt = 100 * filteredSaturation(1)/filteredSaturation(2)

%%
% Decision coverage for the Saturation block is now 75%.
%

%% Justify an MCDC Objective in a Stateflow&reg; Chart
% You can apply the same workflow to justify a specific Stateflow
% action. In this example, we want to justify the |tick| MCDC objective
% that is part of the |after(4, tick)| transition.
%

%%
% First, get the Stateflow root object by using
% <docid:stateflow_ref#bqotzo5-1 |sfroot|>.
%

chartID = sfroot;

%%
% Get the |'after(4, tick)'| transition ID by using the
% <docid:stateflow_api#bqxys2n-1 |find|> method. You can use |find| to
% search for transitions by using the |'-isa'| flag with
% |'Stateflow.Transition'|. You can further specify the exact transition by
% using searching for the label string using additional inputs.
%

transID = chartID.find('-isa','Stateflow.Transition','LabelString','after(4, tick)');

%%
% Get the Simulink ID of the chart by using |Simulink.ID.getSID|.
%

transSID = Simulink.ID.getSID(transID);

%%
% Get the selector for the MCDC objective outcome that we want to filter by
% using |allSelectors|. Pass the Simulink ID of the
% Stateflow transition as the first input. Because we want to justify a
% |tick| outcome, search for |"tick"| in the description.
%

sfSelectors = slcoverage.Selector.allSelectors(transSID,'Description','"tick"')

%%
% |allSelectors| returns three possible selectors. The transition we want
% to filter is the third selector returned.

sfSel = sfSelectors(3)

%%
% Create the rule, add it to the filter, and save it. The filter file is
% already applied to the |cvdata| object.
%

rule2 = slcoverage.FilterRule(sfSel,'tick never false');
filt.addRule(rule2);
filt.save('covfilter');

%%
% For more information about the stateflow programmatic API, see
% <docid:stateflow_api#f13-9347 Using the Stateflow API>.
%

%% Exclude a Block Using Block Selector
% You can filter a block using
% <docid:slcoverage_ref#mw_6c069919-210c-4067-b9f4-d5109046a06b
% |slcoverage.BlockSelector|>. In this case, we want to exclude the
% Switchable config subsystem, so we use the |SubsystemAllContent| selector
% type and the |slcoverage.FilterMode.Exclude| filter mode.
%

subsysSel = slcoverage.BlockSelector(...
slcoverage.BlockSelectorType.SubsystemAllContent,...
'slvnvdemo_covfilt/Switchable config');

%%
% Create the filter rule by passing the selector, rationale, and the
% exclude filter mode as inputs.
%

rule3 = slcoverage.FilterRule(subsysSel,...
'Unused configuration',...
slcoverage.FilterMode.Exclude);

%%
% Add the rule to the filter and save it.
%

filt.addRule(rule3);
filt.save('covfilter');

%%
% Finally, you can view the coverage report by using |cvhtml|. The *Objects
% Filtered from Coverage Analysis* section shows a summary of the filtered
% model objects and the rationales. The |'sRT=0'| flag can be used to
% generate the coverage report but not open the report automatically.
%

cvhtml('filteredCovReport',covData,'-sRT=0');

%%
%
% <<../cov_filt.png>>
%