%

% Copyright 2012-2014 The MathWorks, Inc.

classdef SymmetricTest < coder.rtiostreamtest.TestRunner
%SYMMETRICTEST runs enclosed tests and plots results onto a bar chart
% Conducts the list of tests that is defined in GETDEFAULTS(), each
% result is being plotted onto a bar graph after the test is finished
% running

properties (Constant, GetAccess = 'private')
param_skip = 'skip';

% If no parameters are specified by the user, those will be the
% default values that would be passed to the tests below
default_parameters = {'default_chunks', [1 4 128]}
end

properties (GetAccess = 'private', SetAccess = 'private')
myId = -1;
parameters = coder.rtiostreamtest.SymmetricTest.default_parameters;
end

methods (Static)
function [tests, myId] = getDefaults()
% GETDEFAULTS returns lists of tests to be run
% tests - List of tests to be run. Usually list of
% SymmetricConductor
% myId - List of the ids of "my" results that correspond to
% tests that would be marked in purple on the bar
% chart.

import coder.rtiostreamtest.*;

tests = { ...
...
SymmetricConductor( TestRunnerAverage( { ...
AverageTest( BandwidthOutgoingTest() ), ...
AverageTest( BandwidthIncomingTest() ), ...
}, Test.message('PlotSymmCurrent')) ), ...
...
Constant(950, Test.message('PlotSymm9600baud')), ...
Constant(11000, Test.message('PlotSymm115200baud')), ...
...
LocalTest( SymmetricConductor( TestRunnerAverage( { ...
AverageTest( BandwidthOutgoingTest() ), ...
AverageTest( BandwidthIncomingTest() ), ...
}, Test.message('PlotSymmLocalTCP')) ), 'tcp' ), ...
...
};

myId = 1;
end
end

methods

function this = SymmetricTest(parameters)
[tests, myid] = coder.rtiostreamtest.SymmetricTest.getDefaults();
this = this@coder.rtiostreamtest.TestRunner(tests, coder.rtiostreamtest.Test.message('TitleSymmetricTest'));
this.skip = coder.rtiostreamtest.Test.getSetting(parameters, coder.rtiostreamtest.SymmetricTest.param_skip, this.skip);
this.myId = myid;
if nargin == 1
this.parameters = coder.rtiostreamtest.Test.setSettings(this.parameters, parameters);
end
end

function runTest(this, connection, parameters)

import coder.rtiostreamtest.*;

% Merge with the already existing parameters
new_parameters = Test.setSettings(this.parameters, parameters);

% Get verbose state
verbose = Test.getSetting(parameters, SymmetricTest.param_verbose, SymmetricTest.default_verbose);

% Parse the parameters
raw_chunks = Test.getSetting(new_parameters, SymmetricConductor.param_defaultChunks);

% Eliminate tests which won't fit into the device's buffer
chunks = raw_chunks(raw_chunks <= connection.targetBufferSize);

% Print verbose output
Test.lnverboseln(verbose, 'VerboseSymmInfo', ...
AsymmetricConductor.previewArray(chunks), numel(chunks));

% Save the new chunks array back to the settings, overriding
% the original one
new_parameters = Test.setSetting(new_parameters, SymmetricConductor.param_defaultChunks, chunks);

% Issue a warning if some of the tests will need to be skipped
% because of limited buffer size on the device
if numel(chunks) ~= numel(raw_chunks) && verbose
Test.warning_mild('WarningMildSymmSubtestsSkipped', ...
numel(raw_chunks) - numel(chunks), connection.targetBufferSize, ...
max(raw_chunks), max(raw_chunks));
end

% Array of rows that look like this
% format of each row: {'label', value, is_it_my_result}
% This function evaluates the tests one by one and formats the
% result in this manner
data = this.generateOptions(connection, new_parameters);

% Sort the formatted array in descending order
[labels, currentbars, defaultbars] = this.sortData(data);

% Create plot.
fig1 = figure('name', Test.message('PlotSymmTitle'), ...
'NumberTitle','off', 'Visible', 'off');
hax2 = axes('parent', fig1);
barh(hax2, defaultbars,'y'); % yellow
hold(hax2,'on');
barh(hax2, currentbars, 'm'); % magenta
% use log scale
set(hax2, 'XScale', 'log');

title(hax2,Test.message('PlotSymmTitle'));
xlabel(Test.message('PlotSymmXAxis'));

set(hax2,'yticklabel',labels, 'fontsize',9,...
'ytick',1:numel(labels));
set(fig1, 'Visible', 'on');
end

function data = generateOptions(this, connection, parameters)
%GENERATEOPTIONS runs the tests and formats the outputs
% Each row contains the name of the test, the value of its
% output and a boolean whether the test needs to be colored
% in purple

% Reset progress bar
this.resetProgress();
this.progress(0);

% Allocate array
data = cell(1, numel(this.tests));

% Go through each test
for i = 1 : numel(data)

% Shorthand
profiler = this.tests{i};

% Generate a row
data{i} = {profiler.toString(), ...
profiler.runTest(connection, parameters), ...
numel(this.myId(this.myId == i)) ~= 0};

% Update progress display
this.progress(i / numel(data));
end

% Mark 100%
this.progress('done');
end
end

methods (Static)

function [labels, currentbars, defaultbars] = sortData(data)
% SORTDATA sorts the output of GENERATEOUTPUT descending
% Sorts the lines of a multi-line cell array according to
% values which are contained in the lines.
% Data is in format { ... {LABEL, VALUE, IS_IT_ME} ... }
%
% It will transform a DATA like this:
% { {'a', 4, false}, {'b', 5, true}, {'c', 1, false} }
% Into:
% { {'b', 5, true}, {'a', 4, false}, {'c', 1, false} }
%
% LABELS - contains the labels of the bars in the new
% sorted order
% CURRENTBARS - contains the purple values, all of the rest
% are 0. It has the same dimensions as LABELS
% DEFAULTBARS - contains the yellow values, all of the rest
% are 0. It has the same dimensions as LABELS

% Shorthand for the number of lines in the array
size = numel(data);

% Preallocate arrays
pureData = zeros(1, size); % Contains only the values
labels = cell(1, size); % Contains only the labels
currentbars = zeros(1, size); % Purple values
defaultbars = zeros(1, size); % Yellow values

% Extract the values
for i = 1 : size
pureData(i) = data{i}{2};
end

% Sort them
[~, IDX] = sort(pureData);

% Arrange the labels
for i = 1 : size
idx = IDX(i);

% Choose the color of the value according to whether it is
% representing a current test or a hard coded one
if data{idx}{3}
currentbars(i) = data{idx}{2};
else
defaultbars(i) = data{idx}{2};
end

% Put the right label
labels{i} = data{idx}{1};
end
end

end

end